Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

433 lines
17 KiB

  1. /*==========================================================================;
  2. *
  3. * Copyright (C) 1997 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: helxfrm.c
  6. * Content: Direct3D front-end transform and process vertices
  7. *
  8. ***************************************************************************/
  9. #include "pch.cpp"
  10. #pragma hdrstop
  11. #include "fe.h"
  12. void MatrixProduct2(D3DMATRIXI *result, D3DMATRIXI *a, D3DMATRIXI *b);
  13. //---------------------------------------------------------------------
  14. void CheckWorldViewMatrix(LPD3DFE_PROCESSVERTICES pv)
  15. {
  16. D3DMATRIXI *m = &pv->mWV[0];
  17. D3DMATRIXI res;
  18. res._11 = m->_11*m->_11 + m->_12*m->_12 + m->_13*m->_13;
  19. res._12 = m->_11*m->_21 + m->_12*m->_22 + m->_13*m->_23;
  20. res._13 = m->_11*m->_31 + m->_12*m->_32 + m->_13*m->_33;
  21. res._21 = m->_21*m->_11 + m->_22*m->_12 + m->_23*m->_13;
  22. res._22 = m->_21*m->_21 + m->_22*m->_22 + m->_23*m->_23;
  23. res._23 = m->_21*m->_31 + m->_22*m->_32 + m->_23*m->_33;
  24. res._31 = m->_31*m->_11 + m->_32*m->_12 + m->_33*m->_13;
  25. res._32 = m->_31*m->_21 + m->_32*m->_22 + m->_33*m->_23;
  26. res._33 = m->_31*m->_31 + m->_32*m->_32 + m->_33*m->_33;
  27. const D3DVALUE eps = 0.0001f;
  28. if (m->_14 == 0.0f &&
  29. m->_24 == 0.0f &&
  30. m->_34 == 0.0f &&
  31. m->_44 == 1.0f &&
  32. ABSF(res._12) < eps &&
  33. ABSF(res._13) < eps &&
  34. ABSF(res._21) < eps &&
  35. ABSF(res._23) < eps &&
  36. ABSF(res._31) < eps &&
  37. ABSF(res._32) < eps &&
  38. ABSF(1.0f - res._11) < eps &&
  39. ABSF(1.0f - res._22) < eps &&
  40. ABSF(1.0f - res._33) < eps)
  41. {
  42. pv->dwDeviceFlags |= D3DDEV_MODELSPACELIGHTING;
  43. }
  44. else
  45. {
  46. pv->dwDeviceFlags &= ~D3DDEV_MODELSPACELIGHTING;
  47. }
  48. }
  49. //---------------------------------------------------------------------
  50. void setIdentity(D3DMATRIXI * m)
  51. {
  52. m->_11 = D3DVAL(1.0); m->_12 = D3DVAL(0.0); m->_13 = D3DVAL(0.0); m->_14 = D3DVAL(0.0);
  53. m->_21 = D3DVAL(0.0); m->_22 = D3DVAL(1.0); m->_23 = D3DVAL(0.0); m->_24 = D3DVAL(0.0);
  54. m->_31 = D3DVAL(0.0); m->_32 = D3DVAL(0.0); m->_33 = D3DVAL(1.0); m->_34 = D3DVAL(0.0);
  55. m->_41 = D3DVAL(0.0); m->_42 = D3DVAL(0.0); m->_43 = D3DVAL(0.0); m->_44 = D3DVAL(1.0);
  56. }
  57. //---------------------------------------------------------------------
  58. /*
  59. * Combine all matrices.
  60. */
  61. const DWORD __VPC_DIRTY = D3DFE_VIEWMATRIX_DIRTY |
  62. D3DFE_PROJMATRIX_DIRTY;
  63. void updateTransform(LPD3DHAL lpDevI)
  64. {
  65. D3DFE_PROCESSVERTICES* pv = lpDevI->m_pv;
  66. D3DFE_TRANSFORM& TRANSFORM = lpDevI->transform;
  67. D3DFE_VIEWPORTCACHE& VPORT = pv->vcache;
  68. if (lpDevI->dwFEFlags & D3DFE_PROJMATRIX_DIRTY)
  69. {
  70. // We modify the projection matrix to make the clipping rules to be
  71. // 0 < x,y,z < w
  72. TRANSFORM.mPC._11 = (TRANSFORM.proj._11 + TRANSFORM.proj._14) * D3DVAL(0.5);
  73. TRANSFORM.mPC._12 = (TRANSFORM.proj._12 + TRANSFORM.proj._14) * D3DVAL(0.5);
  74. TRANSFORM.mPC._13 = TRANSFORM.proj._13;
  75. TRANSFORM.mPC._14 = TRANSFORM.proj._14;
  76. TRANSFORM.mPC._21 = (TRANSFORM.proj._21 + TRANSFORM.proj._24) * D3DVAL(0.5);
  77. TRANSFORM.mPC._22 = (TRANSFORM.proj._22 + TRANSFORM.proj._24) * D3DVAL(0.5);
  78. TRANSFORM.mPC._23 = TRANSFORM.proj._23;
  79. TRANSFORM.mPC._24 = TRANSFORM.proj._24;
  80. TRANSFORM.mPC._31 = (TRANSFORM.proj._31 + TRANSFORM.proj._34) * D3DVAL(0.5);
  81. TRANSFORM.mPC._32 = (TRANSFORM.proj._32 + TRANSFORM.proj._34) * D3DVAL(0.5);
  82. TRANSFORM.mPC._33 = TRANSFORM.proj._33;
  83. TRANSFORM.mPC._34 = TRANSFORM.proj._34;
  84. TRANSFORM.mPC._41 = (TRANSFORM.proj._41 + TRANSFORM.proj._44) * D3DVAL(0.5);
  85. TRANSFORM.mPC._42 = (TRANSFORM.proj._42 + TRANSFORM.proj._44) * D3DVAL(0.5);
  86. TRANSFORM.mPC._43 = TRANSFORM.proj._43;
  87. TRANSFORM.mPC._44 = TRANSFORM.proj._44;
  88. }
  89. if (lpDevI->dwFEFlags & (D3DFE_VIEWMATRIX_DIRTY |
  90. D3DFE_PROJMATRIX_DIRTY))
  91. { // Update Mview*Mproj*Mclip
  92. MatrixProduct(&pv->mVPC, &pv->view, &TRANSFORM.mPC);
  93. lpDevI->dwFEFlags |= D3DFE_CLIPMATRIX_DIRTY | D3DFE_CLIPPLANES_DIRTY;
  94. }
  95. MatrixProduct(&pv->mCTM[0], &pv->world[0], &pv->mVPC);
  96. // Set dirty bit for world*view matrix (needed for fog and lighting)
  97. if (lpDevI->dwFEFlags & (D3DFE_VIEWMATRIX_DIRTY |
  98. D3DFE_WORLDMATRIX_DIRTY))
  99. {
  100. lpDevI->dwFEFlags |= D3DFE_WORLDVIEWMATRIX_DIRTY |
  101. D3DFE_INVWORLDVIEWMATRIX_DIRTY |
  102. D3DFE_NEEDCHECKWORLDVIEWVMATRIX;
  103. }
  104. // All matrices are set up
  105. lpDevI->dwFEFlags &= ~D3DFE_TRANSFORM_DIRTY;
  106. // Set dirty bit for lighting
  107. lpDevI->dwFEFlags |= D3DFE_NEED_TRANSFORM_LIGHTS |
  108. D3DFE_FRUSTUMPLANES_DIRTY;
  109. pv->dwDeviceFlags |= D3DDEV_TRANSFORMDIRTY;
  110. // Set this to not to re-compute the matrices
  111. pv->WVCount[0] = pv->MatrixStateCount;
  112. pv->CTMCount[0] = pv->MatrixStateCount;
  113. }
  114. //----------------------------------------------------------------------------
  115. #ifdef DEBUG_PIPELINE
  116. extern DWORD g_DebugFlags;
  117. #endif
  118. //-----------------------------------------------------------------------------
  119. // DoUpdateState should be called for every DrawPrimitive call in the slow path,
  120. // because it sets some internal pipeline flags. These flags are persistent for the
  121. // fast path
  122. //
  123. void DoUpdateState(LPD3DHAL lpDevI)
  124. {
  125. D3DFE_PROCESSVERTICES* pv = lpDevI->m_pv;
  126. pv->dwFlags = 0;
  127. if (lpDevI->m_pv->dwDeviceFlags & D3DDEV_VERTEXSHADERS)
  128. {
  129. // For vertex shaders we need update clip planes only
  130. if (lpDevI->dwFEFlags & D3DFE_CLIPPLANES_DIRTY)
  131. {
  132. DWORD dwMaxUserClipPlanes = 0;
  133. DWORD dwPlanes = lpDevI->rstates[D3DRENDERSTATE_CLIPPLANEENABLE];
  134. for (DWORD i=0; i < __MAXUSERCLIPPLANES; i++)
  135. {
  136. if (dwPlanes & (1 << i))
  137. {
  138. // Clipping planes are transformed by inverse transposed
  139. // view-projection-clip matrix
  140. // For vertex shaders view-projection matrix is identity.
  141. // Inverse transposed clip matrix is
  142. // 2 0 0 -1
  143. // 0 2 0 -1
  144. // 0 0 1 0
  145. // 0 0 0 1
  146. //
  147. float* pOut = (float*)&pv->userClipPlane[dwMaxUserClipPlanes];
  148. float* pIn = (float*)&lpDevI->transform.userClipPlane[i];
  149. pOut[0] = pIn[0]*2;
  150. pOut[1] = pIn[1]*2;
  151. pOut[2] = pIn[2];
  152. pOut[3] = pIn[3] - pIn[0] - pIn[1];
  153. dwMaxUserClipPlanes++;
  154. }
  155. }
  156. pv->dwMaxUserClipPlanes = dwMaxUserClipPlanes;
  157. lpDevI->dwFEFlags &= ~D3DFE_CLIPPLANES_DIRTY;
  158. }
  159. // For PSGP we need to set DONOTCOPY bits
  160. if (!(pv->dwVIDOut & D3DFVF_DIFFUSE))
  161. pv->dwFlags |= D3DPV_DONOTCOPYDIFFUSE;
  162. if (!(pv->dwVIDOut & D3DFVF_SPECULAR))
  163. pv->dwFlags |= D3DPV_DONOTCOPYSPECULAR;
  164. return;
  165. }
  166. UpdateFlagsForOutputFVF(pv);
  167. // only set up lights if something has changed
  168. if (lpDevI->dwFEFlags & D3DFE_LIGHTS_DIRTY)
  169. {
  170. lpDevI->m_dwRuntimeFlags &= ~(D3DRT_DIRECTIONALIGHTPRESENT |
  171. D3DRT_POINTLIGHTPRESENT);
  172. LPDIRECT3DLIGHTI lpD3DLightI;
  173. lpD3DLightI = (LPDIRECT3DLIGHTI)LIST_FIRST(&lpDevI->m_ActiveLights);
  174. pv->lighting.activeLights = NULL;
  175. // Set lights in the device
  176. while (lpD3DLightI)
  177. {
  178. if (lpD3DLightI->m_Light.Type == D3DLIGHT_DIRECTIONAL)
  179. lpDevI->m_dwRuntimeFlags |= D3DRT_DIRECTIONALIGHTPRESENT;
  180. else
  181. lpDevI->m_dwRuntimeFlags |= D3DRT_POINTLIGHTPRESENT;
  182. if (lpD3DLightI->m_LightI.flags & D3DLIGHTI_DIRTY)
  183. lpD3DLightI->SetInternalData();
  184. lpD3DLightI->m_LightI.next = pv->lighting.activeLights;
  185. pv->lighting.activeLights = &lpD3DLightI->m_LightI;
  186. lpD3DLightI = (LPDIRECT3DLIGHTI)LIST_NEXT(lpD3DLightI, m_List);
  187. }
  188. }
  189. // Process vertex blending and tweening settings
  190. if (lpDevI->dwFEFlags & D3DFE_VERTEXBLEND_DIRTY)
  191. {
  192. pv->dwNumVerBlends = lpDevI->rstates[D3DRS_VERTEXBLEND];
  193. pv->dwNumWeights = 0;
  194. if (pv->dwNumVerBlends && (pv->dwNumVerBlends != D3DVBF_TWEENING))
  195. {
  196. if (pv->dwNumVerBlends == D3DVBF_0WEIGHTS)
  197. pv->dwNumVerBlends = 1;
  198. else
  199. pv->dwNumVerBlends++;
  200. // Compute number of floats in a vertex
  201. int nFloats = ((pv->dwVIDIn & D3DFVF_POSITION_MASK) >> 1) - 2;
  202. // Compute number of needed floats
  203. int nFloatsNeeded;
  204. if (pv->dwDeviceFlags & D3DDEV_INDEXEDVERTEXBLENDENABLE)
  205. {
  206. #if DBG
  207. if (D3DVSD_ISLEGACY(lpDevI->m_dwCurrentShaderHandle) &&
  208. ((pv->dwVIDIn & D3DFVF_LASTBETA_UBYTE4) == 0))
  209. {
  210. D3D_THROW_FAIL("D3DFVF_LASTBETA_UBYTE4 must be set for index vertex blending");
  211. }
  212. #endif // DBG
  213. nFloatsNeeded = pv->dwNumVerBlends;
  214. }
  215. else
  216. {
  217. nFloatsNeeded = pv->dwNumVerBlends - 1;
  218. }
  219. if (nFloats < nFloatsNeeded)
  220. {
  221. D3D_THROW_FAIL("Vertex does not have enough data for vertex blending");
  222. }
  223. pv->dwNumWeights = pv->dwNumVerBlends - 1;
  224. // Lighting is done in the camera space when there is vertex blending
  225. if (pv->dwDeviceFlags & D3DDEV_MODELSPACELIGHTING)
  226. {
  227. pv->dwDeviceFlags &= ~(D3DDEV_MODELSPACELIGHTING | D3DFE_NEEDCHECKWORLDVIEWVMATRIX);
  228. // We have to transform lights to the camera space
  229. lpDevI->dwFEFlags |= D3DFE_NEED_TRANSFORM_LIGHTS;
  230. }
  231. }
  232. else
  233. {
  234. // Vertex blending is disabled, so we may be able to do lighting
  235. // in model space. We need to to re-check matrices
  236. if (!(pv->dwDeviceFlags & D3DDEV_MODELSPACELIGHTING))
  237. lpDevI->dwFEFlags |= D3DFE_NEEDCHECKWORLDVIEWVMATRIX;
  238. }
  239. lpDevI->dwFEFlags &= ~D3DFE_VERTEXBLEND_DIRTY;
  240. }
  241. if (lpDevI->rstates[D3DRS_VERTEXBLEND] == D3DVBF_TWEENING)
  242. {
  243. if (pv->position2.lpvData)
  244. pv->dwFlags |= D3DPV_POSITION_TWEENING;
  245. if (pv->normal2.lpvData)
  246. pv->dwFlags |= D3DPV_NORMAL_TWEENING;
  247. pv->dwNumVerBlends = 0; // Disable vertex blending when tweening
  248. #if DBG
  249. if (!(pv->dwFlags & (D3DPV_POSITION_TWEENING | D3DPV_NORMAL_TWEENING)))
  250. {
  251. D3D_THROW_FAIL("Position2 or Normal2 must be set when tweening is enabled");
  252. }
  253. #endif
  254. }
  255. #if DBG
  256. if (!(pv->dwDeviceFlags & D3DDEV_INDEXEDVERTEXBLENDENABLE))
  257. {
  258. if (D3DVSD_ISLEGACY(lpDevI->m_dwCurrentShaderHandle) &&
  259. ((pv->dwVIDIn & D3DFVF_LASTBETA_UBYTE4) != 0))
  260. {
  261. D3D_THROW_FAIL("D3DFVF_LASTBETA_UBYTE4 must be set only when index vertex blending is used");
  262. }
  263. }
  264. #endif // DBG
  265. if (lpDevI->dwFEFlags & D3DFE_TRANSFORM_DIRTY)
  266. {
  267. updateTransform(lpDevI);
  268. }
  269. // We need World-View matrix for lighting, fog, point sprites and when
  270. // texture coordinates are taken from the vertex data in the camera space
  271. if (lpDevI->dwFEFlags & D3DFE_WORLDVIEWMATRIX_DIRTY &&
  272. (pv->dwDeviceFlags & (D3DDEV_LIGHTING | D3DDEV_FOG) ||
  273. lpDevI->rstates[D3DRS_POINTSCALEENABLE] ||
  274. pv->dwDeviceFlags & (D3DDEV_NORMALINCAMERASPACE | D3DDEV_POSITIONINCAMERASPACE)))
  275. {
  276. MatrixProduct(&pv->mWV[0], &pv->world[0],
  277. &pv->view);
  278. lpDevI->dwFEFlags &= ~D3DFE_WORLDVIEWMATRIX_DIRTY;
  279. }
  280. // Detect where to do lighting: in model or eye space
  281. if (lpDevI->dwFEFlags & D3DFE_NEEDCHECKWORLDVIEWVMATRIX &&
  282. pv->dwDeviceFlags & D3DDEV_LIGHTING)
  283. {
  284. // We try to do lighting in the model space if
  285. // 1. we do not have to normalize normals
  286. // 2. we do not need to do vertex blending
  287. pv->dwDeviceFlags &= ~D3DDEV_MODELSPACELIGHTING;
  288. if (pv->dwNumVerBlends == 0 &&
  289. !(pv->dwDeviceFlags & D3DDEV_NORMALIZENORMALS))
  290. {
  291. #ifdef DEBUG_PIPELINE
  292. if (!(g_DebugFlags & __DEBUG_MODELSPACE))
  293. #endif
  294. {
  295. CheckWorldViewMatrix(pv);
  296. lpDevI->dwFEFlags &= ~D3DFE_NEEDCHECKWORLDVIEWVMATRIX;
  297. }
  298. }
  299. // If D3DDEV_MODELSPACELIGHTING has been changed we need to re-transform lights
  300. lpDevI->dwFEFlags |= D3DFE_NEED_TRANSFORM_LIGHTS;
  301. }
  302. // Updating inverse World-View matrix.
  303. // It is needed when we do lighting in the model space or we need normals
  304. // in the camera space
  305. if (lpDevI->dwFEFlags & D3DFE_INVWORLDVIEWMATRIX_DIRTY &&
  306. ((pv->dwDeviceFlags & D3DDEV_LIGHTING &&
  307. !(pv->dwDeviceFlags & D3DDEV_MODELSPACELIGHTING)) ||
  308. pv->dwDeviceFlags & D3DDEV_NORMALINCAMERASPACE))
  309. {
  310. Inverse4x4((D3DMATRIX*)&pv->mWV[0], (D3DMATRIX*)&pv->mWVI);
  311. lpDevI->dwFEFlags &= ~D3DFE_INVWORLDVIEWMATRIX_DIRTY;
  312. pv->WVICount[0] = pv->MatrixStateCount;
  313. }
  314. // Update clipping planes if there are any
  315. if (lpDevI->dwFEFlags & D3DFE_CLIPPLANES_DIRTY)
  316. {
  317. if (lpDevI->dwFEFlags & D3DFE_CLIPMATRIX_DIRTY)
  318. {
  319. // View and projection matrix are inversed separately, because it
  320. // is possible that combined matrix cannot be inverted. This could happend
  321. // when the view matrix has huge _43 value (> 10^7). Floating point precision
  322. // is not enough in this case
  323. D3DMATRIXI mPCInverse;
  324. if (Inverse4x4((D3DMATRIX*)&lpDevI->transform.mPC, (D3DMATRIX*)&mPCInverse))
  325. {
  326. D3D_ERR("Cannot invert projection matrix");
  327. setIdentity((D3DMATRIXI*)&mPCInverse);
  328. }
  329. D3DMATRIXI mViewInverse;
  330. if (Inverse4x4((D3DMATRIX*)&pv->view, (D3DMATRIX*)&mViewInverse))
  331. {
  332. D3D_ERR("Cannot invert view matrix");
  333. setIdentity((D3DMATRIXI*)&mViewInverse);
  334. }
  335. MatrixProduct(&lpDevI->transform.mVPCI, &mPCInverse, &mViewInverse);
  336. lpDevI->dwFEFlags &= ~D3DFE_CLIPMATRIX_DIRTY;
  337. }
  338. DWORD dwMaxUserClipPlanes = 0;
  339. DWORD dwPlanes = lpDevI->rstates[D3DRENDERSTATE_CLIPPLANEENABLE];
  340. for (DWORD i=0; i < __MAXUSERCLIPPLANES; i++)
  341. {
  342. if (dwPlanes & (1 << i))
  343. {
  344. // Clipping planes are transformed by inverse transposed
  345. // view-projection-clip matrix
  346. VecMatMul4HT(&lpDevI->transform.userClipPlane[i],
  347. (D3DMATRIX*)&lpDevI->transform.mVPCI,
  348. &pv->userClipPlane[dwMaxUserClipPlanes]);
  349. dwMaxUserClipPlanes++;
  350. }
  351. }
  352. pv->dwMaxUserClipPlanes = dwMaxUserClipPlanes;
  353. lpDevI->dwFEFlags &= ~D3DFE_CLIPPLANES_DIRTY;
  354. }
  355. if (lpDevI->dwFEFlags & (D3DFE_NEED_TRANSFORM_LIGHTS |
  356. D3DFE_LIGHTS_DIRTY |
  357. D3DFE_MATERIAL_DIRTY))
  358. {
  359. D3DFE_UpdateLights(lpDevI);
  360. // Set a flag for PSGP
  361. pv->dwDeviceFlags |= D3DDEV_LIGHTSDIRTY;
  362. }
  363. // In case if COLORVERTEX is TRUE, the vertexAlpha could be overriden
  364. // by vertex alpha
  365. pv->lighting.alpha = (DWORD)pv->lighting.materialAlpha;
  366. pv->lighting.alphaSpecular = (DWORD)pv->lighting.materialAlphaS;
  367. // This is a hint that only the inPosition pointer needs to be updated
  368. // for speed reasons.
  369. if (((pv->dwVIDIn & ( D3DFVF_DIFFUSE | D3DFVF_SPECULAR | D3DFVF_NORMAL)) == 0) &&
  370. (pv->nTexCoord == 0))
  371. pv->dwFlags |= D3DPV_TRANSFORMONLY;
  372. if (pv->nOutTexCoord == 0)
  373. pv->dwFlags |= D3DPV_DONOTCOPYTEXTURE;
  374. lpDevI->dwFEFlags &= ~D3DFE_FRONTEND_DIRTY;
  375. // Decide whether we always need position and normal in the camera space
  376. if (!(pv->dwFlags2 & __FLAGS2_TEXGEN))
  377. {
  378. // When texture generation is disabled we can recompute NORMAL and
  379. // POSITION flags
  380. pv->dwDeviceFlags &= ~(D3DDEV_NORMALINCAMERASPACE |
  381. D3DDEV_POSITIONINCAMERASPACE);
  382. }
  383. if ((pv->dwDeviceFlags & (D3DDEV_LIGHTING | D3DDEV_MODELSPACELIGHTING)) == D3DDEV_LIGHTING)
  384. {
  385. // We do lighting in camera space
  386. if (lpDevI->m_dwRuntimeFlags & D3DRT_DIRECTIONALIGHTPRESENT &&
  387. lpDevI->m_pv->dwVIDIn & D3DFVF_NORMAL)
  388. pv->dwDeviceFlags |= D3DDEV_NORMALINCAMERASPACE;
  389. if (lpDevI->m_dwRuntimeFlags & D3DRT_POINTLIGHTPRESENT)
  390. pv->dwDeviceFlags |= D3DDEV_POSITIONINCAMERASPACE;
  391. }
  392. if (pv->dwFlags & D3DPV_FOG)
  393. {
  394. pv->dwDeviceFlags |= D3DDEV_POSITIONINCAMERASPACE;
  395. }
  396. }