/******************************Module*Header*******************************\ * Module Name: mcdrend.c * * This file contains routines to do high-level triangle rendering for the * Millenium MCD driver, including culling and face computations. Note that * in this driver, we don't use vertex color pointer at all since all pointer * references need to be checked to avoid the possibility of an invalid * memory reference. Instead, we copy the color data in the cases where we * need to during two-sided operation. This is not the common case, and even * in the case where the color data needs to be copied to colors[0] (and back), * the copy only needs to be done for (on average) half the faces. * * Copyright (c) 1996 Microsoft Corporation \**************************************************************************/ #include "precomp.h" #include "mcdhw.h" #include "mcdutil.h" #include "mcdmath.h" #include "math.h" #if _X86_ #define GET_HALF_AREA(pRc, a, b, c)\ \ __asm{ mov ecx, c };\ __asm{ mov eax, a };\ __asm{ mov ebx, b };\ __asm{ mov edx, pRc };\ __asm{ fld DWORD PTR [OFFSET(MCDVERTEX.windowCoord.x)][ecx] };\ __asm{ fsub DWORD PTR [OFFSET(MCDVERTEX.windowCoord.x)][eax] /* dxAC */ };\ __asm{ fld DWORD PTR [OFFSET(MCDVERTEX.windowCoord.y)][ecx] };\ __asm{ fsub DWORD PTR [OFFSET(MCDVERTEX.windowCoord.y)][ebx] /* dyBC dxAC */ };\ __asm{ fld DWORD PTR [OFFSET(MCDVERTEX.windowCoord.x)][ecx] /* dxBC dyBC dxAC */ };\ __asm{ fsub DWORD PTR [OFFSET(MCDVERTEX.windowCoord.x)][ebx] };\ __asm{ fld DWORD PTR [OFFSET(MCDVERTEX.windowCoord.y)][ecx] };\ __asm{ fsub DWORD PTR [OFFSET(MCDVERTEX.windowCoord.y)][eax] /* dyAC dxBC dyBC dxAC */ };\ __asm{ fxch ST(2) /* dyBC dxBC dyAC dxAC */ };\ __asm{ fst DWORD PTR [OFFSET(DEVRC.dyBC)][edx] };\ __asm{ fmul ST, ST(3) /* dxACdyBC dxBC dyAC dxAC */ };\ __asm{ fxch ST(2) /* dyAC dxBC dxACdyBC dxAC */ };\ __asm{ fst DWORD PTR [OFFSET(DEVRC.dyAC)][edx] };\ __asm{ fmul ST, ST(1) /* dxBCdyAC dxBC dxACdyBC dxAC */ };\ __asm{ fxch ST(1) /* dxBC dxBCdyAC dxACdyBC dxAC */ };\ __asm{ fstp DWORD PTR [OFFSET(DEVRC.dxBC)][edx] /* dxBCdyAC dxACdyBC dxAC */ };\ __asm{ fld DWORD PTR [OFFSET(MCDVERTEX.windowCoord.x)][ebx] };\ __asm{ fsub DWORD PTR [OFFSET(MCDVERTEX.windowCoord.x)][eax] /* dxAB dxBCdyAC dxACdyBC dxAC */ };\ __asm{ fxch ST(1) /* dxBCdyAC dxAB dxACdyBC dxAC */ };\ __asm{ fsubp ST(2), ST /* dxAB area dxAC */ };\ __asm{ fld DWORD PTR [OFFSET(MCDVERTEX.windowCoord.y)][ebx] };\ __asm{ fsub DWORD PTR [OFFSET(MCDVERTEX.windowCoord.y)][eax] /* dyAB dxAB area dxAC */ };\ __asm{ fxch ST(3) /* dxAC dxAB area dyAB */ };\ __asm{ fstp DWORD PTR [OFFSET(DEVRC.dxAC)][edx] /* dxAB area dyAB */ };\ __asm{ fstp DWORD PTR [OFFSET(DEVRC.dxAB)][edx] /* area dyAB */ };\ __asm{ fstp DWORD PTR [OFFSET(DEVRC.halfArea)][edx] /* dyAB */ };\ __asm{ fstp DWORD PTR [OFFSET(DEVRC.dyAB)][edx] /* (empty) */ }; #else #define GET_HALF_AREA(pRc, a, b, c)\ /* Compute signed half-area of the triangle */ \ (pRc)->dxAC = (c)->windowCoord.x - (a)->windowCoord.x; \ (pRc)->dxBC = (c)->windowCoord.x - (b)->windowCoord.x; \ (pRc)->dyAC = (c)->windowCoord.y - (a)->windowCoord.y; \ (pRc)->dyBC = (c)->windowCoord.y - (b)->windowCoord.y; \ (pRc)->dxAB = (b)->windowCoord.x - (a)->windowCoord.x; \ (pRc)->dyAB = (b)->windowCoord.y - (a)->windowCoord.y; \ \ (pRc)->halfArea = (pRc)->dxAC * (pRc)->dyBC - (pRc)->dxBC * (pRc)->dyAC; #endif #define SORT_AND_CULL_FACE(a, b, c, face, ccw)\ { \ LONG reversed; \ MCDVERTEX *temp; \ \ \ reversed = 0; \ if (__MCD_VERTEX_COMPARE((a)->windowCoord.y, <, (b)->windowCoord.y)) { \ if (__MCD_VERTEX_COMPARE((b)->windowCoord.y, <, (c)->windowCoord.y)) { \ /* Already sorted */ \ } else { \ if (__MCD_VERTEX_COMPARE((a)->windowCoord.y, <, (c)->windowCoord.y)) {\ temp=(b); (b)=(c); (c)=temp; \ reversed = 1; \ } else { \ temp=(a); (a)=(c); (c)=(b); (b)=temp; \ } \ } \ } else { \ if (__MCD_VERTEX_COMPARE((b)->windowCoord.y, <, (c)->windowCoord.y)) { \ if (__MCD_VERTEX_COMPARE((a)->windowCoord.y, <, (c)->windowCoord.y)) {\ temp=(a); (a)=(b); (b)=temp; \ reversed = 1; \ } else { \ temp=(a); (a)=(b); (b)=(c); (c)=temp; \ } \ } else { \ temp=(a); (a)=(c); (c)=temp; \ reversed = 1; \ } \ } \ \ GET_HALF_AREA(pRc, (a), (b), (c)); \ \ (ccw) = !__MCD_FLOAT_LTZ(pRc->halfArea); \ \ /* \ ** Figure out if face is culled or not. The face check needs to be \ ** based on the vertex winding before sorting. This code uses the \ ** reversed flag to invert the sense of ccw - an xor accomplishes \ ** this conversion without an if test. \ ** \ ** ccw reversed xor \ ** --- -------- --- \ ** 0 0 0 (remain !ccw) \ ** 1 0 1 (remain ccw) \ ** 0 1 1 (become ccw) \ ** 1 1 0 (become cw) \ */ \ (face) = pRc->polygonFace[(ccw) ^ reversed]; \ if ((face) == pRc->cullFlag) { \ /* Culled */ \ return; \ } \ } //////////////////////////////////////////////////////////////////////// // // VOID FASTCALL __MCDCalcZSlope(DEVRC *pRc, MCDVERTEX *a, MCDVERTEX *b, MCDVERTEX *c) // // Local helper routine to calculate z slopes for z-offseting primitives. // //////////////////////////////////////////////////////////////////////// VOID FASTCALL __MCDCalcZSlope(DEVRC *pRc, MCDVERTEX *a, MCDVERTEX *b, MCDVERTEX *c) { MCDFLOAT oneOverArea, t1, t2, t3, t4; MCDFLOAT dzAC, dzBC; if (CASTINT(pRc->halfArea) == 0) { pRc->dzdx = __MCDZERO; pRc->dzdy = __MCDZERO; return; } oneOverArea = __MCDONE / pRc->halfArea; t1 = pRc->dyAC * oneOverArea; t2 = pRc->dyBC * oneOverArea; t3 = pRc->dxAC * oneOverArea; t4 = pRc->dxBC * oneOverArea; dzAC = c->windowCoord.z - a->windowCoord.z; dzBC = c->windowCoord.z - b->windowCoord.z; pRc->dzdx = (dzAC * t2 - dzBC * t1); pRc->dzdy = (dzBC * t3 - dzAC * t4); } //////////////////////////////////////////////////////////////////////// // // VOID FASTCALL __MCDGetZOffsetDelta(DEVRC *pRc) // // Returns required z offset value for current primitive. Assumes that // z deltas are already in RC. // //////////////////////////////////////////////////////////////////////// MCDFLOAT FASTCALL __MCDGetZOffsetDelta(DEVRC *pRc) { #define FABS(f) ((MCDFLOAT)fabs((double) (f))) MCDFLOAT maxdZ; // Find maximum x or y slope: if(FABS(pRc->dzdx) > FABS(pRc->dzdy)) maxdZ = FABS(pRc->dzdx); else maxdZ = FABS(pRc->dzdy); return (pRc->MCDState.zOffsetFactor * maxdZ); } //////////////////////////////////////////////////////////////////////// // // VOID FASTCALL __MCDRenderGenTriangle(DEVRC *pRc, MCDVERTEX *a, // MCDVERTEX *b, MCDVERTEX *c) // // // This is the generic triangle-rendering routine. This is used if either // of the polygon faces are not GL_FILL. // //////////////////////////////////////////////////////////////////////// //!! Fix clipping logic, add startXXX logic VOID FASTCALL __MCDRenderGenTriangle(DEVRC *pRc, MCDVERTEX *a, MCDVERTEX *b, MCDVERTEX *c) { LONG ccw, face; MCDVERTEX *oa, *ob, *oc; RECTL *pClip; ULONG clipNum; MCDFLOAT zOffset; MCDCOLOR tempA, tempB, tempC; ULONG polygonMode; BOOL backFace; MCDVERTEX *pv; //!! MCDBG_PRINT("__MCDRenderGenTriangle"); /* ** Save old vertex pointers in case we end up not doing a fill. */ oa = a; ob = b; oc = c; SORT_AND_CULL_FACE(a, b, c, face, ccw); if ((clipNum = pRc->pEnumClip->c) > 1) { pClip = &pRc->pEnumClip->arcl[0]; (*pRc->HWSetupClipRect)(pRc, pClip++); } polygonMode = pRc->polygonMode[face]; backFace = (pRc->privateEnables & __MCDENABLE_TWOSIDED) && (face == __MCD_BACKFACE); // Pick correct face color and render the triangle: if (pRc->privateEnables & __MCDENABLE_SMOOTH) { if (backFace) { SWAP_COLOR(a); SWAP_COLOR(b); SWAP_COLOR(c); } } else { // Flat shading pv = pRc->pvProvoking; if (polygonMode == GL_FILL) { if (backFace) { SWAP_COLOR(pv); } } else { SAVE_COLOR(tempA, a); SAVE_COLOR(tempB, b); SAVE_COLOR(tempC, c); if (backFace) { SWAP_COLOR(pv); } a->colors[0] = pv->colors[0]; b->colors[0] = pv->colors[0]; c->colors[0] = pv->colors[0]; } } // Render triangle using the current polygon mode for the face: switch (pRc->polygonMode[face]) { case GL_FILL: if (CASTINT(pRc->halfArea) != 0) { (*pRc->drawTri)(pRc, a, b, c, (BOOL) ccw); while (--clipNum) { (*pRc->HWSetupClipRect)(pRc, pClip++); (*pRc->drawTri)(pRc, a, b, c, (BOOL) ccw); } } break; case GL_POINT: (*pRc->beginPointDrawing)(pRc); if (pRc->MCDState.enables & MCD_POLYGON_OFFSET_POINT_ENABLE) { __MCDCalcZSlope(pRc, a, b, c); zOffset = __MCDGetZOffsetDelta(pRc) + pRc->MCDState.zOffsetUnits; oa->windowCoord.z += zOffset; ob->windowCoord.z += zOffset; oc->windowCoord.z += zOffset; } if (oa->flags & MCDVERTEX_EDGEFLAG) { (*pRc->drawPoint)(pRc, oa); } if (ob->flags & MCDVERTEX_EDGEFLAG) { (*pRc->drawPoint)(pRc, ob); } if (oc->flags & MCDVERTEX_EDGEFLAG) { (*pRc->drawPoint)(pRc, oc); } if (pRc->MCDState.enables & MCD_POLYGON_OFFSET_POINT_ENABLE) { oa->windowCoord.z -= zOffset; ob->windowCoord.z -= zOffset; oc->windowCoord.z -= zOffset; } break; case GL_LINE: if (pRc->MCDState.enables & MCD_POLYGON_OFFSET_LINE_ENABLE) { __MCDCalcZSlope(pRc, a, b, c); zOffset = __MCDGetZOffsetDelta(pRc) + pRc->MCDState.zOffsetUnits; oa->windowCoord.z += zOffset; ob->windowCoord.z += zOffset; oc->windowCoord.z += zOffset; } (*pRc->beginLineDrawing)(pRc); if ((oa->flags & MCDVERTEX_EDGEFLAG) && (ob->flags & MCDVERTEX_EDGEFLAG) && (oc->flags & MCDVERTEX_EDGEFLAG)) { (*pRc->drawLine)(pRc, oa, ob, TRUE); (*pRc->drawLine)(pRc, ob, oc, 0); (*pRc->drawLine)(pRc, oc, oa, 0); } else { if (oa->flags & MCDVERTEX_EDGEFLAG) (*pRc->drawLine)(pRc, oa, ob, TRUE); if (ob->flags & MCDVERTEX_EDGEFLAG) (*pRc->drawLine)(pRc, ob, oc, TRUE); if (oc->flags & MCDVERTEX_EDGEFLAG) (*pRc->drawLine)(pRc, oc, oa, TRUE); } (*pRc->endLineDrawing)(pRc); if (pRc->MCDState.enables & MCD_POLYGON_OFFSET_LINE_ENABLE) { oa->windowCoord.z -= zOffset; ob->windowCoord.z -= zOffset; oc->windowCoord.z -= zOffset; } break; default: break; } // Restore original colors if needed: if (pRc->privateEnables & __MCDENABLE_SMOOTH) { if (backFace) { SWAP_COLOR(a); SWAP_COLOR(b); SWAP_COLOR(c); } } else { // Flat shading if (polygonMode == GL_FILL) { if (backFace) { SWAP_COLOR(pv); } } else { if (backFace) { SWAP_COLOR(pv); } RESTORE_COLOR(tempA, a); RESTORE_COLOR(tempB, b); RESTORE_COLOR(tempC, c); } } } //////////////////////////////////////////////////////////////////////// // // VOID FASTCALL __MCDRenderFlatTriangle(DEVRC *pRc, MCDVERTEX *a, // MCDVERTEX *b, MCDVERTEX *c) // // // This is the top-level flat-shaded triangle renderer. // //////////////////////////////////////////////////////////////////////// VOID FASTCALL __MCDRenderFlatTriangle(DEVRC *pRc, MCDVERTEX *a, MCDVERTEX *b, MCDVERTEX *c) { LONG ccw, face; RECTL *pClip; ULONG clipNum; //!! MCDBG_PRINT("__MCDRenderFlatTriangle"); SORT_AND_CULL_FACE(a, b, c, face, ccw); if (CASTINT(pRc->halfArea) == 0) return; if ((clipNum = pRc->pEnumClip->c) > 1) { pClip = &pRc->pEnumClip->arcl[0]; (*pRc->HWSetupClipRect)(pRc, pClip++); } // Pick correct face color and render the triangle: if ((pRc->privateEnables & __MCDENABLE_TWOSIDED) && (face == __MCD_BACKFACE)) { MCDVERTEX *pv = pRc->pvProvoking; SWAP_COLOR(pv); (*pRc->drawTri)(pRc, a, b, c, (BOOL) ccw); while (--clipNum) { (*pRc->HWSetupClipRect)(pRc, pClip++); (*pRc->drawTri)(pRc, a, b, c, (BOOL) ccw); } SWAP_COLOR(pv); } else { (*pRc->drawTri)(pRc, a, b, c, (BOOL) ccw); while (--clipNum) { (*pRc->HWSetupClipRect)(pRc, pClip++); (*pRc->drawTri)(pRc, a, b, c, (BOOL) ccw); } } } //////////////////////////////////////////////////////////////////////// // // VOID FASTCALL __MCDRenderSmoothTriangle(DEVRC *pRc, MCDVERTEX *a, // MCDVERTEX *b, MCDVERTEX *c) // // // This is the top-level smooth triangle renderer. // //////////////////////////////////////////////////////////////////////// VOID FASTCALL __MCDRenderSmoothTriangle(DEVRC *pRc, MCDVERTEX *a, MCDVERTEX *b, MCDVERTEX *c) { LONG ccw, face; RECTL *pClip; ULONG clipNum; //!! MCDBG_PRINT("__MCDRenderSmoothTriangle"); SORT_AND_CULL_FACE(a, b, c, face, ccw); if (CASTINT(pRc->halfArea) == 0) return; if ((clipNum = pRc->pEnumClip->c) > 1) { pClip = &pRc->pEnumClip->arcl[0]; (*pRc->HWSetupClipRect)(pRc, pClip++); } // Pick correct face color and render the triangle: if ((pRc->privateEnables & __MCDENABLE_TWOSIDED) && (face == __MCD_BACKFACE)) { SWAP_COLOR(a); SWAP_COLOR(b); SWAP_COLOR(c); (*pRc->drawTri)(pRc, a, b, c, (BOOL) ccw); while (--clipNum) { (*pRc->HWSetupClipRect)(pRc, pClip++); (*pRc->drawTri)(pRc, a, b, c, (BOOL) ccw); } SWAP_COLOR(a); SWAP_COLOR(b); SWAP_COLOR(c); } else { (*pRc->drawTri)(pRc, a, b, c, (BOOL) ccw); while (--clipNum) { (*pRc->HWSetupClipRect)(pRc, pClip++); (*pRc->drawTri)(pRc, a, b, c, (BOOL) ccw); } } } VOID FASTCALL __MCDRenderFlatFogTriangle(DEVRC *pRc, MCDVERTEX *pv1, MCDVERTEX *pv2, MCDVERTEX *pv3) { MCDCOLOR c1, c2, c3; MCDCOLOR bc1, bc2, bc3; MCDCOLOR cProvoking; c1 = pv1->colors[0]; c2 = pv2->colors[0]; c3 = pv3->colors[0]; // We have to save a copy of the provoking color since we // can overwrite it! cProvoking = pRc->pvProvoking->colors[0]; __MCDCalcFogColor(pRc, pv1, &pv1->colors[0], &cProvoking); __MCDCalcFogColor(pRc, pv2, &pv2->colors[0], &cProvoking); __MCDCalcFogColor(pRc, pv3, &pv3->colors[0], &cProvoking); if (pRc->privateEnables & __MCDENABLE_TWOSIDED) { cProvoking = pRc->pvProvoking->colors[1]; bc1 = pv1->colors[1]; bc2 = pv2->colors[1]; bc3 = pv3->colors[1]; __MCDCalcFogColor(pRc, pv1, &pv1->colors[1], &cProvoking); __MCDCalcFogColor(pRc, pv2, &pv2->colors[1], &cProvoking); __MCDCalcFogColor(pRc, pv3, &pv3->colors[1], &cProvoking); } (*pRc->renderTriX)(pRc, pv1, pv2, pv3); pv1->colors[0] = c1; pv2->colors[0] = c2; pv3->colors[0] = c3; if (pRc->privateEnables & __MCDENABLE_TWOSIDED) { pv1->colors[1] = bc1; pv2->colors[1] = bc2; pv3->colors[1] = bc3; } }