/******************************Module*Header*******************************\ * Module Name: so_prim.c * * Routines to draw primitives * * Created: 10-16-1995 * Author: Hock San Lee [hockl] * * Copyright (c) 1995 Microsoft Corporation \**************************************************************************/ /* ** Copyright 1991, Silicon Graphics, Inc. ** All Rights Reserved. ** ** This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.; ** the contents of this file may not be disclosed to third parties, copied or ** duplicated in any form, in whole or in part, without the prior written ** permission of Silicon Graphics, Inc. ** ** RESTRICTED RIGHTS LEGEND: ** Use, duplication or disclosure by the Government is subject to restrictions ** as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data ** and Computer Software clause at DFARS 252.227-7013, and/or in similar or ** successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished - ** rights reserved under the Copyright Laws of the United States. ** ** $Revision: 1.13 $ ** $Date: 1993/08/31 16:23:41 $ */ #include "precomp.h" #pragma hdrstop #ifdef _X86_ #include #endif typedef void (FASTCALL *PFN_XFORM) (__GLcoord *, const __GLfloat *, const __GLmatrix *); typedef void (FASTCALL *PFN_POLYARRAYDRAW)(__GLcontext *, POLYARRAY *); typedef void (FASTCALL *PFN_POLYARRAYRENDER)(__GLcontext *, POLYARRAY *); // extern functions void DoDomain1(__GLevaluatorMachine *em, __GLfloat u, __GLevaluator1 *e, __GLfloat *v, __GLfloat *baseData); void DoDomain2(__GLevaluatorMachine *em, __GLfloat u, __GLfloat v, __GLevaluator2 *e, __GLfloat *r, __GLfloat *baseData); void DoDomain2WithDerivs(__GLevaluatorMachine *em, __GLfloat u, __GLfloat v, __GLevaluator2 *e, __GLfloat *r, __GLfloat *du, __GLfloat *dv, __GLfloat *baseData); void ComputeFirstPartials(__GLfloat *p, __GLfloat *pu, __GLfloat *pv); void ComputeNormal2(__GLcontext *gc, __GLfloat *n, __GLfloat *pu, __GLfloat *pv); void FASTCALL vvec(__GLcoord *r, const __GLcoord *p1, const __GLcoord *p2); // The PA* functions apply to one array entry only. // The PolyArray* functions apply to the whole array. void FASTCALL PARenderPoint(__GLcontext *gc, __GLvertex *v); void FASTCALL PARenderTriangle(__GLcontext *gc, __GLvertex *v0, __GLvertex *v1, __GLvertex *v2); void PARenderQuadFast(__GLcontext *gc, __GLvertex *v0, __GLvertex *v1, __GLvertex *v2, __GLvertex *v3); void PARenderQuadSlow(__GLcontext *gc, __GLvertex *v0, __GLvertex *v1, __GLvertex *v2, __GLvertex *v3); void FASTCALL PAApplyMaterial(__GLcontext *gc, __GLmatChange *mat, GLint face); void FASTCALL PASphereGen(POLYDATA *pd, __GLcoord *result); void FASTCALL PADoEval1(__GLcontext *gc, POLYARRAY *pa, POLYDATA *pd); GLuint FASTCALL PAClipCheckFrustum(__GLcontext *gc, POLYDATA *pd); GLuint FASTCALL PAClipCheckFrustum2D(__GLcontext *gc, POLYDATA *pd); GLuint FASTCALL PAClipCheckAll(__GLcontext *gc, POLYDATA *pd); void FASTCALL PolyArrayRenderPoints(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayRenderLines(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayRenderLStrip(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayRenderTriangles(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayRenderTStrip(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayRenderTFan(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayRenderQuads(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayRenderQStrip(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayRenderPolygon(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayDrawPoints(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayDrawLines(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayDrawLLoop(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayDrawLStrip(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayDrawTriangles(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayDrawTStrip(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayDrawTFan(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayDrawQuads(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayDrawQStrip(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayDrawPolygon(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayPropagateIndex(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayPropagateSameIndex(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayPropagateSameColor(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayPropagateColor(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayProcessEye(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayProcessEdgeFlag(POLYARRAY *pa); void FASTCALL PolyArrayProcessEvaluator(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayComputeFog(__GLcontext *gc, POLYARRAY *pa); void FASTCALL PolyArrayApplyMaterials(__GLcontext *gc, POLYARRAY *pa); GLuint FASTCALL PolyArrayCheckClippedPrimitive(__GLcontext *gc, POLYARRAY *pa, GLuint andCodes); POLYARRAY * FASTCALL PolyArrayRemoveClippedPrimitives(POLYARRAY *pa0); // Turn on clipcode optimization #define POLYARRAY_AND_CLIPCODES 1 // Some assertions used in this code // ASSERT_PRIMITIVE #if !((GL_POINTS == 0x0000) \ && (GL_LINES == 0x0001) \ && (GL_LINE_LOOP == 0x0002) \ && (GL_LINE_STRIP == 0x0003) \ && (GL_TRIANGLES == 0x0004) \ && (GL_TRIANGLE_STRIP == 0x0005) \ && (GL_TRIANGLE_FAN == 0x0006) \ && (GL_QUADS == 0x0007) \ && (GL_QUAD_STRIP == 0x0008) \ && (GL_POLYGON == 0x0009)) #error "bad primitive ordering\n" #endif // ASSERT_FACE #if !((__GL_FRONTFACE == 0) && (__GL_BACKFACE == 1)) #error "bad face ordering\n" #endif // ASSERT_MATERIAL #if !((POLYARRAY_MATERIAL_FRONT == POLYDATA_MATERIAL_FRONT) \ && (POLYARRAY_MATERIAL_BACK == POLYDATA_MATERIAL_BACK)) #error "bad material mask\n" #endif // ASSERT_VERTEX #if !((POLYARRAY_VERTEX2 == POLYDATA_VERTEX2) \ && (POLYARRAY_VERTEX3 == POLYDATA_VERTEX3) \ && (POLYARRAY_VERTEX4 == POLYDATA_VERTEX4)) #error "bad vertex flags\n" #endif //!!! Set it to 0! #define ENABLE_PERF_CHECK 0 #if ENABLE_PERF_CHECK // Performance check macro #define PERF_CHECK(expr,str) \ { \ static BOOL bPrinted = FALSE; \ if (!(expr) && !bPrinted) \ { \ bPrinted = TRUE; \ WARNING("PERF_CHECK: " str);\ } \ } #else #define PERF_CHECK(expr,str) #endif // ENABLE_PERF_CHECK // Copy processed vertex. #define PA_COPY_PROCESSED_VERTEX(pdDst,pdSrc) \ { \ *(pdDst) = *(pdSrc); \ /* must update color pointer for polygon to work! */ \ (pdDst)->color = &(pdDst)->colors[__GL_FRONTFACE]; \ } #define PA_COPY_VERTEX(pdDst,pdSrc) PA_COPY_PROCESSED_VERTEX(pdDst,pdSrc) // POLYDATAs are 128 bytes wich is a power of two, so this macro may // be removed... #define PD_OFFSET(idx) \ ((idx) << 7) #define PD_ARRAY(ary, idx) \ ((POLYDATA *)((GLubyte *)(ary)+PD_OFFSET(idx))) #define PD_VERTEX(ary, idx) \ ((__GLvertex *)((GLubyte *)(ary)+PD_OFFSET(idx))) // Poly array draw routines. // ASSERT_PRIMITIVE PFN_POLYARRAYDRAW afnPolyArrayDraw[] = { (PFN_POLYARRAYDRAW) PolyArrayDrawPoints, (PFN_POLYARRAYDRAW) PolyArrayDrawLines, (PFN_POLYARRAYDRAW) PolyArrayDrawLLoop, (PFN_POLYARRAYDRAW) PolyArrayDrawLStrip, (PFN_POLYARRAYDRAW) PolyArrayDrawTriangles, (PFN_POLYARRAYDRAW) PolyArrayDrawTStrip, (PFN_POLYARRAYDRAW) PolyArrayDrawTFan, (PFN_POLYARRAYDRAW) PolyArrayDrawQuads, (PFN_POLYARRAYDRAW) PolyArrayDrawQStrip, (PFN_POLYARRAYDRAW) PolyArrayDrawPolygon, }; #ifdef _X86_ #define X86_MASK_EXCEPTIONS(dwOldMode) \ __asm fnstcw WORD PTR dwOldMode \ __asm mov eax, dwOldMode \ __asm or eax, 0x3f \ __asm mov WORD PTR dwOldMode+2, ax \ __asm fldcw WORD PTR dwOldMode+2 #define X86_RESTORE_MASK(dwOldMode) \ __asm fnclex \ __asm fldcw WORD PTR dwOldMode #else // _X86_ #define X86_MASK_EXCEPTIONS(dwOldMode) #define X86_RESTORE_MASK(dwOldMode) #endif // _X86_ // READ THIS NOTE BEFORE YOU MAKE ANY CHANGES! // // NOTE: This function is also called by RasterPos to compute its associated // color and texture coordinates! // This code has to update current values and material even if there is // no vertex. //!!! special case provoking vertex? void APIPRIVATE __glim_DrawPolyArray(void *_pa0) { __GLtransform *trMV; __GLmatrix *m, *mEye; GLuint enables; GLuint paNeeds, paNeedsSaved; GLuint orCodes, andCodes; GLuint paflagsAll; POLYDATA *pd; POLYARRAY *pa0 = (POLYARRAY *) _pa0; POLYARRAY *pa; PFN_XFORM pfnXform, pfnXformEye; GLuint (FASTCALL *clipCheck)(__GLcontext *gc, POLYDATA *pd); __GLmatrix *mInv; GLboolean doEye; __GLcolor scaledUserColor; GLuint paFlags; DWORD dwOldMode; __GL_SETUP(); ASSERTOPENGL(sizeof(POLYDATA) == 128, "POLYDATA size mismatch with PD_OFFSET\n"); // Force FP exceptions to be masked on x86 because some of our // rendering code can produce FP exceptions X86_MASK_EXCEPTIONS(dwOldMode); // There are 3 possible begin modes. If we are in the begin/end bracket, // it is __GL_IN_BEGIN. If we are not in the begin/end bracket, it is either // __GL_NOT_IN_BEGIN or __GL_NEED_VALIDATE. // Validation should only be done inside the display lock! ASSERTOPENGL(gc->beginMode != __GL_IN_BEGIN, "bad beginMode!"); if (gc->beginMode == __GL_NEED_VALIDATE) (*gc->procs.validate)(gc); gc->beginMode = __GL_IN_BEGIN; // Initialize variables. enables = gc->state.enables.general; // We need to save a copy of paNeeds since we may modify the // copy in the gc: paNeeds = paNeedsSaved = gc->vertex.paNeeds; paflagsAll = 0; #if 0 // This is tricky to do in a DrawPolyArray chain. // We would have to fix up the chain and restore color pointers correctly! // // If no vertex is given and there are no attribute changes, we are done. // We need to close previously decomposed line loop or polygon even if // there are no attribute changes. if (pa->pd0 == pa->pdNextVertex) { if (!pa->pd0->flags) { if (pa->primType == GL_POLYGON || pa->primType == GL_LINE_LOOP) { if (!(pa->flags & (POLYARRAY_OTHER_COLOR|POLYARRAY_PARTIAL_BEGIN))) goto drawpolyarray_exit; } else { if (!(pa->flags & POLYARRAY_OTHER_COLOR)) goto drawpolyarray_exit; } } } #endif // --------------------------------------------------------- // Update final current values and initialize current values at index 0 // if not given. Material changes are updated later. paFlags = 0; if (!gc->modes.colorIndexMode) { __GL_SCALE_AND_CHECK_CLAMP_R(scaledUserColor.r, gc, paFlags, gc->state.current.userColor.r); __GL_SCALE_AND_CHECK_CLAMP_G(scaledUserColor.g, gc, paFlags, gc->state.current.userColor.g); __GL_SCALE_AND_CHECK_CLAMP_B(scaledUserColor.b, gc, paFlags, gc->state.current.userColor.b); __GL_SCALE_AND_CHECK_CLAMP_A(scaledUserColor.a, gc, paFlags, gc->state.current.userColor.a); } else { __GL_CHECK_CLAMP_CI(scaledUserColor.r, gc, paFlags, gc->state.current.userColorIndex); } for (pa = pa0; pa; pa = pa->paNext) { POLYDATA *pd0; pd0 = pa->pd0; if (gc->modes.colorIndexMode) { // CI mode. // Update final current RGBA color incase one is given. if (pa->flags & POLYARRAY_OTHER_COLOR) gc->state.current.userColor = pa->otherColor; // Update final current CI color. if (!(pd0->flags & POLYDATA_COLOR_VALID)) { pd0->flags |= POLYDATA_COLOR_VALID; pd0->colors[0].r = gc->state.current.userColorIndex; pa->flags |= paFlags; } gc->state.current.userColorIndex = pa->pdCurColor->colors[0].r; paFlags = (pa->flags & POLYARRAY_CLAMP_COLOR); } else { // RGBA mode. // Update final current CI color in case one is given. if (pa->flags & POLYARRAY_OTHER_COLOR) gc->state.current.userColorIndex = pa->otherColor.r; // Update final current RGBA color. if (!(pd0->flags & POLYDATA_COLOR_VALID)) { pd0->flags |= POLYDATA_COLOR_VALID; pd0->colors[0] = scaledUserColor; pa->flags |= paFlags; } scaledUserColor = pa->pdCurColor->colors[0]; paFlags = (pa->flags & POLYARRAY_CLAMP_COLOR); } // Update final current normal. if (!(pd0->flags & POLYDATA_NORMAL_VALID)) { pd0->flags |= POLYDATA_NORMAL_VALID; // can also be pd0->normal = gc->state.current.normal! pd0->normal.x = gc->state.current.normal.x; pd0->normal.y = gc->state.current.normal.y; pd0->normal.z = gc->state.current.normal.z; } gc->state.current.normal.x = pa->pdCurNormal->normal.x; gc->state.current.normal.y = pa->pdCurNormal->normal.y; gc->state.current.normal.z = pa->pdCurNormal->normal.z; // Update final current texture coordinates. if (!(pd0->flags & POLYDATA_TEXTURE_VALID)) { pd0->flags |= POLYDATA_TEXTURE_VALID; pd0->texture = gc->state.current.texture; if (__GL_FLOAT_COMPARE_PONE(pd0->texture.w, !=)) pa->flags |= POLYARRAY_TEXTURE4; else if (__GL_FLOAT_NEZ(pd0->texture.z)) pa->flags |= POLYARRAY_TEXTURE3; else if (__GL_FLOAT_NEZ(pd0->texture.y)) pa->flags |= POLYARRAY_TEXTURE2; else pa->flags |= POLYARRAY_TEXTURE1; } gc->state.current.texture = pa->pdCurTexture->texture; // Update the texture key for hardware accelaration: pa->textureKey = gc->textureKey; // Update final current edge flag. if (!(pd0->flags & POLYDATA_EDGEFLAG_VALID)) { if (gc->state.current.edgeTag) pd0->flags |= POLYDATA_EDGEFLAG_VALID | POLYDATA_EDGEFLAG_BOUNDARY; else pd0->flags |= POLYDATA_EDGEFLAG_VALID; } gc->state.current.edgeTag = (GLboolean) (pa->pdCurEdgeFlag->flags & POLYDATA_EDGEFLAG_BOUNDARY); // Accumulate pa flags. paflagsAll |= pa->flags; } // Store the normalized user color: if (!gc->modes.colorIndexMode) { gc->state.current.userColor.r = scaledUserColor.r * gc->oneOverRedVertexScale; gc->state.current.userColor.g = scaledUserColor.g * gc->oneOverGreenVertexScale; gc->state.current.userColor.b = scaledUserColor.b * gc->oneOverBlueVertexScale; gc->state.current.userColor.a = scaledUserColor.a * gc->oneOverAlphaVertexScale; } // --------------------------------------------------------- // Do evaluator. if (paflagsAll & (POLYARRAY_EVALCOORD1 | POLYARRAY_EVALCOORD2 | POLYARRAY_EVALPOINT1 | POLYARRAY_EVALPOINT2)) { paflagsAll = 0; // recompute paflagsAll! for (pa = pa0; pa; pa = pa->paNext) { if (pa->flags & (POLYARRAY_EVALCOORD1 | POLYARRAY_EVALCOORD2 | POLYARRAY_EVALPOINT1 | POLYARRAY_EVALPOINT2)) // pdNextVertex may be changed by PolyArrayProcessEvaluator! PolyArrayProcessEvaluator(gc, pa); paflagsAll |= pa->flags; } } // // Get the modeling matrix: // trMV = gc->transform.modelView; // --------------------------------------------------------- // Initialize the normal matrix: // Normals are not needed after color assignment and texture generation! // // IN: normal matrix // OUT: normal natrix (processed) if (paNeeds & PANEEDS_NORMAL) { if (trMV->updateInverse) __glComputeInverseTranspose(gc, trMV); gc->mInv = mInv = &trMV->inverseTranspose; } #if DBG else gc->mInv = mInv = (__GLmatrix *) -1; #endif // --------------------------------------------------------- // Process texture coordinates. We need to do this while we still have // valid object coordinate data. If we need normals to perform the texture // generation, we also transform the normals. // // Texture coordinates are modified in place. // // IN: texture, obj, (eye), normal // OUT: texture, (eye) if (paNeeds & PANEEDS_TEXCOORD) { if ((gc->procs.paCalcTexture == PolyArrayCalcTexture) && (gc->transform.texture->matrix.matrixType == __GL_MT_IDENTITY)) { for (pa = pa0; pa; pa = pa->paNext) { PERF_CHECK(!(pa->flags & (POLYARRAY_TEXTURE3 | POLYARRAY_TEXTURE4)), "Uses r, q texture coordinates!\n"); // If all incoming vertices have valid texcoords, and texture // matrix is identity, and texgen is disabled, we are done. if ((pa->flags & POLYARRAY_SAME_POLYDATA_TYPE) && (pa->pdCurTexture != pa->pd0) // Need to test 2nd vertex because pdCurTexture may have been // advanced as a result of combining TexCoord command after End && ((pa->pd0 + 1)->flags & POLYDATA_TEXTURE_VALID)) ; else PolyArrayCalcTexture(gc, pa); } } else { for (pa = pa0; pa; pa = pa->paNext) { PERF_CHECK(!(pa->flags & (POLYARRAY_TEXTURE3 | POLYARRAY_TEXTURE4)), "Uses r, q texture coordinates!\n"); if (paNeeds & PANEEDS_NORMAL) { POLYDATA *pdLastNormal; pdLastNormal = pa->pdCurNormal; for (pd = pa->pd0; pd <= pdLastNormal; pd++) { if (pd->flags & POLYDATA_NORMAL_VALID) { (*mInv->xf3)(&pd->normal, &pd->normal.x, mInv); if (enables & __GL_NORMALIZE_ENABLE) __glNormalize(&pd->normal.x, &pd->normal.x); } } } (*gc->procs.paCalcTexture)(gc, pa); } gc->vertex.paNeeds &= ~PANEEDS_NORMAL; paNeeds = gc->vertex.paNeeds; } } // // Process the eye coordinate if: // user clip planes are enabled // we're processing RASTERPOS // we have slow lighting which needs the eye coordinate // // We need to process the eye coordinate here because the // object coordinate gets trashed by the initial obj->clip // transform. // // // clipCheck = gc->procs.paClipCheck; // Compute eye coord first // We need eye coordinates to do user clip plane clipping if ((clipCheck == PAClipCheckAll) || (pa0->flags & POLYARRAY_RASTERPOS) || (gc->procs.paCalcColor == PolyArrayCalcCIColor) || (gc->procs.paCalcColor == PolyArrayCalcRGBColor) || (enables & __GL_FOG_ENABLE && gc->renderMode == GL_RENDER)) { mEye = &trMV->matrix; if (paflagsAll & POLYARRAY_VERTEX4) pfnXformEye = mEye->xf4; else if (paflagsAll & POLYARRAY_VERTEX3) pfnXformEye = mEye->xf3; else pfnXformEye = mEye->xf2; doEye = TRUE; } else doEye = FALSE; // If any incoming coords contains w coord, use xf4. m = &trMV->mvp; if (paflagsAll & POLYARRAY_VERTEX4) pfnXform = m->xf4; else if (paflagsAll & POLYARRAY_VERTEX3) pfnXform = m->xf3; else pfnXform = m->xf2; // --------------------------------------------------------- // Do transform, color, and lighting calculations. // // This is the heart of the rendering pipeline, so we try // to do as many operations as possible while touching the // least amount of memory to reduce cache affects. // // --------------------------------------------------------- for (pa = pa0; pa; pa = pa->paNext) { POLYDATA *pdLast; pdLast = pa->pdNextVertex - 1; // --------------------------------------------------------- // Process the eye coordinate if we will need it in the // pipeline and haven't yet processed it in texture generation. // We have to do this before we trash the object coord in the // next phase. // // IN: obj // OUT: eye if (doEye && !(pa->flags & POLYARRAY_EYE_PROCESSED)) { pa->flags |= POLYARRAY_EYE_PROCESSED; if (mEye->matrixType == __GL_MT_IDENTITY) { for (pd = pa->pd0; pd <= pdLast; pd++) pd->eye = pd->obj; } else { for (pd = pa->pd0; pd <= pdLast; pd++) (*pfnXformEye)(&pd->eye, (__GLfloat *) &pd->obj, mEye); } } // --------------------------------------------------------- // Process the object coordinate. This generates the clip // and window coordinates, along with the clip codes. // // IN: obj (destroyed) // OUT: clip, window orCodes = 0; // accumulate all clip codes #ifdef POLYARRAY_AND_CLIPCODES andCodes = (GLuint) -1; #endif if (clipCheck != PAClipCheckFrustum2D) { if (m->matrixType != __GL_MT_IDENTITY) { for (pd = pa->pd0; pd <= pdLast; pd++) { (*pfnXform)(&pd->clip, (__GLfloat *) &pd->obj, m); pd->clipCode = (*clipCheck)(gc, pd); orCodes |= pd->clipCode; #ifdef POLYARRAY_AND_CLIPCODES andCodes &= pd->clipCode; #endif } } else { for (pd = pa->pd0; pd <= pdLast; pd++) { // pd->clip = pd->obj; ASSERTOPENGL(&pd->clip == &pd->obj, "bad clip offset\n"); pd->clipCode = (*clipCheck)(gc, pd); orCodes |= pd->clipCode; #ifdef POLYARRAY_AND_CLIPCODES andCodes &= pd->clipCode; #endif } } } else { for (pd = pa->pd0; pd <= pdLast; pd++) { if (m->matrixType == __GL_MT_IDENTITY) { // pd->clip = pd->obj; ASSERTOPENGL(&pd->clip == &pd->obj, "bad clip offset\n"); } else (*pfnXform)(&pd->clip, (__GLfloat *) &pd->obj, m); if (pd->flags & POLYDATA_VERTEX2) pd->clipCode = PAClipCheckFrustum2D(gc, pd); else pd->clipCode = PAClipCheckFrustum(gc, pd); orCodes |= pd->clipCode; #ifdef POLYARRAY_AND_CLIPCODES andCodes &= pd->clipCode; #endif } } pa->orClipCodes = orCodes; #ifdef POLYARRAY_AND_CLIPCODES if (andCodes) { andCodes = PolyArrayCheckClippedPrimitive(gc, pa, andCodes); // add POLYARRAY_REMOVE_PRIMITIVE flag paflagsAll |= pa->flags; } pa->andClipCodes = andCodes; #endif // --------------------------------------------------------- // Process colors and materials if we're not in selection and // haven't been completely clipped out. // // IN: obj/eye, color (front), normal // OUT: (normal), color (front and back) if (!(pa->flags & POLYARRAY_REMOVE_PRIMITIVE) && !(paNeeds & PANEEDS_CLIP_ONLY)) { if (!(enables & __GL_LIGHTING_ENABLE)) { // Lighting is disabled. // Clamp RGBA colors, mask color index values. // Only front colors are computed, back colors are not needed. if (paNeeds & PANEEDS_SKIP_LIGHTING) { // Note that when lighting calculation is skipped, we still need to // fill in the colors field. Otherwise, the rasterization routines // may get FP exceptions on invalid colors. pa->flags |= POLYARRAY_SAME_COLOR_DATA; (*gc->procs.paCalcColorSkip)(gc, pa, __GL_FRONTFACE); } else if (pa->flags & POLYARRAY_SAME_COLOR_DATA) { if (gc->modes.colorIndexMode) PolyArrayPropagateSameIndex(gc, pa); else PolyArrayPropagateSameColor(gc, pa); } else if (gc->modes.colorIndexMode) { PolyArrayPropagateIndex(gc, pa); } else { PolyArrayPropagateColor(gc, pa); } } else { // Lighting is enabled. POLYDATA *pd1, *pd2, *pdN; GLint face; GLuint matMask; GLboolean doFrontColor, doBackColor, doColor; // Clear POLYARRAY_SAME_COLOR_DATA flag if lighting is enabled. pa->flags &= ~POLYARRAY_SAME_COLOR_DATA; pdN = pa->pdNextVertex; // Needs only front color for points and lines. // ASSERT_PRIMITIVE if ((unsigned int) pa->primType <= GL_LINE_STRIP) { doFrontColor = GL_TRUE; doBackColor = GL_FALSE; } else { doFrontColor = paNeeds & PANEEDS_FRONT_COLOR; doBackColor = paNeeds & PANEEDS_BACK_COLOR; } // Process front and back colors in two passes. // Do back colors first! //!!! We can potentially optimize 2-sided lighting in the slow path by running // through all vertices and look for color needs for each vertex! // See RenderSmoothTriangle. PERF_CHECK ( !(doFrontColor && doBackColor), "Two-sided lighting - need both colors!\n" ); // ASSERT_FACE // ASSERT_MATERIAL for (face = 1, matMask = POLYARRAY_MATERIAL_BACK, doColor = doBackColor; face >= 0; face--, matMask = POLYARRAY_MATERIAL_FRONT, doColor = doFrontColor ) { POLYMATERIAL *pm; if (!doColor) continue; // If color is not needed, fill in the colors field with default. if (paNeeds & PANEEDS_SKIP_LIGHTING) { (*gc->procs.paCalcColorSkip)(gc, pa, face); continue; } // Process color ranges that include no material changes (excluding color // material) one at a time. Color material changes are handled in the // color procs. if (!(pa->flags & matMask)) { // process the whole color array (*gc->procs.paCalcColor)(gc, face, pa, pa->pd0, pdN - 1); gc->vertex.paNeeds &= ~PANEEDS_NORMAL; continue; } // There are material changes, we need to recompute material and light // source machine values before processing the next color range. // Each range below is given by [pd1, pd2-1]. //!!! it is possible to fix polyarraycalcrgbcolor to accept certain material! pm = GLTEB_CLTPOLYMATERIAL(); // no need to do this material later pa->flags &= ~matMask; for (pd1 = pa->pd0; pd1 <= pdN; pd1 = pd2) { POLYDATA *pdColor, *pdNormal; // Apply material changes to the current vertex. // It also applies trailing material changes following // the last vertex. if (pd1->flags & matMask) PAApplyMaterial(gc, *(&pm->pdMaterial0[pd1 - pa->pdBuffer0].front + face), face); // If this is the trailing material change, we are done. if (pd1 == pdN) break; // Find next vertex with material changes. // We need to track current color and normal so that the // next color range begins with valid color and normal. // We cannot track current values on client side because // we don't have initial current values when batching // this function. pdColor = pd1; pdNormal = pd1; for (pd2 = pd1 + 1; pd2 < pdN; pd2++) { // track current color if (pd2->flags & POLYDATA_COLOR_VALID) pdColor = pd2; // track current normal if (pd2->flags & POLYDATA_NORMAL_VALID) pdNormal = pd2; if (pd2->flags & matMask) break; } // Update next vertex's current color and normal if not given. // The paCalcColor proc assumes that the first vertex contains // a valid current color and normal. We need to save the // current values before they are modified by the color procs. if (!(pd2->flags & POLYDATA_COLOR_VALID)) { pd2->flags |= POLYDATA_COLOR_VALID; pd2->colors[0] = pdColor->colors[0]; } if (!(pd2->flags & POLYDATA_NORMAL_VALID)) { pd2->flags |= POLYDATA_NORMAL_VALID; pd2->normal.x = pdNormal->normal.x; pd2->normal.y = pdNormal->normal.y; pd2->normal.z = pdNormal->normal.z; } // Compute the colos range [pd1, pd2-1] that contains // no material changes. (*gc->procs.paCalcColor)(gc, face, pa, pd1, pd2-1); } // for gc->vertex.paNeeds &= ~PANEEDS_NORMAL; } // for (faces) gc->vertex.paNeeds = paNeeds; } // lighting enabled } // Update material. if (pa->flags & (POLYARRAY_MATERIAL_FRONT | POLYARRAY_MATERIAL_BACK)) PolyArrayApplyMaterials(gc, pa); } // end of transform, color, and lighting calculations. // Restore the original paNeeds in the gc which may have been // modified above: gc->vertex.paNeeds = paNeedsSaved; // --------------------------------------------------------- // This is the end of the main pipeline loop. At this point, // we need to take care of selection, removal of rejected // primitives, cheap fog, and edge-flag processing. // --------------------------------------------------------- // In selection, we need only clip and window (and possibly eye values // computed above.) At this point, we have already applied materials as well. // but we still need to apply materials and colors. if (paNeeds & PANEEDS_CLIP_ONLY) goto drawpolyarray_render_primitives; // If any of the andClipCodes is nonzero, we may be able to throw away // some primitives. #ifdef POLYARRAY_AND_CLIPCODES if (paflagsAll & POLYARRAY_REMOVE_PRIMITIVE) { pa0 = PolyArrayRemoveClippedPrimitives(pa0); // _pa0 = pa0; if (!pa0) goto drawpolyarray_apply_color; } #endif // --------------------------------------------------------- // Process cheap fog. // // IN: obj/eye, color // OUT: eye, fog, color // if this is changed, need to fix RasterPos's setup! if (enables & __GL_FOG_ENABLE && gc->renderMode == GL_RENDER) { for (pa = pa0; pa; pa = pa->paNext) { // Note: the eye coordinate has already been computed. if (gc->polygon.shader.modeFlags & __GL_SHADE_CHEAP_FOG) { // compute fog values PolyArrayComputeFog(gc, pa); // Apply fog if it is smooth shading and in render mode. // In flat shading, cheap fogging is currently done at render procs // we can probably do cheap fog in flat shading here but we will // need to compute the provoking colors with z info correctly so // we can interpolate in the clipping procs. It would require // rewriting the clipping routines in so_clip.c too! if (gc->polygon.shader.modeFlags & __GL_SHADE_SMOOTH_LIGHT) (*gc->procs.paApplyCheapFog)(gc, pa); } else { PERF_CHECK(FALSE, "Uses slow fog\n"); } } } // --------------------------------------------------------- // Process edge flags. // // IN: edge // OUT: edge (all vertices) if (paNeeds & PANEEDS_EDGEFLAG) { for (pa = pa0; pa; pa = pa->paNext) { if (pa->primType == GL_TRIANGLES || pa->primType == GL_QUADS || pa->primType == GL_POLYGON) { // If all incoming vertices have valid edgeflags, we are done. // When all polydata's are of the same type, there are 2 cases // where edge flag processing can be skipped: // 1. All edge flags were given. // 2. No edge flag was given and the initial edge flag (i.e. // current gc edge flag) is non boundary. In this case, // all edge flags were set to non boundary in pd->flags // initialization. if ((pa->flags & POLYARRAY_SAME_POLYDATA_TYPE) && (((pa->pdCurEdgeFlag != pa->pd0) && // Need to test 2nd vertex because pdCurEdgeFlag may have been // advanced as a result of combining EdgeFlag command after End ((pa->pd0 + 1)->flags & POLYDATA_EDGEFLAG_VALID)) || !(pa->pd0->flags & POLYDATA_EDGEFLAG_BOUNDARY))) ; else PolyArrayProcessEdgeFlag(pa); } } } // --------------------------------------------------------- // Process the primitives. drawpolyarray_render_primitives: // Skip the rest if this is a RasterPos if (pa0->flags & POLYARRAY_RASTERPOS) goto drawpolyarray_exit; for (pa = pa0; pa; pa = pa->paNext) { ASSERTOPENGL(pa->primType >= GL_POINTS && pa->primType <= GL_POLYGON, "DrawPolyArray: bad primitive type\n"); (*afnPolyArrayDraw[pa->primType])(gc, pa); } // --------------------------------------------------------- // Update final light source machine. // The user color was initialized above. drawpolyarray_apply_color: (*gc->procs.applyColor)(gc); // --------------------------------------------------------- // Flush the primitive chain. #if 1 if (pa0) glsrvFlushDrawPolyArray(pa0); #endif drawpolyarray_exit: // Out of begin mode. X86_RESTORE_MASK(dwOldMode); ASSERTOPENGL(gc->beginMode == __GL_IN_BEGIN, "bad beginMode!"); gc->beginMode = __GL_NOT_IN_BEGIN; return; } #ifdef POLYARRAY_AND_CLIPCODES // Determine if a clipped primitive can be removed early. // If the logical AND of vertex clip codes of a primitive is non-zero, // the primitive is completely clipped and can be removed early. // However, if a primitive is partially built, we may not be able to // remove it yet to maintain connectivity between the partial primitives. // By eliminating a primitive early, we save on lighting and other calculations. // // Set POLYARRAY_REMOVE_PRIMITIVE flag if the primitve can be removed early. // Return new andCodes. GLuint FASTCALL PolyArrayCheckClippedPrimitive(__GLcontext *gc, POLYARRAY *pa, GLuint andCodes) { ASSERTOPENGL(andCodes, "bad andCodes\n"); // Don't eliminate RasterPos if (pa->flags & POLYARRAY_RASTERPOS) return andCodes; // If this is a partial begin, include previous clipcode. if (pa->flags & POLYARRAY_PARTIAL_BEGIN) { switch (pa->primType) { case GL_LINE_LOOP: // previous vertex andCodes &= gc->vertex.pdSaved[0].clipCode; // loop vertex if (!(pa->flags & POLYARRAY_PARTIAL_END)) andCodes &= gc->vertex.pdSaved[1].clipCode; break; case GL_POLYGON: andCodes &= gc->vertex.pdSaved[2].clipCode; // fall through case GL_TRIANGLE_FAN: case GL_TRIANGLE_STRIP: case GL_QUAD_STRIP: andCodes &= gc->vertex.pdSaved[1].clipCode; // fall through case GL_LINE_STRIP: andCodes &= gc->vertex.pdSaved[0].clipCode; break; case GL_POINTS: case GL_LINES: case GL_TRIANGLES: case GL_QUADS: default: break; } } if (andCodes && ( !(pa->flags & POLYARRAY_PARTIAL_END) || pa->primType == GL_POINTS || pa->primType == GL_LINES || pa->primType == GL_TRIANGLES || pa->primType == GL_QUADS ) ) pa->flags |= POLYARRAY_REMOVE_PRIMITIVE; // return new andCodes. return andCodes; } // Remove completely clipped primitives from the polyarray chain. POLYARRAY * FASTCALL PolyArrayRemoveClippedPrimitives(POLYARRAY *pa0) { POLYARRAY *pa, *paNext, *pa2First, *pa2Last; // Eliminate the trivially clipped primitives and build a new pa chain. pa2First = pa2Last = NULL; for (pa = pa0; pa; pa = paNext) { // get next pa first paNext = pa->paNext; if (pa->flags & POLYARRAY_REMOVE_PRIMITIVE) { PolyArrayRestoreColorPointer(pa); } else { // add to the new pa chain if (!pa2First) pa2First = pa; else pa2Last->paNext = pa; pa2Last = pa; pa2Last->paNext = NULL; } } // Return the new pa chain. return pa2First; } #endif // POLYARRAY_AND_CLIPCODES /******************************Public*Routine******************************\ * glsrvFlushDrawPolyArray * * The dispatch code in glsrvAttention links together the POLYARRAY data * structures of consecutive glim_DrawPolyArray calls. The front end * preprocessing of the vertices in each POLYARRAY is executed immediately * in glim_DrawPolyArray (i.e., PolyArrayDrawXXX), but the actually back end * rendering (PolyArrayRenderXXX) is delayed until the chain is broken (either * by a non-DrawPolyArray call, the end of the batch, or a batch timeout). * * glsrvFlushDrawPolyArray is the function that is called to flush the * chained POLYARRAYs by invoking the back end rendering code. The back end * may be the generic software-only implementation or the MCD driver. * * History: * 12-Feb-1996 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ // Poly array render routines. // ASSERT_PRIMITIVE PFN_POLYARRAYRENDER afnPolyArrayRender[] = { (PFN_POLYARRAYRENDER) PolyArrayRenderPoints, (PFN_POLYARRAYRENDER) PolyArrayRenderLines, (PFN_POLYARRAYRENDER) NULL, // line loop not required (PFN_POLYARRAYRENDER) PolyArrayRenderLStrip, (PFN_POLYARRAYRENDER) PolyArrayRenderTriangles, (PFN_POLYARRAYRENDER) PolyArrayRenderTStrip, (PFN_POLYARRAYRENDER) PolyArrayRenderTFan, (PFN_POLYARRAYRENDER) PolyArrayRenderQuads, (PFN_POLYARRAYRENDER) PolyArrayRenderQStrip, (PFN_POLYARRAYRENDER) PolyArrayRenderPolygon, }; void APIPRIVATE glsrvFlushDrawPolyArray(void *_paBegin) { POLYARRAY *paBegin = (POLYARRAY *)_paBegin; POLYARRAY *pa, *paNext; __GLGENcontext *gengc; __GL_SETUP(); //#define FRONT_END_ONLY 1 #if FRONT_END_ONLY if (paBegin) { for (pa = paNext = paBegin; pa = paNext; ) { ASSERTOPENGL(pa->primType >= GL_POINTS && pa->primType <= GL_POLYGON, "DrawPolyArray: bad primitive type\n"); // Get next pointer first! paNext = pa->paNext; // Restore color pointer in the vertex buffer! PolyArrayRestoreColorPointer(pa); } } return; #endif gengc = (__GLGENcontext *) gc; #ifdef _MCD_ #if DBG if (gengc->pMcdState && !(glDebugFlags & GLDEBUG_DISABLEPRIM) && (gc->renderMode == GL_RENDER)) #else if ((gengc->pMcdState) && (gc->renderMode == GL_RENDER)) #endif { POLYARRAY *paEnd; // Let the MCD driver have first crack. If the MCD processes // the entire batch, then it will return NULL. Otherwise, it // will return a pointer to a chain of unprocessed POLYARRAYs. paEnd = GenMcdDrawPrim(gengc, paBegin); // Restore color pointer in the vertex buffer (for the POLYARRAYs // that have been processed by MCD; leave the unprocessed ones alone). // // If the driver is using DMA, it must do the reset. If not DMA, // we will do it for the driver. if (!(McdDriverInfo.drvMemFlags & MCDRV_MEM_DMA)) { for (pa = paBegin; pa != paEnd; pa = paNext) { paNext = pa->paNext; PolyArrayRestoreColorPointer(pa); } } else { // With DMA, the driver must either process the entire batch // or reject the entire batch. // // Therefore, if GenMcdDrawPrim returns success (paEnd == NULL), // the POLYARRAY is being sent via DMA to the driver and we // need to switch to the other buffer. Otherwise, we need to // drop down into the software implementation below. if (!paEnd) { GenMcdSwapBatch(gengc); } } // If able, let generic handle unprocessed POLYARRAYs (if any). if (gengc->flags & GENGC_GENERIC_COMPATIBLE_FORMAT) { paBegin = paEnd; } else { // Generic cannot be used, so we must abandon the rest of the batch. // Must reset the color pointers in the remainder of the batch. for (pa = paEnd; pa; pa = paNext) { paNext = pa->paNext; PolyArrayRestoreColorPointer(pa); } paBegin = (POLYARRAY *) NULL; __glSetError(GL_OUT_OF_MEMORY); } } if (paBegin) #endif { for (pa = paNext = paBegin; pa = paNext; ) { ASSERTOPENGL(pa->primType >= GL_POINTS && pa->primType <= GL_POLYGON, "DrawPolyArray: bad primitive type\n"); if (pa->flags & POLYARRAY_RENDER_PRIMITIVE) (*afnPolyArrayRender[pa->primType])(gc, pa); // Get next pointer first! paNext = pa->paNext; // Restore color pointer in the vertex buffer! PolyArrayRestoreColorPointer(pa); } } } /****************************************************************************/ // Restore color pointer in the vertex buffer! // However, don't restore the color pointer if it is a RasterPos call. GLvoid FASTCALL PolyArrayRestoreColorPointer(POLYARRAY *pa) { POLYDATA *pd, *pdLast; ASSERTOPENGL(!(pa->flags & POLYARRAY_RASTERPOS), "RasterPos unexpected\n"); // See also glsbResetBuffers. // Reset color pointer in output index array if (pa->aIndices) { ASSERTOPENGL((POLYDATA *) pa->aIndices >= pa->pdBuffer0 && (POLYDATA *) pa->aIndices <= pa->pdBufferMax, "bad index map pointer\n"); pdLast = (POLYDATA *) (pa->aIndices + pa->nIndices); for (pd = (POLYDATA *) pa->aIndices; pd < pdLast; pd++) pd->color = &pd->colors[__GL_FRONTFACE]; ASSERTOPENGL(pd >= pa->pdBuffer0 && pd <= pa->pdBufferMax + 1, "bad polyarray pointer\n"); } // Reset color pointer in the POLYARRAY structure last! ASSERTOPENGL((POLYDATA *) pa >= pa->pdBuffer0 && (POLYDATA *) pa <= pa->pdBufferMax, "bad polyarray pointer\n"); ((POLYDATA *) pa)->color = &((POLYDATA *) pa)->colors[__GL_FRONTFACE]; } /****************************************************************************/ // Compute generic fog value for the poly array. // // IN: eye // OUT: fog void FASTCALL PolyArrayComputeFog(__GLcontext *gc, POLYARRAY *pa) { __GLfloat density, density2neg, end, oneOverEMinusS; POLYDATA *pd, *pdLast; ASSERTOPENGL(pa->flags & POLYARRAY_EYE_PROCESSED, "need eye\n"); pdLast = pa->pdNextVertex-1; switch (gc->state.fog.mode) { case GL_EXP: PERF_CHECK(FALSE, "Uses GL_EXP fog\n"); density = gc->state.fog.density; for (pd = pa->pd0; pd <= pdLast; pd++) { __GLfloat eyeZ; pd->flags |= POLYDATA_FOG_VALID; // used by clipping code! eyeZ = pd->eye.z; if (__GL_FLOAT_LTZ(eyeZ)) pd->fog = __GL_POWF(__glE, density * eyeZ); else pd->fog = __GL_POWF(__glE, -density * eyeZ); // clamp the fog value to [0.0,1.0] if (pd->fog > __glOne) pd->fog = __glOne; } break; case GL_EXP2: PERF_CHECK(FALSE, "Uses GL_EXP2 fog\n"); density2neg = gc->state.fog.density2neg; for (pd = pa->pd0; pd <= pdLast; pd++) { __GLfloat eyeZ; pd->flags |= POLYDATA_FOG_VALID; eyeZ = pd->eye.z; pd->fog = __GL_POWF(__glE, density2neg * eyeZ * eyeZ); // clamp the fog value to [0.0,1.0] if (pd->fog > __glOne) pd->fog = __glOne; } break; case GL_LINEAR: end = gc->state.fog.end; oneOverEMinusS = gc->state.fog.oneOverEMinusS; for (pd = pa->pd0; pd <= pdLast; pd++) { __GLfloat eyeZ; pd->flags |= POLYDATA_FOG_VALID; eyeZ = pd->eye.z; if (__GL_FLOAT_LTZ(eyeZ)) pd->fog = (end + eyeZ) * oneOverEMinusS; else pd->fog = (end - eyeZ) * oneOverEMinusS; // clamp the fog value here if (__GL_FLOAT_LTZ(pd->fog)) pd->fog = __glZero; else if (__GL_FLOAT_COMPARE_PONE(pd->fog, >)) pd->fog = __glOne; } break; } } // Apply cheap fog to RGB colors. // // IN: fog, color (front/back) // OUT: color (front/back) void FASTCALL PolyArrayCheapFogRGBColor(__GLcontext *gc, POLYARRAY *pa) { __GLfloat fogColorR, fogColorG, fogColorB; POLYDATA *pd, *pdLast; GLboolean bGrayFog; GLboolean doFrontColor, doBackColor; if (!(gc->state.enables.general & __GL_LIGHTING_ENABLE)) { ASSERTOPENGL(!(gc->vertex.paNeeds & PANEEDS_BACK_COLOR), "no back color needed when lighting is disabled\n"); } // ASSERT_PRIMITIVE if ((unsigned int) pa->primType <= GL_LINE_STRIP) { doFrontColor = GL_TRUE; doBackColor = GL_FALSE; } else { doFrontColor = gc->vertex.paNeeds & PANEEDS_FRONT_COLOR; doBackColor = gc->vertex.paNeeds & PANEEDS_BACK_COLOR; } pdLast = pa->pdNextVertex-1; fogColorR = gc->state.fog.color.r; fogColorG = gc->state.fog.color.g; fogColorB = gc->state.fog.color.b; bGrayFog = (gc->state.fog.flags & __GL_FOG_GRAY_RGB) ? GL_TRUE : GL_FALSE; PERF_CHECK(bGrayFog, "Uses non gray fog color\n"); if (bGrayFog) { for (pd = pa->pd0; pd <= pdLast; pd++) { __GLfloat fog, oneMinusFog, delta; /* Get the vertex fog value */ fog = pd->fog; oneMinusFog = __glOne - fog; delta = oneMinusFog * fogColorR; /* Now whack the color */ if (doFrontColor) { pd->colors[0].r = fog * pd->colors[0].r + delta; pd->colors[0].g = fog * pd->colors[0].g + delta; pd->colors[0].b = fog * pd->colors[0].b + delta; } if (doBackColor) { pd->colors[1].r = fog * pd->colors[1].r + delta; pd->colors[1].g = fog * pd->colors[1].g + delta; pd->colors[1].b = fog * pd->colors[1].b + delta; } } } else { for (pd = pa->pd0; pd <= pdLast; pd++) { __GLfloat fog, oneMinusFog; /* Get the vertex fog value */ fog = pd->fog; oneMinusFog = __glOne - fog; /* Now whack the color */ if (doFrontColor) { pd->colors[0].r = fog * pd->colors[0].r + oneMinusFog * fogColorR; pd->colors[0].g = fog * pd->colors[0].g + oneMinusFog * fogColorG; pd->colors[0].b = fog * pd->colors[0].b + oneMinusFog * fogColorB; } if (doBackColor) { pd->colors[1].r = fog * pd->colors[1].r + oneMinusFog * fogColorR; pd->colors[1].g = fog * pd->colors[1].g + oneMinusFog * fogColorG; pd->colors[1].b = fog * pd->colors[1].b + oneMinusFog * fogColorB; } } } } // Apply cheap fog to color index values. // // IN: fog, color.r (front/back) // OUT: color.r (front/back) void FASTCALL PolyArrayCheapFogCIColor(__GLcontext *gc, POLYARRAY *pa) { __GLfloat maxR, fogIndex; POLYDATA *pd, *pdLast; GLboolean doFrontColor, doBackColor; if (!(gc->state.enables.general & __GL_LIGHTING_ENABLE)) { ASSERTOPENGL(!(gc->vertex.paNeeds & PANEEDS_BACK_COLOR), "no back color needed when lighting is disabled\n"); } // ASSERT_PRIMITIVE if ((unsigned int) pa->primType <= GL_LINE_STRIP) { doFrontColor = GL_TRUE; doBackColor = GL_FALSE; } else { doFrontColor = gc->vertex.paNeeds & PANEEDS_FRONT_COLOR; doBackColor = gc->vertex.paNeeds & PANEEDS_BACK_COLOR; } fogIndex = gc->state.fog.index; maxR = (1 << gc->modes.indexBits) - 1; pdLast = pa->pdNextVertex-1; for (pd = pa->pd0; pd <= pdLast; pd++) { __GLfloat fogDelta; fogDelta = (__glOne - pd->fog) * fogIndex; /* Now whack the color */ if (doFrontColor) { pd->colors[0].r = pd->colors[0].r + fogDelta; if (pd->colors[0].r > maxR) pd->colors[0].r = maxR; } if (doBackColor) { pd->colors[1].r = pd->colors[1].r + fogDelta; if (pd->colors[1].r > maxR) pd->colors[1].r = maxR; } } } /****************************************************************************/ void FASTCALL PADoEval1(__GLcontext *gc, POLYARRAY *pa, POLYDATA *pd) { __GLevaluator1 *eval = gc->eval.eval1; __GLfloat **evalData = gc->eval.eval1Data; __GLevaluatorMachine em = gc->eval; __GLfloat u; // Find u. if (pd->flags & POLYDATA_EVALCOORD1) { u = pd->obj.x; } else { __GLevaluatorGrid *gu; __GLfloat du; GLint i; ASSERTOPENGL(pd->flags & POLYDATA_EVALPOINT1, "bad pd flags\n"); i = *(GLint *) &pd->obj; gu = &gc->state.evaluator.u1; du = (gu->finish - gu->start)/(__GLfloat)gu->n;/*XXX cache? */ u = (i == gu->n) ? gu->finish : (gu->start + i * du); } // Evaluated color, index, normal and texture coords are ignored in selection if (gc->renderMode != GL_SELECT) { if (gc->modes.colorIndexMode) { if (!(gc->state.enables.general & __GL_LIGHTING_ENABLE)) { if (gc->state.enables.eval1 & __GL_MAP1_INDEX_ENABLE) { DoDomain1(&em, u, &eval[__GL_I], &pd->colors[0].r, evalData[__GL_I]); __GL_CHECK_CLAMP_CI(pd->colors[0].r, gc, pa->flags, pd->colors[0].r); pd->flags |= POLYDATA_COLOR_VALID; } } } else { if (gc->state.enables.eval1 & __GL_MAP1_COLOR_4_ENABLE) { // NOTE: In OpenGL 1.0, color material does not apply to evaluated colors. // In OpenGL 1.1, this behavior was changed. This (1.1) code assumes that // ColorMaterial applies to evaluated colors to simplify the graphics pipeline. // Otherwise, the evaluated colors have no effect when lighting is enabled. DoDomain1(&em, u, &eval[__GL_C4], &pd->colors[0].r, evalData[__GL_C4]); __GL_SCALE_AND_CHECK_CLAMP_R(pd->colors[0].r, gc, pa->flags, pd->colors[0].r); __GL_SCALE_AND_CHECK_CLAMP_G(pd->colors[0].g, gc, pa->flags, pd->colors[0].g); __GL_SCALE_AND_CHECK_CLAMP_B(pd->colors[0].b, gc, pa->flags, pd->colors[0].b); __GL_SCALE_AND_CHECK_CLAMP_A(pd->colors[0].a, gc, pa->flags, pd->colors[0].a); pd->flags |= POLYDATA_COLOR_VALID; } if (gc->state.enables.eval1 & __GL_MAP1_TEXTURE_COORD_4_ENABLE) { DoDomain1(&em, u, &eval[__GL_T4], &pd->texture.x, evalData[__GL_T4]); pd->flags |= POLYDATA_TEXTURE_VALID; pa->flags |= POLYARRAY_TEXTURE4; } else if (gc->state.enables.eval1 & __GL_MAP1_TEXTURE_COORD_3_ENABLE) { DoDomain1(&em, u, &eval[__GL_T3], &pd->texture.x, evalData[__GL_T3]); pd->texture.w = __glOne; pd->flags |= POLYDATA_TEXTURE_VALID; pa->flags |= POLYARRAY_TEXTURE3; } else if (gc->state.enables.eval1 & __GL_MAP1_TEXTURE_COORD_2_ENABLE) { DoDomain1(&em, u, &eval[__GL_T2], &pd->texture.x, evalData[__GL_T2]); pd->texture.z = __glZero; pd->texture.w = __glOne; pd->flags |= POLYDATA_TEXTURE_VALID; pa->flags |= POLYARRAY_TEXTURE2; } else if (gc->state.enables.eval1 & __GL_MAP1_TEXTURE_COORD_1_ENABLE) { DoDomain1(&em, u, &eval[__GL_T1], &pd->texture.x, evalData[__GL_T1]); pd->texture.y = __glZero; pd->texture.z = __glZero; pd->texture.w = __glOne; pd->flags |= POLYDATA_TEXTURE_VALID; pa->flags |= POLYARRAY_TEXTURE1; } } if (gc->state.enables.eval1 & __GL_MAP1_NORMAL_ENABLE) { DoDomain1(&em, u, &eval[__GL_N3], &pd->normal.x, evalData[__GL_N3]); pd->flags |= POLYDATA_NORMAL_VALID; } } if (gc->state.enables.eval1 & __GL_MAP1_VERTEX_4_ENABLE) { DoDomain1(&em, u, &eval[__GL_V4], &pd->obj.x, evalData[__GL_V4]); pd->flags |= POLYDATA_VERTEX4; pa->flags |= POLYARRAY_VERTEX4; } else if (gc->state.enables.eval1 & __GL_MAP1_VERTEX_3_ENABLE) { DoDomain1(&em, u, &eval[__GL_V3], &pd->obj.x, evalData[__GL_V3]); pd->obj.w = __glOne; pd->flags |= POLYDATA_VERTEX3; pa->flags |= POLYARRAY_VERTEX3; } } // Compute the color, texture, normal, vertex based upon u and v. // // NOTE: This function is called by the client side EvalMesh2 functions. // If you modify it, make sure that you modify the callers too! void FASTCALL PADoEval2(__GLcontext *gc, POLYARRAY *pa, POLYDATA *pd, BOOL bScaleColor) { __GLevaluator2 *eval = gc->eval.eval2; __GLfloat **evalData = gc->eval.eval2Data; __GLevaluatorMachine em = gc->eval; __GLfloat u, v; // Find u and v. if (pd->flags & POLYDATA_EVALCOORD2) { u = pd->obj.x; v = pd->obj.y; } else { __GLevaluatorGrid *gu; __GLevaluatorGrid *gv; __GLfloat du; __GLfloat dv; GLint i, j, *pi; ASSERTOPENGL(pd->flags & POLYDATA_EVALPOINT2, "bad pd flags\n"); pi = (GLint *) &pd->obj; i = pi[0]; j = pi[1]; gu = &gc->state.evaluator.u2; gv = &gc->state.evaluator.v2; du = (gu->finish - gu->start)/(__GLfloat)gu->n;/*XXX cache? */ dv = (gv->finish - gv->start)/(__GLfloat)gv->n;/*XXX cache? */ u = (i == gu->n) ? gu->finish : (gu->start + i * du); v = (j == gv->n) ? gv->finish : (gv->start + j * dv); } // Evaluated colors, normals and texture coords are ignored in selection. if (gc->renderMode == GL_SELECT) { if (gc->state.enables.eval2 & __GL_MAP2_VERTEX_4_ENABLE) { DoDomain2(&em, u, v, &eval[__GL_V4], &pd->obj.x, evalData[__GL_V4]); pd->flags |= POLYDATA_VERTEX4; pa->flags |= POLYARRAY_VERTEX4; } else if (gc->state.enables.eval2 & __GL_MAP2_VERTEX_3_ENABLE) { DoDomain2(&em, u, v, &eval[__GL_V3], &pd->obj.x, evalData[__GL_V3]); pd->obj.w = __glOne; pd->flags |= POLYDATA_VERTEX3; pa->flags |= POLYARRAY_VERTEX3; } return; } if (gc->state.enables.general & __GL_AUTO_NORMAL_ENABLE) { if (gc->state.enables.eval2 & __GL_MAP2_VERTEX_4_ENABLE) { __GLfloat du[4]; __GLfloat dv[4]; DoDomain2WithDerivs(&em, u, v, &eval[__GL_V4], &pd->obj.x, du, dv, evalData[__GL_V4]); ComputeFirstPartials(&pd->obj.x, du, dv); ComputeNormal2(gc, &pd->normal.x, du, dv); pd->flags |= POLYDATA_NORMAL_VALID; pd->flags |= POLYDATA_VERTEX4; pa->flags |= POLYARRAY_VERTEX4; } else if (gc->state.enables.eval2 & __GL_MAP2_VERTEX_3_ENABLE) { __GLfloat du[3]; __GLfloat dv[3]; DoDomain2WithDerivs(&em, u, v, &eval[__GL_V3], &pd->obj.x, du, dv, evalData[__GL_V3]); ComputeNormal2(gc, &pd->normal.x, du, dv); pd->obj.w = __glOne; pd->flags |= POLYDATA_NORMAL_VALID; pd->flags |= POLYDATA_VERTEX3; pa->flags |= POLYARRAY_VERTEX3; } } else { if (gc->state.enables.eval2 & __GL_MAP2_NORMAL_ENABLE) { DoDomain2(&em, u, v, &eval[__GL_N3], &pd->normal.x, evalData[__GL_N3]); pd->flags |= POLYDATA_NORMAL_VALID; } if (gc->state.enables.eval2 & __GL_MAP2_VERTEX_4_ENABLE) { DoDomain2(&em, u, v, &eval[__GL_V4], &pd->obj.x, evalData[__GL_V4]); pd->flags |= POLYDATA_VERTEX4; pa->flags |= POLYARRAY_VERTEX4; } else if (gc->state.enables.eval2 & __GL_MAP2_VERTEX_3_ENABLE) { DoDomain2(&em, u, v, &eval[__GL_V3], &pd->obj.x, evalData[__GL_V3]); pd->obj.w = __glOne; pd->flags |= POLYDATA_VERTEX3; pa->flags |= POLYARRAY_VERTEX3; } } if (gc->modes.colorIndexMode) { if (!(gc->state.enables.general & __GL_LIGHTING_ENABLE)) { if (gc->state.enables.eval2 & __GL_MAP2_INDEX_ENABLE) { DoDomain2(&em, u, v, &eval[__GL_I], &pd->colors[0].r, evalData[__GL_I]); __GL_CHECK_CLAMP_CI(pd->colors[0].r, gc, pa->flags, pd->colors[0].r); pd->flags |= POLYDATA_COLOR_VALID; } } } else { if (gc->state.enables.eval2 & __GL_MAP2_COLOR_4_ENABLE) { DoDomain2(&em, u, v, &eval[__GL_C4], &pd->colors[0].r, evalData[__GL_C4]); if (bScaleColor) { __GL_SCALE_AND_CHECK_CLAMP_R(pd->colors[0].r, gc, pa->flags, pd->colors[0].r); __GL_SCALE_AND_CHECK_CLAMP_G(pd->colors[0].g, gc, pa->flags, pd->colors[0].g); __GL_SCALE_AND_CHECK_CLAMP_B(pd->colors[0].b, gc, pa->flags, pd->colors[0].b); __GL_SCALE_AND_CHECK_CLAMP_A(pd->colors[0].a, gc, pa->flags, pd->colors[0].a); } pd->flags |= POLYDATA_COLOR_VALID; } if (gc->state.enables.eval2 & __GL_MAP2_TEXTURE_COORD_4_ENABLE) { DoDomain2(&em, u, v, &eval[__GL_T4], &pd->texture.x, evalData[__GL_T4]); pd->flags |= POLYDATA_TEXTURE_VALID; pa->flags |= POLYARRAY_TEXTURE4; } else if (gc->state.enables.eval2 & __GL_MAP2_TEXTURE_COORD_3_ENABLE) { DoDomain2(&em, u, v, &eval[__GL_T3], &pd->texture.x, evalData[__GL_T3]); pd->texture.w = __glOne; pd->flags |= POLYDATA_TEXTURE_VALID; pa->flags |= POLYARRAY_TEXTURE3; } else if (gc->state.enables.eval2 & __GL_MAP2_TEXTURE_COORD_2_ENABLE) { DoDomain2(&em, u, v, &eval[__GL_T2], &pd->texture.x, evalData[__GL_T2]); pd->texture.z = __glZero; pd->texture.w = __glOne; pd->flags |= POLYDATA_TEXTURE_VALID; pa->flags |= POLYARRAY_TEXTURE2; } else if (gc->state.enables.eval2 & __GL_MAP2_TEXTURE_COORD_1_ENABLE) { DoDomain2(&em, u, v, &eval[__GL_T1], &pd->texture.x, evalData[__GL_T1]); pd->texture.y = __glZero; pd->texture.z = __glZero; pd->texture.w = __glOne; pd->flags |= POLYDATA_TEXTURE_VALID; pa->flags |= POLYARRAY_TEXTURE1; } } } // Fill in the evaluated values. // DrawPolyArray should keep track of final current values because this // function will overwrite them with evaluated values. // The evaluated values do not modify current values. // // Note: The first vertex must have valid color, normal and texture! void FASTCALL PolyArrayProcessEvaluator(__GLcontext *gc, POLYARRAY *pa) { POLYDATA *pd, *pdLast, *pdCur; __GLcolor savedColor; __GLcoord savedNormal, savedTexture; GLboolean updateColor, updateNormal, updateTexture; GLboolean doEval1, doEval2, doRemoveEval1, doRemoveEval2; GLuint enablesEval1, enablesEval2; GLuint pdFlagsEval; GLboolean bSamePolyDataType; ASSERTOPENGL(pa->flags & (POLYARRAY_EVALCOORD1 | POLYARRAY_EVALCOORD2 | POLYARRAY_EVALPOINT1 | POLYARRAY_EVALPOINT2), "no evaluator!\n"); ASSERTOPENGL(!(pa->flags & POLYARRAY_SAME_COLOR_DATA), "POLYARRAY_SAME_COLOR_DATA unexpected in evaluators!\n"); ASSERTOPENGL(pa->pd0->flags & POLYDATA_COLOR_VALID, "need initial color value\n"); ASSERTOPENGL(pa->pd0->flags & POLYDATA_TEXTURE_VALID, "need initial texcoord value\n"); ASSERTOPENGL(pa->pd0->flags & POLYDATA_NORMAL_VALID, "need initial normal\n"); // Find out which attributes are modified by the evaluators. pdLast = pa->pdNextVertex-1; enablesEval1 = gc->state.enables.eval1; enablesEval2 = gc->state.enables.eval2; doEval1 = (pa->flags & (POLYARRAY_EVALCOORD1 | POLYARRAY_EVALPOINT1)) ? GL_TRUE : GL_FALSE; doEval2 = (pa->flags & (POLYARRAY_EVALCOORD2 | POLYARRAY_EVALPOINT2)) ? GL_TRUE : GL_FALSE; pdFlagsEval = pa->flags & (POLYARRAY_EVALCOORD1 | POLYARRAY_EVALCOORD2 | POLYARRAY_EVALPOINT1 | POLYARRAY_EVALPOINT2); bSamePolyDataType = GL_TRUE; // assume the polydata's are of same type updateColor = GL_FALSE; updateNormal = GL_FALSE; updateTexture = GL_FALSE; // Evaluated color, index, normal and texture coords are ignored in selection if (gc->renderMode != GL_SELECT) { if (gc->modes.colorIndexMode) { if (!(gc->state.enables.general & __GL_LIGHTING_ENABLE)) { if ((enablesEval1 & __GL_MAP1_INDEX_ENABLE) && doEval1 || (enablesEval2 & __GL_MAP2_INDEX_ENABLE) && doEval2) { updateColor = GL_TRUE; pa->pdCurColor = pdLast; // update to last vertex! } } } else { if ((enablesEval1 & __GL_MAP1_COLOR_4_ENABLE) && doEval1 || (enablesEval2 & __GL_MAP2_COLOR_4_ENABLE) && doEval2) { updateColor = GL_TRUE; pa->pdCurColor = pdLast; // update to last vertex! } if ((enablesEval1 & (__GL_MAP1_TEXTURE_COORD_1_ENABLE | __GL_MAP1_TEXTURE_COORD_2_ENABLE | __GL_MAP1_TEXTURE_COORD_3_ENABLE | __GL_MAP1_TEXTURE_COORD_4_ENABLE)) && doEval1 || (enablesEval2 & (__GL_MAP2_TEXTURE_COORD_1_ENABLE | __GL_MAP2_TEXTURE_COORD_2_ENABLE | __GL_MAP2_TEXTURE_COORD_3_ENABLE | __GL_MAP2_TEXTURE_COORD_4_ENABLE)) && doEval2) { updateTexture = GL_TRUE; pa->pdCurTexture = pdLast; // update to last vertex! } } if ((enablesEval1 & __GL_MAP1_NORMAL_ENABLE) && doEval1 || (gc->state.enables.general & __GL_AUTO_NORMAL_ENABLE || enablesEval2 & __GL_MAP2_NORMAL_ENABLE) && doEval2) { updateNormal = GL_TRUE; pa->pdCurNormal = pdLast; // update to last vertex! } } // If vertex map is not enabled, we need to propagate current values // for all vertices and pack the poly array! // The poly array code does not handle null entries. // // NOTE: To do this correctly, we need to propagate edge flags and materials // too. Since this is likely an application bug, we will punt on updating // these attributes. doRemoveEval1 = GL_FALSE; doRemoveEval2 = GL_FALSE; if (doEval1 && !(enablesEval1 & (__GL_MAP1_VERTEX_3_ENABLE | __GL_MAP1_VERTEX_4_ENABLE))) doRemoveEval1 = GL_TRUE; if (doEval2 && !(enablesEval2 & (__GL_MAP2_VERTEX_3_ENABLE | __GL_MAP2_VERTEX_4_ENABLE))) doRemoveEval2 = GL_TRUE; if (doRemoveEval1 || doRemoveEval2) { updateColor = GL_TRUE; updateTexture = GL_TRUE; updateNormal = GL_TRUE; } for (pd = pa->pd0; pd <= pdLast; pd++) { // Check if all polydata's contain same eval type with no other attributes. if (pd->flags != pdFlagsEval) bSamePolyDataType = GL_FALSE; // Need to propagate the current values for non evaluated vertices. // Save the current values before they are modified by evaluators. // works for color index too! if (updateColor && pd->flags & POLYDATA_COLOR_VALID) savedColor = pd->colors[0]; if (updateNormal && pd->flags & POLYDATA_NORMAL_VALID) { savedNormal.x = pd->normal.x; savedNormal.y = pd->normal.y; savedNormal.z = pd->normal.z; } if (updateTexture && pd->flags & POLYDATA_TEXTURE_VALID) savedTexture = pd->texture; if (pd->flags & (POLYDATA_EVALCOORD1 | POLYDATA_EVALPOINT1)) PADoEval1(gc, pa, pd); else if (pd->flags & (POLYDATA_EVALCOORD2 | POLYDATA_EVALPOINT2)) PADoEval2(gc, pa, pd, TRUE); else { // This vertex does not use evaluated values. Update its current values. if (updateColor && !(pd->flags & POLYDATA_COLOR_VALID)) { pd->flags |= POLYDATA_COLOR_VALID; pd->colors[0] = savedColor; } if (updateNormal && !(pd->flags & POLYDATA_NORMAL_VALID)) { pd->flags |= POLYDATA_NORMAL_VALID; pd->normal.x = savedNormal.x; pd->normal.y = savedNormal.y; pd->normal.z = savedNormal.z; } if (updateTexture && !(pd->flags & POLYDATA_TEXTURE_VALID)) { pd->flags |= POLYDATA_TEXTURE_VALID; pd->texture = savedTexture; } } } // If the polyarray contain the same data (i.e. evaluators) type with // no attribute changes, set the flag POLYARRAY_SAME_POLYDATA_TYPE. #if 0 // This is now handled correctly in DPA. // Make sure there is no trailing attribute changes. if (pd->flags != 0) bSamePolyDataType = GL_FALSE; #endif if (bSamePolyDataType) pa->flags |= POLYARRAY_SAME_POLYDATA_TYPE; // If vertex map is not enabled, we need to pack the poly array! if (doRemoveEval1 || doRemoveEval2) { PERF_CHECK(FALSE, "PolyArrayProcessEvaluator: vertex map not enabled!\n"); DBGPRINT("PolyArrayProcessEvaluator: vertex map not enabled!\n"); if (doRemoveEval1) pa->flags &= ~(POLYARRAY_EVALCOORD1 | POLYARRAY_EVALPOINT1); if (doRemoveEval2) pa->flags &= ~(POLYARRAY_EVALCOORD2 | POLYARRAY_EVALPOINT2); for (pd = pdCur = pa->pd0; pd <= pdLast; pd++) { if ((pd->flags & (POLYDATA_EVALCOORD1 | POLYDATA_EVALPOINT1)) && doRemoveEval1) { // Need to propagate materials and edge flag! // Ignore first edge flag because it may have been set by // DrawPolyArray initialization code. ASSERTOPENGL ( (!(pd->flags & (POLYDATA_MATERIAL_FRONT | POLYDATA_MATERIAL_BACK))) && (!(pd->flags & POLYDATA_EDGEFLAG_VALID) || pd == pa->pd0), "PolyArrayProcessEvaluator: bad evaluator!\n" ); continue; } if ((pd->flags & (POLYDATA_EVALCOORD2 | POLYDATA_EVALPOINT2)) && doRemoveEval2) { // Need to propagate materials and edge flag! // Ignore first edge flag because it may have been set by // DrawPolyArray initialization code. ASSERTOPENGL ( (!(pd->flags & (POLYDATA_MATERIAL_FRONT | POLYDATA_MATERIAL_BACK))) && (!(pd->flags & POLYDATA_EDGEFLAG_VALID) || pd == pa->pd0), "PolyArrayProcessEvaluator: bad evaluator!\n" ); continue; } PA_COPY_VERTEX(pdCur,pd); pdCur++; } // update poly array pa->pdNextVertex = pdCur; if (pdCur != pa->pd0) { pa->pdCurColor = pa->pdCurNormal = pa->pdCurTexture = pdCur - 1; } else { pa->pdCurColor = pa->pdCurNormal = pa->pdCurTexture = pdCur; } pa->nIndices = pdCur - pa->pd0; } } /****************************************************************************/ // Compute eye coordinates // // IN: obj // OUT: eye void FASTCALL PolyArrayProcessEye(__GLcontext *gc, POLYARRAY *pa) { __GLtransform *trMV; __GLmatrix *m; POLYDATA *pd, *pdLast; if (pa->flags & POLYARRAY_EYE_PROCESSED) return; pa->flags |= POLYARRAY_EYE_PROCESSED; trMV = gc->transform.modelView; m = &trMV->matrix; pdLast = pa->pdNextVertex-1; // The primitive may contain a mix of vertex types (2,3,4)! if (m->matrixType == __GL_MT_IDENTITY) { for (pd = pa->pd0; pd <= pdLast; pd++) pd->eye = pd->obj; } else { PFN_XFORM pfnXform; // If any incoming coords contains w coord, use xf4. if (pa->flags & POLYARRAY_VERTEX4) pfnXform = m->xf4; else if (pa->flags & POLYARRAY_VERTEX3) pfnXform = m->xf3; else pfnXform = m->xf2; for (pd = pa->pd0; pd <= pdLast; pd++) (*pfnXform)(&pd->eye, (__GLfloat *) &pd->obj, m); } } /****************************************************************************/ // Process edge flags. // // IN: edge // OUT: edge (all vertices) void FASTCALL PolyArrayProcessEdgeFlag(POLYARRAY *pa) { POLYDATA *pd, *pdLast; GLuint prevEdgeFlag; PERF_CHECK(FALSE, "Uses edge flags!\n"); ASSERTOPENGL(pa->pd0->flags & POLYDATA_EDGEFLAG_VALID, "need initial edgeflag value\n"); pdLast = pa->pdNextVertex-1; for (pd = pa->pd0; pd <= pdLast; pd++) { if (pd->flags & POLYDATA_EDGEFLAG_VALID) prevEdgeFlag = pd->flags & (POLYDATA_EDGEFLAG_VALID | POLYDATA_EDGEFLAG_BOUNDARY); else pd->flags |= prevEdgeFlag; } } /****************************************************************************/ // transform texture coordinates // there is no generated texture coords. // texture coordinates are modified in place // // IN: texture // OUT: texture (all vertices are updated) void FASTCALL PolyArrayCalcTexture(__GLcontext *gc, POLYARRAY *pa) { __GLmatrix *m; POLYDATA *pd, *pdLast; PFN_XFORM xf; ASSERTOPENGL(pa->pd0->flags & POLYDATA_TEXTURE_VALID, "need initial texcoord value\n"); ASSERTOPENGL(pa->flags & (POLYARRAY_TEXTURE1|POLYARRAY_TEXTURE2| POLYARRAY_TEXTURE3|POLYARRAY_TEXTURE4), "bad paflags\n"); m = &gc->transform.texture->matrix; pdLast = pa->pdNextVertex-1; if (m->matrixType == __GL_MT_IDENTITY) { // Identity texture xform. Incoming texcoord already has all s,t,q,r values. for (pd = pa->pd0; pd <= pdLast; pd++) if (!(pd->flags & POLYDATA_TEXTURE_VALID)) pd->texture = (pd-1)->texture; } else { // If any incoming texture coords contains q coord, use xf4. if (pa->flags & POLYARRAY_TEXTURE4) xf = m->xf4; else if (pa->flags & POLYARRAY_TEXTURE3) xf = m->xf3; else if (pa->flags & POLYARRAY_TEXTURE2) xf = m->xf2; else xf = m->xf1; for (pd = pa->pd0; pd <= pdLast; pd++) { // Apply texture matrix if (pd->flags & POLYDATA_TEXTURE_VALID) (*xf)(&pd->texture, (__GLfloat *) &pd->texture, m); else pd->texture = (pd-1)->texture; } } } // Generate texture coordinates from object coordinates // object linear texture generation // s and t are enabled but r and q are disabled // both s and t use the object linear mode // both s and t have the SAME plane equation // texture coordinates are modified in place // // IN: texture, obj // OUT: texture (all vertices are updated) void FASTCALL PolyArrayCalcObjectLizNearSameST(__GLcontext *gc, POLYARRAY *pa) { __GLmatrix *m; __GLcoord *cs, gen; POLYDATA *pd, *pdLast; PFN_XFORM xf; ASSERTOPENGL(pa->pd0->flags & POLYDATA_TEXTURE_VALID, "need initial texcoord value\n"); ASSERTOPENGL(pa->flags & (POLYARRAY_TEXTURE1|POLYARRAY_TEXTURE2| POLYARRAY_TEXTURE3|POLYARRAY_TEXTURE4), "bad paflags\n"); cs = &gc->state.texture.s.objectPlaneEquation; pdLast = pa->pdNextVertex-1; m = &gc->transform.texture->matrix; if (m->matrixType == __GL_MT_IDENTITY) { for (pd = pa->pd0; pd <= pdLast; pd++) { if (!(pd->flags & POLYDATA_TEXTURE_VALID)) { pd->texture.z = (pd-1)->texture.z; pd->texture.w = (pd-1)->texture.w; } // both s and t have the SAME plane equation pd->texture.x = cs->x * pd->obj.x + cs->y * pd->obj.y + cs->z * pd->obj.z + cs->w * pd->obj.w; pd->texture.y = pd->texture.x; } } else { // If any incoming texture coords contains q coord, use xf4. if (pa->flags & POLYARRAY_TEXTURE4) xf = m->xf4; else if (pa->flags & POLYARRAY_TEXTURE3) xf = m->xf3; else xf = m->xf2; // at least 2 generated values for (pd = pa->pd0; pd <= pdLast; pd++) { if (pd->flags & POLYDATA_TEXTURE_VALID) { gen.z = pd->texture.z; gen.w = pd->texture.w; } // both s and t have the SAME plane equation gen.x = cs->x * pd->obj.x + cs->y * pd->obj.y + cs->z * pd->obj.z + cs->w * pd->obj.w; gen.y = gen.x; // Finally, apply texture matrix (*xf)(&pd->texture, (__GLfloat *) &gen, m); } } } // Generate texture coordinates from object coordinates // object linear texture generation // s and t are enabled but r and q are disabled // both s and t use the object linear mode // both s and t have DIFFERENT plane equations // texture coordinates are modified in place // // IN: texture, obj // OUT: texture (all vertices are updated) void FASTCALL PolyArrayCalcObjectLizNear(__GLcontext *gc, POLYARRAY *pa) { __GLmatrix *m; __GLcoord *cs, *ct, gen; POLYDATA *pd, *pdLast; PFN_XFORM xf; ASSERTOPENGL(pa->pd0->flags & POLYDATA_TEXTURE_VALID, "need initial texcoord value\n"); ASSERTOPENGL(pa->flags & (POLYARRAY_TEXTURE1|POLYARRAY_TEXTURE2| POLYARRAY_TEXTURE3|POLYARRAY_TEXTURE4), "bad paflags\n"); cs = &gc->state.texture.s.objectPlaneEquation; ct = &gc->state.texture.t.objectPlaneEquation; pdLast = pa->pdNextVertex-1; m = &gc->transform.texture->matrix; if (m->matrixType == __GL_MT_IDENTITY) { for (pd = pa->pd0; pd <= pdLast; pd++) { if (!(pd->flags & POLYDATA_TEXTURE_VALID)) { pd->texture.z = (pd-1)->texture.z; pd->texture.w = (pd-1)->texture.w; } pd->texture.x = cs->x * pd->obj.x + cs->y * pd->obj.y + cs->z * pd->obj.z + cs->w * pd->obj.w; pd->texture.y = ct->x * pd->obj.x + ct->y * pd->obj.y + ct->z * pd->obj.z + ct->w * pd->obj.w; } } else { // If any incoming texture coords contains q coord, use xf4. if (pa->flags & POLYARRAY_TEXTURE4) xf = m->xf4; else if (pa->flags & POLYARRAY_TEXTURE3) xf = m->xf3; else xf = m->xf2; // at least 2 generated values for (pd = pa->pd0; pd <= pdLast; pd++) { if (pd->flags & POLYDATA_TEXTURE_VALID) { gen.z = pd->texture.z; gen.w = pd->texture.w; } gen.x = cs->x * pd->obj.x + cs->y * pd->obj.y + cs->z * pd->obj.z + cs->w * pd->obj.w; gen.y = ct->x * pd->obj.x + ct->y * pd->obj.y + ct->z * pd->obj.z + ct->w * pd->obj.w; // Finally, apply texture matrix (*xf)(&pd->texture, (__GLfloat *) &gen, m); } } } // Generate texture coordinates from eye coordinates // eye linear texture generation // s and t are enabled but r and q are disabled // both s and t use the eye linear mode // both s and t have SAME plane equations // texture coordinates are modified in place // we may be able to get away without computing eye coord! // // IN: texture; obj or eye // OUT: texture and eye (all vertices are updated) void FASTCALL PolyArrayCalcEyeLizNearSameST(__GLcontext *gc, POLYARRAY *pa) { __GLmatrix *m; __GLcoord *cs, gen; POLYDATA *pd, *pdLast; PFN_XFORM xf; ASSERTOPENGL(pa->pd0->flags & POLYDATA_TEXTURE_VALID, "need initial texcoord value\n"); ASSERTOPENGL(pa->flags & (POLYARRAY_TEXTURE1|POLYARRAY_TEXTURE2| POLYARRAY_TEXTURE3|POLYARRAY_TEXTURE4), "bad paflags\n"); // Compute eye coord first if (!(pa->flags & POLYARRAY_EYE_PROCESSED)) PolyArrayProcessEye(gc, pa); cs = &gc->state.texture.s.eyePlaneEquation; pdLast = pa->pdNextVertex-1; m = &gc->transform.texture->matrix; if (m->matrixType == __GL_MT_IDENTITY) { for (pd = pa->pd0; pd <= pdLast; pd++) { if (!(pd->flags & POLYDATA_TEXTURE_VALID)) { pd->texture.z = (pd-1)->texture.z; pd->texture.w = (pd-1)->texture.w; } // both s and t have the SAME plane equation pd->texture.x = cs->x * pd->eye.x + cs->y * pd->eye.y + cs->z * pd->eye.z + cs->w * pd->eye.w; pd->texture.y = pd->texture.x; } } else { // If any incoming texture coords contains q coord, use xf4. if (pa->flags & POLYARRAY_TEXTURE4) xf = m->xf4; else if (pa->flags & POLYARRAY_TEXTURE3) xf = m->xf3; else xf = m->xf2; // at least 2 generated values for (pd = pa->pd0; pd <= pdLast; pd++) { if (pd->flags & POLYDATA_TEXTURE_VALID) { gen.z = pd->texture.z; gen.w = pd->texture.w; } // both s and t have the SAME plane equation gen.x = cs->x * pd->eye.x + cs->y * pd->eye.y + cs->z * pd->eye.z + cs->w * pd->eye.w; gen.y = gen.x; // Finally, apply texture matrix (*xf)(&pd->texture, (__GLfloat *) &gen, m); } } } // Generate texture coordinates from eye coordinates // eye linear texture generation // s and t are enabled but r and q are disabled // both s and t use the eye linear mode // both s and t have SAME plane equations // texture coordinates are modified in place // we may be able to get away without computing eye coord! // // IN: texture; obj or eye // OUT: texture and eye (all vertices are updated) void FASTCALL PolyArrayCalcEyeLizNear(__GLcontext *gc, POLYARRAY *pa) { __GLmatrix *m; __GLcoord *cs, *ct, gen; POLYDATA *pd, *pdLast; PFN_XFORM xf; ASSERTOPENGL(pa->pd0->flags & POLYDATA_TEXTURE_VALID, "need initial texcoord value\n"); ASSERTOPENGL(pa->flags & (POLYARRAY_TEXTURE1|POLYARRAY_TEXTURE2| POLYARRAY_TEXTURE3|POLYARRAY_TEXTURE4), "bad paflags\n"); // Compute eye coord first if (!(pa->flags & POLYARRAY_EYE_PROCESSED)) PolyArrayProcessEye(gc, pa); cs = &gc->state.texture.s.eyePlaneEquation; ct = &gc->state.texture.t.eyePlaneEquation; pdLast = pa->pdNextVertex-1; m = &gc->transform.texture->matrix; if (m->matrixType == __GL_MT_IDENTITY) { for (pd = pa->pd0; pd <= pdLast; pd++) { if (!(pd->flags & POLYDATA_TEXTURE_VALID)) { pd->texture.z = (pd-1)->texture.z; pd->texture.w = (pd-1)->texture.w; } pd->texture.x = cs->x * pd->eye.x + cs->y * pd->eye.y + cs->z * pd->eye.z + cs->w * pd->eye.w; pd->texture.y = ct->x * pd->eye.x + ct->y * pd->eye.y + ct->z * pd->eye.z + ct->w * pd->eye.w; } } else { // If any incoming texture coords contains q coord, use xf4. if (pa->flags & POLYARRAY_TEXTURE4) xf = m->xf4; else if (pa->flags & POLYARRAY_TEXTURE3) xf = m->xf3; else xf = m->xf2; // at least 2 generated values for (pd = pa->pd0; pd <= pdLast; pd++) { if (pd->flags & POLYDATA_TEXTURE_VALID) { gen.z = pd->texture.z; gen.w = pd->texture.w; } gen.x = cs->x * pd->eye.x + cs->y * pd->eye.y + cs->z * pd->eye.z + cs->w * pd->eye.w; gen.y = ct->x * pd->eye.x + ct->y * pd->eye.y + ct->z * pd->eye.z + ct->w * pd->eye.w; // Finally, apply texture matrix (*xf)(&pd->texture, (__GLfloat *) &gen, m); } } } // Compute the s & t coordinates for a sphere map. The s & t values // are stored in "result" even if both coordinates are not being // generated. The caller picks the right values out. // // IN: eye, normal void FASTCALL PASphereGen(POLYDATA *pd, __GLcoord *result) { __GLcoord u, r; __GLfloat m, ndotu; // Get unit vector from origin to the vertex in eye coordinates into u __glNormalize(&u.x, &pd->eye.x); // Dot the normal with the unit position u ndotu = pd->normal.x * u.x + pd->normal.y * u.y + pd->normal.z * u.z; // Compute r r.x = u.x - 2 * pd->normal.x * ndotu; r.y = u.y - 2 * pd->normal.y * ndotu; r.z = u.z - 2 * pd->normal.z * ndotu; // Compute m m = 2 * __GL_SQRTF(r.x*r.x + r.y*r.y + (r.z + 1) * (r.z + 1)); if (m) { result->x = r.x / m + __glHalf; result->y = r.y / m + __glHalf; } else { result->x = __glHalf; result->y = __glHalf; } } // Generate texture coordinates for sphere map // sphere map texture generation // s and t are enabled but r and q are disabled // both s and t use the sphere map mode // texture coordinates are modified in place // we may be able to get away without computing eye coord! // // IN: texture; obj or eye; normal // OUT: texture and eye (all vertices are updated) void FASTCALL PolyArrayCalcSphereMap(__GLcontext *gc, POLYARRAY *pa) { __GLmatrix *m; __GLcoord gen; POLYDATA *pd, *pdLast, *pdNormal; PFN_XFORM xf; GLboolean bIdentity; // this is really okay PERF_CHECK(FALSE, "Uses sphere map texture generation!\n"); ASSERTOPENGL(pa->pd0->flags & POLYDATA_TEXTURE_VALID, "need initial texcoord value\n"); ASSERTOPENGL(pa->pd0->flags & POLYDATA_NORMAL_VALID, "need initial normal\n"); ASSERTOPENGL(pa->flags & (POLYARRAY_TEXTURE1|POLYARRAY_TEXTURE2| POLYARRAY_TEXTURE3|POLYARRAY_TEXTURE4), "bad paflags\n"); // Compute eye coord first if (!(pa->flags & POLYARRAY_EYE_PROCESSED)) PolyArrayProcessEye(gc, pa); m = &gc->transform.texture->matrix; bIdentity = (m->matrixType == __GL_MT_IDENTITY); // If any incoming texture coords contains q coord, use xf4. if (pa->flags & POLYARRAY_TEXTURE4) xf = m->xf4; else if (pa->flags & POLYARRAY_TEXTURE3) xf = m->xf3; else xf = m->xf2; // at least 2 generated values pdLast = pa->pdNextVertex-1; for (pd = pa->pd0; pd <= pdLast; pd++) { if (pd->flags & POLYDATA_TEXTURE_VALID) { gen.z = pd->texture.z; gen.w = pd->texture.w; } if (pd->flags & POLYDATA_NORMAL_VALID) { // track current normal pdNormal = pd; } else { // pd->flags |= POLYDATA_NORMAL_VALID; pd->normal.x = pdNormal->normal.x; pd->normal.y = pdNormal->normal.y; pd->normal.z = pdNormal->normal.z; } PASphereGen(pd, &gen); // compute s, t values // Finally, apply texture matrix if (!bIdentity) (*xf)(&pd->texture, (__GLfloat *) &gen, m); else pd->texture = gen; } } // Transform or compute the texture coordinates for the polyarray // It handles all texture generation modes. Texture coordinates are // generated (if necessary) and transformed. // Note that texture coordinates are modified in place. // // IN: texture (always) // obj in GL_OBJECT_LINEAR mode // obj or eye in GL_EYE_LINEAR mode // obj or eye; normal in GL_SPHERE_MAP mode // OUT: texture (all vertices are updated) // eye in GL_EYE_LINEAR and GL_SPHERE_MAP modes (all vertices // are updated) void FASTCALL PolyArrayCalcMixedTexture(__GLcontext *gc, POLYARRAY *pa) { __GLmatrix *m; GLuint enables; POLYDATA *pd, *pdLast, *pdNormal; PFN_XFORM xf; BOOL needNormal, didSphereGen; __GLcoord savedTexture, sphereCoord, *c; GLboolean bIdentity; enables = gc->state.enables.general; PERF_CHECK ( !(enables & (__GL_TEXTURE_GEN_R_ENABLE | __GL_TEXTURE_GEN_Q_ENABLE)), "Uses r, q texture generation!\n" ); if ((enables & __GL_TEXTURE_GEN_S_ENABLE) && (enables & __GL_TEXTURE_GEN_T_ENABLE) && (gc->state.texture.s.mode != gc->state.texture.t.mode)) { PERF_CHECK(FALSE, "Uses different s and t tex gen modes!\n"); } ASSERTOPENGL(pa->pd0->flags & POLYDATA_TEXTURE_VALID, "need initial texcoord value\n"); ASSERTOPENGL(pa->flags & (POLYARRAY_TEXTURE1|POLYARRAY_TEXTURE2| POLYARRAY_TEXTURE3|POLYARRAY_TEXTURE4), "bad paflags\n"); if ((enables & __GL_TEXTURE_GEN_S_ENABLE) && (gc->state.texture.s.mode == GL_SPHERE_MAP) || (enables & __GL_TEXTURE_GEN_T_ENABLE) && (gc->state.texture.t.mode == GL_SPHERE_MAP)) { ASSERTOPENGL(pa->pd0->flags & POLYDATA_NORMAL_VALID, "need initial normal\n"); needNormal = TRUE; } else { needNormal = FALSE; } // Compute eye coord first if (!(pa->flags & POLYARRAY_EYE_PROCESSED)) { if ((enables & __GL_TEXTURE_GEN_S_ENABLE) && (gc->state.texture.s.mode != GL_OBJECT_LINEAR) || (enables & __GL_TEXTURE_GEN_T_ENABLE) && (gc->state.texture.t.mode != GL_OBJECT_LINEAR) || (enables & __GL_TEXTURE_GEN_R_ENABLE) && (gc->state.texture.r.mode != GL_OBJECT_LINEAR) || (enables & __GL_TEXTURE_GEN_Q_ENABLE) && (gc->state.texture.q.mode != GL_OBJECT_LINEAR)) PolyArrayProcessEye(gc, pa); } m = &gc->transform.texture->matrix; bIdentity = (m->matrixType == __GL_MT_IDENTITY); // If any incoming texture coords contains q coord, use xf4. if (pa->flags & POLYARRAY_TEXTURE4 || enables & __GL_TEXTURE_GEN_Q_ENABLE) xf = m->xf4; else if (pa->flags & POLYARRAY_TEXTURE3 || enables & __GL_TEXTURE_GEN_R_ENABLE) xf = m->xf3; else if (pa->flags & POLYARRAY_TEXTURE2 || enables & __GL_TEXTURE_GEN_T_ENABLE) xf = m->xf2; else xf = m->xf1; pdLast = pa->pdNextVertex-1; for (pd = pa->pd0; pd <= pdLast; pd++) { // texture coordinates are modified in place. // save the valid values to use for the invalid entries. if (pd->flags & POLYDATA_TEXTURE_VALID) savedTexture = pd->texture; else pd->texture = savedTexture; if (needNormal) { if (pd->flags & POLYDATA_NORMAL_VALID) { // track current normal pdNormal = pd; } else { // pd->flags |= POLYDATA_NORMAL_VALID; pd->normal.x = pdNormal->normal.x; pd->normal.y = pdNormal->normal.y; pd->normal.z = pdNormal->normal.z; } } didSphereGen = GL_FALSE; /* Generate s coordinate */ if (enables & __GL_TEXTURE_GEN_S_ENABLE) { if (gc->state.texture.s.mode == GL_EYE_LINEAR) { c = &gc->state.texture.s.eyePlaneEquation; pd->texture.x = c->x * pd->eye.x + c->y * pd->eye.y + c->z * pd->eye.z + c->w * pd->eye.w; } else if (gc->state.texture.s.mode == GL_OBJECT_LINEAR) { // the primitive may contain a mix of vertex types (2,3,4)! c = &gc->state.texture.s.objectPlaneEquation; pd->texture.x = c->x * pd->obj.x + c->y * pd->obj.y + c->z * pd->obj.z + c->w * pd->obj.w; } else { ASSERTOPENGL(gc->state.texture.s.mode == GL_SPHERE_MAP, "invalide texture s mode"); PASphereGen(pd, &sphereCoord); // compute s, t values pd->texture.x = sphereCoord.x; didSphereGen = GL_TRUE; } } /* Generate t coordinate */ if (enables & __GL_TEXTURE_GEN_T_ENABLE) { if (gc->state.texture.t.mode == GL_EYE_LINEAR) { c = &gc->state.texture.t.eyePlaneEquation; pd->texture.y = c->x * pd->eye.x + c->y * pd->eye.y + c->z * pd->eye.z + c->w * pd->eye.w; } else if (gc->state.texture.t.mode == GL_OBJECT_LINEAR) { // the primitive may contain a mix of vertex types (2,3,4)! c = &gc->state.texture.t.objectPlaneEquation; pd->texture.y = c->x * pd->obj.x + c->y * pd->obj.y + c->z * pd->obj.z + c->w * pd->obj.w; } else { ASSERTOPENGL(gc->state.texture.t.mode == GL_SPHERE_MAP, "invalide texture t mode"); if (!didSphereGen) PASphereGen(pd, &sphereCoord); // compute s, t values pd->texture.y = sphereCoord.y; } } /* Generate r coordinate */ if (enables & __GL_TEXTURE_GEN_R_ENABLE) { if (gc->state.texture.r.mode == GL_EYE_LINEAR) { c = &gc->state.texture.r.eyePlaneEquation; pd->texture.z = c->x * pd->eye.x + c->y * pd->eye.y + c->z * pd->eye.z + c->w * pd->eye.w; } else { ASSERTOPENGL(gc->state.texture.r.mode == GL_OBJECT_LINEAR, "invalide texture r mode"); // the primitive may contain a mix of vertex types (2,3,4)! c = &gc->state.texture.r.objectPlaneEquation; pd->texture.z = c->x * pd->obj.x + c->y * pd->obj.y + c->z * pd->obj.z + c->w * pd->obj.w; } } /* Generate q coordinate */ if (enables & __GL_TEXTURE_GEN_Q_ENABLE) { if (gc->state.texture.q.mode == GL_EYE_LINEAR) { c = &gc->state.texture.q.eyePlaneEquation; pd->texture.w = c->x * pd->eye.x + c->y * pd->eye.y + c->z * pd->eye.z + c->w * pd->eye.w; } else { ASSERTOPENGL(gc->state.texture.q.mode == GL_OBJECT_LINEAR, "invalide texture q mode"); // the primitive may contain a mix of vertex types (2,3,4)! c = &gc->state.texture.q.objectPlaneEquation; pd->texture.w = c->x * pd->obj.x + c->y * pd->obj.y + c->z * pd->obj.z + c->w * pd->obj.w; } } /* Finally, apply texture matrix */ if (!bIdentity) (*xf)(&pd->texture, (__GLfloat *) &pd->texture, m); } } /****************************************************************************/ // Apply the accumulated material changes to a vertex void FASTCALL PAApplyMaterial(__GLcontext *gc, __GLmatChange *mat, GLint face) { __GLmaterialState *ms; GLuint changeBits; PERF_CHECK(FALSE, "Primitives contain glMaterial calls!\n"); // Don't modify color materials if they are in effect! if (face == __GL_FRONTFACE) { ms = &gc->state.light.front; changeBits = mat->dirtyBits & ~gc->light.front.colorMaterialChange; } else { ms = &gc->state.light.back; changeBits = mat->dirtyBits & ~gc->light.back.colorMaterialChange; } if (changeBits & __GL_MATERIAL_AMBIENT) ms->ambient = mat->ambient; if (changeBits & __GL_MATERIAL_DIFFUSE) ms->diffuse = mat->diffuse; if (changeBits & __GL_MATERIAL_SPECULAR) ms->specular = mat->specular; if (changeBits & __GL_MATERIAL_EMISSIVE) { ms->emissive.r = mat->emissive.r * gc->redVertexScale; ms->emissive.g = mat->emissive.g * gc->greenVertexScale; ms->emissive.b = mat->emissive.b * gc->blueVertexScale; ms->emissive.a = mat->emissive.a * gc->alphaVertexScale; } if (changeBits & __GL_MATERIAL_SHININESS) ms->specularExponent = mat->shininess; if (changeBits & __GL_MATERIAL_COLORINDEXES) { ms->cmapa = mat->cmapa; ms->cmapd = mat->cmapd; ms->cmaps = mat->cmaps; } // Re-calculate the precomputed values. This works for RGBA and CI modes. if (face == __GL_FRONTFACE) __glValidateMaterial(gc, (GLint) changeBits, 0); else __glValidateMaterial(gc, 0, (GLint) changeBits); } void FASTCALL PolyArrayApplyMaterials(__GLcontext *gc, POLYARRAY *pa) { __GLmatChange matChange, *pdMat; GLuint matMask; POLYDATA *pd, *pdN; GLint face; POLYMATERIAL *pm; pm = GLTEB_CLTPOLYMATERIAL(); // Need to apply material changes defined after the last vertex! pdN = pa->pdNextVertex; // ASSERT_FACE for (face = __GL_BACKFACE, matMask = POLYARRAY_MATERIAL_BACK; face >= 0; face--, matMask = POLYARRAY_MATERIAL_FRONT ) { if (!(pa->flags & matMask)) continue; // Accumulate all changes into one material change record // We need to process (n + 1) vertices for material changes! matChange.dirtyBits = 0; for (pd = pa->pd0; pd <= pdN; pd++) { // ASSERT_MATERIAL if (pd->flags & matMask) { GLuint dirtyBits; pdMat = *(&pm->pdMaterial0[pd - pa->pdBuffer0].front + face); dirtyBits = pdMat->dirtyBits; matChange.dirtyBits |= dirtyBits; if (dirtyBits & __GL_MATERIAL_AMBIENT) matChange.ambient = pdMat->ambient; if (dirtyBits & __GL_MATERIAL_DIFFUSE) matChange.diffuse = pdMat->diffuse; if (dirtyBits & __GL_MATERIAL_SPECULAR) matChange.specular = pdMat->specular; if (dirtyBits & __GL_MATERIAL_EMISSIVE) matChange.emissive = pdMat->emissive; if (dirtyBits & __GL_MATERIAL_SHININESS) matChange.shininess = pdMat->shininess; if (dirtyBits & __GL_MATERIAL_COLORINDEXES) { matChange.cmapa = pdMat->cmapa; matChange.cmapd = pdMat->cmapd; matChange.cmaps = pdMat->cmaps; } } } // apply material changes for this face PAApplyMaterial(gc, &matChange, face); } } /****************************************************************************/ #ifndef __GL_ASM_POLYARRAYFILLINDEX0 // Fill the index values with 0 // // IN: none // OUT: colors[face].r (all vertices are updated) void FASTCALL PolyArrayFillIndex0(__GLcontext *gc, POLYARRAY *pa, GLint face) { POLYDATA *pd, *pdLast; ASSERTOPENGL((GLuint) face <= 1, "bad face value\n"); pdLast = pa->pdNextVertex-1; for (pd = pa->pd0; pd <= pdLast; pd++) { pd->colors[face].r = __glZero; } } #endif // __GL_ASM_POLYARRAYFILLINDEX0 #ifndef __GL_ASM_POLYARRAYFILLCOLOR0 // Fill the color values with 0,0,0,0 // // IN: none // OUT: colors[face] (all vertices are updated) void FASTCALL PolyArrayFillColor0(__GLcontext *gc, POLYARRAY *pa, GLint face) { POLYDATA *pd, *pdLast; ASSERTOPENGL((GLuint) face <= 1, "bad face value\n"); pdLast = pa->pdNextVertex-1; for (pd = pa->pd0; pd <= pdLast; pd++) { pd->colors[face].r = __glZero; pd->colors[face].g = __glZero; pd->colors[face].b = __glZero; pd->colors[face].a = __glZero; } } #endif // __GL_ASM_POLYARRAYFILLCOLOR0 #ifndef __GL_ASM_POLYARRAYPROPAGATESAMECOLOR // All vertices have the same color values. // Clamp and scale the current color using the color buffer scales. // From here on out the colors in the vertex are in their final form. // // Note: The first vertex must have a valid color! // Back color is not needed when lighting is disabled. // // IN: color (front) // OUT: color (front) (all vertices are updated) void FASTCALL PolyArrayPropagateSameColor(__GLcontext *gc, POLYARRAY *pa) { POLYDATA *pd, *pdLast; __GLfloat r, g, b, a; pdLast = pa->pdNextVertex-1; pd = pa->pd0; if (pd > pdLast) return; ASSERTOPENGL(pd->flags & POLYDATA_COLOR_VALID, "no initial color\n"); if (pa->flags & POLYARRAY_CLAMP_COLOR) { __GL_CLAMP_R(pd->colors[0].r, gc, pd->colors[0].r); __GL_CLAMP_G(pd->colors[0].g, gc, pd->colors[0].g); __GL_CLAMP_B(pd->colors[0].b, gc, pd->colors[0].b); __GL_CLAMP_A(pd->colors[0].a, gc, pd->colors[0].a); } #ifdef _X86_ __asm { // Register usage. // EAX: r, EBX: g, ECX: b, EDX: a // ESI: &pdLast->colors[0] // EDI: &pd->colors[0] mov edi, pd mov esi, pdLast add edi, PD_colors0 add esi, PD_colors0 mov eax, [edi] mov ebx, [edi+4] mov ecx, [edi+8] mov edx, [edi+12] add edi, sizeof_POLYDATA cmp edi, esi ja Done DoLoop: mov [edi], eax mov [edi+4], ebx mov [edi+8], ecx mov [edi+12], edx add edi, sizeof_POLYDATA cmp edi, esi jbe DoLoop Done: } #else r = pd->colors[0].r; g = pd->colors[0].g; b = pd->colors[0].b; a = pd->colors[0].a; for (pd = pd + 1 ; pd <= pdLast; pd++) { pd->colors[0].r = r; pd->colors[0].g = g; pd->colors[0].b = b; pd->colors[0].a = a; } #endif } #endif // __GL_ASM_POLYARRAYPROPAGATESAMECOLOR #ifndef __GL_ASM_POLYARRAYPROPAGATESAMEINDEX // All vertices have the same index values. // Mask the index values befor color clipping // SGIBUG: The sample implementation fails to do this! // // Note: The first vertex must have a valid color index! // Back color is not needed when lighting is disabled. // // IN: color.r (front) // OUT: color.r (front) (all vertices are updated) void FASTCALL PolyArrayPropagateSameIndex(__GLcontext *gc, POLYARRAY *pa) { POLYDATA *pd, *pdLast; __GLfloat index; pdLast = pa->pdNextVertex-1; pd = pa->pd0; if (pd > pdLast) return; ASSERTOPENGL(pd->flags & POLYDATA_COLOR_VALID, "no initial color index\n"); if (pa->flags & POLYARRAY_CLAMP_COLOR) { __GL_CLAMP_CI(pd->colors[0].r, gc, pd->colors[0].r); } index = pd->colors[0].r; for (pd = pd + 1; pd <= pdLast; pd++) { pd->colors[0].r = index; } } #endif // __GL_ASM_POLYARRAYPROPAGATESAMEINDEX #ifndef __GL_ASM_POLYARRAYPROPAGATEINDEX // Propagate the valid CI colors through the vertex buffer. // // IN: color.r (front) // OUT: color.r (front) (all vertices are updated) void FASTCALL PolyArrayPropagateIndex(__GLcontext *gc, POLYARRAY *pa) { POLYDATA *pd, *pdLast; if (pa->flags & POLYARRAY_CLAMP_COLOR) { pdLast = pa->pdNextVertex-1; for (pd = pa->pd0; pd <= pdLast; pd++) { if (!(pd->flags & POLYDATA_COLOR_VALID)) { // If color has not changed for this vertex, // use the previously computed color. ASSERTOPENGL(pd != pa->pd0, "no initial color index\n"); pd->colors[0].r = (pd-1)->colors[0].r; continue; } __GL_CLAMP_CI(pd->colors[0].r, gc, pd->colors[0].r); } } else { // If all incoming vertices have valid colors, we are done. if ((pa->flags & POLYARRAY_SAME_POLYDATA_TYPE) && (pa->pdCurColor != pa->pd0) // Need to test 2nd vertex because pdCurColor may have been // advanced as a result of combining Color command after End && ((pa->pd0 + 1)->flags & POLYDATA_COLOR_VALID)) ; else { pdLast = pa->pdNextVertex-1; for (pd = pa->pd0; pd <= pdLast; pd++) { if (!(pd->flags & POLYDATA_COLOR_VALID)) { // If color has not changed for this vertex, // use the previously computed color. ASSERTOPENGL(pd != pa->pd0, "no initial color index\n"); pd->colors[0].r = (pd-1)->colors[0].r; } } } } } #endif // __GL_ASM_POLYARRAYPROPAGATEINDEX #ifndef __GL_ASM_POLYARRAYPROPAGATECOLOR // Propagate the valid RGBA colors through the vertex buffer. // // IN: color (front) // OUT: color (front) (all vertices are updated) void FASTCALL PolyArrayPropagateColor(__GLcontext *gc, POLYARRAY *pa) { POLYDATA *pd, *pdLast; if (pa->flags & POLYARRAY_CLAMP_COLOR) { pdLast = pa->pdNextVertex-1; for (pd = pa->pd0; pd <= pdLast; pd++) { if (!(pd->flags & POLYDATA_COLOR_VALID)) { // If color has not changed for this vertex, // use the previously computed color. ASSERTOPENGL(pd != pa->pd0, "no initial color\n"); pd->colors[0] = (pd-1)->colors[0]; continue; } __GL_CLAMP_R(pd->colors[0].r, gc, pd->colors[0].r); __GL_CLAMP_G(pd->colors[0].g, gc, pd->colors[0].g); __GL_CLAMP_B(pd->colors[0].b, gc, pd->colors[0].b); __GL_CLAMP_A(pd->colors[0].a, gc, pd->colors[0].a); } } else { // If all incoming vertices have valid colors, we are done. if ((pa->flags & POLYARRAY_SAME_POLYDATA_TYPE) && (pa->pdCurColor != pa->pd0) // Need to test 2nd vertex because pdCurColor may have been // advanced as a result of combining Color command after End && ((pa->pd0 + 1)->flags & POLYDATA_COLOR_VALID)) ; else { pdLast = pa->pdNextVertex-1; for (pd = pa->pd0; pd <= pdLast; pd++) { if (!(pd->flags & POLYDATA_COLOR_VALID)) { // If color has not changed for this vertex, // use the previously computed color. ASSERTOPENGL(pd != pa->pd0, "no initial color\n"); pd->colors[0] = (pd-1)->colors[0]; } } } } } #endif // __GL_ASM_POLYARRAYPROPAGATECOLOR /****************************************************************************/ #if 0 //!!! remove this // do we need clip? // need __GL_HAS_FOG bit for line and polygon clipping! // The boundaryEdge field is initialized in the calling routine. #define PA_STORE_PROCESSED_POLYGON_VERTEX(v,pd,bits) \ { \ (v)->clip = (pd)->clip; \ (v)->window = (pd)->window; \ (v)->eye.z = (pd)->eye.z; /* needed by slow fog */ \ (v)->fog = (pd)->fog; /* needed by cheap fog in flat shading */ \ (v)->texture.x = (pd)->texture.x; \ (v)->texture.y = (pd)->texture.y; \ (v)->texture.z = (pd)->texture.z; /* z is needed by feedback! */\ (v)->texture.w = (pd)->texture.w; \ (v)->color = &(pd)->color; \ (v)->clipCode = (pd)->clipCode; \ (v)->has = bits; \ } // need eye if there is eyeClipPlanes // need texture if there is texture // need __GL_HAS_FOG bit for line and polygon clipping! #define PA_STORE_PROCESSED_LINE_VERTEX(v,pd,bits) \ { \ (v)->clip = (pd)->clip; \ (v)->window = (pd)->window; \ (v)->eye.z = (pd)->eye.z; /* needed by slow fog */ \ (v)->fog = (pd)->fog; /* needed by cheap fog in flat shading */ \ (v)->texture.x = (pd)->texture.x; \ (v)->texture.y = (pd)->texture.y; \ (v)->texture.z = (pd)->texture.z; /* z is needed by feedback! */\ (v)->texture.w = (pd)->texture.w; \ (v)->colors[__GL_FRONTFACE] = (pd)->color; \ (v)->clipCode = (pd)->clipCode; \ (v)->has = bits; \ } // need eye if antialised is on // need texture if there is texture #define PA_STORE_PROCESSED_POINT_VERTEX(v,pd,bits) \ { \ (v)->window = (pd)->window; \ (v)->eye.z = (pd)->eye.z; /* needed by slow fog */ \ (v)->fog = (pd)->fog; /* needed by cheap fog in flat shading */ \ (v)->clip.w = (pd)->clip.w; /* needed by feedback! */ \ (v)->texture.x = (pd)->texture.x; \ (v)->texture.y = (pd)->texture.y; \ (v)->texture.z = (pd)->texture.z; /* z is needed by feedback! */\ (v)->texture.w = (pd)->texture.w; \ (v)->color = &(pd)->color; \ (v)->has = bits; \ } #endif // 0 // --------------------------------------------------------- // The primitive is clipped. void FASTCALL PARenderPoint(__GLcontext *gc, __GLvertex *v) { if (v->clipCode == 0) (*gc->procs.renderPoint)(gc, v); } // --------------------------------------------------------- // The primitive is clipped. void FASTCALL PARenderLine(__GLcontext *gc, __GLvertex *v0, __GLvertex *v1, GLuint flags) { 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) __glClipLine(gc, v0, v1, flags); } else { // Line is trivially accepted so render it (*gc->procs.renderLine)(gc, v0, v1, flags); } } // --------------------------------------------------------- // The primitive is clipped. void FASTCALL PARenderTriangle(__GLcontext *gc, __GLvertex *v0, __GLvertex *v1, __GLvertex *v2) { GLuint 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)) (*gc->procs.clipTriangle)(gc, v0, v1, v2, orCodes); } else { (*gc->procs.renderTriangle)(gc, v0, v1, v2); } } // --------------------------------------------------------- // The primitive is not clipped. void PARenderQuadFast(__GLcontext *gc, __GLvertex *v0, __GLvertex *v1, __GLvertex *v2, __GLvertex *v3) { // Vertex ordering is important. Line stippling uses it. // SGIBUG: The sample implementation does it wrong. GLuint savedTag; /* Render the quad as two triangles */ savedTag = v2->has & __GL_HAS_EDGEFLAG_BOUNDARY; v2->has &= ~__GL_HAS_EDGEFLAG_BOUNDARY; (*gc->procs.renderTriangle)(gc, v0, v1, v2); v2->has |= savedTag; savedTag = v0->has & __GL_HAS_EDGEFLAG_BOUNDARY; v0->has &= ~__GL_HAS_EDGEFLAG_BOUNDARY; (*gc->procs.renderTriangle)(gc, v2, v3, v0); v0->has |= savedTag; } // --------------------------------------------------------- // The primitive is clipped. void PARenderQuadSlow(__GLcontext *gc, __GLvertex *v0, __GLvertex *v1, __GLvertex *v2, __GLvertex *v3) { GLuint 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 */ __GLvertex *iv[4]; iv[0] = v0; iv[1] = v1; iv[2] = v2; iv[3] = v3; __glDoPolygonClip(gc, &iv[0], 4, orCodes); } } else { PARenderQuadFast(gc, v0, v1, v2, v3); } } // --------------------------------------------------------- void FASTCALL PolyArrayDrawPoints(__GLcontext *gc, POLYARRAY *pa) { // Index mapping is always identity in Points. ASSERTOPENGL(!pa->aIndices, "Index mapping must be identity\n"); // Assert that pa->nIndices is correct ASSERTOPENGL(pa->nIndices == pa->pdNextVertex - pa->pd0, "bad nIndices\n"); // Call PolyArrayRenderPoints later pa->flags |= POLYARRAY_RENDER_PRIMITIVE; } void FASTCALL PolyArrayRenderPoints(__GLcontext *gc, POLYARRAY *pa) { GLint i, nIndices; POLYDATA *pd0; void (FASTCALL *rp)(__GLcontext *gc, __GLvertex *v); // Index mapping is always identity in Points. ASSERTOPENGL(!pa->aIndices, "Index mapping must be identity\n"); nIndices = pa->nIndices; pd0 = pa->pd0; rp = pa->orClipCodes ? PARenderPoint : gc->procs.renderPoint; // Identity mapping for (i = 0; i < nIndices; i++) /* Render the point */ (*rp)(gc, (__GLvertex *) &pd0[i]); } // --------------------------------------------------------- void FASTCALL PolyArrayDrawLines(__GLcontext *gc, POLYARRAY *pa) { // Assert that pa->nIndices is correct if aIndices is identity ASSERTOPENGL(pa->aIndices || pa->nIndices == pa->pdNextVertex - pa->pd0, "bad nIndices\n"); // Call PolyArrayRenderLines later pa->flags |= POLYARRAY_RENDER_PRIMITIVE; } void FASTCALL PolyArrayRenderLines(__GLcontext *gc, POLYARRAY *pa) { GLint i, iLast2; GLubyte *aIndices; POLYDATA *pd0; PFN_RENDER_LINE rl; iLast2 = pa->nIndices - 2; pd0 = pa->pd0; rl = pa->orClipCodes ? PARenderLine : gc->procs.renderLine; (*gc->procs.lineBegin)(gc); if (!(aIndices = pa->aIndices)) { // Identity mapping for (i = 0; i <= iLast2; i += 2) { /* setup for rendering this line */ gc->line.notResetStipple = GL_FALSE; (*rl)(gc, (__GLvertex *) &pd0[i ], (__GLvertex *) &pd0[i+1], __GL_LVERT_FIRST); } } else { for (i = 0; i <= iLast2; i += 2) { /* setup for rendering this line */ gc->line.notResetStipple = GL_FALSE; (*rl)(gc, (__GLvertex *) &pd0[aIndices[i ]], (__GLvertex *) &pd0[aIndices[i+1]], __GL_LVERT_FIRST); } } (*gc->procs.lineEnd)(gc); } // --------------------------------------------------------- void FASTCALL PolyArrayDrawLLoop(__GLcontext *gc, POLYARRAY *pa) { GLint nIndices; POLYDATA *pd, *pd0; // Index mapping is always identity in Line Loop. ASSERTOPENGL(!pa->aIndices, "Index mapping must be identity\n"); // A line loop is the same as a line strip except that a final segment is // added from the final specified vertex to the first vertex. We will // convert the line loop into a strip here. nIndices = pa->nIndices; // If we are continuing with a previously decomposed line loop, we need to // connect the last vertex of the previous primitive and the first vertex // of the current primitive with a line segment. if (pa->flags & POLYARRAY_PARTIAL_BEGIN) { ASSERTOPENGL(!(pa->flags & POLYARRAY_RESET_STIPPLE), "bad stipple reset flag!\n"); // Insert previous end vertex at the beginning and update clip code pd = --pa->pd0; ASSERTOPENGL(pd > (POLYDATA *) pa, "vertex underflows\n"); PA_COPY_PROCESSED_VERTEX(pd, &gc->vertex.pdSaved[0]); pa->orClipCodes |= pd->clipCode; #ifdef POLYARRAY_AND_CLIPCODES pa->andClipCodes &= pd->clipCode; #endif } else { // New line loop. ASSERTOPENGL(pa->flags & POLYARRAY_RESET_STIPPLE, "bad stipple reset flag!\n"); // At least two vertices must be given for anything to occur. // An extra vertex was added to close the loop. if (nIndices < 3) { ASSERTOPENGL(!(pa->flags & POLYARRAY_PARTIAL_END), "Partial end with insufficient vertices\n"); pa->nIndices--; goto DrawLLoop_end; } } pd0 = pa->pd0; // If the primitive is only partially complete, save the last vertex for // next batch. if (pa->flags & POLYARRAY_PARTIAL_END) { pd = &pd0[nIndices-1]; PA_COPY_PROCESSED_VERTEX(&gc->vertex.pdSaved[0], pd); // Save the the original first vertex for closing the loop later. if (!(pa->flags & POLYARRAY_PARTIAL_BEGIN)) PA_COPY_PROCESSED_VERTEX(&gc->vertex.pdSaved[1], pd0); // Need not render this partial primitive if it is completely clipped. #ifdef POLYARRAY_AND_CLIPCODES if (pa->andClipCodes != 0) goto DrawLLoop_end; #endif } else { POLYDATA *pdOrigin; // Insert the original first vertex to close the loop and update clip code. if (pa->flags & POLYARRAY_PARTIAL_BEGIN) pdOrigin = &gc->vertex.pdSaved[1]; else pdOrigin = pd0; pd = pa->pdNextVertex++; ASSERTOPENGL(pd <= pa->pdBufferMax, "vertex overflows\n"); PA_COPY_PROCESSED_VERTEX(pd, pdOrigin); pa->orClipCodes |= pd->clipCode; #ifdef POLYARRAY_AND_CLIPCODES pa->andClipCodes &= pd->clipCode; #endif } // Assert that pa->nIndices is correct ASSERTOPENGL(pa->nIndices == pa->pdNextVertex - pa->pd0, "bad nIndices\n"); // Render the line strip. // Call PolyArrayRenderLStrip later pa->flags |= POLYARRAY_RENDER_PRIMITIVE; DrawLLoop_end: // Change primitive type to line strip! pa->primType = GL_LINE_STRIP; } void FASTCALL PolyArrayRenderLStrip(__GLcontext *gc, POLYARRAY *pa) { GLint i, iLast; GLubyte *aIndices; POLYDATA *pd0; PFN_RENDER_LINE rl; // Render the line strip. iLast = pa->nIndices - 1; pd0 = pa->pd0; rl = pa->orClipCodes ? PARenderLine : gc->procs.renderLine; if (iLast <= 0) return; // Reset the line stipple if this is a new strip. if (pa->flags & POLYARRAY_RESET_STIPPLE) gc->line.notResetStipple = GL_FALSE; (*gc->procs.lineBegin)(gc); if (!(aIndices = pa->aIndices)) { // Identity mapping // Add first line segment (NOTE: 0, 1) (*rl)(gc, (__GLvertex *) &pd0[0], (__GLvertex *) &pd0[1], __GL_LVERT_FIRST); // Add subsequent line segments (NOTE: i, i+1) for (i = 1; i < iLast; i++) (*rl)(gc, (__GLvertex *) &pd0[i ], (__GLvertex *) &pd0[i+1], 0); } else { // Add first line segment (NOTE: 0, 1) (*rl)(gc, (__GLvertex *) &pd0[aIndices[0]], (__GLvertex *) &pd0[aIndices[1]], __GL_LVERT_FIRST); // Add subsequent line segments (NOTE: i, i+1) for (i = 1; i < iLast; i++) (*rl)(gc, (__GLvertex *) &pd0[aIndices[i ]], (__GLvertex *) &pd0[aIndices[i+1]], 0); } (*gc->procs.lineEnd)(gc); } // --------------------------------------------------------- void FASTCALL PolyArrayDrawLStrip(__GLcontext *gc, POLYARRAY *pa) { GLint nIndices; GLubyte *aIndices; POLYDATA *pd, *pd0; nIndices = pa->nIndices; aIndices = pa->aIndices; // If we are continuing with a previously decomposed line strip, we need to // connect the last vertex of the previous primitive and the first vertex // of the current primitive with a line segment. if (pa->flags & POLYARRAY_PARTIAL_BEGIN) { ASSERTOPENGL(!(pa->flags & POLYARRAY_RESET_STIPPLE), "bad stipple reset flag!\n"); // Insert previous end vertex at the beginning and update clip code pd = --pa->pd0; ASSERTOPENGL(pd > (POLYDATA *) pa, "vertex underflows\n"); PA_COPY_PROCESSED_VERTEX(pd, &gc->vertex.pdSaved[0]); pa->orClipCodes |= pd->clipCode; #ifdef POLYARRAY_AND_CLIPCODES pa->andClipCodes &= pd->clipCode; #endif // Assert that aIndices[0] was initialized in Begin ASSERTOPENGL(!pa->aIndices || pa->aIndices[0] == 0, "bad index mapping\n"); } else { // New line strip. ASSERTOPENGL(pa->flags & POLYARRAY_RESET_STIPPLE, "bad stipple reset flag!\n"); } // At least two vertices must be given for anything to occur. if (nIndices < 2) { ASSERTOPENGL(!(pa->flags & POLYARRAY_PARTIAL_END), "Partial end with insufficient vertices\n"); return; } // If the primitive is only partially complete, save the last vertex for // next batch. if (pa->flags & POLYARRAY_PARTIAL_END) { pd0 = pa->pd0; pd = aIndices ? &pd0[aIndices[nIndices-1]] : &pd0[nIndices-1]; PA_COPY_PROCESSED_VERTEX(&gc->vertex.pdSaved[0], pd); // Need not render this partial primitive if it is completely clipped. #ifdef POLYARRAY_AND_CLIPCODES if (pa->andClipCodes != 0) return; #endif } // Assert that pa->nIndices is correct if aIndices is identity ASSERTOPENGL(pa->aIndices || pa->nIndices == pa->pdNextVertex - pa->pd0, "bad nIndices\n"); // Render the line strip. // Call PolyArrayRenderLStrip later pa->flags |= POLYARRAY_RENDER_PRIMITIVE; } // --------------------------------------------------------- void FASTCALL PolyArrayDrawTriangles(__GLcontext *gc, POLYARRAY *pa) { // Assert that pa->nIndices is correct if aIndices is identity ASSERTOPENGL(pa->aIndices || pa->nIndices == pa->pdNextVertex - pa->pd0, "bad nIndices\n"); // Call PolyArrayRenderTriangles later pa->flags |= POLYARRAY_RENDER_PRIMITIVE; } void FASTCALL PolyArrayRenderTriangles(__GLcontext *gc, POLYARRAY *pa) { GLint i, iLast3; GLubyte *aIndices, *aIndicesEnd; POLYDATA *pd0; __GLvertex *provoking; PFN_RENDER_TRIANGLE rt; // Vertex ordering is important. Line stippling uses it. // SGIBUG: The sample implementation does it wrong. iLast3 = pa->nIndices - 3; pd0 = pa->pd0; rt = pa->orClipCodes ? PARenderTriangle : gc->procs.renderTriangle; if (!(aIndices = pa->aIndices)) { // Identity mapping for (i = 0; i <= iLast3; i += 3) { /* setup for rendering this triangle */ gc->line.notResetStipple = GL_FALSE; gc->vertex.provoking = (__GLvertex *) &pd0[i+2]; /* Render the triangle (NOTE: i, i+1, i+2) */ (*rt)(gc, (__GLvertex *) &pd0[i ], (__GLvertex *) &pd0[i+1], (__GLvertex *) &pd0[i+2]); } } else { #if 0 for (i = 0; i <= iLast3; i += 3) { /* setup for rendering this triangle */ gc->line.notResetStipple = GL_FALSE; gc->vertex.provoking = (__GLvertex *) &pd0[aIndices[i+2]]; /* Render the triangle (NOTE: i, i+1, i+2) */ (*rt)(gc, (__GLvertex *) &pd0[aIndices[i ]], (__GLvertex *) &pd0[aIndices[i+1]], (__GLvertex *) &pd0[aIndices[i+2]]); } #else aIndicesEnd = aIndices+iLast3; while (aIndices <= aIndicesEnd) { /* setup for rendering this triangle */ gc->line.notResetStipple = GL_FALSE; provoking = PD_VERTEX(pd0, aIndices[2]); gc->vertex.provoking = provoking; /* Render the triangle (NOTE: i, i+1, i+2) */ (*rt)(gc, PD_VERTEX(pd0, aIndices[0]), PD_VERTEX(pd0, aIndices[1]), provoking); aIndices += 3; } #endif } } // --------------------------------------------------------- void FASTCALL PolyArrayDrawTStrip(__GLcontext *gc, POLYARRAY *pa) { GLint nIndices; GLubyte *aIndices; POLYDATA *pd, *pd0; nIndices = pa->nIndices; aIndices = pa->aIndices; // If we are continuing with a previously decomposed triangle strip, we need to // start from the last two vertices of the previous primitive. // // Note that the flush vertex ensures that the continuing triangle strip is in // the default orientation so that it can fall through the normal code. if (pa->flags & POLYARRAY_PARTIAL_BEGIN) { // Insert previous end vertices at the beginning and update clip code pd = --pa->pd0; ASSERTOPENGL(pd > (POLYDATA *) pa, "vertex underflows\n"); PA_COPY_PROCESSED_VERTEX(pd, &gc->vertex.pdSaved[1]); pa->orClipCodes |= pd->clipCode; #ifdef POLYARRAY_AND_CLIPCODES pa->andClipCodes &= pd->clipCode; #endif // Assert that aIndices[1] was initialized in Begin ASSERTOPENGL(!pa->aIndices || pa->aIndices[1] == 1, "bad index mapping\n"); pd = --pa->pd0; ASSERTOPENGL(pd > (POLYDATA *) pa, "vertex underflows\n"); PA_COPY_PROCESSED_VERTEX(pd, &gc->vertex.pdSaved[0]); pa->orClipCodes |= pd->clipCode; #ifdef POLYARRAY_AND_CLIPCODES pa->andClipCodes &= pd->clipCode; #endif // Assert that aIndices[0] was initialized in Begin ASSERTOPENGL(!pa->aIndices || pa->aIndices[0] == 0, "bad index mapping\n"); } // Need at least 3 vertices. if (nIndices < 3) { ASSERTOPENGL(!(pa->flags & POLYARRAY_PARTIAL_END), "Partial end with insufficient vertices\n"); return; } // If the primitive is only partially complete, save the last two vertices for // next batch. if (pa->flags & POLYARRAY_PARTIAL_END) { pd0 = pa->pd0; pd = aIndices ? &pd0[aIndices[nIndices-2]] : &pd0[nIndices-2]; PA_COPY_PROCESSED_VERTEX(&gc->vertex.pdSaved[0], pd); pd = aIndices ? &pd0[aIndices[nIndices-1]] : &pd0[nIndices-1]; PA_COPY_PROCESSED_VERTEX(&gc->vertex.pdSaved[1], pd); // Need not render this partial primitive if it is completely clipped. #ifdef POLYARRAY_AND_CLIPCODES if (pa->andClipCodes != 0) return; #endif } // Assert that pa->nIndices is correct if aIndices is identity ASSERTOPENGL(pa->aIndices || pa->nIndices == pa->pdNextVertex - pa->pd0, "bad nIndices\n"); // Render the triangle strip. // Call PolyArrayRenderTStrip later pa->flags |= POLYARRAY_RENDER_PRIMITIVE; } void FASTCALL PolyArrayRenderTStrip(__GLcontext *gc, POLYARRAY *pa) { GLint i, iLast3; GLubyte *aIndices; POLYDATA *pd0; PFN_RENDER_TRIANGLE rt; iLast3 = pa->nIndices - 3; pd0 = pa->pd0; rt = pa->orClipCodes ? PARenderTriangle : gc->procs.renderTriangle; if (iLast3 < 0) return; // Vertex ordering is important. Line stippling uses it. if (!(aIndices = pa->aIndices)) { // Identity mapping // Initialize first 2 vertices so we can start rendering the strip // below. The edge flags are not modified by our lower level routines. pd0[0].flags |= POLYDATA_EDGEFLAG_BOUNDARY; pd0[1].flags |= POLYDATA_EDGEFLAG_BOUNDARY; for (i = 0; i <= iLast3; ) { /* setup for rendering this triangle */ gc->line.notResetStipple = GL_FALSE; gc->vertex.provoking = (__GLvertex *) &pd0[i+2]; pd0[i+2].flags |= POLYDATA_EDGEFLAG_BOUNDARY; /* Render the triangle (NOTE: i, i+1, i+2) */ (*rt)(gc, (__GLvertex *) &pd0[i ], (__GLvertex *) &pd0[i+1], (__GLvertex *) &pd0[i+2]); i++; if (i > iLast3) break; /* setup for rendering this triangle */ gc->line.notResetStipple = GL_FALSE; gc->vertex.provoking = (__GLvertex *) &pd0[i+2]; pd0[i+2].flags |= POLYDATA_EDGEFLAG_BOUNDARY; /* Render the triangle (NOTE: i+1, i, i+2) */ (*rt)(gc, (__GLvertex *) &pd0[i+1], (__GLvertex *) &pd0[i ], (__GLvertex *) &pd0[i+2]); i++; } } else { // Initialize first 2 vertices so we can start rendering the strip // below. The edge flags are not modified by our lower level routines. pd0[aIndices[0]].flags |= POLYDATA_EDGEFLAG_BOUNDARY; pd0[aIndices[1]].flags |= POLYDATA_EDGEFLAG_BOUNDARY; for (i = 0; i <= iLast3; ) { /* setup for rendering this triangle */ gc->line.notResetStipple = GL_FALSE; gc->vertex.provoking = (__GLvertex *) &pd0[aIndices[i+2]]; pd0[aIndices[i+2]].flags |= POLYDATA_EDGEFLAG_BOUNDARY; /* Render the triangle (NOTE: i, i+1, i+2) */ (*rt)(gc, (__GLvertex *) &pd0[aIndices[i ]], (__GLvertex *) &pd0[aIndices[i+1]], (__GLvertex *) &pd0[aIndices[i+2]]); i++; if (i > iLast3) break; /* setup for rendering this triangle */ gc->line.notResetStipple = GL_FALSE; gc->vertex.provoking = (__GLvertex *) &pd0[aIndices[i+2]]; pd0[aIndices[i+2]].flags |= POLYDATA_EDGEFLAG_BOUNDARY; /* Render the triangle (NOTE: i+1, i, i+2) */ (*rt)(gc, (__GLvertex *) &pd0[aIndices[i+1]], (__GLvertex *) &pd0[aIndices[i ]], (__GLvertex *) &pd0[aIndices[i+2]]); i++; } } } // --------------------------------------------------------- void FASTCALL PolyArrayDrawTFan(__GLcontext *gc, POLYARRAY *pa) { GLint nIndices; GLubyte *aIndices; POLYDATA *pd, *pd0; nIndices = pa->nIndices; aIndices = pa->aIndices; // If we are continuing with a previously decomposed triangle fan, we need to // connect the last vertex of the previous primitive and the first vertex // of the current primitive with a triangle. if (pa->flags & POLYARRAY_PARTIAL_BEGIN) { // Insert previous end vertex at the beginning and update clip code pd = --pa->pd0; ASSERTOPENGL(pd > (POLYDATA *) pa, "vertex underflows\n"); PA_COPY_PROCESSED_VERTEX(pd, &gc->vertex.pdSaved[1]); pa->orClipCodes |= pd->clipCode; #ifdef POLYARRAY_AND_CLIPCODES pa->andClipCodes &= pd->clipCode; #endif // Assert that aIndices[1] was initialized in Begin ASSERTOPENGL(!pa->aIndices || pa->aIndices[1] == 1, "bad index mapping\n"); // Insert the origin first vertex at the beginning and update clip code pd = --pa->pd0; ASSERTOPENGL(pd > (POLYDATA *) pa, "vertex underflows\n"); PA_COPY_PROCESSED_VERTEX(pd, &gc->vertex.pdSaved[0]); pa->orClipCodes |= pd->clipCode; #ifdef POLYARRAY_AND_CLIPCODES pa->andClipCodes &= pd->clipCode; #endif // Assert that aIndices[0] was initialized in Begin ASSERTOPENGL(!pa->aIndices || pa->aIndices[0] == 0, "bad index mapping\n"); } // Need at least 3 vertices. if (nIndices < 3) { ASSERTOPENGL(!(pa->flags & POLYARRAY_PARTIAL_END), "Partial end with insufficient vertices\n"); return; } // If the primitive is only partially complete, save the last vertex for // next batch. Also save the original first vertex of the triangle fan. if (pa->flags & POLYARRAY_PARTIAL_END) { pd0 = pa->pd0; pd = aIndices ? &pd0[aIndices[nIndices-1]] : &pd0[nIndices-1]; PA_COPY_PROCESSED_VERTEX(&gc->vertex.pdSaved[1], pd); if (!(pa->flags & POLYARRAY_PARTIAL_BEGIN)) { pd = aIndices ? &pd0[aIndices[0]] : &pd0[0]; PA_COPY_PROCESSED_VERTEX(&gc->vertex.pdSaved[0], pd); } // Need not render this partial primitive if it is completely clipped. #ifdef POLYARRAY_AND_CLIPCODES if (pa->andClipCodes != 0) return; #endif } // Assert that pa->nIndices is correct if aIndices is identity ASSERTOPENGL(pa->aIndices || pa->nIndices == pa->pdNextVertex - pa->pd0, "bad nIndices\n"); // Render the triangle fan. // Call PolyArrayRenderTFan later pa->flags |= POLYARRAY_RENDER_PRIMITIVE; } void FASTCALL PolyArrayRenderTFan(__GLcontext *gc, POLYARRAY *pa) { GLint i, iLast2; GLubyte *aIndices; POLYDATA *pd0; PFN_RENDER_TRIANGLE rt; iLast2 = pa->nIndices - 2; pd0 = pa->pd0; rt = pa->orClipCodes ? PARenderTriangle : gc->procs.renderTriangle; if (iLast2 <= 0) return; // Vertex ordering is important. Line stippling uses it. // SGIBUG: The sample implementation does it wrong. if (!(aIndices = pa->aIndices)) { // Identity mapping // Initialize first 2 vertices so we can start rendering the tfan // below. The edge flags are not modified by our lower level routines. pd0[0].flags |= POLYDATA_EDGEFLAG_BOUNDARY; pd0[1].flags |= POLYDATA_EDGEFLAG_BOUNDARY; for (i = 1; i <= iLast2; i++) { /* setup for rendering this triangle */ gc->line.notResetStipple = GL_FALSE; gc->vertex.provoking = (__GLvertex *) &pd0[i+1]; pd0[i+1].flags |= POLYDATA_EDGEFLAG_BOUNDARY; /* Render the triangle (NOTE: 0, i, i+1) */ (*rt)(gc, (__GLvertex *) &pd0[0 ], (__GLvertex *) &pd0[i ], (__GLvertex *) &pd0[i+1]); } } else { POLYDATA *pdOrigin; // Initialize first 2 vertices so we can start rendering the tfan // below. The edge flags are not modified by our lower level routines. pd0[aIndices[0]].flags |= POLYDATA_EDGEFLAG_BOUNDARY; pd0[aIndices[1]].flags |= POLYDATA_EDGEFLAG_BOUNDARY; pdOrigin = &pd0[aIndices[0]]; for (i = 1; i <= iLast2; i++) { /* setup for rendering this triangle */ gc->line.notResetStipple = GL_FALSE; gc->vertex.provoking = (__GLvertex *) &pd0[aIndices[i+1]]; pd0[aIndices[i+1]].flags |= POLYDATA_EDGEFLAG_BOUNDARY; /* Render the triangle (NOTE: 0, i, i+1) */ (*rt)(gc, (__GLvertex *) pdOrigin, (__GLvertex *) &pd0[aIndices[i ]], (__GLvertex *) &pd0[aIndices[i+1]]); } } } // --------------------------------------------------------- void FASTCALL PolyArrayDrawQuads(__GLcontext *gc, POLYARRAY *pa) { // Assert that pa->nIndices is correct if aIndices is identity ASSERTOPENGL(pa->aIndices || pa->nIndices == pa->pdNextVertex - pa->pd0, "bad nIndices\n"); // Call PolyArrayRenderQuad later pa->flags |= POLYARRAY_RENDER_PRIMITIVE; } void FASTCALL PolyArrayRenderQuads(__GLcontext *gc, POLYARRAY *pa) { GLint i, iLast4; GLubyte *aIndices; POLYDATA *pd0; void (*rq)(__GLcontext *gc, __GLvertex *v0, __GLvertex *v1, __GLvertex *v2, __GLvertex *v3); // Vertex ordering is important. Line stippling uses it. iLast4 = pa->nIndices - 4; pd0 = pa->pd0; rq = pa->orClipCodes ? PARenderQuadSlow : PARenderQuadFast; if (!(aIndices = pa->aIndices)) { // Identity mapping for (i = 0; i <= iLast4; i += 4) { /* setup for rendering this quad */ gc->line.notResetStipple = GL_FALSE; gc->vertex.provoking = (__GLvertex *) &pd0[i+3]; /* Render the quad (NOTE: i, i+1, i+2, i+3) */ (*rq)(gc, (__GLvertex *) &pd0[i ], (__GLvertex *) &pd0[i+1], (__GLvertex *) &pd0[i+2], (__GLvertex *) &pd0[i+3]); } } else { for (i = 0; i <= iLast4; i += 4) { /* setup for rendering this quad */ gc->line.notResetStipple = GL_FALSE; gc->vertex.provoking = (__GLvertex *) &pd0[aIndices[i+3]]; /* Render the quad (NOTE: i, i+1, i+2, i+3) */ (*rq)(gc, (__GLvertex *) &pd0[aIndices[i ]], (__GLvertex *) &pd0[aIndices[i+1]], (__GLvertex *) &pd0[aIndices[i+2]], (__GLvertex *) &pd0[aIndices[i+3]]); } } } // --------------------------------------------------------- void FASTCALL PolyArrayDrawQStrip(__GLcontext *gc, POLYARRAY *pa) { GLint nIndices; GLubyte *aIndices; POLYDATA *pd, *pd0; nIndices = pa->nIndices; aIndices = pa->aIndices; // If we are continuing with a previously decomposed quad strip, we need to // start from the last two vertices of the previous primitive. // // Note that the flush vertex ensures that the continuing quad strip starts // at an odd vertex so that it can fall through the normal code. if (pa->flags & POLYARRAY_PARTIAL_BEGIN) { // Insert previous end vertices at the beginning and update clip code pd = --pa->pd0; ASSERTOPENGL(pd > (POLYDATA *) pa, "vertex underflows\n"); PA_COPY_PROCESSED_VERTEX(pd, &gc->vertex.pdSaved[1]); pa->orClipCodes |= pd->clipCode; #ifdef POLYARRAY_AND_CLIPCODES pa->andClipCodes &= pd->clipCode; #endif // Assert that aIndices[1] was initialized in Begin ASSERTOPENGL(!pa->aIndices || pa->aIndices[1] == 1, "bad index mapping\n"); pd = --pa->pd0; ASSERTOPENGL(pd > (POLYDATA *) pa, "vertex underflows\n"); PA_COPY_PROCESSED_VERTEX(pd, &gc->vertex.pdSaved[0]); pa->orClipCodes |= pd->clipCode; #ifdef POLYARRAY_AND_CLIPCODES pa->andClipCodes &= pd->clipCode; #endif // Assert that aIndices[0] was initialized in Begin ASSERTOPENGL(!pa->aIndices || pa->aIndices[0] == 0, "bad index mapping\n"); } // Need at least 4 vertices. if (nIndices < 4) { ASSERTOPENGL(!(pa->flags & POLYARRAY_PARTIAL_END), "Partial end with insufficient vertices\n"); return; } // If the primitive is only partially complete, save the last two vertices for // next batch. if (pa->flags & POLYARRAY_PARTIAL_END) { pd0 = pa->pd0; pd = aIndices ? &pd0[aIndices[nIndices-2]] : &pd0[nIndices-2]; PA_COPY_PROCESSED_VERTEX(&gc->vertex.pdSaved[0], pd); pd = aIndices ? &pd0[aIndices[nIndices-1]] : &pd0[nIndices-1]; PA_COPY_PROCESSED_VERTEX(&gc->vertex.pdSaved[1], pd); // Need not render this partial primitive if it is completely clipped. #ifdef POLYARRAY_AND_CLIPCODES if (pa->andClipCodes != 0) return; #endif } // Assert that pa->nIndices is correct if aIndices is identity ASSERTOPENGL(pa->aIndices || pa->nIndices == pa->pdNextVertex - pa->pd0, "bad nIndices\n"); // Render the quad strip. // Call PolyArrayRenderQStrip later pa->flags |= POLYARRAY_RENDER_PRIMITIVE; } void FASTCALL PolyArrayRenderQStrip(__GLcontext *gc, POLYARRAY *pa) { GLint i, iLast4; GLubyte *aIndices; POLYDATA *pd0; void (*rq)(__GLcontext *gc, __GLvertex *v0, __GLvertex *v1, __GLvertex *v2, __GLvertex *v3); iLast4 = pa->nIndices - 4; pd0 = pa->pd0; rq = pa->orClipCodes ? PARenderQuadSlow : PARenderQuadFast; if (iLast4 < 0) return; // Vertex ordering is important. Line stippling uses it. if (!(aIndices = pa->aIndices)) { // Identity mapping // Initialize first 2 vertices so we can start rendering the quad // below. The edge flags are not modified by our lower level routines. pd0[0].flags |= POLYDATA_EDGEFLAG_BOUNDARY; pd0[1].flags |= POLYDATA_EDGEFLAG_BOUNDARY; for (i = 0; i <= iLast4; i += 2) { /* setup for rendering this quad */ gc->line.notResetStipple = GL_FALSE; gc->vertex.provoking = (__GLvertex *) &pd0[i+3]; pd0[i+2].flags |= POLYDATA_EDGEFLAG_BOUNDARY; pd0[i+3].flags |= POLYDATA_EDGEFLAG_BOUNDARY; /* Render the quad (NOTE: i, i+1, i+3, i+2) */ (*rq)(gc, (__GLvertex *) &pd0[i ], (__GLvertex *) &pd0[i+1], (__GLvertex *) &pd0[i+3], (__GLvertex *) &pd0[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. pd0[aIndices[0]].flags |= POLYDATA_EDGEFLAG_BOUNDARY; pd0[aIndices[1]].flags |= POLYDATA_EDGEFLAG_BOUNDARY; for (i = 0; i <= iLast4; i += 2) { /* setup for rendering this quad */ gc->line.notResetStipple = GL_FALSE; gc->vertex.provoking = (__GLvertex *) &pd0[aIndices[i+3]]; pd0[aIndices[i+2]].flags |= POLYDATA_EDGEFLAG_BOUNDARY; pd0[aIndices[i+3]].flags |= POLYDATA_EDGEFLAG_BOUNDARY; /* Render the quad (NOTE: i, i+1, i+3, i+2) */ (*rq)(gc, (__GLvertex *) &pd0[aIndices[i ]], (__GLvertex *) &pd0[aIndices[i+1]], (__GLvertex *) &pd0[aIndices[i+3]], (__GLvertex *) &pd0[aIndices[i+2]]); } } } // --------------------------------------------------------- void FASTCALL PolyArrayDrawPolygon(__GLcontext *gc, POLYARRAY *pa) { GLint nIndices; POLYDATA *pd, *pd0; // Index mapping is always identity in Polygon. ASSERTOPENGL(!pa->aIndices, "Index mapping must be identity\n"); nIndices = pa->nIndices; // If we are continuing with a previously decomposed polygon, we need to // insert the original first vertex and the last two vertices of the // previous polygon at the beginning of the current batch (see note below). // The decomposer expects the polygon vertices to be in sequential memory order. if (pa->flags & POLYARRAY_PARTIAL_BEGIN) { ASSERTOPENGL(!(pa->flags & POLYARRAY_RESET_STIPPLE), "bad stipple reset flag!\n"); // Insert previous end vertices at the beginning and update clip code pd = --pa->pd0; ASSERTOPENGL(pd > (POLYDATA *) pa, "vertex underflows\n"); PA_COPY_PROCESSED_VERTEX(pd, &gc->vertex.pdSaved[2]); pa->orClipCodes |= pd->clipCode; #ifdef POLYARRAY_AND_CLIPCODES pa->andClipCodes &= pd->clipCode; #endif pd = --pa->pd0; ASSERTOPENGL(pd > (POLYDATA *) pa, "vertex underflows\n"); PA_COPY_PROCESSED_VERTEX(pd, &gc->vertex.pdSaved[1]); pa->orClipCodes |= pd->clipCode; #ifdef POLYARRAY_AND_CLIPCODES pa->andClipCodes &= pd->clipCode; #endif // Insert the origin first vertex at the beginning and update clip code pd = --pa->pd0; ASSERTOPENGL(pd > (POLYDATA *) pa, "vertex underflows\n"); PA_COPY_PROCESSED_VERTEX(pd, &gc->vertex.pdSaved[0]); pa->orClipCodes |= pd->clipCode; #ifdef POLYARRAY_AND_CLIPCODES pa->andClipCodes &= pd->clipCode; #endif } else { // New polygon. ASSERTOPENGL(pa->flags & POLYARRAY_RESET_STIPPLE, "bad stipple reset flag!\n"); } // Need at least 3 vertices. if (nIndices < 3) { ASSERTOPENGL(!(pa->flags & POLYARRAY_PARTIAL_END), "Partial end with insufficient vertices\n"); ASSERTOPENGL(!(pa->flags & POLYARRAY_PARTIAL_BEGIN), "Partial begin with insufficient vertices\n"); return; } // If the primitive is only partially complete, save the last 2 vertices for // next batch. Also save the original first vertex of the polygon. if (pa->flags & POLYARRAY_PARTIAL_END) { // Since there may be no vertex following this partial primitive, we // cannot determine the edge flag of the last vertex in this batch. So // we save the last vertex for next batch instead. pd0 = pa->pd0; pd = &pd0[nIndices-1]; PA_COPY_PROCESSED_VERTEX(&gc->vertex.pdSaved[2], pd); // Remove the last vertex from this partial primitive nIndices = --pa->nIndices; pa->pdNextVertex--; // Mark the closing edge of this decomposed polygon as non-boundary // because we are synthetically generating it. pd--; PA_COPY_PROCESSED_VERTEX(&gc->vertex.pdSaved[1], pd); pd->flags &= ~POLYDATA_EDGEFLAG_BOUNDARY; if (!(pa->flags & POLYARRAY_PARTIAL_BEGIN)) { PA_COPY_PROCESSED_VERTEX(&gc->vertex.pdSaved[0], pd0); // Mark the first polygon vertex's edge tag as non-boundary // because when it gets rendered again it will no longer be // a boundary edge. gc->vertex.pdSaved[0].flags &= ~POLYDATA_EDGEFLAG_BOUNDARY; } // Need not render this partial primitive if it is completely clipped. #ifdef POLYARRAY_AND_CLIPCODES if (pa->andClipCodes != 0) return; #endif } // The polygon clipper can only handle this many vertices. ASSERTOPENGL(nIndices <= __GL_MAX_POLYGON_CLIP_SIZE, "too many points for the polygon clipper!\n"); // Assert that pa->nIndices is correct ASSERTOPENGL(pa->nIndices == pa->pdNextVertex - pa->pd0, "bad nIndices\n"); // Render the polygon. // Call PolyArrayRenderPolygon later pa->flags |= POLYARRAY_RENDER_PRIMITIVE; } void FASTCALL PolyArrayRenderPolygon(__GLcontext *gc, POLYARRAY *pa) { // Index mapping is always identity in Polygon. ASSERTOPENGL(!pa->aIndices, "Index mapping must be identity\n"); // Reset the line stipple if this is a new polygon. if (pa->flags & POLYARRAY_RESET_STIPPLE) gc->line.notResetStipple = GL_FALSE; // Note that the provoking vertex is set to pd0 in clipPolygon (*gc->procs.clipPolygon)(gc, (__GLvertex *) pa->pd0, pa->nIndices); } /****************************************************************************/ // Note: The first vertex must have a valid normal! // // IN: obj/eye, normal // OUT: eye, color.r (front or back depending on face) (all vertices are // updated) void FASTCALL PolyArrayCalcCIColor(__GLcontext *gc, GLint face, POLYARRAY *pa, POLYDATA *pdFirst, POLYDATA *pdLast) { __GLfloat nxi, nyi, nzi; __GLfloat zero; __GLlightSourceMachine *lsm; __GLmaterialState *ms; __GLmaterialMachine *msm; __GLfloat msm_threshold, msm_scale, *msm_specTable; __GLfloat ms_cmapa, ms_cmapd, ms_cmaps; __GLfloat si, di; POLYDATA *pd; GLfloat redMaxF; GLint redMaxI; GLboolean eyeWIsZero, localViewer; static __GLcoord Pe = { 0, 0, 0, 1 }; PERF_CHECK(FALSE, "Uses slow lights\n"); zero = __glZero; if (face == __GL_FRONTFACE) { ms = &gc->state.light.front; msm = &gc->light.front; } else { ms = &gc->state.light.back; msm = &gc->light.back; } msm_scale = msm->scale; msm_threshold = msm->threshold; msm_specTable = msm->specTable; ms_cmapa = ms->cmapa; ms_cmapd = ms->cmapd; ms_cmaps = ms->cmaps; localViewer = gc->state.light.model.localViewer; redMaxF = (GLfloat) gc->frontBuffer.redMax; redMaxI = (GLint) gc->frontBuffer.redMax; // Eye coord should have been processed ASSERTOPENGL(pa->flags & POLYARRAY_EYE_PROCESSED, "need eye\n"); // NOTE: the following values may be re-used in the next iteration: // nxi, nyi, nzi for (pd = pdFirst; pd <= pdLast; pd++) { __GLfloat ci; if (pd->flags & POLYDATA_NORMAL_VALID) { if (gc->vertex.paNeeds & PANEEDS_NORMAL) { (*gc->mInv->xf3)(&pd->normal, &pd->normal.x, gc->mInv); if (gc->state.enables.general & __GL_NORMALIZE_ENABLE) __glNormalize(&pd->normal.x, &pd->normal.x); } if (face == __GL_FRONTFACE) { nxi = pd->normal.x; nyi = pd->normal.y; nzi = pd->normal.z; } else { nxi = -pd->normal.x; nyi = -pd->normal.y; nzi = -pd->normal.z; } } else { // use previous normal (nxi, nyi, nzi)! ASSERTOPENGL(pd != pdFirst, "no initial normal\n"); } si = zero; di = zero; eyeWIsZero = __GL_FLOAT_EQZ(pd->eye.w); for (lsm = gc->light.sources; lsm; lsm = lsm->next) { if (lsm->slowPath || eyeWIsZero) { __GLfloat n1, n2, att, attSpot; __GLcoord vPliHat, vPli, hHat, vPeHat; __GLfloat hv[3]; /* Compute vPli, hi (normalized) */ vvec(&vPli, &pd->eye, &lsm->position); __glNormalize(&vPliHat.x, &vPli.x); if (localViewer) { vvec(&vPeHat, &pd->eye, &Pe); __glNormalize(&vPeHat.x, &vPeHat.x); hv[0] = vPliHat.x + vPeHat.x; hv[1] = vPliHat.y + vPeHat.y; hv[2] = vPliHat.z + vPeHat.z; } else { hv[0] = vPliHat.x; hv[1] = vPliHat.y; hv[2] = vPliHat.z + __glOne; } __glNormalize(&hHat.x, hv); /* Compute attenuation */ if (__GL_FLOAT_NEZ(lsm->position.w)) { __GLfloat k0, k1, k2, dist; k0 = lsm->constantAttenuation; k1 = lsm->lizNearAttenuation; k2 = lsm->quadraticAttenuation; if (__GL_FLOAT_EQZ(k1) && __GL_FLOAT_EQZ(k2)) { /* Use pre-computed 1/k0 */ att = lsm->attenuation; } else { dist = __GL_SQRTF(vPli.x*vPli.x + vPli.y*vPli.y + vPli.z*vPli.z); att = __glOne / (k0 + k1 * dist + k2 * dist * dist); } } else { att = __glOne; } /* Compute spot effect if light is a spot light */ attSpot = att; if (lsm->isSpot) { __GLfloat dot, px, py, pz; px = -vPliHat.x; py = -vPliHat.y; pz = -vPliHat.z; dot = px * lsm->direction.x + py * lsm->direction.y + pz * lsm->direction.z; if ((dot >= lsm->threshold) && (dot >= lsm->cosCutOffAngle)) { GLint ix = (GLint)((dot - lsm->threshold) * lsm->scale + __glHalf); if (ix < __GL_SPOT_LOOKUP_TABLE_SIZE) attSpot = att * lsm->spotTable[ix]; } else { attSpot = zero; } } /* Add in remaining effect of light, if any */ if (attSpot) { n1 = nxi * vPliHat.x + nyi * vPliHat.y + nzi * vPliHat.z; if (__GL_FLOAT_GTZ(n1)) { n2 = nxi * hHat.x + nyi * hHat.y + nzi * hHat.z; n2 -= msm_threshold; if (__GL_FLOAT_GEZ(n2)) { #ifdef NT __GLfloat fx = n2 * msm_scale + __glHalf; if( fx < (__GLfloat)__GL_SPEC_LOOKUP_TABLE_SIZE ) n2 = msm_specTable[(GLint)fx]; else n2 = __glOne; #else GLint ix = (GLint)(n2 * msm_scale + __glHalf); if (ix < __GL_SPEC_LOOKUP_TABLE_SIZE) n2 = msm_specTable[ix]; else n2 = __glOne; #endif si += attSpot * n2 * lsm->sli; } di += attSpot * n1 * lsm->dli; } } } else { __GLfloat n1, n2; /* Compute specular contribution */ n1 = nxi * lsm->unitVPpli.x + nyi * lsm->unitVPpli.y + nzi * lsm->unitVPpli.z; if (__GL_FLOAT_GTZ(n1)) { n2 = nxi * lsm->hHat.x + nyi * lsm->hHat.y + nzi * lsm->hHat.z; n2 -= msm_threshold; if (__GL_FLOAT_GEZ(n2)) { #ifdef NT __GLfloat fx = n2 * msm_scale + __glHalf; if( fx < (__GLfloat)__GL_SPEC_LOOKUP_TABLE_SIZE ) n2 = msm_specTable[(GLint)fx]; else n2 = __glOne; #else GLint ix = (GLint)(n2 * msm_scale + __glHalf); if (ix < __GL_SPEC_LOOKUP_TABLE_SIZE) n2 = msm_specTable[ix]; else n2 = __glOne; #endif si += n2 * lsm->sli; } di += n1 * lsm->dli; } } } /* Compute final color */ if (si > __glOne) si = __glOne; ci = ms_cmapa + (__glOne - si) * di * (ms_cmapd - ms_cmapa) + si * (ms_cmaps - ms_cmapa); if (ci > ms_cmaps) ci = ms_cmaps; // need to mask color index before color clipping if (ci > redMaxF) { GLfloat fraction; GLint integer; integer = (GLint) ci; fraction = ci - (GLfloat) integer; integer = integer & redMaxI; ci = (GLfloat) integer + fraction; } else if (ci < 0) { GLfloat fraction; GLint integer; integer = (GLint) __GL_FLOORF(ci); fraction = ci - (GLfloat) integer; integer = integer & redMaxI; ci = (GLfloat) integer + fraction; } pd->colors[face].r = ci; } } // No slow lights version // Note: The first vertex must have a valid normal! // // IN: normal // OUT: color.r (front or back depending on face) (all vertices are updated) void FASTCALL PolyArrayFastCalcCIColor(__GLcontext *gc, GLint face, POLYARRAY *pa, POLYDATA *pdFirst, POLYDATA *pdLast) { __GLfloat nxi, nyi, nzi; __GLfloat zero; __GLlightSourceMachine *lsm; __GLmaterialState *ms; __GLmaterialMachine *msm; __GLfloat msm_threshold, msm_scale, *msm_specTable; __GLfloat ms_cmapa, ms_cmapd, ms_cmaps; __GLfloat si, di; POLYDATA *pd; GLfloat redMaxF; GLint redMaxI; #if LATER // if eye.w is zero, it should really take the slow path! // Since the RGB version ignores it, we will also ignore it here. // Even the original generic implementation may not have computed eye values. #endif zero = __glZero; if (face == __GL_FRONTFACE) { ms = &gc->state.light.front; msm = &gc->light.front; } else { ms = &gc->state.light.back; msm = &gc->light.back; } msm_scale = msm->scale; msm_threshold = msm->threshold; msm_specTable = msm->specTable; ms_cmapa = ms->cmapa; ms_cmapd = ms->cmapd; ms_cmaps = ms->cmaps; redMaxF = (GLfloat) gc->frontBuffer.redMax; redMaxI = (GLint) gc->frontBuffer.redMax; // NOTE: the following values may be re-used in the next iteration: // nxi, nyi, nzi for (pd = pdFirst; pd <= pdLast; pd++) { __GLfloat ci; // If normal has not changed for this vertex, use the previously computed // color index. if (!(pd->flags & POLYDATA_NORMAL_VALID)) { ASSERTOPENGL(pd != pdFirst, "no initial normal\n"); pd->colors[face].r = (pd-1)->colors[face].r; continue; } if (gc->vertex.paNeeds & PANEEDS_NORMAL) { (*gc->mInv->xf3)(&pd->normal, &pd->normal.x, gc->mInv); if (gc->state.enables.general & __GL_NORMALIZE_ENABLE) __glNormalize(&pd->normal.x, &pd->normal.x); } if (face == __GL_FRONTFACE) { nxi = pd->normal.x; nyi = pd->normal.y; nzi = pd->normal.z; } else { nxi = -pd->normal.x; nyi = -pd->normal.y; nzi = -pd->normal.z; } si = zero; di = zero; for (lsm = gc->light.sources; lsm; lsm = lsm->next) { __GLfloat n1, n2; /* Compute specular contribution */ n1 = nxi * lsm->unitVPpli.x + nyi * lsm->unitVPpli.y + nzi * lsm->unitVPpli.z; if (__GL_FLOAT_GTZ(n1)) { n2 = nxi * lsm->hHat.x + nyi * lsm->hHat.y + nzi * lsm->hHat.z; n2 -= msm_threshold; if (__GL_FLOAT_GEZ(n2)) { #ifdef NT __GLfloat fx = n2 * msm_scale + __glHalf; if( fx < (__GLfloat)__GL_SPEC_LOOKUP_TABLE_SIZE ) n2 = msm_specTable[(GLint)fx]; else n2 = __glOne; #else GLint ix = (GLint)(n2 * msm_scale + __glHalf); if (ix < __GL_SPEC_LOOKUP_TABLE_SIZE) n2 = msm_specTable[ix]; else n2 = __glOne; #endif si += n2 * lsm->sli; } di += n1 * lsm->dli; } } /* Compute final color */ if (si > __glOne) si = __glOne; ci = ms_cmapa + (__glOne - si) * di * (ms_cmapd - ms_cmapa) + si * (ms_cmaps - ms_cmapa); if (ci > ms_cmaps) ci = ms_cmaps; // need to mask color index before color clipping // SGIBUG: The sample implementation fails to do this! if (ci > redMaxF) { GLfloat fraction; GLint integer; integer = (GLint) ci; fraction = ci - (GLfloat) integer; integer = integer & redMaxI; ci = (GLfloat) integer + fraction; } else if (ci < 0) { GLfloat fraction; GLint integer; integer = (GLint) __GL_FLOORF(ci); fraction = ci - (GLfloat) integer; integer = integer & redMaxI; ci = (GLfloat) integer + fraction; } pd->colors[face].r = ci; } } // If both front and back colors are needed, the back colors must be computed // first! Otherwise, the front colors can be overwritten prematurely. // Note: The first vertex must have valid normal and color! // // IN: obj/eye, color (front), normal // OUT: eye, color (front or back depending on face) (all vertices are updated) void FASTCALL PolyArrayCalcRGBColor(__GLcontext *gc, GLint face, POLYARRAY *pa, POLYDATA *pdFirst, POLYDATA *pdLast) { __GLfloat nxi, nyi, nzi; __GLfloat zero; __GLlightSourcePerMaterialMachine *lspmm; __GLlightSourceMachine *lsm; __GLlightSourceState *lss; __GLfloat ri, gi, bi; __GLfloat alpha; __GLfloat rsi, gsi, bsi; __GLcolor sceneColorI; __GLmaterialMachine *msm; __GLcolor lm_ambient; __GLfloat msm_alpha, msm_threshold, msm_scale, *msm_specTable; __GLcolor msm_paSceneColor; GLuint msm_colorMaterialChange; POLYDATA *pd; GLboolean eyeWIsZero, localViewer; static __GLcoord Pe = { 0, 0, 0, 1 }; PERF_CHECK(FALSE, "Uses slow lights\n"); zero = __glZero; // Eye coord should have been processed ASSERTOPENGL(pa->flags & POLYARRAY_EYE_PROCESSED, "need eye\n"); if (face == __GL_FRONTFACE) msm = &gc->light.front; else msm = &gc->light.back; lm_ambient.r = gc->state.light.model.ambient.r; lm_ambient.g = gc->state.light.model.ambient.g; lm_ambient.b = gc->state.light.model.ambient.b; msm_scale = msm->scale; msm_threshold = msm->threshold; msm_specTable = msm->specTable; msm_alpha = msm->alpha; msm_colorMaterialChange = msm->colorMaterialChange; msm_paSceneColor = msm->paSceneColor; localViewer = gc->state.light.model.localViewer; // Get invarient scene color if there is no ambient or emissive color material. sceneColorI.r = msm_paSceneColor.r; sceneColorI.g = msm_paSceneColor.g; sceneColorI.b = msm_paSceneColor.b; // NOTE: the following values may be re-used in the next iteration: // ri, gi, bi, alpha, nxi, nyi, nzi, sceneColorI for (pd = pdFirst; pd <= pdLast; pd++) { if (pd->flags & POLYDATA_COLOR_VALID) { // Save latest colors normalized to 0..1 ri = pd->colors[0].r * gc->oneOverRedVertexScale; gi = pd->colors[0].g * gc->oneOverGreenVertexScale; bi = pd->colors[0].b * gc->oneOverBlueVertexScale; alpha = pd->colors[0].a; // Compute scene color. // If color has not changed, the previous sceneColorI values are used! if (msm_colorMaterialChange & (__GL_MATERIAL_AMBIENT | __GL_MATERIAL_EMISSIVE)) { if (msm_colorMaterialChange & __GL_MATERIAL_AMBIENT) { sceneColorI.r = msm_paSceneColor.r + ri * lm_ambient.r; sceneColorI.g = msm_paSceneColor.g + gi * lm_ambient.g; sceneColorI.b = msm_paSceneColor.b + bi * lm_ambient.b; } else { sceneColorI.r = msm_paSceneColor.r + pd->colors[0].r; sceneColorI.g = msm_paSceneColor.g + pd->colors[0].g; sceneColorI.b = msm_paSceneColor.b + pd->colors[0].b; } } } else { // use previous ri, gi, bi, alpha, and sceneColorI! ASSERTOPENGL(pd != pdFirst, "no initial color\n"); } // Compute the diffuse and specular components for this vertex. if (pd->flags & POLYDATA_NORMAL_VALID) { if (gc->vertex.paNeeds & PANEEDS_NORMAL) { (*gc->mInv->xf3)(&pd->normal, &pd->normal.x, gc->mInv); if (gc->state.enables.general & __GL_NORMALIZE_ENABLE) __glNormalize(&pd->normal.x, &pd->normal.x); } if (face == __GL_FRONTFACE) { nxi = pd->normal.x; nyi = pd->normal.y; nzi = pd->normal.z; } else { nxi = -pd->normal.x; nyi = -pd->normal.y; nzi = -pd->normal.z; } } else { // use previous normal (nxi, nyi, nzi)! ASSERTOPENGL(pd != pdFirst, "no initial normal\n"); } rsi = sceneColorI.r; gsi = sceneColorI.g; bsi = sceneColorI.b; eyeWIsZero = __GL_FLOAT_EQZ(pd->eye.w); for (lsm = gc->light.sources; lsm; lsm = lsm->next) { __GLfloat n1, n2; lss = lsm->state; lspmm = &lsm->front + face; if (lsm->slowPath || eyeWIsZero) { __GLcoord hHat, vPli, vPliHat, vPeHat; __GLfloat att, attSpot; __GLfloat hv[3]; /* Compute unit h[i] */ vvec(&vPli, &pd->eye, &lsm->position); __glNormalize(&vPliHat.x, &vPli.x); if (localViewer) { vvec(&vPeHat, &pd->eye, &Pe); __glNormalize(&vPeHat.x, &vPeHat.x); hv[0] = vPliHat.x + vPeHat.x; hv[1] = vPliHat.y + vPeHat.y; hv[2] = vPliHat.z + vPeHat.z; } else { hv[0] = vPliHat.x; hv[1] = vPliHat.y; hv[2] = vPliHat.z + __glOne; } __glNormalize(&hHat.x, hv); /* Compute attenuation */ if (__GL_FLOAT_NEZ(lsm->position.w)) { __GLfloat k0, k1, k2, dist; k0 = lsm->constantAttenuation; k1 = lsm->lizNearAttenuation; k2 = lsm->quadraticAttenuation; if (__GL_FLOAT_EQZ(k1) && __GL_FLOAT_EQZ(k2)) { /* Use pre-computed 1/k0 */ att = lsm->attenuation; } else { dist = __GL_SQRTF(vPli.x*vPli.x + vPli.y*vPli.y + vPli.z*vPli.z); att = __glOne / (k0 + k1 * dist + k2 * dist * dist); } } else { att = __glOne; } /* Compute spot effect if light is a spot light */ attSpot = att; if (lsm->isSpot) { __GLfloat dot, px, py, pz; px = -vPliHat.x; py = -vPliHat.y; pz = -vPliHat.z; dot = px * lsm->direction.x + py * lsm->direction.y + pz * lsm->direction.z; if ((dot >= lsm->threshold) && (dot >= lsm->cosCutOffAngle)) { GLint ix = (GLint)((dot - lsm->threshold) * lsm->scale + __glHalf); if (ix < __GL_SPOT_LOOKUP_TABLE_SIZE) attSpot = att * lsm->spotTable[ix]; } else { attSpot = zero; } } /* Add in remaining effect of light, if any */ if (attSpot) { __GLfloat n1, n2; __GLcolor sum; if (msm_colorMaterialChange & __GL_MATERIAL_AMBIENT) { sum.r = ri * lss->ambient.r; sum.g = gi * lss->ambient.g; sum.b = bi * lss->ambient.b; } else { sum.r = lspmm->ambient.r; sum.g = lspmm->ambient.g; sum.b = lspmm->ambient.b; } n1 = nxi * vPliHat.x + nyi * vPliHat.y + nzi * vPliHat.z; if (__GL_FLOAT_GTZ(n1)) { n2 = nxi * hHat.x + nyi * hHat.y + nzi * hHat.z; n2 -= msm_threshold; if (__GL_FLOAT_GEZ(n2)) { #ifdef NT __GLfloat fx = n2 * msm_scale + __glHalf; if( fx < (__GLfloat)__GL_SPEC_LOOKUP_TABLE_SIZE ) n2 = msm_specTable[(GLint)fx]; else n2 = __glOne; #else GLint ix = (GLint)(n2 * msm_scale + __glHalf); if (ix < __GL_SPEC_LOOKUP_TABLE_SIZE) n2 = msm_specTable[ix]; else n2 = __glOne; #endif if (msm_colorMaterialChange & __GL_MATERIAL_SPECULAR) { /* Recompute per-light per-material cached specular */ sum.r += n2 * ri * lss->specular.r; sum.g += n2 * gi * lss->specular.g; sum.b += n2 * bi * lss->specular.b; } else { sum.r += n2 * lspmm->specular.r; sum.g += n2 * lspmm->specular.g; sum.b += n2 * lspmm->specular.b; } } if (msm_colorMaterialChange & __GL_MATERIAL_DIFFUSE) { /* Recompute per-light per-material cached diffuse */ sum.r += n1 * ri * lss->diffuse.r; sum.g += n1 * gi * lss->diffuse.g; sum.b += n1 * bi * lss->diffuse.b; } else { sum.r += n1 * lspmm->diffuse.r; sum.g += n1 * lspmm->diffuse.g; sum.b += n1 * lspmm->diffuse.b; } } rsi += attSpot * sum.r; gsi += attSpot * sum.g; bsi += attSpot * sum.b; } } else { __GLfloat n1, n2; if (msm_colorMaterialChange & __GL_MATERIAL_AMBIENT) { rsi += ri * lss->ambient.r; gsi += gi * lss->ambient.g; bsi += bi * lss->ambient.b; } else { rsi += lspmm->ambient.r; gsi += lspmm->ambient.g; bsi += lspmm->ambient.b; } /* Add in specular and diffuse effect of light, if any */ n1 = nxi * lsm->unitVPpli.x + nyi * lsm->unitVPpli.y + nzi * lsm->unitVPpli.z; if (__GL_FLOAT_GTZ(n1)) { n2 = nxi * lsm->hHat.x + nyi * lsm->hHat.y + nzi * lsm->hHat.z; n2 -= msm_threshold; if (__GL_FLOAT_GEZ(n2)) { #ifdef NT __GLfloat fx = n2 * msm_scale + __glHalf; if( fx < (__GLfloat)__GL_SPEC_LOOKUP_TABLE_SIZE ) n2 = msm_specTable[(GLint)fx]; else n2 = __glOne; #else GLint ix = (GLint)(n2 * msm_scale + __glHalf); if (ix < __GL_SPEC_LOOKUP_TABLE_SIZE) n2 = msm_specTable[ix]; else n2 = __glOne; #endif if (msm_colorMaterialChange & __GL_MATERIAL_SPECULAR) { /* Recompute per-light per-material cached specular */ rsi += n2 * ri * lss->specular.r; gsi += n2 * gi * lss->specular.g; bsi += n2 * bi * lss->specular.b; } else { rsi += n2 * lspmm->specular.r; gsi += n2 * lspmm->specular.g; bsi += n2 * lspmm->specular.b; } } if (msm_colorMaterialChange & __GL_MATERIAL_DIFFUSE) { /* Recompute per-light per-material cached diffuse */ rsi += n1 * ri * lss->diffuse.r; gsi += n1 * gi * lss->diffuse.g; bsi += n1 * bi * lss->diffuse.b; } else { rsi += n1 * lspmm->diffuse.r; gsi += n1 * lspmm->diffuse.g; bsi += n1 * lspmm->diffuse.b; } } } } { __GLcolor *pd_color_dst; pd_color_dst = &pd->colors[face]; __GL_CLAMP_R(pd_color_dst->r, gc, rsi); __GL_CLAMP_G(pd_color_dst->g, gc, gsi); __GL_CLAMP_B(pd_color_dst->b, gc, bsi); if (msm_colorMaterialChange & __GL_MATERIAL_DIFFUSE) { if (pa->flags & POLYARRAY_CLAMP_COLOR) { __GL_CLAMP_A(pd_color_dst->a, gc, alpha); } else pd_color_dst->a = alpha; } else { pd_color_dst->a = msm_alpha; } } } } // If both front and back colors are needed, the back color must be computed // first! Otherwise, the front color can be overwritten prematurely. // Note: The first vertex must have valid normal and color! // // IN: color (front), normal // OUT: color (front or back depending on face) (all vertices are updated) #ifndef __GL_ASM_POLYARRAYFASTCALCRGBCOLOR void FASTCALL PolyArrayFastCalcRGBColor(__GLcontext *gc, GLint face, POLYARRAY *pa, POLYDATA *pdFirst, POLYDATA *pdLast) { __GLfloat nxi, nyi, nzi; __GLfloat zero; __GLlightSourcePerMaterialMachine *lspmm; __GLlightSourceMachine *lsm; __GLlightSourceState *lss; __GLfloat ri, gi, bi; __GLfloat alpha; __GLcolor emissiveAmbientI; __GLcolor diffuseSpecularI; __GLcolor baseEmissiveAmbient; __GLmaterialMachine *msm; __GLcolor lm_ambient; __GLfloat msm_alpha, msm_threshold, msm_scale, *msm_specTable; GLuint msm_colorMaterialChange; POLYDATA *pd; #if LATER // if eye.w is zero, it should really take the slow path! // Since the sample implementation ignores it, we will also ignore it here. #endif PERF_CHECK(FALSE, "Primitives contain glColorMaterial calls\n"); zero = __glZero; if (face == __GL_FRONTFACE) msm = &gc->light.front; else msm = &gc->light.back; // If there is no color material change for this face, we can call the // zippy function! msm_colorMaterialChange = msm->colorMaterialChange; if (!msm_colorMaterialChange) { PolyArrayZippyCalcRGBColor(gc, face, pa, pdFirst, pdLast); return; } lm_ambient.r = gc->state.light.model.ambient.r; lm_ambient.g = gc->state.light.model.ambient.g; lm_ambient.b = gc->state.light.model.ambient.b; msm_scale = msm->scale; msm_threshold = msm->threshold; msm_specTable = msm->specTable; msm_alpha = msm->alpha; // Compute invarient emissive and ambient components for this vertex. baseEmissiveAmbient.r = msm->paSceneColor.r; baseEmissiveAmbient.g = msm->paSceneColor.g; baseEmissiveAmbient.b = msm->paSceneColor.b; // add invarient per-light per-material cached ambient if (!(msm_colorMaterialChange & __GL_MATERIAL_AMBIENT)) { for (lsm = gc->light.sources; lsm; lsm = lsm->next) { lspmm = &lsm->front + face; baseEmissiveAmbient.r += lspmm->ambient.r; baseEmissiveAmbient.g += lspmm->ambient.g; baseEmissiveAmbient.b += lspmm->ambient.b; } } // If there is no emissive or ambient color material change, this // will be the emissive and ambient components. emissiveAmbientI.r = baseEmissiveAmbient.r; emissiveAmbientI.g = baseEmissiveAmbient.g; emissiveAmbientI.b = baseEmissiveAmbient.b; // NOTE: the following values may be re-used in the next iteration: // ri, gi, bi, alpha, nxi, nyi, nzi, emissiveAmbientI, diffuseSpecularI for (pd = pdFirst; pd <= pdLast; pd++) { // If color and normal have not changed for this vertex, use the previously // computed color. if (!(pd->flags & (POLYDATA_NORMAL_VALID | POLYDATA_COLOR_VALID))) { ASSERTOPENGL(pd != pdFirst, "no initial normal and color\n"); pd->colors[face] = (pd-1)->colors[face]; continue; } if (pd->flags & POLYDATA_COLOR_VALID) { // Save latest colors normalized to 0..1 ri = pd->colors[0].r * gc->oneOverRedVertexScale; gi = pd->colors[0].g * gc->oneOverGreenVertexScale; bi = pd->colors[0].b * gc->oneOverBlueVertexScale; alpha = pd->colors[0].a; // Compute the emissive and ambient components for this vertex if necessary. // If color has not changed, the previous emissveAmbientI values are used! if (msm_colorMaterialChange & (__GL_MATERIAL_AMBIENT | __GL_MATERIAL_EMISSIVE)) { if (msm_colorMaterialChange & __GL_MATERIAL_AMBIENT) { emissiveAmbientI.r = baseEmissiveAmbient.r + ri * lm_ambient.r; emissiveAmbientI.g = baseEmissiveAmbient.g + gi * lm_ambient.g; emissiveAmbientI.b = baseEmissiveAmbient.b + bi * lm_ambient.b; // Add per-light per-material ambient for (lsm = gc->light.sources; lsm; lsm = lsm->next) { lss = lsm->state; emissiveAmbientI.r += ri * lss->ambient.r; emissiveAmbientI.g += gi * lss->ambient.g; emissiveAmbientI.b += bi * lss->ambient.b; } } else { emissiveAmbientI.r = baseEmissiveAmbient.r + pd->colors[0].r; emissiveAmbientI.g = baseEmissiveAmbient.g + pd->colors[0].g; emissiveAmbientI.b = baseEmissiveAmbient.b + pd->colors[0].b; } } } else { // use previous ri, gi, bi, alpha, and emissiveAmbientI! ASSERTOPENGL(pd != pdFirst, "no initial color\n"); } // Compute the diffuse and specular components for this vertex. if (pd->flags & POLYDATA_NORMAL_VALID) { if (gc->vertex.paNeeds & PANEEDS_NORMAL) { (*gc->mInv->xf3)(&pd->normal, &pd->normal.x, gc->mInv); if (gc->state.enables.general & __GL_NORMALIZE_ENABLE) __glNormalize(&pd->normal.x, &pd->normal.x); } if (face == __GL_FRONTFACE) { nxi = pd->normal.x; nyi = pd->normal.y; nzi = pd->normal.z; } else { nxi = -pd->normal.x; nyi = -pd->normal.y; nzi = -pd->normal.z; } } else { ASSERTOPENGL(pd != pdFirst, "no initial normal\n"); // If normal and diffuse and specular components have not changed, // use the previously computed diffuse and specular values. // otherwise, use previous normal (nxi, nyi, nzi) and // diffuseSpecularI! if (!(msm_colorMaterialChange & (__GL_MATERIAL_SPECULAR | __GL_MATERIAL_DIFFUSE))) goto store_color; } diffuseSpecularI.r = zero; diffuseSpecularI.g = zero; diffuseSpecularI.b = zero; for (lsm = gc->light.sources; lsm; lsm = lsm->next) { __GLfloat n1, n2; lss = lsm->state; lspmm = &lsm->front + face; /* Add in specular and diffuse effect of light, if any */ n1 = nxi * lsm->unitVPpli.x + nyi * lsm->unitVPpli.y + nzi * lsm->unitVPpli.z; if (__GL_FLOAT_GTZ(n1)) { n2 = nxi * lsm->hHat.x + nyi * lsm->hHat.y + nzi * lsm->hHat.z; n2 -= msm_threshold; if (__GL_FLOAT_GEZ(n2)) { #ifdef NT __GLfloat fx = n2 * msm_scale + __glHalf; if( fx < (__GLfloat)__GL_SPEC_LOOKUP_TABLE_SIZE ) n2 = msm_specTable[(GLint)fx]; else n2 = __glOne; #else GLint ix = (GLint)(n2 * msm_scale + __glHalf); if (ix < __GL_SPEC_LOOKUP_TABLE_SIZE) n2 = msm_specTable[ix]; else n2 = __glOne; #endif if (msm_colorMaterialChange & __GL_MATERIAL_SPECULAR) { /* Recompute per-light per-material cached specular */ diffuseSpecularI.r += n2 * ri * lss->specular.r; diffuseSpecularI.g += n2 * gi * lss->specular.g; diffuseSpecularI.b += n2 * bi * lss->specular.b; } else { diffuseSpecularI.r += n2 * lspmm->specular.r; diffuseSpecularI.g += n2 * lspmm->specular.g; diffuseSpecularI.b += n2 * lspmm->specular.b; } } if (msm_colorMaterialChange & __GL_MATERIAL_DIFFUSE) { /* Recompute per-light per-material cached diffuse */ diffuseSpecularI.r += n1 * ri * lss->diffuse.r; diffuseSpecularI.g += n1 * gi * lss->diffuse.g; diffuseSpecularI.b += n1 * bi * lss->diffuse.b; } else { diffuseSpecularI.r += n1 * lspmm->diffuse.r; diffuseSpecularI.g += n1 * lspmm->diffuse.g; diffuseSpecularI.b += n1 * lspmm->diffuse.b; } } } store_color: { __GLcolor *pd_color_dst; pd_color_dst = &pd->colors[face]; __GL_CLAMP_R(pd_color_dst->r, gc, emissiveAmbientI.r + diffuseSpecularI.r); __GL_CLAMP_G(pd_color_dst->g, gc, emissiveAmbientI.g + diffuseSpecularI.g); __GL_CLAMP_B(pd_color_dst->b, gc, emissiveAmbientI.b + diffuseSpecularI.b); if (msm_colorMaterialChange & __GL_MATERIAL_DIFFUSE) { if (pa->flags & POLYARRAY_CLAMP_COLOR) { __GL_CLAMP_A(pd_color_dst->a, gc, alpha); } else pd_color_dst->a = alpha; } else { pd_color_dst->a = msm_alpha; } } } } #endif // __GL_ASM_POLYARRAYFASTCALCRGBCOLOR // This function is called when color material is disabled and there are no // slow lights. // // Note: The first vertex must have a valid normal! // // IN: normal // OUT: color (front or back depending on face) (all vertices are updated) #ifndef __GL_ASM_POLYARRAYZIPPYCALCRGBCOLOR void FASTCALL PolyArrayZippyCalcRGBColor(__GLcontext *gc, GLint face, POLYARRAY *pa, POLYDATA *pdFirst, POLYDATA *pdLast) { __GLfloat nxi, nyi, nzi; __GLlightSourcePerMaterialMachine *lspmm; __GLlightSourceMachine *lsm; __GLlightSourceState *lss; __GLcolor baseEmissiveAmbient; __GLmaterialMachine *msm; __GLfloat msm_alpha, msm_threshold, msm_scale, *msm_specTable; POLYDATA *pd; #if LATER // if eye.w is zero, it should really take the slow path! // Since the sample implementation ignores it, we will also ignore it here. #endif if (face == __GL_FRONTFACE) msm = &gc->light.front; else msm = &gc->light.back; msm_scale = msm->scale; msm_threshold = msm->threshold; msm_specTable = msm->specTable; msm_alpha = msm->alpha; // Compute invarient emissive and ambient components for this vertex. baseEmissiveAmbient.r = msm->paSceneColor.r; baseEmissiveAmbient.g = msm->paSceneColor.g; baseEmissiveAmbient.b = msm->paSceneColor.b; // add invarient per-light per-material cached ambient for (lsm = gc->light.sources; lsm; lsm = lsm->next) { lspmm = &lsm->front + face; baseEmissiveAmbient.r += lspmm->ambient.r; baseEmissiveAmbient.g += lspmm->ambient.g; baseEmissiveAmbient.b += lspmm->ambient.b; } // NOTE: the following values may be re-used in the next iteration: // nxi, nyi, nzi for (pd = pdFirst; pd <= pdLast; pd++) { __GLfloat rsi, gsi, bsi; // If normal has not changed for this vertex, use the previously computed color. if (!(pd->flags & POLYDATA_NORMAL_VALID)) { ASSERTOPENGL(pd != pdFirst, "no initial normal\n"); pd->colors[face] = (pd-1)->colors[face]; continue; } if (gc->vertex.paNeeds & PANEEDS_NORMAL) { (*gc->mInv->xf3)(&pd->normal, &pd->normal.x, gc->mInv); if (gc->state.enables.general & __GL_NORMALIZE_ENABLE) __glNormalize(&pd->normal.x, &pd->normal.x); } if (face == __GL_FRONTFACE) { nxi = pd->normal.x; nyi = pd->normal.y; nzi = pd->normal.z; } else { nxi = -pd->normal.x; nyi = -pd->normal.y; nzi = -pd->normal.z; } rsi = baseEmissiveAmbient.r; gsi = baseEmissiveAmbient.g; bsi = baseEmissiveAmbient.b; // Compute the diffuse and specular components for this vertex. for (lsm = gc->light.sources; lsm; lsm = lsm->next) { __GLfloat n1, n2; lss = lsm->state; lspmm = &lsm->front + face; /* Add in specular and diffuse effect of light, if any */ n1 = nxi * lsm->unitVPpli.x + nyi * lsm->unitVPpli.y + nzi * lsm->unitVPpli.z; if (__GL_FLOAT_GTZ(n1)) { n2 = (nxi * lsm->hHat.x + nyi * lsm->hHat.y + nzi * lsm->hHat.z) - msm_threshold; if (__GL_FLOAT_GEZ(n2)) { #ifdef NT __GLfloat fx = n2 * msm_scale + __glHalf; if( fx < (__GLfloat)__GL_SPEC_LOOKUP_TABLE_SIZE ) n2 = msm_specTable[(GLint)fx]; else n2 = __glOne; #else GLint ix = (GLint)(n2 * msm_scale + __glHalf); if (ix < __GL_SPEC_LOOKUP_TABLE_SIZE) n2 = msm_specTable[ix]; else n2 = __glOne; #endif rsi += n2 * lspmm->specular.r; gsi += n2 * lspmm->specular.g; bsi += n2 * lspmm->specular.b; } rsi += n1 * lspmm->diffuse.r; gsi += n1 * lspmm->diffuse.g; bsi += n1 * lspmm->diffuse.b; } } { __GLcolor *pd_color_dst; pd_color_dst = &pd->colors[face]; __GL_CLAMP_R(pd_color_dst->r, gc, rsi); __GL_CLAMP_G(pd_color_dst->g, gc, gsi); __GL_CLAMP_B(pd_color_dst->b, gc, bsi); pd_color_dst->a = msm_alpha; } } } #endif // __GL_ASM_POLYARRAYZIPPYCALCRGBCOLOR /****************************************************************************/ // Clip check the clip coordinates against the frustum planes. // Compute the window coordinates if not clipped! // // IN: clip // OUT: window (if not clipped) // Clip codes indexed by: // 2 1 0 // wsign xsign w-xsign // For both the x > w and x < -w cases static GLubyte __glXClipCodes[8] = { 0, __GL_CLIP_RIGHT, 0, __GL_CLIP_LEFT, __GL_CLIP_LEFT | __GL_CLIP_RIGHT, __GL_CLIP_RIGHT, __GL_CLIP_LEFT | __GL_CLIP_RIGHT, __GL_CLIP_LEFT, }; static GLubyte __glYClipCodes[8] = { 0, __GL_CLIP_TOP, 0, __GL_CLIP_BOTTOM, __GL_CLIP_BOTTOM | __GL_CLIP_TOP, __GL_CLIP_TOP, __GL_CLIP_BOTTOM | __GL_CLIP_TOP, __GL_CLIP_BOTTOM, }; static GLubyte __glZClipCodes[8] = { 0, __GL_CLIP_FAR, 0, __GL_CLIP_NEAR, __GL_CLIP_NEAR | __GL_CLIP_FAR, __GL_CLIP_FAR, __GL_CLIP_NEAR | __GL_CLIP_FAR, __GL_CLIP_NEAR, }; #ifndef __GL_ASM_PACLIPCHECKFRUSTUM GLuint FASTCALL PAClipCheckFrustum(__GLcontext *gc, POLYDATA *pd) { __GLfloat x, y, z, w, invW, negW; GLuint code; w = pd->clip.w; if (__GL_FLOAT_COMPARE_PONE(w, ==)) { pd->window.w = __glOne; /* Set clip codes */ #ifdef _X86_ // Always computes both add/sub even though // it's not technically necessary. The overhead of // doing a test and branch for each value plus the // loss of overlap doesn't seem worth it // This also isn't the common case __asm { mov edx, pd xor edi, edi xor ecx, ecx // Accumulate w sign and abs(w) mov eax, __FLOAT_ONE // Accumulate x sign and abs(x) // Compute abs(w)-abs(x) and accumulate sign mov ebx, [edx+PD_clip] shl ebx, 1 mov esi, eax rcl edi, 1 shr ebx, 1 sub esi, ebx shl esi, 1 rcl edi, 1 // Get clip codes for index in edi and reset edi mov cl, __glXClipCodes[edi] shr edi, 2 // Accumulate y sign and abs(y) // Compute abs(w)-abs(y) and accumulate sign mov ebx, [edx+PD_clip+4] shl ebx, 1 mov esi, eax rcl edi, 1 shr ebx, 1 sub esi, ebx shl esi, 1 rcl edi, 1 // Get clip codes for index in edi and reset edi or cl, __glYClipCodes[edi] shr edi, 2 // Accumulate z sign and abs(z) // Compute abs(w)-abs(z) and accumulate sign mov ebx, [edx+PD_clip+8] shl ebx, 1 mov esi, eax rcl edi, 1 shr ebx, 1 sub esi, ebx shl esi, 1 rcl edi, 1 // Get clip codes for index in edi or cl, __glZClipCodes[edi] mov code, ecx or ecx, ecx jnz Clipped mov eax, gc fld DWORD PTR [edx+PD_clip] fmul DWORD PTR [eax+GC_VIEWPORT_xScale] fld DWORD PTR [edx+PD_clip+4] fmul DWORD PTR [eax+GC_VIEWPORT_yScale] fld DWORD PTR [edx+PD_clip+8] fmul DWORD PTR [eax+GC_VIEWPORT_zScale] fld DWORD PTR [eax+GC_VIEWPORT_xCenter] fld DWORD PTR [eax+GC_VIEWPORT_yCenter] fld DWORD PTR [eax+GC_VIEWPORT_zCenter] faddp st(3), st(0) faddp st(3), st(0) faddp st(3), st(0) fxch st(2) fstp DWORD PTR [edx+PD_window] fstp DWORD PTR [edx+PD_window+4] fstp DWORD PTR [edx+PD_window+8] Clipped: } #else x = pd->clip.x; y = pd->clip.y; z = pd->clip.z; code = 0; negW = __glMinusOne; if (x < negW) code |= __GL_CLIP_LEFT; else if (x > w) code |= __GL_CLIP_RIGHT; if (y < negW) code |= __GL_CLIP_BOTTOM; else if (y > w) code |= __GL_CLIP_TOP; if (z < negW) code |= __GL_CLIP_NEAR; else if (z > w) code |= __GL_CLIP_FAR; /* Compute window coordinates if not clipped */ if (!code) { __GLfloat wx, wy, wz; wx = x * gc->state.viewport.xScale + gc->state.viewport.xCenter; wy = y * gc->state.viewport.yScale + gc->state.viewport.yCenter; wz = z * gc->state.viewport.zScale + gc->state.viewport.zCenter; pd->window.x = wx; pd->window.y = wy; pd->window.z = wz; } #endif } else { // Enable single precision divides CHOP_ROUND_ON(); /* Set clip codes */ #ifdef _X86_ __asm { // Start 1/w divide mov eax, w or eax, eax jz WEqZero fld __glOne fdiv w jmp WDivStarted WEqZero: fld __glZero WDivStarted: mov edx, pd xor edi, edi xor ecx, ecx // Accumulate w sign and abs(w) // Accumulate x sign and abs(x) // Compute abs(w)-abs(x) and accumulate sign shl eax, 1 mov ebx, [edx+PD_clip] rcl edi, 1 shr eax, 1 shl ebx, 1 mov esi, eax rcl edi, 1 shr ebx, 1 sub esi, ebx shl esi, 1 rcl edi, 1 // Get clip codes for index in edi and reset edi mov cl, __glXClipCodes[edi] shr edi, 2 // Accumulate y sign and abs(y) // Compute abs(w)-abs(y) and accumulate sign mov ebx, [edx+PD_clip+4] shl ebx, 1 mov esi, eax rcl edi, 1 shr ebx, 1 sub esi, ebx shl esi, 1 rcl edi, 1 // Get clip codes for index in edi and reset edi or cl, __glYClipCodes[edi] shr edi, 2 // Accumulate z sign and abs(z) // Compute abs(w)-abs(z) and accumulate sign mov ebx, [edx+PD_clip+8] shl ebx, 1 mov esi, eax rcl edi, 1 shr ebx, 1 sub esi, ebx shl esi, 1 rcl edi, 1 // Get clip codes for index in edi or cl, __glZClipCodes[edi] mov code, ecx or ecx, ecx // Save invW fstp invW jnz ClippedW mov eax, gc fld DWORD PTR [edx+PD_clip] fmul DWORD PTR [eax+GC_VIEWPORT_xScale] fld DWORD PTR [edx+PD_clip+4] fmul DWORD PTR [eax+GC_VIEWPORT_yScale] fld DWORD PTR [edx+PD_clip+8] fmul DWORD PTR [eax+GC_VIEWPORT_zScale] fld invW ; iW z y x fmul st(3), st(0) fld DWORD PTR [eax+GC_VIEWPORT_xCenter] ; xc iW z y xiW fxch st(1) ; iW xc z y xiW fmul st(3), st(0) fld DWORD PTR [eax+GC_VIEWPORT_yCenter] ; yc iW xc z yiW xiW fxch st(1) ; iW yc xc z yiW xiW fmulp st(3), st(0) ; yc xc ziW yiW xiW fld DWORD PTR [eax+GC_VIEWPORT_zCenter] ; zc yc xc ziW yiW xiW faddp st(3), st(0) faddp st(3), st(0) faddp st(3), st(0) fxch st(2) fstp DWORD PTR [edx+PD_window] fstp DWORD PTR [edx+PD_window+4] fstp DWORD PTR [edx+PD_window+8] ClippedW: } pd->window.w = invW; #else /* XXX (mf) prevent divide-by-zero */ if (__GL_FLOAT_NEZ(w)) { __GL_FLOAT_SIMPLE_BEGIN_DIVIDE(__glOne, w, invW); } else { invW = __glZero; } x = pd->clip.x; y = pd->clip.y; z = pd->clip.z; code = 0; negW = -w; __GL_FLOAT_SIMPLE_END_DIVIDE(invW); pd->window.w = invW; /* ** NOTE: it is possible for x to be less than negW and greater than w ** (if w is negative). Otherwise there would be "else" clauses here. */ if (x < negW) code |= __GL_CLIP_LEFT; if (x > w) code |= __GL_CLIP_RIGHT; if (y < negW) code |= __GL_CLIP_BOTTOM; if (y > w) code |= __GL_CLIP_TOP; if (z < negW) code |= __GL_CLIP_NEAR; if (z > w) code |= __GL_CLIP_FAR; /* Compute window coordinates if not clipped */ if (!code) { __GLfloat wx, wy, wz; wx = x * gc->state.viewport.xScale * invW + gc->state.viewport.xCenter; wy = y * gc->state.viewport.yScale * invW + gc->state.viewport.yCenter; wz = z * gc->state.viewport.zScale * invW + gc->state.viewport.zCenter; pd->window.x = wx; pd->window.y = wy; pd->window.z = wz; } #endif CHOP_ROUND_OFF(); } return code; } #endif // __GL_ASM_PACLIPCHECKFRUSTUM // Clip check the clip coordinates against the frustum planes. // Compute the window coordinates if not clipped! // // IN: clip // OUT: window (if not clipped) #ifndef __GL_ASM_PACLIPCHECKFRUSTUM2D GLuint FASTCALL PAClipCheckFrustum2D(__GLcontext *gc, POLYDATA *pd) { __GLfloat x, y, z, w, negW, invW; GLuint code; /* W is 1.0 */ pd->window.w = __glOne; #ifdef _X86_ __asm { mov edx, pd xor edi, edi xor ecx, ecx // Accumulate w sign and abs(w) mov eax, __FLOAT_ONE // Accumulate x sign and abs(x) // Compute abs(w)-abs(x) and accumulate sign mov ebx, [edx+PD_clip] shl ebx, 1 mov esi, eax rcl edi, 1 shr ebx, 1 sub esi, ebx shl esi, 1 rcl edi, 1 // Get clip codes for index in edi and reset edi mov cl, __glXClipCodes[edi] shr edi, 2 // Accumulate y sign and abs(y) // Compute abs(w)-abs(y) and accumulate sign mov ebx, [edx+PD_clip+4] shl ebx, 1 mov esi, eax rcl edi, 1 shr ebx, 1 sub esi, ebx shl esi, 1 rcl edi, 1 // Get clip codes for index in edi and reset edi or cl, __glYClipCodes[edi] mov code, ecx or ecx, ecx jnz Clipped mov eax, gc fld DWORD PTR [edx+PD_clip] fmul DWORD PTR [eax+GC_VIEWPORT_xScale] fld DWORD PTR [edx+PD_clip+4] fmul DWORD PTR [eax+GC_VIEWPORT_yScale] fld DWORD PTR [edx+PD_clip+8] fmul DWORD PTR [eax+GC_VIEWPORT_zScale] fld DWORD PTR [eax+GC_VIEWPORT_xCenter] fld DWORD PTR [eax+GC_VIEWPORT_yCenter] fld DWORD PTR [eax+GC_VIEWPORT_zCenter] faddp st(3), st(0) faddp st(3), st(0) faddp st(3), st(0) fxch st(2) fstp DWORD PTR [edx+PD_window] fstp DWORD PTR [edx+PD_window+4] fstp DWORD PTR [edx+PD_window+8] Clipped: } #else x = pd->clip.x; y = pd->clip.y; z = pd->clip.z; w = pd->clip.w; negW = __glMinusOne; /* Set clip codes */ code = 0; if (x < negW) code |= __GL_CLIP_LEFT; else if (x > w) code |= __GL_CLIP_RIGHT; if (y < negW) code |= __GL_CLIP_BOTTOM; else if (y > w) code |= __GL_CLIP_TOP; /* Compute window coordinates if not clipped */ if (!code) { __GLfloat wx, wy, wz; wx = x * gc->state.viewport.xScale + gc->state.viewport.xCenter; wy = y * gc->state.viewport.yScale + gc->state.viewport.yCenter; wz = z * gc->state.viewport.zScale + gc->state.viewport.zCenter; pd->window.x = wx; pd->window.y = wy; pd->window.z = wz; } #endif return code; } #endif // __GL_ASM_PACLIPCHECKFRUSTUM2D // Clip check against the frustum and user clipping planes. // Compute the window coordinates if not clipped! // // IN: clip, eye // OUT: window (if not clipped) GLuint FASTCALL PAClipCheckAll(__GLcontext *gc, POLYDATA *pd) { __GLfloat x, y, z, w, negW, invW; GLuint code, bit, clipPlanesMask; __GLcoord *plane; // Enable single precision divides CHOP_ROUND_ON(); PERF_CHECK(FALSE, "Performs user plane clipping!\n"); /* ** Do frustum checks. ** ** NOTE: it is possible for x to be less than negW and greater than w ** (if w is negative). Otherwise there would be "else" clauses here. */ x = pd->clip.x; y = pd->clip.y; z = pd->clip.z; w = pd->clip.w; /* Set clip codes */ #ifdef _X86_ __asm { // Start 1/w divide mov eax, w or eax, eax jz WEqZero fld __glOne fdiv w jmp WDivStarted WEqZero: fld __glZero WDivStarted: xor edi, edi xor ecx, ecx // Accumulate w sign and abs(w) // Accumulate x sign and abs(x) // Compute abs(w)-abs(x) and accumulate sign shl eax, 1 mov ebx, x rcl edi, 1 shr eax, 1 shl ebx, 1 mov esi, eax rcl edi, 1 shr ebx, 1 sub esi, ebx shl esi, 1 rcl edi, 1 // Get clip codes for index in edi and reset edi mov cl, __glXClipCodes[edi] shr edi, 2 // Accumulate y sign and abs(y) // Compute abs(w)-abs(y) and accumulate sign mov ebx, y shl ebx, 1 mov esi, eax rcl edi, 1 shr ebx, 1 sub esi, ebx shl esi, 1 rcl edi, 1 // Get clip codes for index in edi and reset edi or cl, __glYClipCodes[edi] shr edi, 2 // Accumulate z sign and abs(z) // Compute abs(w)-abs(z) and accumulate sign mov ebx, z shl ebx, 1 mov esi, eax rcl edi, 1 shr ebx, 1 sub esi, ebx shl esi, 1 rcl edi, 1 // Get clip codes for index in edi or cl, __glZClipCodes[edi] // Store invW fstp invW mov code, ecx } pd->window.w = invW; #else /* XXX (mf) prevent divide-by-zero */ if (__GL_FLOAT_NEZ(w)) { __GL_FLOAT_SIMPLE_BEGIN_DIVIDE(__glOne, w, invW); __GL_FLOAT_SIMPLE_END_DIVIDE(invW); } else { invW = __glZero; } pd->window.w = invW; negW = -w; code = 0; if (x < negW) code |= __GL_CLIP_LEFT; if (x > w) code |= __GL_CLIP_RIGHT; if (y < negW) code |= __GL_CLIP_BOTTOM; if (y > w) code |= __GL_CLIP_TOP; if (z < negW) code |= __GL_CLIP_NEAR; if (z > w) code |= __GL_CLIP_FAR; #endif /* ** Now do user clip plane checks */ x = pd->eye.x; y = pd->eye.y; z = pd->eye.z; w = pd->eye.w; clipPlanesMask = gc->state.enables.clipPlanes; plane = &gc->state.transform.eyeClipPlanes[0]; bit = __GL_CLIP_USER0; while (clipPlanesMask) { if (clipPlanesMask & 1) { /* ** Dot the vertex clip coordinate against the clip plane and see ** if the sign is negative. If so, then the point is out. */ if (x * plane->x + y * plane->y + z * plane->z + w * plane->w < __glZero) { code |= bit; } } clipPlanesMask >>= 1; bit <<= 1; plane++; } /* Compute window coordinates if not clipped */ if (!code) { __GLfloat wx, wy, wz; x = pd->clip.x; y = pd->clip.y; z = pd->clip.z; #ifdef _X86_ __asm { mov edx, pd mov eax, gc fld DWORD PTR [edx+PD_clip] fmul DWORD PTR [eax+GC_VIEWPORT_xScale] fld DWORD PTR [edx+PD_clip+4] fmul DWORD PTR [eax+GC_VIEWPORT_yScale] fld DWORD PTR [edx+PD_clip+8] fmul DWORD PTR [eax+GC_VIEWPORT_zScale] fld invW ; iW z y x fmul st(3), st(0) fld DWORD PTR [eax+GC_VIEWPORT_xCenter] ; xc iW z y xiW fxch st(1) ; iW xc z y xiW fmul st(3), st(0) fld DWORD PTR [eax+GC_VIEWPORT_yCenter] ; yc iW xc z yiW xiW fxch st(1) ; iW yc xc z yiW xiW fmulp st(3), st(0) ; yc xc ziW yiW xiW fld DWORD PTR [eax+GC_VIEWPORT_zCenter] ; zc yc xc ziW yiW xiW faddp st(3), st(0) faddp st(3), st(0) faddp st(3), st(0) fxch st(2) fstp DWORD PTR [edx+PD_window] fstp DWORD PTR [edx+PD_window+4] fstp DWORD PTR [edx+PD_window+8] } #else wx = x * gc->state.viewport.xScale * invW + gc->state.viewport.xCenter; wy = y * gc->state.viewport.yScale * invW + gc->state.viewport.yCenter; wz = z * gc->state.viewport.zScale * invW + gc->state.viewport.zCenter; pd->window.x = wx; pd->window.y = wy; pd->window.z = wz; #endif } CHOP_ROUND_OFF(); return code; } /****************************************************************************/ void APIPRIVATE __glim_EdgeFlag(GLboolean tag) { __GL_SETUP(); gc->state.current.edgeTag = tag; } void APIPRIVATE __glim_TexCoord4fv(const GLfloat x[4]) { __GL_SETUP(); gc->state.current.texture.x = x[0]; gc->state.current.texture.y = x[1]; gc->state.current.texture.z = x[2]; gc->state.current.texture.w = x[3]; } void APIPRIVATE __glim_Normal3fv(const GLfloat v[3]) { __GL_SETUP(); GLfloat x, y, z; x = v[0]; y = v[1]; z = v[2]; gc->state.current.normal.x = x; gc->state.current.normal.y = y; gc->state.current.normal.z = z; } void APIPRIVATE __glim_Color4fv(const GLfloat v[4]) { __GL_SETUP(); gc->state.current.userColor.r = v[0]; gc->state.current.userColor.g = v[1]; gc->state.current.userColor.b = v[2]; gc->state.current.userColor.a = v[3]; (*gc->procs.applyColor)(gc); } void APIPRIVATE __glim_Indexf(GLfloat c) { __GL_SETUP(); gc->state.current.userColorIndex = c; } #if DBG #define DEBUG_RASTERPOS 1 #endif // This is not very efficient but it should work fine. void APIPRIVATE __glim_RasterPos4fv(const GLfloat v[4]) { POLYDATA pd3[3]; // one pa, one pd, followed by one spare. POLYARRAY *pa = (POLYARRAY *) &pd3[0]; POLYDATA *pd = &pd3[1]; __GLvertex *rp; GLuint oldPaNeeds, oldEnables; #ifdef DEBUG_RASTERPOS void (FASTCALL *oldRenderPoint)(__GLcontext *gc, __GLvertex *v); #endif GLuint pdflags; __GL_SETUP_NOT_IN_BEGIN_VALIDATE(); // ASSERT_VERTEX if (v[3] == (GLfloat) 1.0) { if (v[2] == (GLfloat) 0.0) pdflags = POLYDATA_VERTEX2; else pdflags = POLYDATA_VERTEX3; } else { pdflags = POLYDATA_VERTEX4; } rp = &gc->state.current.rasterPos; // Initialize POLYARRAY structure with one vertex pa->flags = pdflags | POLYARRAY_RASTERPOS; pa->pdNextVertex = pd+1; pa->pdCurColor = pa->pdCurNormal = pa->pdCurTexture = pa->pdCurEdgeFlag = pa->pd0 = pd; pa->primType = GL_POINTS; pa->nIndices = 1; pa->aIndices = NULL; // identity mapping pa->paNext = NULL; pd->flags = pdflags; pd->obj = *(__GLcoord *) &v[0]; pd->color = &pd->colors[__GL_FRONTFACE]; pd->clipCode = 1; // set for debugging (pd+1)->flags = 0; // Set up states. // need transformed texcoord in all cases oldPaNeeds = gc->vertex.paNeeds; gc->vertex.paNeeds |= PANEEDS_TEXCOORD; // no front-end optimization gc->vertex.paNeeds &= ~(PANEEDS_CLIP_ONLY | PANEEDS_SKIP_LIGHTING | PANEEDS_NORMAL); // set normal need if (gc->vertex.paNeeds & PANEEDS_RASTERPOS_NORMAL) gc->vertex.paNeeds |= PANEEDS_NORMAL; // don't apply cheap fog! oldEnables = gc->state.enables.general; gc->state.enables.general &= ~__GL_FOG_ENABLE; #ifdef DEBUG_RASTERPOS // Debug only! // allow DrawPolyArray to perform selection but not feedback and rendering oldRenderPoint = gc->procs.renderPoint; if (gc->renderMode != GL_SELECT) gc->procs.renderPoint = NULL; // was __glRenderPointNop but set to 0 // for debugging #endif // Call DrawPolyArray to 'draw' the point. // Begin validation has already been done. __glim_DrawPolyArray(pa); // 'Render' the point in selection but not in feedback and render modes. if (gc->renderMode == GL_SELECT) { PARenderPoint(gc, (__GLvertex *)pa->pd0); } // Eye coord should have been processed ASSERTOPENGL(pa->flags & POLYARRAY_EYE_PROCESSED, "need eye\n"); // Restore states. gc->vertex.paNeeds = oldPaNeeds; gc->state.enables.general = oldEnables; #ifdef DEBUG_RASTERPOS gc->procs.renderPoint = oldRenderPoint; #endif // If the point is clipped, the raster position is invalid. if (pd->clipCode) { gc->state.current.validRasterPos = GL_FALSE; return; } gc->state.current.validRasterPos = GL_TRUE; // Update raster pos data structure! // Only the following fields are needed. rp->window.x = pd->window.x; rp->window.y = pd->window.y; rp->window.z = pd->window.z; rp->clip.w = pd->clip.w; rp->eyeZ = pd->eye.z; rp->colors[__GL_FRONTFACE] = pd->colors[__GL_FRONTFACE]; rp->texture = pd->texture; assert(rp->color = &rp->colors[__GL_FRONTFACE]); } /************************************************************************/ void FASTCALL __glNop(void) {} void FASTCALL __glNopGC(__GLcontext* gc) {} GLboolean FASTCALL __glNopGCBOOL(__GLcontext* gc) { return FALSE; } void FASTCALL __glNopGCFRAG(__GLcontext* gc, __GLfragment *frag, __GLtexel *texel) {} void FASTCALL __glNopGCCOLOR(__GLcontext* gc, __GLcolor *color, __GLtexel *texel) {} void FASTCALL __glNopLight(__GLcontext*gc, GLint i, __GLvertex*v) {} #ifdef NT_DEADCODE_POLYARRAY void FASTCALL __glNopVertexInt(__GLcontext *gc, __GLvertex *vx, GLuint needs) {} void FASTCALL __glNopVertex(__GLcontext *gc, __GLvertex *vx) {} void FASTCALL __glNopGCListOp(__GLcontext *gc, __GLdlistOp *op) {} #endif void FASTCALL __glNopExtract(__GLmipMapLevel *level, __GLtexture *tex, GLint row, GLint col, __GLtexel *result) {} #ifdef NT_DEADCODE_POLYARRAY /* ** End a primitive that needs no special end processing */ void FASTCALL __glEndPrim(__GLcontext *gc) { gc->procs.vertex = __glNopVertex; gc->procs.endPrim = __glEndPrim; } #endif void FASTCALL ComputeColorMaterialChange(__GLcontext *gc) { gc->light.front.colorMaterialChange = 0; gc->light.back.colorMaterialChange = 0; if (gc->modes.rgbMode && gc->state.enables.general & __GL_COLOR_MATERIAL_ENABLE) { GLuint colorMaterialChange; switch (gc->state.light.colorMaterialParam) { case GL_EMISSION: colorMaterialChange = __GL_MATERIAL_EMISSIVE; break; case GL_SPECULAR: colorMaterialChange = __GL_MATERIAL_SPECULAR; break; case GL_AMBIENT: colorMaterialChange = __GL_MATERIAL_AMBIENT; break; case GL_DIFFUSE: colorMaterialChange = __GL_MATERIAL_DIFFUSE; break; case GL_AMBIENT_AND_DIFFUSE: colorMaterialChange = __GL_MATERIAL_AMBIENT | __GL_MATERIAL_DIFFUSE; break; } if (gc->state.light.colorMaterialFace == GL_FRONT_AND_BACK || gc->state.light.colorMaterialFace == GL_FRONT) gc->light.front.colorMaterialChange = colorMaterialChange; if (gc->state.light.colorMaterialFace == GL_FRONT_AND_BACK || gc->state.light.colorMaterialFace == GL_BACK) gc->light.back.colorMaterialChange = colorMaterialChange; } } void FASTCALL __glGenericPickVertexProcs(__GLcontext *gc) { GLuint enables = gc->state.enables.general; GLenum mvpMatrixType; __GLmatrix *m; m = &(gc->transform.modelView->mvp); mvpMatrixType = m->matrixType; /* Pick paClipCheck proc */ //!!! are there better clip procs? if (gc->state.enables.clipPlanes) { gc->procs.paClipCheck = PAClipCheckAll; } else { if (mvpMatrixType >= __GL_MT_IS2D && m->matrix[3][2] >= -1.0f && m->matrix[3][2] <= 1.0f) gc->procs.paClipCheck = PAClipCheckFrustum2D; else gc->procs.paClipCheck = PAClipCheckFrustum; } } // Allocate the POLYDATA vertex buffer. // Align the buffer on a cache line boundary GLboolean FASTCALL PolyArrayAllocBuffer(__GLcontext *gc, GLuint nVertices) { GLuint cjSize; // Make sure that the vertex buffer holds a minimum number of vertices. if (nVertices < MINIMUM_POLYDATA_BUFFER_SIZE) { ASSERTOPENGL(FALSE, "vertex buffer too small\n"); return GL_FALSE; } // Allocate the vertex buffer. cjSize = (nVertices * sizeof(POLYDATA)); if (!(gc->vertex.pdBuf = (POLYDATA *) __wglMallocAlign32(gc, cjSize))) return GL_FALSE; gc->vertex.pdBufSizeBytes = cjSize; // Only (n-1) vertices are available for use. The last one is reserved // by polyarray code. gc->vertex.pdBufSize = nVertices - 1; // Initialize the vertex buffer. PolyArrayResetBuffer(gc); return GL_TRUE; } // Reset the color pointers in vertex buffer. GLvoid FASTCALL PolyArrayResetBuffer(__GLcontext *gc) { GLuint i; for (i = 0; i <= gc->vertex.pdBufSize; i++) gc->vertex.pdBuf[i].color = &gc->vertex.pdBuf[i].colors[__GL_FRONTFACE]; } // Free the POLYDATA vertex buffer. GLvoid FASTCALL PolyArrayFreeBuffer(__GLcontext *gc) { #ifdef _MCD_ // If MCD, the POLYDATA vertex buffer is freed when the MCD context is // destroyed (see GenMcdDestroy). if (((__GLGENcontext *) gc)->_pMcdState) return; #endif if (gc->vertex.pdBuf) __wglFreeAlign32(gc, gc->vertex.pdBuf); gc->vertex.pdBufSizeBytes = 0; gc->vertex.pdBufSize = 0; }