#include "pch.cpp" #pragma hdrstop //--------------------------------------------------------------------- // Constructor: // //--------------------------------------------------------------------- RefVP::RefVP() : m_LightArray(), m_LightVertexTable() { m_LightArray.SetGrowSize( 32 ); memset( &m_Material, 0, sizeof(m_Material) ); memset( &m_xfmProj, 0, sizeof(m_xfmProj) ); memset( &m_xfmView, 0, sizeof(m_xfmView) ); memset( m_xfmWorld, 0, sizeof(m_xfmView)*RD_MAX_WORLD_MATRICES ); m_dwTLState = 0; m_dwDirtyFlags = 0; memset( m_xfmCurrent, 0, sizeof(m_xfmView)*RD_MAX_WORLD_MATRICES ); memset( m_xfmToEye, 0, sizeof(m_xfmView)*RD_MAX_WORLD_MATRICES ); memset( m_xfmToEyeInv, 0, sizeof(m_xfmView)*RD_MAX_WORLD_MATRICES ); m_qwFVFIn = 0; m_numVertexBlends = 0; memset( &m_TransformData, 0, sizeof(m_TransformData) ) ; m_fPointSize = 0; m_fPointAttA = 0; m_fPointAttB = 0; m_fPointAttC = 0; m_fPointSizeMin = 0; m_fPointSizeMax = RD_MAX_POINT_SIZE; m_fTweenFactor = 0.0f; m_LightVertexTable.pfnDirectional = RDLV_Directional; m_LightVertexTable.pfnParallelPoint = RDLV_Directional; m_LightVertexTable.pfnSpot = RDLV_PointAndSpot; m_LightVertexTable.pfnPoint = RDLV_PointAndSpot; m_dwNumActiveTextureStages = 0; m_pDev = NULL; } //--------------------------------------------------------------------- // SetupStrides: //--------------------------------------------------------------------- HRESULT RefDev::SetupStrides() { RDVDeclaration& Decl = m_pCurrentVShader->m_Declaration; // Null out the PtrStrides m_RefVP.m_position.Null(); m_RefVP.m_position2.Null(); m_RefVP.m_blendweights.Null(); m_RefVP.m_blendindices.Null(); m_RefVP.m_normal.Null(); m_RefVP.m_normal2.Null(); m_RefVP.m_specular.Null(); m_RefVP.m_diffuse.Null(); m_RefVP.m_pointsize.Null(); for( int t = 0; t < 8 ; t++ ) m_RefVP.m_tex[t].Null(); for( DWORD i = 0; i < Decl.m_dwNumElements; i++ ) { RDVElement& Element = Decl.m_VertexElements[i]; RDVStream& Stream = m_VStream[Element.m_dwStreamIndex]; DWORD dwStride = Stream.m_dwStride; DWORD dwStartVertex = m_dwStartVertex; switch( Element.m_dwRegister ) { case D3DVSDE_POSITION: m_RefVP.m_position.Init( (LPBYTE)Stream.m_pData + Element.m_dwOffset + dwStride * dwStartVertex, dwStride ); break; case D3DVSDE_POSITION2: m_RefVP.m_position2.Init( (LPBYTE)Stream.m_pData + Element.m_dwOffset + dwStride * dwStartVertex, dwStride ); break; case D3DVSDE_BLENDWEIGHT: m_RefVP.m_blendweights.Init( (LPBYTE)Stream.m_pData + Element.m_dwOffset + dwStride * dwStartVertex, dwStride ); break; case D3DVSDE_BLENDINDICES: // This only happens if the vertex declaration is a non-FVF one _ASSERT( !RDVSD_ISLEGACY( m_CurrentVShaderHandle ), "FVF shader could not have provided a" " separate blend-index" ); m_RefVP.m_blendindices.Init( (LPBYTE)Stream.m_pData + Element.m_dwOffset + dwStride * dwStartVertex, dwStride ); break; case D3DVSDE_NORMAL: m_RefVP.m_normal.Init( (LPBYTE)Stream.m_pData + Element.m_dwOffset + dwStride * dwStartVertex, dwStride ); break; case D3DVSDE_NORMAL2: m_RefVP.m_normal2.Init( (LPBYTE)Stream.m_pData + Element.m_dwOffset + dwStride * dwStartVertex, dwStride ); break; case D3DVSDE_PSIZE: m_RefVP.m_pointsize.Init( (LPBYTE)Stream.m_pData + Element.m_dwOffset + dwStride * dwStartVertex, dwStride ); break; case D3DVSDE_DIFFUSE: m_RefVP.m_diffuse.Init( (LPBYTE)Stream.m_pData + Element.m_dwOffset + dwStride * dwStartVertex, dwStride ); break; case D3DVSDE_SPECULAR: m_RefVP.m_specular.Init( (LPBYTE)Stream.m_pData + Element.m_dwOffset + dwStride * dwStartVertex, dwStride ); break; case D3DVSDE_TEXCOORD0: m_RefVP.m_tex[0].Init( (LPBYTE)Stream.m_pData + Element.m_dwOffset + dwStride * dwStartVertex, dwStride ); break; case D3DVSDE_TEXCOORD1: m_RefVP.m_tex[1].Init( (LPBYTE)Stream.m_pData + Element.m_dwOffset + dwStride * dwStartVertex, dwStride ); break; case D3DVSDE_TEXCOORD2: m_RefVP.m_tex[2].Init( (LPBYTE)Stream.m_pData + Element.m_dwOffset + dwStride * dwStartVertex, dwStride ); break; case D3DVSDE_TEXCOORD3: m_RefVP.m_tex[3].Init( (LPBYTE)Stream.m_pData + Element.m_dwOffset + dwStride * dwStartVertex, dwStride ); break; case D3DVSDE_TEXCOORD4: m_RefVP.m_tex[4].Init( (LPBYTE)Stream.m_pData + Element.m_dwOffset + dwStride * dwStartVertex, dwStride ); break; case D3DVSDE_TEXCOORD5: m_RefVP.m_tex[5].Init( (LPBYTE)Stream.m_pData + Element.m_dwOffset + dwStride * dwStartVertex, dwStride ); break; case D3DVSDE_TEXCOORD6: m_RefVP.m_tex[6].Init( (LPBYTE)Stream.m_pData + Element.m_dwOffset + dwStride * dwStartVertex, dwStride ); break; case D3DVSDE_TEXCOORD7: m_RefVP.m_tex[7].Init( (LPBYTE)Stream.m_pData + Element.m_dwOffset + dwStride * dwStartVertex, dwStride ); break; } } // If it is a FVF vertex shader and index-vertex blending is enabled // then the blend-indices are found in the last beta (which is the blend // weight) of the vertex. if( RDVSD_ISLEGACY( m_CurrentVShaderHandle ) && (m_RefVP.m_dwTLState & RDPV_DOINDEXEDVERTEXBLEND) ) { m_RefVP.m_blendindices.Init( (float *)m_RefVP.m_blendweights.GetFirst() + m_RefVP.m_numVertexBlends, m_RefVP.m_blendweights.GetStride() ); } return S_OK; } /////////////////////////////////////////////////////////////////////////////// // Process primitives implementation: // 1) Compute FVF info // 2) Grow buffers to the requisite size // 3) Initialize clipping state // 4) Update T&L state // 5) Transform, Light and compute clipping for vertices // 6) Clip and Draw the primitives // /////////////////////////////////////////////////////////////////////////////// HRESULT RefDev::ProcessPrimitive( D3DPRIMITIVETYPE PrimType, DWORD StartVertex, DWORD cVertices, DWORD StartIndex, DWORD cIndices ) { HRESULT hr = D3D_OK; DWORD dwVertexPoolSize = 0; // Save Prim Type for later use m_primType = PrimType; m_dwNumVertices = cVertices; m_dwStartVertex = StartVertex; m_dwNumIndices = cIndices; m_dwStartIndex = StartIndex; // // Update T&L state (must be before FVFData is set up) // // Update Lighting and related state and flags and computes Output FVF HR_RET( UpdateTLState() ); // // Clipping information depends both on the output FVF computation // and the other State, so do it here after both have been computed // HR_RET( UpdateClipper() ); // // Grow TLVArray if required // if( FAILED( m_TLVArray.Grow( m_dwNumVertices ) ) ) { DPFERR( "Could not grow TL vertex buffer" ); return DDERR_OUTOFMEMORY; } // // Transform, Light and compute clipping for vertices // DWORD clipIntersection = m_RefVP.ProcessVertices( m_qwFVFOut, m_TLVArray, m_dwNumVertices ); if( m_primType == D3DPT_POINTLIST ) { // We clip points by Z planes and user clip planes, because point // sprites could be still visible when a point is outside X or Y plane clipIntersection &= ~(RDCLIP_LEFT | RDCLIP_RIGHT | RDCLIP_TOP | RDCLIP_BOTTOM | RDCLIPGB_ALL); } if( clipIntersection ) { // If the entire primitive lies outside the view frustum, quit // without drawing return D3D_OK; } // // Clip and Draw the primitives // if( m_dwNumIndices ) { if( !NeedClipping(m_Clipper.UseGuardBand(), m_Clipper.m_clipUnion) ) { if( m_IndexStream.m_dwStride == 4 ) hr = DrawOneIndexedPrimitive( m_TLVArray, 0, (LPDWORD)m_IndexStream.m_pData, m_dwStartIndex, m_dwNumIndices, m_primType ); else hr = DrawOneIndexedPrimitive( m_TLVArray, 0, (LPWORD)m_IndexStream.m_pData, m_dwStartIndex, m_dwNumIndices, m_primType ); } else { if( m_IndexStream.m_dwStride == 4 ) hr = m_Clipper.DrawOneIndexedPrimitive( m_TLVArray, 0, (LPDWORD)m_IndexStream.m_pData, m_dwStartIndex, m_dwNumIndices, m_primType ); else hr = m_Clipper.DrawOneIndexedPrimitive( m_TLVArray, 0, (LPWORD)m_IndexStream.m_pData, m_dwStartIndex, m_dwNumIndices, m_primType ); } } else { if( !NeedClipping((m_Clipper.UseGuardBand()), m_Clipper.m_clipUnion) ) { hr = DrawOnePrimitive( m_TLVArray, 0, m_primType, m_dwNumVertices ); } else { hr = m_Clipper.DrawOnePrimitive( m_TLVArray, 0, m_primType, m_dwNumVertices ); } } return hr; } //--------------------------------------------------------------------- // RefDev::UpdateTLState // Updates transform and lighting related state //--------------------------------------------------------------------- HRESULT RefDev::UpdateTLState() { HRESULT hr = D3D_OK; UINT64 qwFVFIn = m_RefVP.m_qwFVFIn; // // Sort out vertex blending. // // Total number of floats/dwords provided per vertex according to // the FVF. DWORD numBetas = 0; if( (qwFVFIn & D3DFVF_POSITION_MASK) != (D3DFVF_XYZ & D3DFVF_POSITION_MASK) ) numBetas = ((qwFVFIn & D3DFVF_POSITION_MASK) >> 1) - 2; DWORD numWeights = GetRS()[D3DRENDERSTATE_VERTEXBLEND]; // If tweening is enabled, there better be Position2 or Normal2 if( numWeights == D3DVBF_TWEENING ) { if( (qwFVFIn & (D3DFVFP_POSITION2 | D3DFVFP_NORMAL2)) == 0 ) { DPFERR( "Tweening is enabled, but there is neither position2" " nor normal2 available\n" ); return E_FAIL; } if( qwFVFIn & D3DFVFP_POSITION2 ) m_RefVP.m_dwTLState |= RDPV_DOPOSITIONTWEENING; if( qwFVFIn & D3DFVFP_NORMAL2 ) m_RefVP.m_dwTLState |= RDPV_DONORMALTWEENING; numWeights = 0; } else { m_RefVP.m_dwTLState &= ~(RDPV_DOPOSITIONTWEENING | RDPV_DONORMALTWEENING); } if( numWeights == D3DVBF_DISABLE ) m_RefVP.m_dwTLState &= ~RDPV_DOINDEXEDVERTEXBLEND; if( numWeights == D3DVBF_0WEIGHTS ) numWeights = 0; if( m_RefVP.m_dwTLState & RDPV_DOINDEXEDVERTEXBLEND ) { // If it is a FVF shader (legacy) the blend indices are provided as // the betas. There should be enough betas to cover this. if( RDVSD_ISLEGACY( m_CurrentVShaderHandle ) && (numBetas < (numWeights + 1)) ) { DPFERR( "Not enough blend-weights to do indexed vertex blending" ); return E_FAIL; } else if( !RDVSD_ISLEGACY( m_CurrentVShaderHandle ) && ((qwFVFIn & D3DFVFP_BLENDINDICES) == 0) ) { DPFERR( "Blend-indices not provided" ); return E_FAIL; } } else if( numWeights ) { if( numBetas < numWeights ) { DPFERR( "Not enough blend-weights to do vertex blending" ); return E_FAIL; } } m_RefVP.m_numVertexBlends = numWeights; // // Check prim type to see if point size computation is needed // Need to set this before the transform state is set // m_RefVP.m_dwTLState &= ~(RDPV_DOCOMPUTEPOINTSIZE | RDPV_DOPOINTSCALE); switch(m_primType) { case D3DPT_POINTLIST: m_RefVP.m_dwTLState |= RDPV_DOCOMPUTEPOINTSIZE; if( GetRS()[D3DRS_POINTSCALEENABLE] ) m_RefVP.m_dwTLState |= RDPV_DOPOINTSCALE; break; } // Fog or not: // Compute fog if: 1) Fogging is enabled // 2) VertexFog mode is not FOG_NONE // 3) TableFog mode is FOG_NONE // If both table and vertex fog are not FOG_NONE, table fog // is applied. if( GetRS()[D3DRENDERSTATE_FOGENABLE] && GetRS()[D3DRENDERSTATE_FOGVERTEXMODE] && !GetRS()[D3DRENDERSTATE_FOGTABLEMODE] ) { m_RefVP.m_dwTLState |= RDPV_DOFOG; // Range Fog if( GetRS()[D3DRENDERSTATE_RANGEFOGENABLE] ) { m_RefVP.m_dwTLState |= RDPV_RANGEFOG; } else { m_RefVP.m_dwTLState &= ~RDPV_RANGEFOG; } } else { m_RefVP.m_dwTLState &= ~(RDPV_DOFOG | RDPV_RANGEFOG); } // // Evaluate if any texture transform/gen is required. If so, then compute // the output Texture Coordinates // UpdateActiveTexStageCount(); m_RefVP.m_dwNumActiveTextureStages = m_cActiveTextureStages; m_RefVP.m_dwTLState &= ~(RDPV_DOTEXGEN | RDPV_DOTEXXFORM | RDPV_NEEDEYENORMAL | RDPV_NEEDEYEXYZ); for( DWORD dwStage=0; dwStage<(DWORD)m_cActiveTextureStages; dwStage++ ) { if( (GetTSS(dwStage)[D3DTSS_TEXTURETRANSFORMFLAGS] & ~D3DTTFF_PROJECTED) != D3DTTFF_DISABLE ) { m_RefVP.m_dwTLState |= RDPV_DOTEXXFORM; } if( GetTSS(dwStage)[D3DTSS_TEXCOORDINDEX] & 0xffff0000 ) { m_RefVP.m_dwTLState |= RDPV_DOTEXGEN; } } // Something changed in the transformation state // Recompute digested transform state HR_RET(m_RefVP.UpdateXformData()); // Something changed in the lighting state if( (m_RefVP.m_dwTLState & RDPV_DOLIGHTING) && (m_RefVP.m_dwDirtyFlags & RDPV_DIRTY_LIGHTING) ) { RDLIGHTINGDATA& LData = m_RefVP.m_lighting; // // Compute Colorvertex flags only if the lighting is enabled // m_RefVP.m_dwTLState &= ~RDPV_COLORVERTEXFLAGS; LData.pAmbientSrc = &LData.matAmb; LData.pDiffuseSrc = &LData.matDiff; LData.pSpecularSrc = &LData.matSpec; LData.pEmissiveSrc = &LData.matEmis; LData.pDiffuseAlphaSrc = &LData.materialDiffAlpha; LData.pSpecularAlphaSrc = &LData.materialSpecAlpha; if( GetRS()[D3DRENDERSTATE_COLORVERTEX] ) { switch( GetRS()[D3DRENDERSTATE_AMBIENTMATERIALSOURCE] ) { case D3DMCS_MATERIAL: break; case D3DMCS_COLOR1: { if( qwFVFIn & D3DFVF_DIFFUSE ) { m_RefVP.m_dwTLState |= (RDPV_VERTEXDIFFUSENEEDED | RDPV_COLORVERTEXAMB); LData.pAmbientSrc = &LData.vertexDiffuse; } } break; case D3DMCS_COLOR2: { if( qwFVFIn & D3DFVF_SPECULAR ) { m_RefVP.m_dwTLState |= (RDPV_VERTEXSPECULARNEEDED | RDPV_COLORVERTEXAMB); LData.pAmbientSrc = &LData.vertexSpecular; } } break; } switch( GetRS()[D3DRENDERSTATE_DIFFUSEMATERIALSOURCE] ) { case D3DMCS_MATERIAL: break; case D3DMCS_COLOR1: { if( qwFVFIn & D3DFVF_DIFFUSE ) { m_RefVP.m_dwTLState |= (RDPV_VERTEXDIFFUSENEEDED | RDPV_COLORVERTEXDIFF); LData.pDiffuseSrc = &LData.vertexDiffuse; LData.pDiffuseAlphaSrc = &LData.vertexDiffAlpha; } } break; case D3DMCS_COLOR2: { if( qwFVFIn & D3DFVF_SPECULAR ) { m_RefVP.m_dwTLState |= (RDPV_VERTEXSPECULARNEEDED | RDPV_COLORVERTEXDIFF); LData.pDiffuseSrc = &LData.vertexSpecular; LData.pDiffuseAlphaSrc = &LData.vertexSpecAlpha; } } break; } switch( GetRS()[D3DRENDERSTATE_SPECULARMATERIALSOURCE] ) { case D3DMCS_MATERIAL: break; case D3DMCS_COLOR1: { if( qwFVFIn & D3DFVF_DIFFUSE ) { m_RefVP.m_dwTLState |= (RDPV_VERTEXDIFFUSENEEDED | RDPV_COLORVERTEXSPEC); LData.pSpecularSrc = &LData.vertexDiffuse; LData.pSpecularAlphaSrc = &LData.vertexDiffAlpha; } } break; case D3DMCS_COLOR2: { if( qwFVFIn & D3DFVF_SPECULAR ) { m_RefVP.m_dwTLState |= (RDPV_VERTEXSPECULARNEEDED | RDPV_COLORVERTEXSPEC); LData.pSpecularSrc = &LData.vertexSpecular; LData.pSpecularAlphaSrc = &LData.vertexSpecAlpha; } } break; } switch( GetRS()[D3DRENDERSTATE_EMISSIVEMATERIALSOURCE] ) { case D3DMCS_MATERIAL: break; case D3DMCS_COLOR1: { if( qwFVFIn & D3DFVF_DIFFUSE ) { m_RefVP.m_dwTLState |= (RDPV_VERTEXDIFFUSENEEDED | RDPV_COLORVERTEXEMIS); LData.pEmissiveSrc = &LData.vertexDiffuse; } } break; case D3DMCS_COLOR2: { if( qwFVFIn & D3DFVF_SPECULAR ) { m_RefVP.m_dwTLState |= (RDPV_VERTEXSPECULARNEEDED | RDPV_COLORVERTEXEMIS); LData.pEmissiveSrc = &LData.vertexSpecular; } } break; } } // If specular is needed in the output and has been provided // in the input, force the copy of specular data if( qwFVFIn & D3DFVF_SPECULAR ) { m_RefVP.m_dwTLState |= RDPV_VERTEXSPECULARNEEDED; } // // Update the remaining light state // HR_RET(m_RefVP.UpdateLightingData()); } if( (m_RefVP.m_dwTLState & RDPV_DOFOG) && (m_RefVP.m_dwDirtyFlags & RDPV_DIRTY_FOG) ) { HR_RET(m_RefVP.UpdateFogData()); } // // Compute output FVF // BOOL bFogEnabled = GetRS()[D3DRENDERSTATE_FOGENABLE]; BOOL bSpecularEnabled = GetRS()[D3DRENDERSTATE_SPECULARENABLE]; m_qwFVFOut = D3DFVF_XYZRHW; // If normal is present we have to compute specular and diffuse // Otherwise set these bits the same as input. // Not that normal should not be present for XYZRHW position type if( m_RefVP.m_dwTLState & RDPV_DOLIGHTING ) { m_qwFVFOut |= D3DFVF_DIFFUSE | D3DFVF_SPECULAR; } else { m_qwFVFOut |= (qwFVFIn & (D3DFVF_DIFFUSE | D3DFVF_SPECULAR)); } // Clear specular flag if specular disabled // else if( !this->rstates[D3DRENDERSTATE_SPECULARENABLE] ) if( !bSpecularEnabled && ((qwFVFIn & D3DFVF_SPECULAR) == 0)) { m_qwFVFOut &= ~D3DFVF_SPECULAR; } // Always set specular flag if fog is enabled // if( this->rstates[D3DRENDERSTATE_FOGENABLE] ) if( bFogEnabled && (!GetRS()[D3DRENDERSTATE_FOGTABLEMODE]) ) { m_qwFVFOut |= D3DFVFP_FOG; } // Reserve space for point size, if needed if( m_RefVP.m_dwTLState & RDPV_DOCOMPUTEPOINTSIZE ) { m_qwFVFOut |= D3DFVF_PSIZE; } if( m_RefVP.m_dwTLState & (RDPV_DOTEXGEN | RDPV_DOTEXXFORM) ) { // If there was any need for TexTransform or TexGen, we need to // override the TCI per stage. m_bOverrideTCI = TRUE; m_qwFVFOut |= (m_cActiveTextureStages << D3DFVF_TEXCOUNT_SHIFT); // Now compute the texture formats for( dwStage = 0; dwStage < (DWORD)m_cActiveTextureStages; dwStage++ ) { DWORD dwTextureFormat = 0; DWORD TCI = GetTSS(dwStage)[D3DTSS_TEXCOORDINDEX]; DWORD TexGenMode = TCI & ~0xFFFF; TCI &= 0xFFFF; switch( TexGenMode ) { case D3DTSS_TCI_CAMERASPACENORMAL: dwTextureFormat = D3DFVF_TEXCOORDSIZE3(dwStage); m_RefVP.m_dwTLState |= RDPV_NEEDEYENORMAL; break; case D3DTSS_TCI_CAMERASPACEPOSITION: dwTextureFormat = D3DFVF_TEXCOORDSIZE3(dwStage); m_RefVP.m_dwTLState |= RDPV_NEEDEYEXYZ; break; case D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR: dwTextureFormat = D3DFVF_TEXCOORDSIZE3(dwStage); m_RefVP.m_dwTLState |= (RDPV_NEEDEYENORMAL | RDPV_NEEDEYEXYZ); break; case 0: // No TexGen // Set back the input texture format dwTextureFormat = (D3DFVF_GETTEXCOORDSIZE( qwFVFIn, TCI ) << (dwStage*2 + 16)); break; default: DPFERR( "Unknown TexGen mode" ); return E_FAIL; } // Adjust the format for TexTransform DWORD TexXfmFlags = GetTSS(dwStage)[D3DTSS_TEXTURETRANSFORMFLAGS]; if( TexXfmFlags ) { switch( TexXfmFlags & ~D3DTTFF_PROJECTED ) { case D3DTTFF_DISABLE: break; case D3DTTFF_COUNT1: dwTextureFormat = D3DFVF_TEXCOORDSIZE1( dwStage ); break; case D3DTTFF_COUNT2: dwTextureFormat = D3DFVF_TEXCOORDSIZE2( dwStage ); break; case D3DTTFF_COUNT3: dwTextureFormat = D3DFVF_TEXCOORDSIZE3( dwStage ); break; case D3DTTFF_COUNT4: dwTextureFormat = D3DFVF_TEXCOORDSIZE4( dwStage ); break; default: DPFERR( "Unknown dimension" ); return E_FAIL; } } m_qwFVFOut |= dwTextureFormat; } } else { // Set up number of texture coordinates and copy texture formats DWORD numTex = FVF_TEXCOORD_NUMBER(qwFVFIn); m_qwFVFOut |= (numTex << D3DFVF_TEXCOUNT_SHIFT) | (qwFVFIn & 0xFFFF0000); } m_RefVP.m_qwFVFOut = m_qwFVFOut; // // Set up the strides for the vertex processing // return SetupStrides(); } /////////////////////////////////////////////////////////////////////////////// // RefVP method implementations /////////////////////////////////////////////////////////////////////////////// //--------------------------------------------------------------------- // RefVP::ProcessVertices //--------------------------------------------------------------------- RDCLIPCODE RefVP::ProcessVertices( UINT64 outFVF, GArrayT& VtxArray, DWORD count ) { UINT64 inFVF = m_qwFVFIn; RefClipper& Clipper = m_pDev->GetClipper(); DWORD flags = m_dwTLState; RDCLIPCODE clipIntersection = ~0; RDCLIPCODE clipUnion = 0; RDLIGHTINGELEMENT le; BOOL bVertexInEyeSpace = FALSE; DWORD dwCurrVtx = 0; RDVECTOR3* pPos = (RDVECTOR3 *)m_position.GetFirst(); RDVECTOR3* pPos2 = (RDVECTOR3 *)m_position2.GetFirst(); float* pBlendFactors = (float *)m_blendweights.GetFirst(); DWORD* pBlendIndices = (DWORD *)m_blendindices.GetFirst(); RDVECTOR3* pNormal = (RDVECTOR3 *)m_normal.GetFirst(); RDVECTOR3* pNormal2 = (RDVECTOR3 *)m_normal2.GetFirst(); DWORD* pDiffuse = (DWORD *)m_diffuse.GetFirst(); DWORD* pSpecular = (DWORD *)m_specular.GetFirst(); float* pPointSize = (float *)m_pointsize.GetFirst(); float* pTex[8]; RDVECTOR3 positionT, normalT; // Tweening results are saved here for( int t = 0; t < 8 ; t++ ) pTex[t] = (float *)m_tex[t].GetFirst(); // // Number of vertices to blend. i.e number of blend-matrices to // use is numVertexBlends+1. // int numVertexBlends = m_numVertexBlends; m_lighting.outDiffuse = RD_DEFAULT_DIFFUSE; m_lighting.outSpecular = RD_DEFAULT_SPECULAR; // // The main transform loop // for( DWORD i = count; i; i-- ) { RDVertex& Vout = VtxArray[dwCurrVtx++]; Vout.SetFVF( outFVF | D3DFVFP_CLIP ); float x_clip=0.0f, y_clip=0.0f, z_clip=0.0f, w_clip=0.0f; float inv_w_clip=0.0f; float cumulBlend = 0; // Blend accumulated so far ZeroMemory( &le, sizeof(RDLIGHTINGELEMENT) ); RDVECTOR3 ZeroNormal; if( pNormal == NULL ) { pNormal = &ZeroNormal; } // // Transform vertex to the clipping space, and position and normal // into eye space, if needed. // // Tween the Position if needed if( flags & RDPV_DOPOSITIONTWEENING ) { positionT.x = pPos->x * (1.0f - m_fTweenFactor) + pPos2->x * m_fTweenFactor; positionT.y = pPos->y * (1.0f - m_fTweenFactor) + pPos2->y * m_fTweenFactor; positionT.z = pPos->z * (1.0f - m_fTweenFactor) + pPos2->z * m_fTweenFactor; pPos = &positionT; } if( flags & RDPV_DONORMALTWEENING ) { normalT.x = pNormal->x * (1.0f - m_fTweenFactor) + pNormal2->x * m_fTweenFactor; normalT.y = pNormal->y * (1.0f - m_fTweenFactor) + pNormal2->y * m_fTweenFactor; normalT.z = pNormal->z * (1.0f - m_fTweenFactor) + pNormal2->z * m_fTweenFactor; pNormal = &normalT; } for( int j=0; j<=numVertexBlends; j++) { float blend; if( numVertexBlends == 0 ) { blend = 1.0f; } else if( j == numVertexBlends ) { blend = 1.0f - cumulBlend; } else { blend = pBlendFactors[j]; cumulBlend += pBlendFactors[j]; } if( flags & (RDPV_DOCOMPUTEPOINTSIZE | RDPV_DOLIGHTING | RDPV_NEEDEYEXYZ) ) { bVertexInEyeSpace = TRUE; if( flags & RDPV_DOINDEXEDVERTEXBLEND ) { BYTE m = ((BYTE *)pBlendIndices)[j]; UpdateWorld( m ); le.dvPosition.x += (pPos->x*m_xfmToEye[m]._11 + pPos->y*m_xfmToEye[m]._21 + pPos->z*m_xfmToEye[m]._31 + m_xfmToEye[m]._41) * blend; le.dvPosition.y += (pPos->x*m_xfmToEye[m]._12 + pPos->y*m_xfmToEye[m]._22 + pPos->z*m_xfmToEye[m]._32 + m_xfmToEye[m]._42) * blend; le.dvPosition.z += (pPos->x*m_xfmToEye[m]._13 + pPos->y*m_xfmToEye[m]._23 + pPos->z*m_xfmToEye[m]._33 + m_xfmToEye[m]._43) * blend; } else { le.dvPosition.x += (pPos->x*m_xfmToEye[j]._11 + pPos->y*m_xfmToEye[j]._21 + pPos->z*m_xfmToEye[j]._31 + m_xfmToEye[j]._41) * blend; le.dvPosition.y += (pPos->x*m_xfmToEye[j]._12 + pPos->y*m_xfmToEye[j]._22 + pPos->z*m_xfmToEye[j]._32 + m_xfmToEye[j]._42) * blend; le.dvPosition.z += (pPos->x*m_xfmToEye[j]._13 + pPos->y*m_xfmToEye[j]._23 + pPos->z*m_xfmToEye[j]._33 + m_xfmToEye[j]._43) * blend; } } if( flags & (RDPV_DOLIGHTING | RDPV_NEEDEYENORMAL) ) { if( flags & RDPV_DOINDEXEDVERTEXBLEND ) { BYTE m = ((BYTE *)pBlendIndices)[j]; UpdateWorld( m ); le.dvNormal.x += (pNormal->x*m_xfmToEyeInv[m]._11 + pNormal->y*m_xfmToEyeInv[m]._12 + pNormal->z*m_xfmToEyeInv[m]._13) * blend; le.dvNormal.y += (pNormal->x*m_xfmToEyeInv[m]._21 + pNormal->y*m_xfmToEyeInv[m]._22 + pNormal->z*m_xfmToEyeInv[m]._23) * blend; le.dvNormal.z += (pNormal->x*m_xfmToEyeInv[m]._31 + pNormal->y*m_xfmToEyeInv[m]._32 + pNormal->z*m_xfmToEyeInv[m]._33) * blend; } else { // Transform vertex normal to the eye space // We use inverse transposed matrix le.dvNormal.x += (pNormal->x*m_xfmToEyeInv[j]._11 + pNormal->y*m_xfmToEyeInv[j]._12 + pNormal->z*m_xfmToEyeInv[j]._13) * blend; le.dvNormal.y += (pNormal->x*m_xfmToEyeInv[j]._21 + pNormal->y*m_xfmToEyeInv[j]._22 + pNormal->z*m_xfmToEyeInv[j]._23) * blend; le.dvNormal.z += (pNormal->x*m_xfmToEyeInv[j]._31 + pNormal->y*m_xfmToEyeInv[j]._32 + pNormal->z*m_xfmToEyeInv[j]._33) * blend; } } if( flags & RDPV_DOINDEXEDVERTEXBLEND ) { BYTE m = ((BYTE *)pBlendIndices)[j]; UpdateWorld( m ); x_clip += (pPos->x*m_xfmCurrent[m]._11 + pPos->y*m_xfmCurrent[m]._21 + pPos->z*m_xfmCurrent[m]._31 + m_xfmCurrent[m]._41) * blend; y_clip += (pPos->x*m_xfmCurrent[m]._12 + pPos->y*m_xfmCurrent[m]._22 + pPos->z*m_xfmCurrent[m]._32 + m_xfmCurrent[m]._42) * blend; z_clip += (pPos->x*m_xfmCurrent[m]._13 + pPos->y*m_xfmCurrent[m]._23 + pPos->z*m_xfmCurrent[m]._33 + m_xfmCurrent[m]._43) * blend; w_clip += (pPos->x*m_xfmCurrent[m]._14 + pPos->y*m_xfmCurrent[m]._24 + pPos->z*m_xfmCurrent[m]._34 + m_xfmCurrent[m]._44) * blend; } else { // Apply WORLDj x_clip += (pPos->x*m_xfmCurrent[j]._11 + pPos->y*m_xfmCurrent[j]._21 + pPos->z*m_xfmCurrent[j]._31 + m_xfmCurrent[j]._41) * blend; y_clip += (pPos->x*m_xfmCurrent[j]._12 + pPos->y*m_xfmCurrent[j]._22 + pPos->z*m_xfmCurrent[j]._32 + m_xfmCurrent[j]._42) * blend; z_clip += (pPos->x*m_xfmCurrent[j]._13 + pPos->y*m_xfmCurrent[j]._23 + pPos->z*m_xfmCurrent[j]._33 + m_xfmCurrent[j]._43) * blend; w_clip += (pPos->x*m_xfmCurrent[j]._14 + pPos->y*m_xfmCurrent[j]._24 + pPos->z*m_xfmCurrent[j]._34 + m_xfmCurrent[j]._44) * blend; } } // Save the clip-coordinates Vout.m_clip_x = x_clip; Vout.m_clip_y = y_clip; Vout.m_clip_z = z_clip; Vout.m_clip_w = w_clip; if( (flags & RDPV_NORMALIZENORMALS) && (flags & (RDPV_DOLIGHTING | RDPV_NEEDEYENORMAL)) ) { Normalize(le.dvNormal); } FLOAT fPointSize = 0.0f; if( flags & RDPV_DOCOMPUTEPOINTSIZE ) { FLOAT fDist = (FLOAT)sqrt(le.dvPosition.x*le.dvPosition.x + le.dvPosition.y*le.dvPosition.y + le.dvPosition.z*le.dvPosition.z); if( inFVF & D3DFVF_PSIZE ) { fPointSize = *pPointSize; } else { // from D3DRENDERSTATE_POINTSIZE fPointSize = m_fPointSize; } if( flags & RDPV_DOPOINTSCALE ) { fPointSize = (float)Clipper.m_Viewport.dwHeight* fPointSize*(FLOAT)sqrt(1.0f/ (m_fPointAttA + m_fPointAttB*fDist + m_fPointAttC*fDist*fDist)); } fPointSize = max(m_fPointSizeMin, fPointSize); fPointSize = min(m_fPointSizeMax, fPointSize); FLOAT *pfSOut = &Vout.m_pointsize; *pfSOut = fPointSize; } // // Compute clip codes if needed // if( flags & RDPV_DOCLIPPING ) { RDCLIPCODE clip = Clipper.ComputeClipCodes( &clipIntersection, &clipUnion, x_clip, y_clip, z_clip, w_clip); if( clip == 0 ) { Vout.m_clip = 0; inv_w_clip = D3DVAL(1)/w_clip; } else { if( Clipper.UseGuardBand() ) { if( (clip & ~RDCLIP_INGUARDBAND) == 0 ) { // If vertex is inside the guardband we have to compute // screen coordinates inv_w_clip = D3DVAL(1)/w_clip; Vout.m_clip = (RDCLIPCODE)clip; goto l_DoScreenCoord; } } Vout.m_clip = (RDCLIPCODE)clip; // If vertex is outside the frustum we can not compute screen // coordinates. skip to lighting #if 0 Vout.m_pos.x = x_clip; Vout.m_pos.y = y_clip; Vout.m_pos.z = z_clip; Vout.m_rhw = w_clip; #endif goto l_DoLighting; } } else { // 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 "we" is zero. if( !FLOAT_EQZ(w_clip) ) inv_w_clip = D3DVAL(1)/w_clip; else inv_w_clip = __HUGE_PWR2; } l_DoScreenCoord: Vout.m_pos.x = x_clip * inv_w_clip * Clipper.scaleX + Clipper.offsetX; Vout.m_pos.y = y_clip * inv_w_clip * Clipper.scaleY + Clipper.offsetY; Vout.m_pos.z = z_clip * inv_w_clip * Clipper.scaleZ + Clipper.offsetZ; Vout.m_rhw = inv_w_clip; l_DoLighting: if( flags & RDPV_DOLIGHTING ) { bVertexInEyeSpace = TRUE; // // If Diffuse color is needed, extract it for color vertex. // if( flags & RDPV_VERTEXDIFFUSENEEDED ) { const DWORD color = *pDiffuse; MakeRDCOLOR3(&m_lighting.vertexDiffuse, color); m_lighting.vertexDiffAlpha = color & 0xff000000; } // // If Specular color is needed and provided // , extract it for color vertex. // if( flags & RDPV_VERTEXSPECULARNEEDED ) { const DWORD color = *pSpecular; MakeRDCOLOR3(&m_lighting.vertexSpecular, color); m_lighting.vertexSpecAlpha = color & 0xff000000; } // // Light the vertex // LightVertex( &le ); if( outFVF & D3DFVFP_FOG ) { Vout.m_fog = (FLOAT)RGBA_GETALPHA( *(m_lighting.pSpecularAlphaSrc) )/255.0f; } } else if( inFVF & (D3DFVF_DIFFUSE | D3DFVF_SPECULAR) ) { if( inFVF & D3DFVF_DIFFUSE ) m_lighting.outDiffuse = *pDiffuse; if( inFVF & D3DFVF_SPECULAR ) { m_lighting.outSpecular = *pSpecular; if( outFVF & D3DFVFP_FOG ) { Vout.m_fog = (FLOAT)RGBA_GETALPHA( *pSpecular )/255.0f; } } } // // Compute Vertex Fog if needed // if( flags & RDPV_DOFOG ) { FogVertex( Vout, *pPos, &le, numVertexBlends, pBlendFactors, bVertexInEyeSpace ); } if( outFVF & D3DFVF_DIFFUSE ) { MakeRDCOLOR4( &Vout.m_diffuse, m_lighting.outDiffuse ); } if( outFVF & D3DFVF_SPECULAR ) { MakeRDCOLOR4( &Vout.m_specular, m_lighting.outSpecular ); } if( flags & (RDPV_DOTEXGEN | RDPV_DOTEXXFORM) ) { for( DWORD dwStage = 0; dwStage < m_dwNumActiveTextureStages; dwStage++ ) { DWORD TexXfmFlags = m_pDev->GetTSS(dwStage)[D3DTSS_TEXTURETRANSFORMFLAGS]; DWORD TCI = m_pDev->GetTSS(dwStage)[D3DTSS_TEXCOORDINDEX]; DWORD TexGenMode = TCI & ~0xFFFF; TCI &= 0xFFFF; // Perform TexGen switch( TexGenMode ) { case D3DTSS_TCI_CAMERASPACENORMAL: Vout.m_tex[dwStage].x = le.dvNormal.x; Vout.m_tex[dwStage].y = le.dvNormal.y; Vout.m_tex[dwStage].z = le.dvNormal.z; Vout.m_tex[dwStage].w = 1.0f; break; case D3DTSS_TCI_CAMERASPACEPOSITION: Vout.m_tex[dwStage].x = le.dvPosition.x; Vout.m_tex[dwStage].y = le.dvPosition.y; Vout.m_tex[dwStage].z = le.dvPosition.z; Vout.m_tex[dwStage].w = 1.0f; break; case D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR: { FLOAT fNX = le.dvNormal.x; FLOAT fNY = le.dvNormal.y; FLOAT fNZ = le.dvNormal.z; FLOAT fNorm = 0; if( m_pDev->GetRS()[D3DRENDERSTATE_LOCALVIEWER] == TRUE ) { FLOAT fX = le.dvPosition.x; FLOAT fY = le.dvPosition.y; FLOAT fZ = le.dvPosition.z; // have to normalize before we reflect, // result will be normalized FLOAT fDist = (FLOAT)sqrt(fX*fX + fY*fY + fZ*fZ); if( FLOAT_NEZ( fDist ) ) { fNorm = 1.0f/fDist; } fX *= fNorm; fY *= fNorm; fZ *= fNorm; FLOAT fDot2 = 2.0f*(fX*fNX + fY*fNY + fZ*fNZ); Vout.m_tex[dwStage].x = fX - fNX*fDot2; Vout.m_tex[dwStage].y = fY - fNY*fDot2; Vout.m_tex[dwStage].z = fZ - fNZ*fDot2; } else { FLOAT fDot2 = 2.0f*fNZ; Vout.m_tex[dwStage].x = -fNX*fDot2; Vout.m_tex[dwStage].y = -fNY*fDot2; Vout.m_tex[dwStage].z = 1.f - fNZ*fDot2; } Vout.m_tex[dwStage].w = 1.0f; break; } case 0: // No TexGen { // Copy the tex coordinate for this stage DWORD n = GetTexCoordDim( inFVF, TCI ); float *pCoord = (float *)&Vout.m_tex[dwStage]; for( DWORD j = 0; j < n; j++ ) { pCoord[j] = pTex[TCI][j]; } if( n < 4 ) pCoord[n] = 1.0f; for( j = n+1; j < 4; j++ ) pCoord[j] = 0.0f; break; } default: DPFERR( "Unknown TexGen mode" ); return E_FAIL; } // Perform TexTransform if( ( TexXfmFlags & ~D3DTTFF_PROJECTED ) != D3DTTFF_DISABLE ) { LPD3DMATRIX pM = &m_xfmTex[dwStage]; FLOAT fX = Vout.m_tex[dwStage].x; FLOAT fY = Vout.m_tex[dwStage].y; FLOAT fZ = Vout.m_tex[dwStage].z; FLOAT fW = Vout.m_tex[dwStage].w; FLOAT fXout = fX*pM->_11 + fY*pM->_21 + fZ*pM->_31 + fW*pM->_41; FLOAT fYout = fX*pM->_12 + fY*pM->_22 + fZ*pM->_32 + fW*pM->_42; FLOAT fZout = fX*pM->_13 + fY*pM->_23 + fZ*pM->_33 + fW*pM->_43; FLOAT fWout = fX*pM->_14 + fY*pM->_24 + fZ*pM->_34 + fW*pM->_44; Vout.m_tex[dwStage].x = fXout; Vout.m_tex[dwStage].y = fYout; Vout.m_tex[dwStage].z = fZout; Vout.m_tex[dwStage].w = fWout; } } } else { // Copy the textures over // If there is no TexGen or TexTransform DWORD i, j; DWORD numTex = FVF_TEXCOORD_NUMBER(outFVF); for( i = 0; i < numTex; i++ ) { DWORD n = GetTexCoordDim( outFVF, i ); // DWORD n = (DWORD)(m_dwTexCoordSizeArray[i] >> 2); float *pCoord = (float *)&Vout.m_tex[i]; for( j = 0; j < n; j++ ) { pCoord[j] = pTex[i][j]; } } } // // Update the current pointers // pPos = (RDVECTOR3 *)m_position.Next(); pPos2 = (RDVECTOR3 *)m_position2.Next(); pBlendFactors = (float *)m_blendweights.Next(); pBlendIndices = (DWORD *)m_blendindices.Next(); pNormal = (RDVECTOR3 *)m_normal.Next(); pNormal2 = (RDVECTOR3 *)m_normal2.Next(); pDiffuse = (DWORD *)m_diffuse.Next(); pSpecular = (DWORD *)m_specular.Next(); pPointSize = (float *)m_pointsize.Next(); for( t = 0; t < 8; t++ ) pTex[t] = (float *)m_tex[t].Next(); } if( flags & RDPV_DOCLIPPING ) { Clipper.m_clipIntersection = clipIntersection; Clipper.m_clipUnion = clipUnion; } else { Clipper.m_clipIntersection = 0; Clipper.m_clipUnion = 0; } // Returns whether all the vertices were off screen return Clipper.m_clipIntersection; } /////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////