/////////////////////////////////////////////////////////////////////////////// // Copyright (C) Microsoft Corporation, 1998. // // setup.cpp // // Direct3D Reference Rasterizer - Primitive Setup // /////////////////////////////////////////////////////////////////////////////// #include "pch.cpp" #pragma hdrstop //----------------------------------------------------------------------------- // // SetPrimitiveAttributeFunctions - Common routine to compute attribute // functions used for triangles, lines, and points. (This could be done more // efficiently for lines and points...). // //----------------------------------------------------------------------------- void ReferenceRasterizer::SetPrimitiveAttributeFunctions( const RRFVFExtractor& Vtx0, const RRFVFExtractor& Vtx1, const RRFVFExtractor& Vtx2, const RRFVFExtractor& VtxFlat ) { // compute depth function m_pSCS->AttribFuncs[ATTRFUNC_Z].SetLinearFunc( Vtx0.GetZ(), Vtx1.GetZ(), Vtx2.GetZ() ); // compute depth range for primitive (needed because we may sample slightly outside // the primitive when antialiasing which is generally OK for color and texture indices // but not for depth buffering) if ( D3DZB_USEW == m_dwRenderState[D3DRENDERSTATE_ZENABLE] ) { // using W for depth buffering FLOAT fW0 = 1./Vtx0.GetRHW(); FLOAT fW1 = 1./Vtx1.GetRHW(); FLOAT fW2 = 1./Vtx2.GetRHW(); m_pSCS->fDepthMin = MIN( fW0, fW1 ); m_pSCS->fDepthMin = MIN( m_pSCS->fDepthMin, fW2 ); m_pSCS->fDepthMax = MAX( fW0, fW1 ); m_pSCS->fDepthMax = MAX( m_pSCS->fDepthMax, fW2 ); } else { // using Z for depth buffering m_pSCS->fDepthMin = MIN( Vtx0.GetZ(), Vtx1.GetZ() ); m_pSCS->fDepthMin = MIN( m_pSCS->fDepthMin, Vtx2.GetZ() ); m_pSCS->fDepthMax = MAX( Vtx0.GetZ(), Vtx1.GetZ() ); m_pSCS->fDepthMax = MAX( m_pSCS->fDepthMax, Vtx2.GetZ() ); } // compute diffuse color functions if ( D3DSHADE_FLAT != m_dwRenderState[D3DRENDERSTATE_SHADEMODE] ) { RRColor VtxColor0( Vtx0.GetDiffuse() ); RRColor VtxColor1( Vtx1.GetDiffuse() ); RRColor VtxColor2( Vtx2.GetDiffuse() ); m_pSCS->AttribFuncs[ATTRFUNC_R].SetPerspFunc( VtxColor0.R, VtxColor1.R, VtxColor2.R ); m_pSCS->AttribFuncs[ATTRFUNC_G].SetPerspFunc( VtxColor0.G, VtxColor1.G, VtxColor2.G ); m_pSCS->AttribFuncs[ATTRFUNC_B].SetPerspFunc( VtxColor0.B, VtxColor1.B, VtxColor2.B ); m_pSCS->AttribFuncs[ATTRFUNC_A].SetPerspFunc( VtxColor0.A, VtxColor1.A, VtxColor2.A ); } else { RRColor VtxColor0( VtxFlat.GetDiffuse() ); m_pSCS->AttribFuncs[ATTRFUNC_R].SetConstant( VtxColor0.R ); m_pSCS->AttribFuncs[ATTRFUNC_G].SetConstant( VtxColor0.G ); m_pSCS->AttribFuncs[ATTRFUNC_B].SetConstant( VtxColor0.B ); m_pSCS->AttribFuncs[ATTRFUNC_A].SetConstant( VtxColor0.A ); } // compute specular functions if ( m_qwFVFControl & D3DFVF_SPECULAR ) { if ( D3DSHADE_FLAT != m_dwRenderState[D3DRENDERSTATE_SHADEMODE] ) { RRColor VtxSpecular0( Vtx0.GetSpecular() ); RRColor VtxSpecular1( Vtx1.GetSpecular() ); RRColor VtxSpecular2( Vtx2.GetSpecular() ); m_pSCS->AttribFuncs[ATTRFUNC_SR].SetPerspFunc( VtxSpecular0.R, VtxSpecular1.R, VtxSpecular2.R ); m_pSCS->AttribFuncs[ATTRFUNC_SG].SetPerspFunc( VtxSpecular0.G, VtxSpecular1.G, VtxSpecular2.G ); m_pSCS->AttribFuncs[ATTRFUNC_SB].SetPerspFunc( VtxSpecular0.B, VtxSpecular1.B, VtxSpecular2.B ); m_pSCS->AttribFuncs[ATTRFUNC_SA].SetPerspFunc( VtxSpecular0.A, VtxSpecular1.A, VtxSpecular2.A ); } else { RRColor VtxSpecular0( VtxFlat.GetSpecular() ); m_pSCS->AttribFuncs[ATTRFUNC_SR].SetConstant( VtxSpecular0.R ); m_pSCS->AttribFuncs[ATTRFUNC_SG].SetConstant( VtxSpecular0.G ); m_pSCS->AttribFuncs[ATTRFUNC_SB].SetConstant( VtxSpecular0.B ); m_pSCS->AttribFuncs[ATTRFUNC_SA].SetConstant( VtxSpecular0.A ); } } // compute vertex fog function if ( m_dwRenderState[D3DRENDERSTATE_FOGENABLE] && ( m_dwRenderState[D3DRENDERSTATE_FOGTABLEMODE] == D3DFOG_NONE ) ) { FLOAT fF0 = (1/255.F)*(FLOAT)RGBA_GETALPHA( Vtx0.GetSpecular() ); FLOAT fF1 = (1/255.F)*(FLOAT)RGBA_GETALPHA( Vtx1.GetSpecular() ); FLOAT fF2 = (1/255.F)*(FLOAT)RGBA_GETALPHA( Vtx2.GetSpecular() ); m_pSCS->AttribFuncs[ATTRFUNC_F].SetPerspFunc( fF0, fF1, fF2 ); } // compute functions for all potential texture coordinates for(INT32 iStage = 0; iStage < m_cActiveTextureStages; iStage++) { for(INT32 i = 0; i < 4; i++) { if (m_pTexture[iStage]) { m_pSCS->TextureFuncs[iStage][TEXFUNC_0 + i].SetPerspFunc( m_pSCS->fTexCoord[iStage][0][i], m_pSCS->fTexCoord[iStage][1][i], m_pSCS->fTexCoord[iStage][2][i], m_pSCS->bWrap[iStage][i], ((m_pTexture[iStage]->m_uFlags & RR_TEXTURE_SHADOWMAP) != 0)); } } } } /////////////////////////////////////////////////////////////////////////////// // // // Triangle Drawing // // // /////////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- // // DoAreaCalcs - Takes 3 vertices and does screen area computations. // Saves x, y, w's in RRSCANCNVSTATE, computes determinant, and does // screen bounding box calculations. Returns TRUE if the triangle is visible, // FALSE otherwise. // //----------------------------------------------------------------------------- BOOL ReferenceRasterizer::DoAreaCalcs(FLOAT* pfDet, RRFVFExtractor* pVtx0, RRFVFExtractor* pVtx1, RRFVFExtractor* pVtx2) { // set vertex data m_pSCS->fX0 = pVtx0->GetX(); m_pSCS->fY0 = pVtx0->GetY(); m_pSCS->fRHW0 = pVtx0->GetRHW(); m_pSCS->fX1 = pVtx1->GetX(); m_pSCS->fY1 = pVtx1->GetY(); m_pSCS->fRHW1 = pVtx1->GetRHW(); m_pSCS->fX2 = pVtx2->GetX(); m_pSCS->fY2 = pVtx2->GetY(); m_pSCS->fRHW2 = pVtx2->GetRHW(); // compute determinant *pfDet = ComputeDeterminant( m_pSCS->fX0, m_pSCS->fY0, m_pSCS->fX1, m_pSCS->fY1, m_pSCS->fX2, m_pSCS->fY2 ); if ( 0. == *pfDet ) { return FALSE; } // bail out if degenerate (no area) // // compute bounding box for scan area // FLOAT fXMin = MIN( m_pSCS->fX0, MIN( m_pSCS->fX1, m_pSCS->fX2 ) ); FLOAT fXMax = MAX( m_pSCS->fX0, MAX( m_pSCS->fX1, m_pSCS->fX2 ) ); FLOAT fYMin = MIN( m_pSCS->fY0, MIN( m_pSCS->fY1, m_pSCS->fY2 ) ); FLOAT fYMax = MAX( m_pSCS->fY0, MAX( m_pSCS->fY1, m_pSCS->fY2 ) ); // convert to integer (round to +inf) m_pSCS->iXMin = (INT16)(fXMin+.5); m_pSCS->iXMax = (INT16)(fXMax+.5); m_pSCS->iYMin = (INT16)(fYMin+.5); m_pSCS->iYMax = (INT16)(fYMax+.5); // clip bbox to rendering surface m_pSCS->iXMin = MAX( m_pSCS->iXMin, m_pRenderTarget->m_Clip.left ); m_pSCS->iXMax = MIN( m_pSCS->iXMax, m_pRenderTarget->m_Clip.right ); m_pSCS->iYMin = MAX( m_pSCS->iYMin, m_pRenderTarget->m_Clip.top ); m_pSCS->iYMax = MIN( m_pSCS->iYMax, m_pRenderTarget->m_Clip.bottom ); // reject if no coverage if ( ( m_pSCS->iXMin < m_pRenderTarget->m_Clip.left ) || ( m_pSCS->iXMax > m_pRenderTarget->m_Clip.right ) || ( m_pSCS->iYMin < m_pRenderTarget->m_Clip.top ) || ( m_pSCS->iYMax > m_pRenderTarget->m_Clip.bottom ) ) { return FALSE; } return TRUE; } //----------------------------------------------------------------------------- // // DoTexCoordCalcs - Takes 2 or 3 vertices and does texture coordinate setup. // Sets up wrap flags, and conditionally does texture transform. // //----------------------------------------------------------------------------- void ReferenceRasterizer::DoTexCoordCalcs(INT32 iStage, RRFVFExtractor* pVtx0, RRFVFExtractor* pVtx1, RRFVFExtractor* pVtx2) { INT32 iCoordSet = m_pTexture[iStage]->m_pStageState[iStage].m_dwVal[D3DTSS_TEXCOORDINDEX]; INT32 iTexGen = iCoordSet & 0xffff0000; iCoordSet &= 0xffff; // map per-coordinate set WRAP controls into per-stage WRAP controls m_pSCS->bWrap[iStage][0] = (m_dwRenderState[D3DRENDERSTATE_WRAP0+iCoordSet] & (1<<0))?TRUE:FALSE; m_pSCS->bWrap[iStage][1] = (m_dwRenderState[D3DRENDERSTATE_WRAP0+iCoordSet] & (1<<1))?TRUE:FALSE; m_pSCS->bWrap[iStage][2] = (m_dwRenderState[D3DRENDERSTATE_WRAP0+iCoordSet] & (1<<2))?TRUE:FALSE; m_pSCS->bWrap[iStage][3] = (m_dwRenderState[D3DRENDERSTATE_WRAP0+iCoordSet] & (1<<3))?TRUE:FALSE; INT32 iNumCoords = 0; switch (D3DFVF_GETTEXCOORDSIZE(m_qwFVFControl, iCoordSet)) { case D3DFVF_TEXTUREFORMAT1: iNumCoords = 1; break; case D3DFVF_TEXTUREFORMAT2: iNumCoords = 2; break; case D3DFVF_TEXTUREFORMAT3: iNumCoords = 3; break; case D3DFVF_TEXTUREFORMAT4: iNumCoords = 4; break; } FLOAT fTexGen[3][3]; if (iTexGen != D3DTSS_TCI_PASSTHRU) { iNumCoords = 3; RRFVFExtractor* ppVtx[3] = { pVtx0, pVtx1, pVtx2 }; for (INT32 i = 0; i < 3; i++) { if (ppVtx[i]) { switch (iTexGen) { case D3DTSS_TCI_CAMERASPACENORMAL: fTexGen[i][0] = ppVtx[i]->GetEyeNormal(0); fTexGen[i][1] = ppVtx[i]->GetEyeNormal(1); fTexGen[i][2] = ppVtx[i]->GetEyeNormal(2); break; case D3DTSS_TCI_CAMERASPACEPOSITION: fTexGen[i][0] = ppVtx[i]->GetEyeXYZ(0); fTexGen[i][1] = ppVtx[i]->GetEyeXYZ(1); fTexGen[i][2] = ppVtx[i]->GetEyeXYZ(2); break; case D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR: { FLOAT fNX = ppVtx[i]->GetEyeNormal(0); FLOAT fNY = ppVtx[i]->GetEyeNormal(1); FLOAT fNZ = ppVtx[i]->GetEyeNormal(2); if( GetRenderState()[D3DRENDERSTATE_LOCALVIEWER] == TRUE ) { FLOAT fX = ppVtx[i]->GetEyeXYZ(0); FLOAT fY = ppVtx[i]->GetEyeXYZ(1); FLOAT fZ = ppVtx[i]->GetEyeXYZ(2); // have to normalize before we reflect, // result will be normalized FLOAT fNorm = 1.0f/(FLOAT)sqrt(fX*fX + fY*fY + fZ*fZ); fX *= fNorm; fY *= fNorm; fZ *= fNorm; FLOAT fDot2 = 2.0f*(fX*fNX + fY*fNY + fZ*fNZ); fTexGen[i][0] = fX - fNX*fDot2; fTexGen[i][1] = fY - fNY*fDot2; fTexGen[i][2] = fZ - fNZ*fDot2; } else { FLOAT fDot2 = 2.0f*fNZ; fTexGen[i][0] = -fNX*fDot2; fTexGen[i][1] = -fNY*fDot2; fTexGen[i][2] = 1.f - fNZ*fDot2; } } break; } } } } FLOAT fC[3][4]; for (INT32 i = 0; i < 4; i++) { if (i < iNumCoords) { if (iTexGen != D3DTSS_TCI_PASSTHRU) { fC[0][i] = fTexGen[0][i]; fC[1][i] = fTexGen[1][i]; fC[2][i] = fTexGen[2][i]; } else { fC[0][i] = pVtx0->GetTexCrd(i, iCoordSet); fC[1][i] = pVtx1->GetTexCrd(i, iCoordSet); if (pVtx2) { fC[2][i] = pVtx2->GetTexCrd(i, iCoordSet); } } } else { if (i == iNumCoords) { fC[0][i] = 1.0f; fC[1][i] = 1.0f; fC[2][i] = 1.0f; } else { fC[0][i] = 0.0f; fC[1][i] = 0.0f; fC[2][i] = 0.0f; } } } // Do texture transform only if the original // vertices passed to the refrast were untransformed BOOL bAlreadyXfmd = FVF_TRANSFORMED( m_dwFVFIn ); if (m_bPointSprite) { // disable texture transform if in point sprite mode bAlreadyXfmd = TRUE; } m_pTexture[iStage]->DoTextureTransform( iStage, bAlreadyXfmd, fC[0], m_pSCS->fTexCoord[iStage][0], &m_pSCS->fRHQW[iStage][0] ); m_pTexture[iStage]->DoTextureTransform( iStage, bAlreadyXfmd, fC[1], m_pSCS->fTexCoord[iStage][1], &m_pSCS->fRHQW[iStage][1] ); if (pVtx2) { m_pTexture[iStage]->DoTextureTransform( iStage, bAlreadyXfmd, fC[2], m_pSCS->fTexCoord[iStage][2], &m_pSCS->fRHQW[iStage][2] ); } // shadow map interpolation must not envolve the W of the current // (viewing) perspective transform if ((m_pTexture[iStage]->m_uFlags & RR_TEXTURE_SHADOWMAP) == 0) { m_pSCS->fRHQW[iStage][0] *= m_pSCS->fRHW0; m_pSCS->fRHQW[iStage][1] *= m_pSCS->fRHW1; if (pVtx2) { m_pSCS->fRHQW[iStage][2] *= m_pSCS->fRHW2; } } } //----------------------------------------------------------------------------- // // DrawTriangle - Takes three vertices and does triangle setup, setting the // primitive structure which is input to the triangle scanner, then // invokes the scan conversion. // // This computes the triangle determinant (for culling and normalization) and // the normalized edge distance and attribute functions. // // wFlags - Edge (and other) flags. // //----------------------------------------------------------------------------- void ReferenceRasterizer::DrawTriangle( void* pvV0, void* pvV1, void* pvV2, WORD wFlags ) { DPFM(3, SETUP, ("DrawTriangle:\n")); // encase FVF vertex pointer and control in class to extract fields RRFVFExtractor Vtx0( pvV0, m_qwFVFControl, m_dwRenderState[D3DRENDERSTATE_TEXTUREPERSPECTIVE] ); RRFVFExtractor Vtx1( pvV1, m_qwFVFControl, m_dwRenderState[D3DRENDERSTATE_TEXTUREPERSPECTIVE] ); RRFVFExtractor Vtx2( pvV2, m_qwFVFControl, m_dwRenderState[D3DRENDERSTATE_TEXTUREPERSPECTIVE] ); FLOAT fDet; if (DoAreaCalcs(&fDet, &Vtx0, &Vtx1, &Vtx2) == FALSE) { return; } // do culling if (!m_bPointSprite) { switch ( m_dwRenderState[D3DRENDERSTATE_CULLMODE] ) { case D3DCULL_NONE: break; case D3DCULL_CW: if ( fDet > 0. ) { return; } break; case D3DCULL_CCW: if ( fDet < 0. ) { return; } break; } } // // process point and wireframe fill mode // if (!m_bPointSprite) { if ( m_dwRenderState[D3DRENDERSTATE_FILLMODE] == D3DFILL_POINT ) { DrawPoint( pvV0, pvV0 ); DrawPoint( pvV1, pvV0 ); DrawPoint( pvV2, pvV0 ); return; } else if ( m_dwRenderState[D3DRENDERSTATE_FILLMODE] == D3DFILL_WIREFRAME ) { if ( wFlags & D3DTRIFLAG_EDGEENABLE1 ) { DrawLine( pvV0, pvV1, pvV0 ); } if ( wFlags & D3DTRIFLAG_EDGEENABLE2 ) { DrawLine( pvV1, pvV2, pvV0 ); } if ( wFlags & D3DTRIFLAG_EDGEENABLE3 ) { DrawLine( pvV2, pvV0, pvV0 ); } return; } } // // compute edge functions // m_pSCS->EdgeFuncs[0].Set( m_pSCS->fX0, m_pSCS->fY0, m_pSCS->fX1, m_pSCS->fY1, fDet, m_bFragmentProcessingEnabled ); m_pSCS->EdgeFuncs[1].Set( m_pSCS->fX1, m_pSCS->fY1, m_pSCS->fX2, m_pSCS->fY2, fDet, m_bFragmentProcessingEnabled ); m_pSCS->EdgeFuncs[2].Set( m_pSCS->fX2, m_pSCS->fY2, m_pSCS->fX0, m_pSCS->fY0, fDet, m_bFragmentProcessingEnabled ); // compute functions for texture coordinates if (m_cActiveTextureStages) { for ( INT32 iStage=0; iStageAttribFuncStatic.SetPerTriangleData( m_pSCS->fX0, m_pSCS->fY0, m_pSCS->fRHW0, m_pSCS->fX1, m_pSCS->fY1, m_pSCS->fRHW1, m_pSCS->fX2, m_pSCS->fY2, m_pSCS->fRHW2, m_cActiveTextureStages, (FLOAT*)&m_pSCS->fRHQW[0][0], fDet ); // set attribute functions SetPrimitiveAttributeFunctions( Vtx0, Vtx1, Vtx2, Vtx0 ); // not culled, so rasterize it DoScanCnvTri(3); } /////////////////////////////////////////////////////////////////////////////// // // // Line Drawing // // // /////////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- // // PointDiamondCheck - Tests if vertex is within diamond of nearest candidate // position. The +.5 (lower-right) tests are used because this is pixel-relative // test - this corresponds to an upper-left test for a vertex-relative position. // //----------------------------------------------------------------------------- BOOL PointDiamondCheck( INT32 iXFrac, INT32 iYFrac, BOOL bSlopeIsOne, BOOL bSlopeIsPosOne ) { const INT32 iPosHalf = 0x8; const INT32 iNegHalf = -0x8; INT32 iFracAbsSum = labs( iXFrac ) + labs( iYFrac ); // return TRUE if point is in fully-exclusive diamond if ( iFracAbsSum < iPosHalf ) return TRUE; // else return TRUE if diamond is on left or top extreme of point if ( ( iXFrac == ( bSlopeIsPosOne ? iNegHalf : iPosHalf ) ) && ( iYFrac == 0 ) ) return TRUE; if ( ( iYFrac == iPosHalf ) && ( iXFrac == 0 ) ) return TRUE; // return true if slope is one, vertex is on edge, and (other conditions...) if ( bSlopeIsOne && ( iFracAbsSum == iPosHalf ) ) { if ( bSlopeIsPosOne && ( iXFrac < 0 ) && ( iYFrac > 0 ) ) return TRUE; if ( !bSlopeIsPosOne && ( iXFrac > 0 ) && ( iYFrac > 0 ) ) return TRUE; } return FALSE; } //----------------------------------------------------------------------------- // // DrawLine - Takes two vertices and draws a line. // // This implements the Grid Intersect Quanization (GIQ) convention (which is // also used in Windows). // //----------------------------------------------------------------------------- void ReferenceRasterizer::DrawLine( void* pvV0, void* pvV1, void* pvVFlat ) { DPFM(3, SETUP, ("DrawLine:\n")); // encase FVF vertex pointer and control in class to extract fields RRFVFExtractor Vtx0( pvV0, m_qwFVFControl, m_dwRenderState[D3DRENDERSTATE_TEXTUREPERSPECTIVE] ); RRFVFExtractor Vtx1( pvV1, m_qwFVFControl, m_dwRenderState[D3DRENDERSTATE_TEXTUREPERSPECTIVE] ); RRFVFExtractor VtxFlat( ( ( NULL != pvVFlat ) ? pvVFlat : pvV0 ), m_qwFVFControl, m_dwRenderState[D3DRENDERSTATE_TEXTUREPERSPECTIVE] ); // set vertex data m_pSCS->fX0 = Vtx0.GetX(); m_pSCS->fY0 = Vtx0.GetY(); m_pSCS->fRHW0 = Vtx0.GetRHW(); m_pSCS->fX1 = Vtx1.GetX(); m_pSCS->fY1 = Vtx1.GetY(); m_pSCS->fRHW1 = Vtx1.GetRHW(); // compute n.4 fixed point vertex values INT32 iX0 = AS_INT32( (DOUBLE)m_pSCS->fX0 + DOUBLE_4_SNAP ); INT32 iX1 = AS_INT32( (DOUBLE)m_pSCS->fX1 + DOUBLE_4_SNAP ); INT32 iY0 = AS_INT32( (DOUBLE)m_pSCS->fY0 + DOUBLE_4_SNAP ); INT32 iY1 = AS_INT32( (DOUBLE)m_pSCS->fY1 + DOUBLE_4_SNAP ); // compute x,y extents of the line (fixed point) INT32 iXSize = iX1 - iX0; INT32 iYSize = iY1 - iY0; // TODO: is this right??? if ( ( iXSize == 0 ) && ( iYSize == 0 ) ) { return; } // determine major direction and compute line function FLOAT fLineMajorExtent; // signed extent from V0 to V1 in major direction // use GreaterEqual compare here so X major will be used when slope is // exactly one - this forces the per-pixel evaluation to be done on the // Y axis and thus adheres to the rule of inclusive right (instead of // inclusive left) for slope == 1 cases if ( labs( iXSize ) >= labs( iYSize ) ) { // here for X major m_pSCS->bXMajor = TRUE; fLineMajorExtent = (FLOAT)iXSize * (1./16.); // line function: y = F(x) = ( [0]*x + [1] ) / [2] m_pSCS->iLineEdgeFunc[0] = iYSize; m_pSCS->iLineEdgeFunc[1] = (INT64)iY0*(INT64)iX1 - (INT64)iY1*(INT64)iX0; m_pSCS->iLineEdgeFunc[2] = iXSize; } else { // here for Y major m_pSCS->bXMajor = FALSE; fLineMajorExtent = (FLOAT)iYSize * (1./16.); // line function: x = F(y) = ( [0]*y + [1] ) / [2] m_pSCS->iLineEdgeFunc[0] = iXSize; m_pSCS->iLineEdgeFunc[1] = (INT64)iX0*(INT64)iY1 - (INT64)iX1*(INT64)iY0; m_pSCS->iLineEdgeFunc[2] = iYSize; } BOOL bSlopeIsOne = ( labs( iXSize ) == labs( iYSize ) ); BOOL bSlopeIsPosOne = bSlopeIsOne && ( ( (FLOAT)m_pSCS->iLineEdgeFunc[0]/(FLOAT)m_pSCS->iLineEdgeFunc[2] ) > 0. ); // compute candidate pixel location for line endpoints // // n n // O-------* *-------O // n-.5 n+.5 n-.5 n+.5 // // Nearest Ceiling Nearest Floor // // always nearest ceiling for Y; use nearest floor for X for exception (slope == +1) // case else use nearest ceiling // // nearest ceiling of Y is ceil( Y - .5), and is done by converting to floor via: // // ceil( A/B ) = floor( (A+B-1)/B ) // // where A is coordinate - .5, and B is 0x10 (thus A/B is an n.4 fixed point number) // // A+B-1 = ( (Y - half) + B - 1 = ( (Y-0x8) + 0x10 - 0x1 = Y + 0x7 // since B is 2**4, divide by B is right shift by 4 // INT32 iPixX0 = ( iX0 + ( bSlopeIsPosOne ? 0x8 : 0x7 ) ) >> 4; INT32 iPixX1 = ( iX1 + ( bSlopeIsPosOne ? 0x8 : 0x7 ) ) >> 4; INT32 iPixY0 = ( iY0 + 0x7 ) >> 4; INT32 iPixY1 = ( iY1 + 0x7 ) >> 4; // check for vertices in/out of diamond BOOL bV0InDiamond = PointDiamondCheck( iX0 - (iPixX0<<4), iY0 - (iPixY0<<4), bSlopeIsOne, bSlopeIsPosOne ); BOOL bV1InDiamond = PointDiamondCheck( iX1 - (iPixX1<<4), iY1 - (iPixY1<<4), bSlopeIsOne, bSlopeIsPosOne ); // compute step value m_pSCS->iLineStep = ( fLineMajorExtent > 0 ) ? ( +1 ) : ( -1 ); // compute float and integer major start (V0) and end (V1) positions INT32 iLineMajor0 = ( m_pSCS->bXMajor ) ? ( iX0 ) : ( iY0 ); INT32 iLineMajor1 = ( m_pSCS->bXMajor ) ? ( iX1 ) : ( iY1 ); m_pSCS->iLineMin = ( m_pSCS->bXMajor ) ? ( iPixX0 ) : ( iPixY0 ); m_pSCS->iLineMax = ( m_pSCS->bXMajor ) ? ( iPixX1 ) : ( iPixY1 ); // need to do lots of compares which are flipped if major direction is negative #define LINEDIR_CMP( _A, _B ) \ ( ( fLineMajorExtent > 0 ) ? ( (_A) < (_B) ) : ( (_A) > (_B) ) ) // do first pixel handling - keep first pixel if not in or behind diamond if ( !( bV0InDiamond || LINEDIR_CMP( iLineMajor0, (m_pSCS->iLineMin<<4) ) ) ) { m_pSCS->iLineMin += m_pSCS->iLineStep; } // do last-pixel handling - keep last pixel if past diamond (in which case // the pixel is always filled) or if in diamond and rendering last pixel if ( !( ( !bV1InDiamond && LINEDIR_CMP( (m_pSCS->iLineMax<<4), iLineMajor1 ) ) || ( bV1InDiamond && m_dwRenderState[D3DRENDERSTATE_LASTPIXEL] ) ) ) { m_pSCS->iLineMax -= m_pSCS->iLineStep; } // return if no (major) extent (both before and after clamping to render buffer) if ( LINEDIR_CMP( m_pSCS->iLineMax, m_pSCS->iLineMin ) ) return; // snap major extent to render buffer INT16 iRendBufMajorMin = m_pSCS->bXMajor ? m_pRenderTarget->m_Clip.left : m_pRenderTarget->m_Clip.top; INT16 iRendBufMajorMax = m_pSCS->bXMajor ? m_pRenderTarget->m_Clip.right : m_pRenderTarget->m_Clip.bottom; if ( ( ( m_pSCS->iLineMin < iRendBufMajorMin ) && ( m_pSCS->iLineMax < iRendBufMajorMin ) ) || ( ( m_pSCS->iLineMin > iRendBufMajorMax ) && ( m_pSCS->iLineMax > iRendBufMajorMax ) ) ) { return; } m_pSCS->iLineMin = MAX( 0, MIN( iRendBufMajorMax, m_pSCS->iLineMin ) ); m_pSCS->iLineMax = MAX( 0, MIN( iRendBufMajorMax, m_pSCS->iLineMax ) ); // return if no (major) extent if ( LINEDIR_CMP( m_pSCS->iLineMax, m_pSCS->iLineMin ) ) return; // reject if line does not cross surface { // TODO } // compute functions for texture coordinates if (m_cActiveTextureStages) { for ( INT32 iStage=0; iStageAttribFuncStatic.SetPerLineData( m_pSCS->fX0, m_pSCS->fY0, m_pSCS->fRHW0, m_pSCS->fX1, m_pSCS->fY1, m_pSCS->fRHW1, m_cActiveTextureStages, (FLOAT*)&m_pSCS->fRHQW[0][0], fLineMajorExtent, m_pSCS->bXMajor ); // set attribute functions SetPrimitiveAttributeFunctions( Vtx0, Vtx1, Vtx1, VtxFlat ); // rasterize it DoScanCnvLine(); } /////////////////////////////////////////////////////////////////////////////// // // // Point Drawing // // // /////////////////////////////////////////////////////////////////////////////// void ReferenceRasterizer::DrawPoint( void* pvV0Public, void* pvVFlat ) { DPFM(3, SETUP, ("DrawPoint:\n")); DWORD dwStride = GetFVFVertexSize(m_qwFVFControl); void *pvV0 = MEMALLOC( dwStride ); void *pvV1 = MEMALLOC( dwStride ); void *pvV2 = MEMALLOC( dwStride ); _ASSERTa( ( NULL != pvV0 ) && ( NULL != pvV1 ) && ( NULL != pvV2), "malloc failure on ReferenceRasterizer::DrawPoint", return; ); memcpy(pvV0, pvV0Public, dwStride); memcpy(pvV1, pvV0Public, dwStride); memcpy(pvV2, pvV0Public, dwStride); // encase FVF vertex pointer and control in class to extract fields RRFVFExtractor Vtx0( pvV0, m_qwFVFControl, m_dwRenderState[D3DRENDERSTATE_TEXTUREPERSPECTIVE] ); RRFVFExtractor Vtx1( pvV1, m_qwFVFControl, m_dwRenderState[D3DRENDERSTATE_TEXTUREPERSPECTIVE] ); RRFVFExtractor Vtx2( pvV2, m_qwFVFControl, m_dwRenderState[D3DRENDERSTATE_TEXTUREPERSPECTIVE] ); // use per vertex S if it exists, otherwise use D3DRENDERSTATE_POINTSIZE BOOL bAlreadyXfmd = FVF_TRANSFORMED( m_dwFVFIn ); FLOAT fS = 1.0f; #ifdef __POINTSPRITES if (m_qwFVFControl & D3DFVF_S) { fS = Vtx0.GetS(); } else if( m_dwDriverType > RRTYPE_DP2HAL ) { fS = m_fRenderState[D3DRENDERSTATE_POINTSIZE]; } #endif // divide point size by 2 to get delta fS *= .5f; // Move points based on point size FLOAT *pXY = Vtx0.GetPtrXYZ(); FLOAT fX3 = pXY[0] + fS; FLOAT fY3 = pXY[1] + fS; pXY[0] += -fS; pXY[1] += -fS; pXY = Vtx1.GetPtrXYZ(); pXY[0] += fS; pXY[1] += -fS; pXY = Vtx2.GetPtrXYZ(); pXY[0] += -fS; pXY[1] += fS; FLOAT fDet; if (DoAreaCalcs(&fDet, &Vtx0, &Vtx1, &Vtx2) == FALSE) { goto PointCleanupAndExit; } // // compute edge functions // m_pSCS->EdgeFuncs[0].Set( m_pSCS->fX0, m_pSCS->fY0, m_pSCS->fX1, m_pSCS->fY1, fDet, m_bFragmentProcessingEnabled ); m_pSCS->EdgeFuncs[1].Set( m_pSCS->fX1, m_pSCS->fY1, fX3, fY3, fDet, m_bFragmentProcessingEnabled ); m_pSCS->EdgeFuncs[2].Set( fX3, fY3, m_pSCS->fX2, m_pSCS->fY2, fDet, m_bFragmentProcessingEnabled ); m_pSCS->EdgeFuncs[3].Set( m_pSCS->fX2, m_pSCS->fY2, m_pSCS->fX0, m_pSCS->fY0, fDet, m_bFragmentProcessingEnabled ); // compute functions for texture coordinates if (m_cActiveTextureStages) { for ( INT32 iStage=0; iStagefTexCoord[iStage][0][0] = 0.0f; m_pSCS->fTexCoord[iStage][0][1] = 0.0f; m_pSCS->fTexCoord[iStage][0][2] = 1.0f; m_pSCS->fTexCoord[iStage][0][3] = 0.0f; m_pSCS->fRHQW[iStage][0] = m_pSCS->fRHW0; // vtx1 m_pSCS->fTexCoord[iStage][1][0] = SPRITETEXCOORDMAX; m_pSCS->fTexCoord[iStage][1][1] = 0.0f; m_pSCS->fTexCoord[iStage][1][2] = 1.0f; m_pSCS->fTexCoord[iStage][1][3] = 0.0f; m_pSCS->fRHQW[iStage][1] = m_pSCS->fRHW1; // vtx2 m_pSCS->fTexCoord[iStage][2][0] = 0.0f; m_pSCS->fTexCoord[iStage][2][1] = SPRITETEXCOORDMAX; m_pSCS->fTexCoord[iStage][2][2] = 1.0f; m_pSCS->fTexCoord[iStage][2][3] = 0.0f; m_pSCS->fRHQW[iStage][2] = m_pSCS->fRHW2; } #endif //__POINTSPRITES } } } // set attribute function static data to values for this quad // (since slopes are constant for quad, any triangle can be used // to set them). m_pSCS->AttribFuncStatic.SetPerTriangleData( m_pSCS->fX0, m_pSCS->fY0, m_pSCS->fRHW0, m_pSCS->fX1, m_pSCS->fY1, m_pSCS->fRHW1, m_pSCS->fX2, m_pSCS->fY2, m_pSCS->fRHW2, m_cActiveTextureStages, (FLOAT*)&m_pSCS->fRHQW[0][0], fDet ); // set attribute functions SetPrimitiveAttributeFunctions( Vtx0, Vtx1, Vtx2, Vtx0 ); // not culled, so rasterize it DoScanCnvTri(4); PointCleanupAndExit: MEMFREE(pvV0); MEMFREE(pvV1); MEMFREE(pvV2); } /////////////////////////////////////////////////////////////////////////////// // end