/*==========================================================================; * * Copyright (C) 1998 Microsoft Corporation. All Rights Reserved. * * File: loops.mcp * Content: Generates code for multiple loop geometry pipeline * ***************************************************************************/ #include "pch.cpp" #pragma hdrstop include(`pvvid.mh') dnl #include "stdio.h" dnl//------------------------------------------------------------------ dnl// d_ComputeSpecular dnl// dnl// Generates code to compute specular component based on a dot product dnl// dnl// Arguments: dnl// $1 - margin count dnl// $2 - if present, equal to the attenuation factor dnl// dot - dot product dnl// pv - process vertices structure dnl// d_Op - operation "=" or "+=" dnl// d_LightingFlags - DWORD dnl// d_SPECULARCOMPUTED - bit dnl// d_pInpSpecular - vertex specular color (DWORD*) dnl// d_OutSpecular - output specular color, (D3DFE_COLOR) dnl// define(`d_ComputeSpecular',`dnl d_empty_($1)if (FLOAT_CMP_POS(dot, >=, pv->lighting.specThreshold)) d_margin($1){ d_margin($1) d_LightingFlags |= __LIGHT_SPECULARCOMPUTED; d_margin($1) // Compute power = dot**SpecularExponent; d_margin($1) D3DVALUE power; d_margin($1) if (FLOAT_CMP_PONE(dot, <)) d_margin($1) { d_margin($1) int indx; d_margin($1) float v; d_margin($1) float dot_floor; d_margin($1) dot *= 255.0f; d_margin($1) dot_floor = (float)floor(dot); d_margin($1) indx = FTOI(dot_floor); d_margin($1) dot -= dot_floor; d_margin($1) v = pv->lighting.currentSpecTable[indx]; d_margin($1) power = v + (pv->lighting.currentSpecTable[indx+1] - v)*dot; d_margin($1) } d_margin($1) else d_margin($1) power = pv->lighting.currentSpecTable[255]; dnl ifelse($#,2,`d_margin($1+1)power*= $2;')dnl// If parameter 2 (attenuation) is present, use it d_margin($1) // Update specular component d_margin($1) if (!(dwFlags & D3DPV_COLORVERTEX_S)) d_margin($1) { d_margin($1) d_OutSpecular.r d_Op light->specularMat.r * power; d_margin($1) d_OutSpecular.g d_Op light->specularMat.g * power; d_margin($1) d_OutSpecular.b d_Op light->specularMat.b * power; d_margin($1) } d_margin($1) else d_margin($1) { d_margin($1) const D3DVALUE r = (D3DVALUE)RGBA_GETRED(*d_pInpSpecular); d_margin($1) const D3DVALUE g = (D3DVALUE)RGBA_GETGREEN(*d_pInpSpecular); d_margin($1) const D3DVALUE b = (D3DVALUE)RGBA_GETBLUE(*d_pInpSpecular); d_margin($1) d_OutSpecular.r d_Op light->specular.r * r * power; d_margin($1) d_OutSpecular.g d_Op light->specular.g * g * power; d_margin($1) d_OutSpecular.b d_Op light->specular.b * b * power; d_margin($1) } d_margin($1)}')dnl dnl//------------------------------------------------------------------ dnl// d_UpdateDiffuseColor dnl// dnl// Generates code to compute diffuse component, based on a dot product dnl// dnl// Arguments: dnl// $1 - margin count dnl// $2 - operation "=" or "+=" dnl// dot - dot product dnl// d_LightingFlags - DWORD dnl// d_pInpDiffuse - vertex specular color (DWORD*) dnl// d_OutDiffuse - output specular color, (D3DFE_COLOR) dnl// define(`d_UpdateDiffuseColor',`dnl d_empty_($1)if (!(dwFlags & D3DPV_COLORVERTEX_D)) d_margin($1){ d_margin($1) d_OutDiffuse.r $2 light->diffuseMat.r * dot; d_margin($1) d_OutDiffuse.g $2 light->diffuseMat.g * dot; d_margin($1) d_OutDiffuse.b $2 light->diffuseMat.b * dot; d_margin($1)} d_margin($1)else d_margin($1){ d_margin($1) const D3DVALUE r = (D3DVALUE)RGBA_GETRED(*d_pInpDiffuse); d_margin($1) const D3DVALUE g = (D3DVALUE)RGBA_GETGREEN(*d_pInpDiffuse); d_margin($1) const D3DVALUE b = (D3DVALUE)RGBA_GETBLUE(*d_pInpDiffuse); d_margin($1) d_OutDiffuse.r $2 light->diffuse.r * r * dot; d_margin($1) d_OutDiffuse.g $2 light->diffuse.g * g * dot; d_margin($1) d_OutDiffuse.b $2 light->diffuse.b * b * dot; d_margin($1)} d_margin($1)d_LightingFlags |= __LIGHT_DIFFUSECOMPUTED;')dnl dnl//------------------------------------------------------------------ dnl// d_UpdateAmbientColor dnl// dnl// Generates code to compute ambient component dnl// dnl// Arguments: dnl// $1 - margin count dnl// $2 - "* att" or empty dnl// dot - dot product dnl// d_Op - operation "=" or "+=" dnl// d_LightingFlags - DWORD dnl// d_OutDiffuse - output specular color, (D3DFE_COLOR) dnl// define(`d_UpdateAmbientColor',`dnl d_empty_($1)if (!(light->flags & D3DLIGHTI_AMBIENT_IS_ZERO)) d_margin($1){ d_margin($1) if (!(dwFlags & D3DPV_COLORVERTEX_A)) d_margin($1) { d_margin($1) d_OutDiffuse.r d_Op light->ambientMat.r $2; d_margin($1) d_OutDiffuse.g d_Op light->ambientMat.g $2; d_margin($1) d_OutDiffuse.b d_Op light->ambientMat.b $2; d_margin($1) } d_margin($1) else d_margin($1) { d_margin($1) const D3DVALUE r = (D3DVALUE)RGBA_GETRED(*d_pInpAmbient); d_margin($1) const D3DVALUE g = (D3DVALUE)RGBA_GETGREEN(*d_pInpAmbient); d_margin($1) const D3DVALUE b = (D3DVALUE)RGBA_GETBLUE(*d_pInpAmbient); d_margin($1) d_OutDiffuse.r d_Op light->ambient.r * r $2; d_margin($1) d_OutDiffuse.g d_Op light->ambient.g * g $2; d_margin($1) d_OutDiffuse.b d_Op light->ambient.b * b $2; d_margin($1) } d_margin($1) d_LightingFlags |= __LIGHT_DIFFUSECOMPUTED; d_margin($1)}')dnl dnl//------------------------------------------------------------------ dnl// d_Directional7 dnl// dnl// Generate code to light a vertex using directional or parallel point light. dnl// Model space and camera space lighting are handled dnl// dnl// Arguments: dnl/ $1 - margin count dnl// d_pInpPosition - input position pointer (D3DVERTEX*) dnl// d_TmpPosition - temporary position buffer (D3DVECTOR). dnl// Used in camera space lighting dnl// d_pInpNormal - input normal pointer (D3DVECTOR*) dnl// d_TmpNormal - temporary normal buffer (D3DVECTOR) dnl// Used in camera space lighting dnl// d_Space - Defines the coordinate system: modelSpace or cameraSpace dnl// d_LightingFlags - DWORD where __LIGHT_ bits are defined dnl// dnl// For camera space lighting vertex normal is assumed to be already transformed dnl// define(`d_Directional7',`dnl d_empty_($1)D3DVALUE dot; d_margin($1)d_UpdateAmbientColor($1) d_margin($1)if (!(pv->dwVIDIn & D3DFVF_NORMAL)) d_margin($1) goto l_exit; d_margin($1) ifelse(d_Space,modelSpace,` d_margin($1)dot = VecDot(light->model_direction, (*d_pInpNormal));',` d_margin($1)dot = VecDot(light->model_direction, d_TmpNormal);') dnl// endif d_margin($1) d_margin($1)if (FLOAT_GTZ(dot)) d_margin($1){ ifelse(d_Op,+=,`dnl d_margin($1) d_UpdateDiffuseColor($1+1,+=)',` d_margin($1) if (!(d_LightingFlags & __LIGHT_DIFFUSECOMPUTED)) d_margin($1) { d_margin($1) d_UpdateDiffuseColor($1+2, d_Op) d_margin($1) } d_margin($1) else d_margin($1) { d_margin($1) d_UpdateDiffuseColor($1+2,+=) d_margin($1) }') d_margin($1) if (light->flags & D3DLIGHTI_COMPUTE_SPECULAR) d_margin($1) { d_margin($1) D3DVECTOR h; // halfway vector d_margin($1) D3DVECTOR eye; // incident vector ie vector from eye d_margin($1)ifelse(d_Space,modelSpace,` d_margin($1) if (pv->dwDeviceFlags & D3DDEV_LOCALVIEWER) d_margin($1) { d_margin($1) // calc vector from vertex to the camera d_margin($1) VecSub(pv->lighting.model_eye, (*(D3DVECTOR*)d_pInpPosition), eye); d_margin($1) VecNormalizeFast(eye); d_margin($1) VecAdd(light->model_direction, eye, h); // calc halfway vector d_margin($1) dot = VecDot(h, (*d_pInpNormal)); d_margin($1) } d_margin($1) else d_margin($1) { d_margin($1) dot = VecDot(light->halfway, (*d_pInpNormal)); d_margin($1) }',` dnl// else d_margin($1)ifelse(d_Op,+=,` d_margin($1) if (!(d_LightingFlags & __LIGHT_VERTEXTRANSFORMED))') dnl// endif d_margin($1) { d_margin($1) // For tweening vertex position is already blended d_margin($1) d_TransformVertexToCameraSpace($1+3, d_pInpPosition, (&d_TmpPosition), pWeights, pMatrixIndices) d_margin($1) d_LightingFlags |= __LIGHT_VERTEXTRANSFORMED; d_margin($1) } d_margin($1) if (pv->dwDeviceFlags & D3DDEV_LOCALVIEWER) d_margin($1) { d_margin($1) // calc vector from vertex to the camera d_margin($1) VecSub(pv->lighting.model_eye, d_TmpPosition, eye); d_margin($1) VecNormalizeFast(eye); d_margin($1) VecAdd(light->model_direction, eye, h); // calc halfway vector d_margin($1) dot = VecDot(h, d_TmpNormal); d_margin($1) } d_margin($1) else d_margin($1) { d_margin($1) dot = VecDot(light->halfway, d_TmpNormal); d_margin($1) }') dnl// endif d_margin($1) if (FLOAT_GTZ(dot)) d_margin($1) { d_margin($1) if (pv->dwDeviceFlags & D3DDEV_LOCALVIEWER) d_margin($1) dot *= ISQRTF(VecLenSq(h)); d_margin($1) d_ComputeSpecular($1+3); d_margin($1) } d_margin($1) } d_margin($1)} d_margin($1)l_exit:; d_margin($1)')dnl dnl//------------------------------------------------------------------ dnl// d_PointSpot7 dnl// dnl// Generate code to light a vertex using point spot light. dnl// Model space and camera space lighting are handled dnl// dnl// Arguments: dnl/ $1 - margin count dnl// d_pInpPosition - input position pointer (D3DVERTEX*) dnl// d_TmpPosition - temporary position buffer (D3DVECTOR). dnl// Used in camera space lighting dnl// d_pInpNormal - input normal pointer (D3DVECTOR*) dnl// d_TmpNormal - temporary normal buffer (D3DVECTOR) dnl// Used in camera space lighting dnl// d_Space - Defines the coordinate system: modelSpace or cameraSpace dnl// d_LightingFlags - DWORD where __LIGHT_ bits are defined dnl// dnl// For camera space lighting vertex position is assumed to be already transformed dnl// define(`d_PointSpot7',`dnl d_margin($1)D3DVALUE dot; // dot product d_margin($1)D3DVALUE dist; // Distance from light to the vertex d_margin($1)D3DVALUE dist2; // Square of the dist d_margin($1)D3DVECTOR d; // Direction to light d_margin($1)D3DVALUE att; // attenuation ifelse(d_Space,modelSpace,`dnl d_margin($1)VecSub(light->model_position, (*(D3DVECTOR*)d_pInpPosition), d);',`dnl d_margin($1)VecSub(light->model_position, d_TmpPosition, d);')dnl dnl// endif d_margin($1)// early out if out of range or exactly on the vertex d_margin($1)dist2 = d.x*d.x + d.y*d.y + d.z*d.z; d_margin($1)if (FLOAT_CMP_POS(dist2, >=, light->range_squared) || FLOAT_EQZ(dist2)) d_margin($1) goto l_exit; d_margin($1)dot = 0; // It is possible not to have normals (ambient component only) d_margin($1) // So we set dot to zero for this case d_margin($1)// Calc dot product of light dir with normal. Note that since we d_margin($1)// did not normalize the direction the result is scaled by the distance. ifelse(d_Space,modelSpace,`dnl d_margin($1)if (pv->dwVIDIn & D3DFVF_NORMAL) d_margin($1){ d_margin($1) dot = VecDot(d, (*d_pInpNormal)); d_margin($1)}',` d_margin($1)if (pv->dwVIDIn & D3DFVF_NORMAL) d_margin($1){ ifelse(d_Op,+=,`dnl Normal should be transformed by the first light. So do not check. d_margin($1) if (!(d_LightingFlags & __LIGHT_NORMALTRANSFORMED))') d_margin($1) { d_margin($1) // For tweening normal should be already blended d_margin($1) d_TransformNormalToCameraSpace($1+1, d_pInpNormal, (&d_TmpNormal), pWeights, pMatrixIndices) d_margin($1) d_LightingFlags |= __LIGHT_NORMALTRANSFORMED; d_margin($1) } d_margin($1) dot = VecDot(d, d_TmpNormal); d_margin($1)}')dnl d_margin($1)if (!(light->flags & D3DLIGHTI_AMBIENT_IS_ZERO) || FLOAT_GTZ(dot)) d_margin($1){ d_margin($1) dist = SQRTF(dist2); d_margin($1) att = light->attenuation0 + d_margin($1) light->attenuation1 * dist + d_margin($1) light->attenuation2 * dist2; d_margin($1) if (!FLOAT_EQZ(att)) d_margin($1) att = (D3DVALUE)1.0/att; d_margin($1) else d_margin($1) att = (D3DVALUE)FLT_MAX; d_margin($1) dist = D3DVAL(1)/dist; d_margin($1) if (light->type == D3DLIGHT_SPOT) d_margin($1) { d_margin($1) D3DVALUE cone_dot; d_margin($1) // Calc dot product of direction to light with light direction to d_margin($1) // be compared anganst the cone angles to see if we are in the light. d_margin($1) // Note that cone_dot is still scaled by dist d_margin($1) cone_dot = VecDot(d, light->model_direction)*dist; d_margin($1) if (FLOAT_CMP_POS(cone_dot, <=, light->cos_phi_by_2)) d_margin($1) goto l_exit; d_margin($1) // modify att if in the region between phi and theta d_margin($1) if (FLOAT_CMP_POS(cone_dot, <, light->cos_theta_by_2)) d_margin($1) { d_margin($1) D3DVALUE val; d_margin($1) val = (cone_dot - light->cos_phi_by_2) * light->inv_theta_minus_phi; d_margin($1) if (!(light->flags & D3DLIGHTI_LINEAR_FALLOFF)) d_margin($1) { d_margin($1) val = POWF(val, light->falloff); d_margin($1) } d_margin($1) att *= val; d_margin($1) } d_margin($1) } d_margin($1) d_UpdateAmbientColor($1+1,* att) d_margin($1) if (FLOAT_LEZ(dot)) d_margin($1) goto l_exit; d_margin($1) dot *= dist*att; ifelse(d_Op,+=,`dnl d_margin($1) d_UpdateDiffuseColor($1+1,+=)',` d_margin($1) if (!(d_LightingFlags & __LIGHT_DIFFUSECOMPUTED)) d_margin($1) { d_margin($1) d_UpdateDiffuseColor($1+2, d_Op) d_margin($1) } d_margin($1) else d_margin($1) { d_margin($1) d_UpdateDiffuseColor($1+2,+=) d_margin($1) }') d_margin($1) if (light->flags & D3DLIGHTI_COMPUTE_SPECULAR) d_margin($1) { d_margin($1) D3DVECTOR eye; d_margin($1) D3DVECTOR h; d_margin($1) // normalize light direction d_margin($1) d.x *= dist; d_margin($1) d.y *= dist; d_margin($1) d.z *= dist; d_margin($1) // calc vector from vertex to the camera dnl ifelse(d_Space,modelSpace,`dnl dnl d_margin($1) if (pv->dwDeviceFlags & D3DDEV_LOCALVIEWER) d_margin($1) { d_margin($1) VecSub(pv->lighting.model_eye, (*(D3DVECTOR*)d_pInpPosition), eye); d_margin($1) VecNormalizeFast(eye); d_margin($1) VecAdd(d, eye, h); // halfway vector d_margin($1) } d_margin($1) else d_margin($1) { d_margin($1) VecAdd(d, pv->lighting.directionToCamera, h); d_margin($1) } d_margin($1) VecNormalizeFast(h); d_margin($1) dot = VecDot(h, *d_pInpNormal);',`dnl dnl dnl// else dnl d_margin($1) if (pv->dwDeviceFlags & D3DDEV_LOCALVIEWER) d_margin($1) { d_margin($1) VecSub(pv->lighting.model_eye, d_TmpPosition, eye); d_margin($1) VecNormalizeFast(eye); d_margin($1) VecAdd(d, eye, h); // halfway vector d_margin($1) } d_margin($1) else d_margin($1) { d_margin($1) h.x = d.x; d_margin($1) h.y = d.y; d_margin($1) h.z = d.z - 1.0f; d_margin($1) } d_margin($1) VecNormalizeFast(h); d_margin($1) dot = VecDot(h, d_TmpNormal);')dnl dnl dnl// endif d_margin($1) d_ComputeSpecular($1+2,att) d_margin($1) } d_margin($1)l_exit:; d_margin($1)}')dnl dnl//------------------------------------------------------------------ dnl// d_LightVertices dnl// dnl// Generate code to light vertices in a small batch using directional or dnl// parallel point light. dnl// Handles strided and non-strided cases dnl// dnl// Arguments: dnl// $1 - function name dnl// $2 - Light type: d_Directional7 or d_PointSpot7 dnl// define(`d_LightVertices',`dnl //--------------------------------------------------------------------- void $1(LPD3DFE_PROCESSVERTICES pv, DWORD dwVerCount, BATCHBUFFER *pBatchBuffer, D3DI_LIGHT *light, D3DVERTEX *pCoord, D3DVALUE* pWeights, BYTE* pMatrixIndices, D3DVECTOR *pNormal, DWORD *pDiffuse, DWORD *pSpecular) { // Setup vertex data pointers DWORD dwFlags = pv->dwFlags; DWORD *pColors[2] = {pDiffuse, pSpecular}; DWORD **ppEmissiveSource = pColors + pv->lighting.dwEmissiveSrcIndex; DWORD **ppAmbientSource = pColors + pv->lighting.dwAmbientSrcIndex; DWORD **ppSpecularSource = pColors + pv->lighting.dwSpecularSrcIndex;; DWORD **ppDiffuseSource = pColors + pv->lighting.dwDiffuseSrcIndex; for (DWORD i = dwVerCount; i; i--) { $2(2) NEXT(pCoord, pv->position.dwStride, D3DVERTEX); NEXT(pNormal, pv->normal.dwStride, D3DVECTOR); NEXT(pWeights, pv->weights.dwStride, D3DVALUE); NEXT(pMatrixIndices, pv->matrixIndices.dwStride, BYTE); if (dwFlags & D3DPV_DOCOLORVERTEX) { NEXT(pColors[0], pv->diffuse.dwStride, DWORD); NEXT(pColors[1], pv->specular.dwStride, DWORD); } pBatchBuffer++; } }') dnl //-------------------------------------------------------------------------- // This batch buffer used to hold temporary vertex data for every small loop // const DWORD BATCH_SIZE = 10; // Number of vertices in the batch struct BATCHBUFFER { D3DVALUE sx,sy,sz,rhw; // Screen coordinates D3DFE_COLOR diffuse; D3DFE_COLOR specular; D3DVECTOR position; // Vertex position in the camera space D3DVECTOR normal; // Vertex normal in the camera space DWORD dwFlags; // 8 low bits are the same as lighting // flags from D3DFE }; dnl//====================================================================== dnl// Generate light functions for batch processing dnl// dnl define(`d_LightingFlags',pBatchBuffer->dwFlags)dnl define(`d_pInpAmbient',*ppAmbientSource)dnl define(`d_pInpDiffuse',*ppDiffuseSource)dnl define(`d_pInpSpecular',*ppSpecularSource)dnl define(`d_OutDiffuse',pBatchBuffer->diffuse)dnl define(`d_OutSpecular',pBatchBuffer->specular)dnl define(`d_pInpPosition',`pCoord')dnl define(`d_TmpPosition',`'pBatchBuffer->position)dnl define(`d_pInpNormal',`pNormal')dnl define(`d_TmpNormal',`pBatchBuffer->normal')dnl dnl define(`d_Op',=)dnl define(`d_Space',cameraSpace)dnl d_LightVertices(DirectionalFirst,`d_Directional7') d_LightVertices(PointSpotFirst,`d_PointSpot7') dnl define(`d_Space',modelSpace)dnl d_LightVertices(DirectionalFirstModel,`d_Directional7') d_LightVertices(PointSpotFirstModel,`d_PointSpot7') dnl define(`d_Op',+=)dnl dnl define(`d_Space',cameraSpace)dnl d_LightVertices(DirectionalNext,`d_Directional7') d_LightVertices(PointSpotNext,`d_PointSpot7') dnl define(`d_Space',modelSpace)dnl d_LightVertices(DirectionalNextModel,`d_Directional7') d_LightVertices(PointSpotNextModel,`d_PointSpot7') dnl//====================================================================== dnl// Generate light functions for one vertex processing //------------------------------------------------------------------------- // Directional light, computed in the camera space // define(`d_LightingFlags',pv->lighting.dwLightingFlags)dnl define(`d_pInpAmbient',(&pv->lighting.vertexAmbient))dnl define(`d_pInpDiffuse',(&pv->lighting.vertexDiffuse))dnl define(`d_pInpSpecular',(&pv->lighting.vertexSpecular))dnl define(`d_OutDiffuse',pv->lighting.diffuse)dnl define(`d_OutSpecular',pv->lighting.specular)dnl define(`d_pInpPosition',`(pInpCoord)')dnl define(`d_TmpPosition',`'*(D3DVERTEX*)pEyeSpaceData)dnl define(`d_pInpNormal',`(pInpNormal)')dnl define(`d_TmpNormal',`pEyeSpaceData->dvNormal')dnl define(`d_Space',cameraSpace)dnl dnl void Directional7(LPD3DFE_PROCESSVERTICES pv, D3DI_LIGHT *light, D3DVERTEX *pInpCoord, D3DVALUE *pWeights, BYTE* pMatrixIndices, D3DVECTOR *pInpNormal, D3DLIGHTINGELEMENT *pEyeSpaceData) { DWORD dwFlags = pv->dwFlags; d_Directional7(1) } //--------------------------------------------------------------------- // Directional light, computed in the model space // define(`d_Space',modelSpace)dnl dnl void Directional7Model(LPD3DFE_PROCESSVERTICES pv, D3DI_LIGHT *light, D3DVERTEX *pInpCoord, D3DVALUE *pWeights, BYTE* pMatrixIndices, D3DVECTOR *pInpNormal, D3DLIGHTINGELEMENT *pEyeSpaceData) { DWORD dwFlags = pv->dwFlags; d_Directional7(1) } //--------------------------------------------------------------------- // Point-spot light, computed in the camera space // define(`d_Space',cameraSpace)dnl void PointSpot7(LPD3DFE_PROCESSVERTICES pv, D3DI_LIGHT *light, D3DVERTEX *pInpCoord, D3DVALUE *pWeights, BYTE* pMatrixIndices, D3DVECTOR *pInpNormal, D3DLIGHTINGELEMENT *pEyeSpaceData) { DWORD dwFlags = pv->dwFlags; d_PointSpot7(1) } //--------------------------------------------------------------------- // Point-spot light, computed in the model space // define(`d_Space',modelSpace)dnl void PointSpot7Model(LPD3DFE_PROCESSVERTICES pv, D3DI_LIGHT *light, D3DVERTEX *pInpCoord, D3DVALUE *pWeights, BYTE* pMatrixIndices, D3DVECTOR *pInpNormal, D3DLIGHTINGELEMENT *pEyeSpaceData) { DWORD dwFlags = pv->dwFlags; d_PointSpot7(1) } //-------------------------------------------------------------------------- // Prototype to transform vertices in batches // typedef DWORD (*PFN_TRANSFORMLOOP)(LPD3DFE_PROCESSVERTICES pv, DWORD dwVerCount, D3DVERTEX *in, D3DVALUE* pWeights, BYTE* pMatrixIndices, D3DTLVERTEX **ppOut, D3DFE_CLIPCODE **ppClipCodes); //--------------------------------------------------------------------- // Transform vertices in a batch with clipping // // Arguments: // dwVerCount - number of vertices in the batch // in - pointer to the input coordinates // ppOut - pointer to the output vertices // ppClipVodes - pointer to the clip code buffer // Returns: // Number of processed vertices // Notes: // ppOut and ppClipCodes will be set to the next vertex after the batch // DWORD TransformClip(LPD3DFE_PROCESSVERTICES pv, DWORD dwVerCount, D3DVERTEX *in, D3DVALUE* pWeights, BYTE* pMatrixIndices, D3DTLVERTEX **ppOut, D3DFE_CLIPCODE **ppClipCodes) { float x, y, z, w; D3DMATRIX *m = (D3DMATRIX*)&pv->mCTM[0]; DWORD dwInpVerSize = pv->position.dwStride; DWORD dwOutVerSize = pv->dwOutputSize; D3DFE_CLIPCODE *pClipCodes = *ppClipCodes; D3DTLVERTEX *out = *ppOut; DWORD dwDeviceFlags = pv->dwDeviceFlags; for (DWORD i = dwVerCount; i; i--) { // Transform vertex to the clipping space d_TransformVertex(2, in, m, x, y, z, w, pWeights, pMatrixIndices) DWORD clip; // Compute clip code d_ComputeClipCode(2) if (clip == 0) { pv->dwClipIntersection = 0; *pClipCodes++ = 0; w = D3DVAL(1)/w; } else { if (dwDeviceFlags & D3DDEV_GUARDBAND) { // We do guardband check in the projection space, so // we transform X and Y of the vertex there d_ComputeClipCodeGB(4) if ((clip & ~__D3DCS_INGUARDBAND) == 0) { // If vertex is inside the guardband we have to compute // screen coordinates w = D3DVAL(1)/w; *pClipCodes++ = (D3DFE_CLIPCODE)clip; pv->dwClipIntersection &= clip; pv->dwClipUnion |= clip; goto l_DoScreenCoord; } } if (pv->dwFlags & D3DPV_ONEPASSCLIPPING) { return dwVerCount - i; } pv->dwClipIntersection &= clip; pv->dwClipUnion |= clip; *pClipCodes++ = (D3DFE_CLIPCODE)clip; // If vertex is outside the frustum we can not compute screen // coordinates out->sx = x; out->sy = y; out->sz = z; out->rhw = w; goto l_Continue; } l_DoScreenCoord: d_ComputeScreenCoordinates(2, x, y, z, w, out) l_Continue: NEXT(in, dwInpVerSize, D3DVERTEX); NEXT(out, dwOutVerSize, D3DTLVERTEX); NEXT(pWeights, pv->weights.dwStride, D3DVALUE); NEXT(pMatrixIndices, pv->matrixIndices.dwStride, BYTE); } *ppClipCodes = pClipCodes; *ppOut = out; return dwVerCount; } //--------------------------------------------------------------------- // Transform vertices in a batch without clipping // // Arguments: // dwVerCount - number of vertices in the batch // in - pointer to the input coordinates // ppOut - pointer to the output vertices // ppClipVodes - pointer to the clip code buffer // Returns: // Number of processed vertices // Notes: // ppOut and ppClipCodes will be set to the next vertex after the batch // DWORD TransformNoClip(LPD3DFE_PROCESSVERTICES pv, DWORD dwVerCount, D3DVERTEX *in, D3DVALUE* pWeights, BYTE* pMatrixIndices, D3DTLVERTEX **ppOut, D3DFE_CLIPCODE **pClipCodes) { float x, y, z, w; D3DMATRIX *m = (D3DMATRIX*)&pv->mCTM[0]; DWORD dwInpVerSize = pv->position.dwStride; DWORD dwOutVerSize = pv->dwOutputSize; D3DTLVERTEX *out = *ppOut; for (DWORD i = dwVerCount; i; i--) { // Transform vertex to the clipping space d_TransformVertex(2, in, m, x, y, z, w, pWeights, pMatrixIndices) // We have to check this only for DONOTCLIP case, because otherwise // the vertex with "we = 0" will be clipped and screen coordinates // will not be computed // "clip" is not zero, if "w" is zero. if (!FLOAT_EQZ(w)) w = D3DVAL(1)/w; else w = __HUGE_PWR2; d_ComputeScreenCoordinates(2, x, y, z, w, out) NEXT(in, dwInpVerSize, D3DVERTEX); NEXT(pWeights, pv->weights.dwStride, D3DVALUE); NEXT(pMatrixIndices, pv->matrixIndices.dwStride, BYTE); NEXT(out, dwOutVerSize, D3DTLVERTEX); } *ppOut = out; return dwVerCount; } //--------------------------------------------------------------------- // Transforms, lights vertices, computes clip codes // Processing is done in small batches (BATCH_SIZE). // // The following fields from pv are used: // dwFlags // dwNumVertices // all pointer and strides // position.lpvStrides // dwVIDIn // dwVIDOut // lpvOut // lpClipFlags // nTexCoord // Returns: // returns dwClipIntersection or 0 (if D3DDEV_DONOTCLIP is set) // Side effects: // dwClipUnion, dwClipIntersection are set only if D3DDEV_DONOTCLIP is not set // #undef DPF_MODNAME #define DPF_MODNAME "ProcessVerticesLoops" DWORD ProcessVerticesLoop(LPD3DFE_PROCESSVERTICES pv) { D3DFE_CLIPCODE *hout = pv->lpClipFlags; D3DTLVERTEX *out = (D3DTLVERTEX*)pv->lpvOut; D3DMATRIXI *m = &pv->mCTM[0]; DWORD dwNumVertices = pv->dwNumVertices; D3DVALUE *pOutTexture = (D3DVALUE*)((char*)out + pv->texOffsetOut); DWORD *pOutDiffuse = (DWORD*)((char*)out + pv->diffuseOffsetOut); DWORD *pOutSpecular = (DWORD*)((char*)out + pv->specularOffsetOut); float* pOutPointSize = (float*)((char*)out + pv->pointSizeOffsetOut); DWORD dwNumTexCoord = pv->nOutTexCoord; DWORD *pOutFogFactor = pOutSpecular; PFN_TRANSFORMLOOP pfnTransform; float PointSizeRs = *(float*)&pv->lpdwRStates[D3DRS_POINTSIZE]; float PointSizeMin = *(float*)&pv->lpdwRStates[D3DRS_POINTSIZE_MIN]; float A, B, C; // Point size scales BOOL bDoPointScale = FALSE; d_Setup() if (pv->lpdwRStates[D3DRS_POINTSCALEENABLE] != 0) { bDoPointScale = TRUE; A = *(float*)&pv->lpdwRStates[D3DRS_POINTSCALE_A]; B = *(float*)&pv->lpdwRStates[D3DRS_POINTSCALE_B]; C = *(float*)&pv->lpdwRStates[D3DRS_POINTSCALE_C]; } if (pv->dwFlags & D3DPV_DONOTCOPYTEXTURE) dwNumTexCoord = 0; BATCHBUFFER batchBuffer[BATCH_SIZE]; DWORD dwInpVerSizeBatch = dwInpVerSize * BATCH_SIZE; DWORD dwOutVerSizeBatch = dwOutVerSize * BATCH_SIZE; DWORD dwNormalStrideBatch = pv->normal.dwStride * BATCH_SIZE; DWORD dwWeightsStrideBatch = pv->weights.dwStride * BATCH_SIZE; DWORD dwMatrixIndicesStrideBatch = pv->matrixIndices.dwStride * BATCH_SIZE; if (!(dwDeviceFlags & D3DDEV_DONOTCLIP)) { pfnTransform = TransformClip; pv->dwClipIntersection = ~0; pv->dwClipUnion = 0; } else { pfnTransform = TransformNoClip; pv->dwClipIntersection = 0; pv->dwClipUnion = 0; } // When we do tweening we make "in" and "inNormal" pointers to point // to the tweened value. We also change position and normal stride. // But need to restore the strides later UINT oldPositionStride = pv->position.dwStride; UINT oldNormalStride = pv->normal.dwStride; if (pv->dwFlags & (D3DPV_POSITION_TWEENING | D3DPV_NORMAL_TWEENING)) { pv->tweenFactor = *(float*)&pv->lpdwRStates[D3DRS_TWEENFACTOR]; // Replace strides because we will use blended positions and normals if (pv->dwFlags & D3DPV_POSITION_TWEENING) pv->position.dwStride = sizeof(D3DVECTOR); if (pv->dwFlags & D3DPV_NORMAL_TWEENING) pv->normal.dwStride = sizeof(D3DVECTOR); } // Input vertex pointers for tweening D3DVECTOR* inT = in; D3DVECTOR* inNormalT = inNormal; // These two arrays are used when we do tweening. // We blend positions and normals in model space using tweenFactor // and then transform then to the camera (clipping) space // D3DVECTOR posT[BATCH_SIZE]; // Blended position in model space D3DVECTOR normT[BATCH_SIZE]; // Blended normal in model space do { DWORD count1 = min(dwNumVertices, BATCH_SIZE); // Count of vertices to process after transformation. It could be less // than "count1" because of clipping DWORD count; if (pv->dwFlags & D3DPV_POSITION_TWEENING) { // Blend vertices in the model space for (UINT i=0; i < count1; i++) { DoBlending(pv->tweenFactor, inT, in2, &posT[i]); inT = (D3DVECTOR*)((BYTE*)inT + oldPositionStride); in2 = (D3DVECTOR*)((BYTE*)in2 + pv->position2.dwStride); } // Substitute input pointer in = posT; } if (pv->dwFlags & D3DPV_NORMAL_TWEENING) { for (UINT i=0; i < count1; i++) { DoBlending(pv->tweenFactor, inNormalT, inNormal2, &normT[i]); inNormalT = (D3DVECTOR*)((BYTE*)inNormalT + oldNormalStride); inNormal2 = (D3DVECTOR*)((BYTE*)inNormal2 + pv->normal2.dwStride); } // Substitute input pointer inNormal = normT; } count = (*pfnTransform)(pv, count1, (D3DVERTEX*)in, inWeights, inMatrixIndices, &out, &hout); if (pv->dwFlags & (D3DPV_FOG | D3DPV_LIGHTING) || bDoPointScale || pv->dwDeviceFlags & (D3DDEV_POSITIONINCAMERASPACE | D3DDEV_NORMALINCAMERASPACE)) { memset(batchBuffer, 0, sizeof(batchBuffer)); } // Compute camera space position if needed if (pv->dwDeviceFlags & (D3DDEV_POSITIONINCAMERASPACE | D3DDEV_NORMALINCAMERASPACE) || bDoPointScale) { BATCHBUFFER *buf = batchBuffer; D3DVECTOR* pVertex = in; D3DVECTOR* pNormal = inNormal; D3DVALUE* pWeights = inWeights; BYTE* pMatrixIndices = inMatrixIndices; for (DWORD i=count; i; i--) { if (pv->dwDeviceFlags & D3DDEV_POSITIONINCAMERASPACE || bDoPointScale) { d_TransformVertexToCameraSpace(5, pVertex, ((D3DVERTEX*)&buf->position), pWeights, pMatrixIndices) buf->dwFlags |= __LIGHT_VERTEXTRANSFORMED; } if (pv->dwDeviceFlags & D3DDEV_NORMALINCAMERASPACE) { d_TransformNormalToCameraSpace(5, pNormal, ((D3DVERTEX*)&buf->normal), pWeights, pMatrixIndices) buf->dwFlags |= __LIGHT_NORMALTRANSFORMED; NEXT(pNormal, pv->normal.dwStride, D3DVECTOR); } NEXT(pVertex, pv->position.dwStride, D3DVECTOR); NEXT(pWeights, pv->weights.dwStride, D3DVALUE); NEXT(pMatrixIndices, pv->matrixIndices.dwStride, BYTE); buf++; } } if (pv->dwFlags & D3DPV_LIGHTING) { // Light vertices. Output goes to the batch buffer D3DI_LIGHT *light = pv->lighting.activeLights; if (light) { light->pfnLightFirst(pv, count, batchBuffer, light, (D3DVERTEX*)in, inWeights, inMatrixIndices, inNormal, inDiffuse, inSpecular); while(light = light->next) { light->pfnLightNext(pv, count, batchBuffer, light, (D3DVERTEX*)in, inWeights, inMatrixIndices, inNormal, inDiffuse, inSpecular); } } // Copy vertices from the batch buffer to the output BATCHBUFFER *buf = batchBuffer; dnl define(`d_OutDiffuse',buf->diffuse)dnl define(`d_OutSpecular',buf->specular)dnl define(`d_dwOutSpecular',*pOutSpecular)dnl define(`d_dwOutDiffuse',*pOutDiffuse)dnl define(`d_LightingFlags',buf->dwFlags)dnl dnl if (pv->dwFlags & D3DPV_DOCOLORVERTEX) { for (DWORD i = count; i; i--) { d_MakeOutputColors(5) buf++; NEXT(pOutSpecular, dwOutVerSize, DWORD); NEXT(pOutDiffuse, dwOutVerSize, DWORD); NEXT(inDiffuse, pv->diffuse.dwStride, DWORD); NEXT(inSpecular, pv->specular.dwStride, DWORD); } } else { for (DWORD i = count; i; i--) { d_MakeOutputColorsNoColorVertex(5) buf++; NEXT(pOutSpecular, dwOutVerSize, DWORD); NEXT(pOutDiffuse, dwOutVerSize, DWORD); NEXT(inDiffuse, pv->diffuse.dwStride, DWORD); NEXT(inSpecular, pv->specular.dwStride, DWORD); } } } else { // If there is no lighting, we have to copy vertex color or // default color to the output if (!(pv->dwFlags & D3DPV_DONOTCOPYDIFFUSE)) { if (pv->dwVIDIn & D3DFVF_DIFFUSE) { for (DWORD i = count; i; i--) { *pOutDiffuse = *inDiffuse; NEXT(pOutDiffuse, dwOutVerSize, DWORD); NEXT(inDiffuse, pv->diffuse.dwStride, DWORD); } } else { for (DWORD i = count; i; i--) { *pOutDiffuse = __DEFAULT_DIFFUSE; NEXT(pOutDiffuse, dwOutVerSize, DWORD); } } } if (!(pv->dwFlags & D3DPV_DONOTCOPYSPECULAR)) { if (pv->dwVIDIn & D3DFVF_SPECULAR) { for (DWORD i = count; i; i--) { *pOutSpecular = *inSpecular; NEXT(pOutSpecular, dwOutVerSize, DWORD); NEXT(inSpecular, pv->specular.dwStride, DWORD); } } else { for (DWORD i = count; i; i--) { *pOutSpecular = __DEFAULT_SPECULAR; NEXT(pOutSpecular, dwOutVerSize, DWORD); } } } } if (pv->dwFlags & D3DPV_FOG) { BATCHBUFFER* buf = batchBuffer; D3DVECTOR* pVertex = in; D3DVALUE* pWeights = inWeights; BYTE* pMatrixIndices = inMatrixIndices; for (DWORD i = count; i; i--) { D3DVALUE dist; // Vertex is already transformed to the camera space if (dwDeviceFlags & D3DDEV_RANGEBASEDFOG) dist = SQRTF(buf->position.x*buf->position.x + buf->position.y*buf->position.y + buf->position.z*buf->position.z); else dist = ABSF(buf->position.z); ComputeFogFactor(pv, dist, pOutFogFactor); NEXT(pVertex, pv->position.dwStride, D3DVECTOR); NEXT(pWeights, pv->weights.dwStride, D3DVALUE); NEXT(pMatrixIndices, pv->matrixIndices.dwStride, BYTE); NEXT(pOutFogFactor, dwOutVerSize, DWORD); buf++; } } if (pv->dwVIDOut & D3DFVF_PSIZE) { float PointSize; BATCHBUFFER *buf = batchBuffer; for (DWORD i = count; i; i--) { if (pv->dwVIDIn & D3DFVF_PSIZE) PointSize = *inPointSize; else PointSize = PointSizeRs; if (bDoPointScale) { float dist = SQRTF(buf->position.x*buf->position.x + buf->position.y*buf->position.y + buf->position.z*buf->position.z); float v = A + B*dist + C*dist*dist; if (v <= 0) { PointSize = pv->PointSizeMax; } else { // Clamping of the point size to [PointSizeMin, PointSizeMax] // will be done by hardware or when we expand points float PointSizeScale = pv->vcache.dvHeight * (float)sqrt(1.0/v); PointSize *= PointSizeScale; } buf++; } *pOutPointSize = PointSize; NEXT(pOutPointSize, dwOutVerSize, float); NEXT(inPointSize, pv->psize.dwStride, float); } } // Process texture coordinates if (dwNumTexCoord != 0) { if (pv->dwDeviceFlags & D3DDEV_STRIDE) { if (!(pv->dwDeviceFlags & (D3DDEV_TEXTURETRANSFORM | D3DDEV_REMAPTEXTUREINDICES))) { for (DWORD i=count; i; i--) { D3DVALUE *pTexture = pOutTexture; for (DWORD k=0; k < dwNumTexCoord; k++) { const DWORD dwSize = pv->dwTextureCoordSize[k]; memcpy(pTexture, inTexture[k], dwSize); pTexture = (D3DVALUE*)((char*)pTexture + dwSize); NEXT(inTexture[k], pv->textures[k].dwStride, D3DVALUE); } NEXT(pOutTexture, dwOutVerSize, D3DVALUE); } } else { if (!(pv->dwDeviceFlags & D3DDEV_REMAPTEXTUREINDICES)) { D3DVALUE *pOut = pOutTexture; for (DWORD k=0; k < dwNumTexCoord; k++) { const DWORD dwSize = pv->dwTextureCoordSize[k]; const DWORD dwInpSize = pv->dwInpTextureCoordSize[k]; const DWORD dwStride = pv->textures[k].dwStride; D3DVALUE *pInpTexture = inTexture[k]; if (pv->pmTexture[k] == NULL) { D3DVALUE *pOutTmp = pOut; for (DWORD i=count; i; i--) { memcpy(pOutTmp, pInpTexture, dwSize); NEXT(pInpTexture, dwStride, D3DVALUE); NEXT(pOutTmp, dwOutVerSize, D3DVALUE); } } else { const DWORD n = dwSize >> 2; // Number of input tex. coord. const DWORD m = dwInpSize >> 2; // Number of input tex. coord. (*(g_pfnTextureTransformLoop[MakeTexTransformFuncIndex(m, n)])) (pInpTexture, pOut, pv->pmTexture[k], count, dwStride, dwOutVerSize); } NEXT(pOut, dwSize, D3DVALUE); NEXT(inTexture[k], dwStride*BATCH_SIZE, D3DVALUE); } NEXT(pOutTexture, dwOutVerSizeBatch, D3DVALUE); } else { D3DVALUE *pOut = pOutTexture; for (DWORD k=0; k < pv->dwNumTextureStages; k++) { const LPD3DFE_TEXTURESTAGE pStage = &pv->textureStage[k]; const DWORD dwOutTexSize = pv->dwTextureCoordSize[k]; DWORD dwStride; D3DVALUE *pIn; D3DVECTOR reflectionVector[BATCH_SIZE]; if (pStage->dwTexGenMode == 0) { const DWORD dwInpIndex = pStage->dwInpCoordIndex; pIn = inTexture[dwInpIndex]; dwStride = pv->textures[dwInpIndex].dwStride; } else if (pStage->dwTexGenMode == D3DTSS_TCI_CAMERASPACEPOSITION) { pIn = (D3DVALUE*)&batchBuffer[0].position; dwStride = sizeof(BATCHBUFFER); } else if (pStage->dwTexGenMode == D3DTSS_TCI_CAMERASPACENORMAL) { pIn = (D3DVALUE*)&batchBuffer[0].normal; dwStride = sizeof(BATCHBUFFER); } else // D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR { if (pv->dwDeviceFlags & D3DDEV_LOCALVIEWER) { for (DWORD i=0; i < count; i++) { ComputeReflectionVector(&batchBuffer[i].position, &batchBuffer[i].normal, &reflectionVector[i]); } } else { for (DWORD i=0; i < count; i++) { ComputeReflectionVectorInfiniteViewer(&batchBuffer[i].normal, &reflectionVector[i]); } } pIn = (D3DVALUE*)reflectionVector; dwStride = sizeof(D3DVECTOR); } if (pStage->bDoTextureProjection) { // We need to do emulation of texture projection if (pStage->pmTextureTransform == NULL) { D3DVALUE *pOutTmp = pOut; for (DWORD i=count; i; i--) { DoTextureProjection(pIn, pOutTmp, dwOutTexSize); NEXT(pIn, dwStride, D3DVALUE); NEXT(pOutTmp, dwOutVerSize, D3DVALUE); } } else { D3DVALUE *pOutTmp = pOut; for (DWORD i=count; i; i--) { float TmpOutputTexture[4]; (*(g_pfnTextureTransform[pStage->dwTexTransformFuncIndex])) (pIn, TmpOutputTexture, pStage->pmTextureTransform); DoTextureProjection(TmpOutputTexture, pOutTmp, dwOutTexSize); NEXT(pIn, dwStride, D3DVALUE); NEXT(pOutTmp, dwOutVerSize, D3DVALUE); } } } else if (pStage->pmTextureTransform == NULL) { D3DVALUE *pOutTmp = pOut; for (DWORD i=count; i; i--) { memcpy(pOutTmp, pIn, dwOutTexSize); NEXT(pIn, dwStride, D3DVALUE); NEXT(pOutTmp, dwOutVerSize, D3DVALUE); } } else { (*(g_pfnTextureTransformLoop[pStage->dwTexTransformFuncIndex])) (pIn, pOut, pStage->pmTextureTransform, count, dwStride, dwOutVerSize); } NEXT(pOut, dwOutTexSize, D3DVALUE); } NEXT(pOutTexture, dwOutVerSizeBatch, D3DVALUE); for (DWORD m=0; m < pv->nTexCoord; m++) { NEXT(inTexture[m], pv->textures[m].dwStride*BATCH_SIZE, D3DVALUE); } } } } else { if (!(pv->dwDeviceFlags & (D3DDEV_TEXTURETRANSFORM | D3DDEV_REMAPTEXTUREINDICES))) { for (DWORD i=count; i; i--) { memcpy(pOutTexture, inTexture[0], pv->dwTextureCoordSizeTotal); NEXT(pOutTexture, dwOutVerSize, D3DVALUE); NEXT(inTexture[0], dwInpVerSize, D3DVALUE); } } else if (!(pv->dwDeviceFlags & D3DDEV_REMAPTEXTUREINDICES)) { D3DVALUE *pIn = inTexture[0]; D3DVALUE *pOut = pOutTexture; for (DWORD k=0; k < dwNumTexCoord; k++) { const DWORD dwSize = pv->dwTextureCoordSize[k]; const DWORD dwInpSize = pv->dwInpTextureCoordSize[k]; if (pv->pmTexture[k] == NULL) { D3DVALUE *pOutTmp = pOut; D3DVALUE *pInpTmp = pIn; for (DWORD i=count; i; i--) { memcpy(pOutTmp, pInpTmp, dwSize); NEXT(pInpTmp, dwInpVerSize, D3DVALUE); NEXT(pOutTmp, dwOutVerSize, D3DVALUE); } } else { const DWORD n = dwSize >> 2; // Number of output tex. coord. const DWORD m = dwInpSize >> 2; // Number of input tex. coord. (*(g_pfnTextureTransformLoop[MakeTexTransformFuncIndex(m, n)])) (pIn, pOut, pv->pmTexture[k], count, dwInpVerSize, dwOutVerSize); } NEXT(pIn, dwInpSize, D3DVALUE); NEXT(pOut, dwSize, D3DVALUE); } NEXT(inTexture[0], dwInpVerSizeBatch, D3DVALUE); NEXT(pOutTexture, dwOutVerSizeBatch, D3DVALUE); } else { D3DVALUE *pOut = pOutTexture; for (DWORD i=0; i < pv->dwNumTextureStages; i++) { LPD3DFE_TEXTURESTAGE pStage = &pv->textureStage[i]; const DWORD dwSize = pv->dwTextureCoordSize[i]; D3DVALUE *pIn; DWORD dwStride; D3DVECTOR reflectionVector[BATCH_SIZE]; if (pStage->dwTexGenMode == 0) { pIn = (D3DVALUE*)((BYTE*)inTexture[0] + pStage->dwInpOffset); dwStride = dwInpVerSize; } else if (pStage->dwTexGenMode == D3DTSS_TCI_CAMERASPACEPOSITION) { pIn = (D3DVALUE*)&batchBuffer[0].position; dwStride = sizeof(BATCHBUFFER); } else if (pStage->dwTexGenMode == D3DTSS_TCI_CAMERASPACENORMAL) { pIn = (D3DVALUE*)&batchBuffer[0].normal; dwStride = sizeof(BATCHBUFFER); } else // D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR { if (pv->dwDeviceFlags & D3DDEV_LOCALVIEWER) { for (DWORD i=0; i < count; i++) { ComputeReflectionVector(&batchBuffer[i].position, &batchBuffer[i].normal, &reflectionVector[i]); } } else { for (DWORD i=0; i < count; i++) { ComputeReflectionVectorInfiniteViewer(&batchBuffer[i].normal, &reflectionVector[i]); } } pIn = (D3DVALUE*)reflectionVector; dwStride = sizeof(D3DVECTOR); } if (pStage->bDoTextureProjection) { // We need to do emulation of texture projection if (pStage->pmTextureTransform == NULL) { D3DVALUE *pOutTmp = pOut; for (DWORD i=count; i; i--) { DoTextureProjection(pIn, pOutTmp, dwSize); NEXT(pIn, dwStride, D3DVALUE); NEXT(pOutTmp, dwOutVerSize, D3DVALUE); } } else { D3DVALUE *pOutTmp = pOut; for (DWORD i=count; i; i--) { float TmpOutputTexture[4]; (*(g_pfnTextureTransform[pStage->dwTexTransformFuncIndex])) (pIn, TmpOutputTexture, pStage->pmTextureTransform); DoTextureProjection(TmpOutputTexture, pOutTmp, dwSize); NEXT(pIn, dwStride, D3DVALUE); NEXT(pOutTmp, dwOutVerSize, D3DVALUE); } } } else if (pStage->pmTextureTransform == NULL) { D3DVALUE *pOutTmp = pOut; for (DWORD i=count; i; i--) { memcpy(pOutTmp, pIn, dwSize); NEXT(pIn, dwStride, D3DVALUE); NEXT(pOutTmp, dwOutVerSize, D3DVALUE); } } else { (*(g_pfnTextureTransformLoop[pStage->dwTexTransformFuncIndex])) (pIn, pOut, pStage->pmTextureTransform, count, dwStride, dwOutVerSize); } NEXT(pOut, dwSize, D3DVALUE); } NEXT(inTexture[0], dwInpVerSizeBatch, D3DVALUE); NEXT(pOutTexture, dwOutVerSizeBatch, D3DVALUE); } } } if (count != count1) { pv->dwFirstClippedVertex = pv->dwNumVertices - dwNumVertices + count; break; } NEXT(inNormal, dwNormalStrideBatch, D3DVECTOR); NEXT(in, dwInpVerSizeBatch, D3DVECTOR); NEXT(inWeights, dwWeightsStrideBatch, D3DVALUE); NEXT(inMatrixIndices, dwMatrixIndicesStrideBatch, BYTE); dwNumVertices -= count; } while (dwNumVertices); // Restore original strides, because they could changed for tweening pv->position.dwStride = oldPositionStride; pv->normal.dwStride = oldNormalStride; return pv->dwClipIntersection; }