/******************************Module*Header*******************************\ * Module Name: mcdprim.c * * These routines process the OpenGL rendering commands that appear in an * MCDrvDraw() batch. Note that the only OpenGL primitive which is invalid * is LineLoop. This gets decomposed by the caller into a LineStrip command. * * Copyright (c) 1996 Microsoft Corporation \**************************************************************************/ #include "precomp.h" #include "mcdhw.h" #include "mcdutil.h" #define MEMCHECK_VERTEX(p)\ if (((UCHAR *)p < pRc->pMemMin) ||\ ((UCHAR *)p > pRc->pMemMax)) {\ MCDBG_PRINT("Invalid MCD vertex pointer!");\ return NULL;\ } //////////////////////////////////////////////////////////////////////// // // The functions below are local rendering-helper functions which call // the real rendering routines in the driver. // //////////////////////////////////////////////////////////////////////// VOID static FASTCALL __MCDRenderPoint(DEVRC *pRc, MCDVERTEX *v) { if (v->clipCode == 0) (*pRc->renderPoint)(pRc, v); } VOID static FASTCALL __MCDRenderLine(DEVRC *pRc, MCDVERTEX *v0, MCDVERTEX *v1, BOOL bResetLine) { if (v0->clipCode | v1->clipCode) { /* * The line must be clipped more carefully. Cannot * trivially accept the lines. * * If anding the codes is non-zero then every vertex * in the line is outside of the same set of clipping * planes (at least one). Trivially reject the line. */ if ((v0->clipCode & v1->clipCode) == 0) (*pRc->clipLine)(pRc, v0, v1, bResetLine); } else { // Line is trivially accepted so render it (*pRc->renderLine)(pRc, v0, v1, bResetLine); } } VOID static FASTCALL __MCDRenderTriangle(DEVRC *pRc, MCDVERTEX *v0, MCDVERTEX *v1, MCDVERTEX *v2) { ULONG orCodes; /* Clip check */ orCodes = v0->clipCode | v1->clipCode | v2->clipCode; if (orCodes) { /* Some kind of clipping is needed. * * If anding the codes is non-zero then every vertex * in the triangle is outside of the same set of * clipping planes (at least one). Trivially reject * the triangle. */ if (!(v0->clipCode & v1->clipCode & v2->clipCode)) (*pRc->clipTri)(pRc, v0, v1, v2, orCodes); } else { (*pRc->renderTri)(pRc, v0, v1, v2); } } VOID static FASTCALL __MCDRenderQuad(DEVRC *pRc, MCDVERTEX *v0, MCDVERTEX *v1, MCDVERTEX *v2, MCDVERTEX *v3) { // Vertex ordering is important. Line stippling uses it. ULONG savedTag; /* Render the quad as two triangles */ savedTag = v2->flags & MCDVERTEX_EDGEFLAG; v2->flags &= ~MCDVERTEX_EDGEFLAG; (*pRc->renderTri)(pRc, v0, v1, v2); v2->flags |= savedTag; savedTag = v0->flags & MCDVERTEX_EDGEFLAG; v0->flags &= ~MCDVERTEX_EDGEFLAG; (*pRc->renderTri)(pRc, v2, v3, v0); v0->flags |= savedTag; } VOID static FASTCALL __MCDRenderClippedQuad(DEVRC *pRc, MCDVERTEX *v0, MCDVERTEX *v1, MCDVERTEX *v2, MCDVERTEX *v3) { ULONG orCodes; orCodes = v0->clipCode | v1->clipCode | v2->clipCode | v3->clipCode; if (orCodes) { /* Some kind of clipping is needed. * * If anding the codes is non-zero then every vertex * in the quad is outside of the same set of * clipping planes (at least one). Trivially reject * the quad. */ if (!(v0->clipCode & v1->clipCode & v2->clipCode & v3->clipCode)) { /* Clip the quad as a polygon */ MCDVERTEX *iv[4]; iv[0] = v0; iv[1] = v1; iv[2] = v2; iv[3] = v3; (pRc->doClippedPoly)(pRc, &iv[0], 4, orCodes); } } else { __MCDRenderQuad(pRc, v0, v1, v2, v3); } } //////////////////////////////////////////////////////////////////////// // // The functions below handle the processing of all of the primitives // which may appear in an MCDCOMMAND. This includes all of the OpenGL // primitives, with the exception of line loops which are handled as // line strips. // //////////////////////////////////////////////////////////////////////// MCDCOMMAND * FASTCALL __MCDPrimDrawPoints(DEVRC *pRc, MCDCOMMAND *pCmd) { LONG i, nIndices; MCDVERTEX *pv; VOID (FASTCALL *rp)(DEVRC *pRc, MCDVERTEX *v); // Index mapping is always identity in Points. // ASSERTOPENGL(!pa->aIndices, "Index mapping must be identity\n"); if (pCmd->clipCodes) rp = __MCDRenderPoint; else rp = pRc->renderPoint; (*pRc->beginPointDrawing)(pRc); // Render the points: pv = pCmd->pStartVertex; MEMCHECK_VERTEX(pv); i = pCmd->numIndices; MEMCHECK_VERTEX(pv + (i - 1)); for (; i > 0; i--, pv++) (*rp)(pRc, pv); return pCmd->pNextCmd; } MCDCOMMAND * FASTCALL __MCDPrimDrawLines(DEVRC *pRc, MCDCOMMAND *pCmd) { LONG i, iLast2; UCHAR *pIndices; MCDVERTEX *pv, *pv0, *pv1; VOID (FASTCALL *rl)(DEVRC *pRc, MCDVERTEX *pv1, MCDVERTEX *pv2, BOOL bResetLine); iLast2 = pCmd->numIndices - 2; pv = pCmd->pStartVertex; rl = pCmd->clipCodes ? __MCDRenderLine : pRc->renderLine; (*pRc->beginLineDrawing)(pRc); if (!(pIndices = pCmd->pIndices)) { // Identity mapping MEMCHECK_VERTEX(pv); MEMCHECK_VERTEX(pv + (iLast2 + 1)); for (i = 0; i <= iLast2; i += 2) { /* setup for rendering this line */ pRc->resetLineStipple = TRUE; (*rl)(pRc, &pv[i], &pv[i+1], TRUE); } } else { for (i = 0; i <= iLast2; i += 2) { pv0 = &pv[pIndices[i]]; pv1 = &pv[pIndices[i+1]]; MEMCHECK_VERTEX(pv0); MEMCHECK_VERTEX(pv1); /* setup for rendering this line */ pRc->resetLineStipple = TRUE; (*rl)(pRc, pv0, pv1, TRUE); } } (*pRc->endLineDrawing)(pRc); return pCmd->pNextCmd; } MCDCOMMAND * FASTCALL __MCDPrimDrawLineLoop(DEVRC *pRc, MCDCOMMAND *pCmd) { // NOTE: // Line loops are always converted tp line strips at the OpenGL // API level. This routine is currently not used. MCDBG_PRINT("MCDPrimLineLoop: Invalid MCD command!"); return pCmd->pNextCmd; } MCDCOMMAND * FASTCALL __MCDPrimDrawLineStrip(DEVRC *pRc, MCDCOMMAND *pCmd) { LONG i, iLast; UCHAR *pIndices; MCDVERTEX *pv, *pv0, *pv1; MCDVERTEX *vOld; VOID (FASTCALL *rl)(DEVRC *pRc, MCDVERTEX *pv1, MCDVERTEX *pv2, BOOL bResetLine); iLast = pCmd->numIndices - 1; pv = pCmd->pStartVertex; rl = pCmd->clipCodes ? __MCDRenderLine : pRc->renderLine; if (iLast <= 0) return pCmd->pNextCmd; if (pCmd->flags & MCDCOMMAND_RESET_STIPPLE) pRc->resetLineStipple = TRUE; (*pRc->beginLineDrawing)(pRc); if (!(pIndices = pCmd->pIndices)) { // Identity mapping // Add first line segment (NOTE: 0, 1) MEMCHECK_VERTEX(pv); MEMCHECK_VERTEX(pv + iLast); (*rl)(pRc, &pv[0], &pv[1], TRUE); // Add subsequent line segments (NOTE: i, i+1) for (i = 1; i < iLast; i++) { (*rl)(pRc, &pv[i], &pv[i+1], FALSE); } } else { // Add first line segment (NOTE: 0, 1) pv0 = &pv[pIndices[0]]; pv1 = &pv[pIndices[1]]; (*rl)(pRc, pv0, pv1, TRUE); // Add subsequent line segments (NOTE: i, i+1) for (i = 1; i < iLast; i++) { pv0 = pv1; pv1 = &pv[pIndices[i+1]]; MEMCHECK_VERTEX(pv1); (*rl)(pRc, pv0, pv1, FALSE); } } (*pRc->endLineDrawing)(pRc); return pCmd->pNextCmd; } MCDCOMMAND * FASTCALL __MCDPrimDrawTriangles(DEVRC *pRc, MCDCOMMAND *pCmd) { LONG i, iLast3; UCHAR *pIndices; MCDVERTEX *pv, *pv0, *pv1, *pv2; VOID (FASTCALL *rt)(DEVRC *pRc, MCDVERTEX *v0, MCDVERTEX *v1, MCDVERTEX *v2); iLast3 = pCmd->numIndices - 3; pv = pCmd->pStartVertex; if (pCmd->clipCodes) rt = __MCDRenderTriangle; else rt = pRc->renderTri; if (!(pIndices = pCmd->pIndices)) { // Identity mapping MEMCHECK_VERTEX(pv); MEMCHECK_VERTEX(pv + (iLast3 + 2)); for (i = 0; i <= iLast3; i += 3) { /* setup for rendering this triangle */ pRc->resetLineStipple = TRUE; pRc->pvProvoking = &pv[i+2]; /* Render the triangle (NOTE: i, i+1, i+2) */ (*rt)(pRc, &pv[i], &pv[i+1], &pv[i+2]); } } else { for (i = 0; i <= iLast3; i += 3) { /* setup for rendering this triangle */ pv0 = &pv[pIndices[i ]]; pv1 = &pv[pIndices[i+1]]; pv2 = &pv[pIndices[i+2]]; MEMCHECK_VERTEX(pv0); MEMCHECK_VERTEX(pv1); MEMCHECK_VERTEX(pv2); pRc->resetLineStipple = TRUE; pRc->pvProvoking = pv2; /* Render the triangle (NOTE: i, i+1, i+2) */ (*rt)(pRc, pv0, pv1, pv2); } } return pCmd->pNextCmd; } MCDCOMMAND * FASTCALL __MCDPrimDrawTriangleStrip(DEVRC *pRc, MCDCOMMAND *pCmd) { LONG i, iLast3; UCHAR *pIndices; MCDVERTEX *pv, *pv0, *pv1, *pv2; VOID (FASTCALL *rt)(DEVRC *pRc, MCDVERTEX *v0, MCDVERTEX *v1, MCDVERTEX *v2); iLast3 = pCmd->numIndices - 3; pv = pCmd->pStartVertex; if (pCmd->clipCodes) rt = __MCDRenderTriangle; else rt = pRc->renderTri; if (iLast3 < 0) return pCmd->pNextCmd; if (!(pIndices = pCmd->pIndices)) { // Identity mapping MEMCHECK_VERTEX(pv); MEMCHECK_VERTEX(pv + (iLast3 + 2)); pv[0].flags |= MCDVERTEX_EDGEFLAG; pv[1].flags |= MCDVERTEX_EDGEFLAG; for (i = 0; i <= iLast3; i++) { /* setup for rendering this triangle */ pRc->resetLineStipple = TRUE; pRc->pvProvoking = &pv[i+2]; pv[i+2].flags |= MCDVERTEX_EDGEFLAG; /* Render the triangle (NOTE: i, i+1, i+2) */ (*rt)(pRc, &pv[i], &pv[i+1], &pv[i+2]); if (++i > iLast3) break; pRc->resetLineStipple = TRUE; pRc->pvProvoking = &pv[i+2]; pv[i+2].flags |= MCDVERTEX_EDGEFLAG; /* Render the triangle (NOTE: i+1, i, i+2) */ (*rt)(pRc, &pv[i+1], &pv[i], &pv[i+2]); } } else { pv1 = &pv[pIndices[0]]; MEMCHECK_VERTEX(pv1); pv1->flags |= MCDVERTEX_EDGEFLAG; pv2 = &pv[pIndices[1]]; MEMCHECK_VERTEX(pv2); pv2->flags |= MCDVERTEX_EDGEFLAG; for (i = 0; i <= iLast3; i++) { /* setup for rendering this triangle */ pRc->resetLineStipple = TRUE; pv0 = pv1; pv1 = pv2; pv2 = &pv[pIndices[i+2]]; MEMCHECK_VERTEX(pv2); pv2->flags |= MCDVERTEX_EDGEFLAG; pRc->resetLineStipple = TRUE; pRc->pvProvoking = pv2; /* Render the triangle (NOTE: i, i+1, i+2) */ (*rt)(pRc, pv0, pv1, pv2); if (++i > iLast3) break; pv0 = pv1; pv1 = pv2; pv2 = &pv[pIndices[i+2]]; MEMCHECK_VERTEX(pv2); pv2->flags |= MCDVERTEX_EDGEFLAG; /* setup for rendering this triangle */ pRc->resetLineStipple = TRUE; pRc->pvProvoking = pv2; /* Render the triangle (NOTE: i+1, i, i+2) */ (*rt)(pRc, pv1, pv0, pv2); } } return pCmd->pNextCmd; } MCDCOMMAND * FASTCALL __MCDPrimDrawTriangleFan(DEVRC *pRc, MCDCOMMAND *pCmd) { LONG i, iLast2; UCHAR *pIndices; MCDVERTEX *pv, *pv0, *pv1, *pv2; VOID (FASTCALL *rt)(DEVRC *pRc, MCDVERTEX *v0, MCDVERTEX *v1, MCDVERTEX *v2); iLast2 = pCmd->numIndices - 2; pv = pCmd->pStartVertex; if (pCmd->clipCodes) rt = __MCDRenderTriangle; else rt = pRc->renderTri; if (iLast2 <= 0) return pCmd->pNextCmd; if (!(pIndices = pCmd->pIndices)) { // Identity mapping MEMCHECK_VERTEX(pv); MEMCHECK_VERTEX(pv + (iLast2 + 1)); pv[0].flags |= MCDVERTEX_EDGEFLAG; pv[1].flags |= MCDVERTEX_EDGEFLAG; for (i = 1; i <= iLast2; i++) { /* setup for rendering this triangle */ pRc->resetLineStipple = TRUE; pRc->pvProvoking = &pv[i+1]; pv[i+1].flags |= MCDVERTEX_EDGEFLAG; /* Render the triangle (NOTE: 0, i, i+1) */ (*rt)(pRc, &pv[0], &pv[i], &pv[i+1]); } } else { // Initialize first 2 vertices so we can start rendering the tfan // below. The edge flags are not modified by our lower level routines. pv0 = &pv[pIndices[0]]; MEMCHECK_VERTEX(pv0); pv0->flags |= MCDVERTEX_EDGEFLAG; pv2 = &pv[pIndices[1]]; MEMCHECK_VERTEX(pv2); pv2->flags |= MCDVERTEX_EDGEFLAG; for (i = 1; i <= iLast2; i++) { pv1 = pv2; pv2 = &pv[pIndices[i+1]]; MEMCHECK_VERTEX(pv2); pv2->flags |= MCDVERTEX_EDGEFLAG; /* setup for rendering this triangle */ pRc->resetLineStipple = TRUE; pRc->pvProvoking = pv2; /* Render the triangle (NOTE: 0, i, i+1) */ (*rt)(pRc, pv0, pv1, pv2); } } return pCmd->pNextCmd; } MCDCOMMAND * FASTCALL __MCDPrimDrawQuads(DEVRC *pRc, MCDCOMMAND *pCmd) { LONG i, iLast4; UCHAR *pIndices; MCDVERTEX *pv, *pv0, *pv1, *pv2, *pv3; VOID (FASTCALL *rq)(DEVRC *pRc, MCDVERTEX *v0, MCDVERTEX *v1, MCDVERTEX *v2, MCDVERTEX *v3); iLast4 = pCmd->numIndices - 4; pv = pCmd->pStartVertex; if (pCmd->clipCodes) rq = __MCDRenderClippedQuad; else rq = __MCDRenderQuad; if (!(pIndices = pCmd->pIndices)) { MEMCHECK_VERTEX(pv); MEMCHECK_VERTEX(pv + (iLast4 + 3)); // Identity mapping for (i = 0; i <= iLast4; i += 4) { pRc->resetLineStipple = TRUE; pRc->pvProvoking = &pv[i+3]; /* Render the quad (NOTE: i, i+1, i+2, i+3) */ (*rq)(pRc, &pv[i], &pv[i+1], &pv[i+2], &pv[i+3]); } } else { for (i = 0; i <= iLast4; i += 4) { pv0 = &pv[pIndices[i ]]; pv1 = &pv[pIndices[i+1]]; pv2 = &pv[pIndices[i+2]]; pv3 = &pv[pIndices[i+3]]; MEMCHECK_VERTEX(pv0); MEMCHECK_VERTEX(pv1); MEMCHECK_VERTEX(pv2); MEMCHECK_VERTEX(pv3); pRc->resetLineStipple = TRUE; pRc->pvProvoking = pv3; /* Render the quad (NOTE: i, i+1, i+2, i+3) */ (*rq)(pRc, pv0, pv1, pv2, pv3); } } return pCmd->pNextCmd; } MCDCOMMAND * FASTCALL __MCDPrimDrawQuadStrip(DEVRC *pRc, MCDCOMMAND *pCmd) { ULONG i, iLast4; UCHAR *pIndices; MCDVERTEX *pv, *pv0, *pv1, *pv2, *pv3; VOID (FASTCALL *rq)(DEVRC *pRc, MCDVERTEX *v0, MCDVERTEX *v1, MCDVERTEX *v2, MCDVERTEX *v3); iLast4 = pCmd->numIndices - 4; pv = pCmd->pStartVertex; if (pCmd->clipCodes) rq = __MCDRenderClippedQuad; else rq = __MCDRenderQuad; // Vertex ordering is important. Line stippling uses it. if (!(pIndices = pCmd->pIndices)) { // Identity mapping MEMCHECK_VERTEX(pv); MEMCHECK_VERTEX(pv + (iLast4 + 3)); pv[0].flags |= MCDVERTEX_EDGEFLAG; pv[1].flags |= MCDVERTEX_EDGEFLAG; for (i = 0; i <= iLast4; i += 2) { pv[i+2].flags |= MCDVERTEX_EDGEFLAG; pv[i+3].flags |= MCDVERTEX_EDGEFLAG; /* setup for rendering this quad */ pRc->pvProvoking = &pv[i+3]; pRc->resetLineStipple = TRUE; /* Render the quad (NOTE: i, i+1, i+3, i+2) */ (*rq)(pRc, &pv[i], &pv[i+1], &pv[i+3], &pv[i+2]); } } else { // Initialize first 2 vertices so we can start rendering the quad // below. The edge flags are not modified by our lower level routines. pv2 = &pv[pIndices[0]]; MEMCHECK_VERTEX(pv2); pv2->flags |= MCDVERTEX_EDGEFLAG; pv3 = &pv[pIndices[1]]; MEMCHECK_VERTEX(pv3); pv3->flags |= MCDVERTEX_EDGEFLAG; for (i = 0; i <= iLast4; i += 2) { pv0 = pv2; pv1 = pv3; pv2 = &pv[pIndices[i+2]]; MEMCHECK_VERTEX(pv2); pv2->flags |= MCDVERTEX_EDGEFLAG; pv3 = &pv[pIndices[i+3]]; MEMCHECK_VERTEX(pv3); pv3->flags |= MCDVERTEX_EDGEFLAG; /* setup for rendering this quad */ pRc->resetLineStipple = TRUE; pRc->pvProvoking = pv3; /* Render the quad (NOTE: i, i+1, i+3, i+2) */ (*rq)(pRc, pv0, pv1, pv3, pv2); } } return pCmd->pNextCmd; } MCDCOMMAND * FASTCALL __MCDPrimDrawPolygon(DEVRC *pRc, MCDCOMMAND *pCmd) { // ASSERTOPENGL(!pCmd->pIndices, "Index mapping must be identity\n"); // Reset the line stipple if this is a new polygon: if (pCmd->flags & MCDCOMMAND_RESET_STIPPLE) pRc->resetLineStipple = TRUE; // Note that the provoking vertex is set in clipPolygon: MEMCHECK_VERTEX(pCmd->pStartVertex); MEMCHECK_VERTEX(pCmd->pStartVertex + (pCmd->numIndices-1)); (*pRc->clipPoly)(pRc, pCmd->pStartVertex, pCmd->numIndices); return pCmd->pNextCmd; }