/******************************module*header*******************************\ * Module Name: mcdtri.c * * Contains the low-level (rasterization) triangle-rendering routines for the * Cirrus Logic 546X MCD driver. * * Copyright (c) 1997 Cirrus Logic, Inc. \**************************************************************************/ #include "precomp.h" #include "mcdhw.h" #include "mcdutil.h" #include "mcdmath.h" #define QUAKEEDGE_FIX #define FASTER_RECIP_ORTHO #define HALF 0x08000 #define ONE 0x10000 #define MCDTRI_PRINT //#define MAX_W_RATIO (float)1.45 #define MAX_W_RATIO (float)1.80 #define W_RATIO_PERSP_EQ_LINEAR (float)1.03 #define EDGE_SUBDIVIDE_TEST(start,end,WRAMAX,WRBMAX,SPLIT) \ SPLIT = ((start->windowCoord.w > WRAMAX) || (end->windowCoord.w > WRBMAX)) ? 1 : 0; #define FIND_MIDPOINT(start, end, mid) { \ float recip; \ mid->windowCoord.x = (start->windowCoord.x + end->windowCoord.x) * (float)0.5; \ mid->windowCoord.y = (start->windowCoord.y + end->windowCoord.y) * (float)0.5; \ mid->windowCoord.z = (start->windowCoord.z + end->windowCoord.z) * (float)0.5; \ mid->colors[0].r = (start->colors[0].r + end->colors[0].r) * (float)0.5; \ mid->colors[0].g = (start->colors[0].g + end->colors[0].g) * (float)0.5; \ mid->colors[0].b = (start->colors[0].b + end->colors[0].b) * (float)0.5; \ mid->colors[0].a = (start->colors[0].a + end->colors[0].a) * (float)0.5; \ mid->fog = (start->fog + end->fog) * (float)0.5; \ mid->windowCoord.w = (start->windowCoord.w + end->windowCoord.w) * (float)0.5; \ recip = (float)0.5/mid->windowCoord.w; /* pre-mult by .5 for use below */ \ mid->texCoord.x = recip * (start->texCoord.x * start->windowCoord.w + \ end->texCoord.x * end->windowCoord.w); \ mid->texCoord.y = recip * (start->texCoord.y * start->windowCoord.w + \ end->texCoord.y * end->windowCoord.w); \ } VOID FASTCALL __MCDSubdivideTriangle(DEVRC *pRc, MCDVERTEX *a, MCDVERTEX *b, MCDVERTEX *c, int split12, int split23, int split31, int subdiv_levels) { MCDVERTEX Vmid12,Vmid23,Vmid31; // 3 possible midpoints subdiv_levels++; // find midpoint of edges if they need to be split if (split12) FIND_MIDPOINT(a,b,((MCDVERTEX *)&Vmid12)); if (split23) FIND_MIDPOINT(b,c,((MCDVERTEX *)&Vmid23)); if (split31) FIND_MIDPOINT(c,a,((MCDVERTEX *)&Vmid31)); #define SPLIT12 0x4 #define SPLIT23 0x2 #define SPLIT31 0x1 // from original vertices and any midpoints found above, create a batch of triangles switch ((split12<<2) | (split23<<1) | split31) { case SPLIT12: // 2 triangles, 1->2 edge was divided __MCDPerspTxtTriangle(pRc, a, &Vmid12, c, subdiv_levels); __MCDPerspTxtTriangle(pRc, b, &Vmid12, c, subdiv_levels); break; case SPLIT23: // 2 triangles, 2->3 edge was divided __MCDPerspTxtTriangle(pRc, b, &Vmid23, a, subdiv_levels); __MCDPerspTxtTriangle(pRc, c, &Vmid23, a, subdiv_levels); break; case SPLIT31: // 2 triangles, 3->1 edge was divided __MCDPerspTxtTriangle(pRc, c, &Vmid31, b, subdiv_levels); __MCDPerspTxtTriangle(pRc, a, &Vmid31, b, subdiv_levels); break; case (SPLIT12|SPLIT23): // 3 triangles, 1->2 and 2->3 edges were divided __MCDPerspTxtTriangle(pRc, a, &Vmid23, c, subdiv_levels); __MCDPerspTxtTriangle(pRc, a, &Vmid23, &Vmid12, subdiv_levels); __MCDPerspTxtTriangle(pRc, &Vmid12, &Vmid23, b, subdiv_levels); break; case (SPLIT23|SPLIT31): // 3 triangles, 2->3 and 3->1 edges were divided __MCDPerspTxtTriangle(pRc, a, &Vmid31, b, subdiv_levels); __MCDPerspTxtTriangle(pRc, b, &Vmid31, &Vmid23, subdiv_levels); __MCDPerspTxtTriangle(pRc, &Vmid23, &Vmid31, c, subdiv_levels); break; case (SPLIT12|SPLIT31): // 3 triangles, 1->2 and 3->1 edges were divided __MCDPerspTxtTriangle(pRc, a, &Vmid31, &Vmid12, subdiv_levels); __MCDPerspTxtTriangle(pRc, b, &Vmid31, &Vmid12, subdiv_levels); __MCDPerspTxtTriangle(pRc, b, &Vmid31, c, subdiv_levels); break; case (SPLIT12|SPLIT23|SPLIT31): // 4 triangles, all 3 edges were divided __MCDPerspTxtTriangle(pRc, a, &Vmid31, &Vmid12, subdiv_levels); __MCDPerspTxtTriangle(pRc, b, &Vmid23, &Vmid12, subdiv_levels); __MCDPerspTxtTriangle(pRc, c, &Vmid31, &Vmid23, subdiv_levels); __MCDPerspTxtTriangle(pRc, &Vmid12, &Vmid23, &Vmid31, subdiv_levels); break; default: // original triangle - no subdivisions // this routine should never be called for this case, but here's insurance __MCDPerspTxtTriangle(pRc, a, b, c, subdiv_levels); break; } // endswitch } #define EXCHANGE(i,j) \ { \ ptemp=i; \ i=j; j=ptemp; \ } #define ROTATE_L(i,j,k) \ { \ ptemp=j; \ j=k;k=i;i=ptemp; \ } #define SORT_Y_ORDER(a,b,c) \ { \ void *ptemp; \ if( a->windowCoord.y > b->windowCoord.y ) \ if( c->windowCoord.y < b->windowCoord.y ) \ EXCHANGE(a,c) \ else \ if( c->windowCoord.y < a->windowCoord.y ) \ ROTATE_L(a,b,c) \ else \ EXCHANGE(a,b) \ else \ if( c->windowCoord.y < a->windowCoord.y ) \ ROTATE_L(c,b,a) \ else \ if( c->windowCoord.y < b->windowCoord.y ) \ EXCHANGE(b,c) \ } VOID FASTCALL __MCDPerspTxtTriangle(DEVRC *pRc, MCDVERTEX *a, MCDVERTEX *b, MCDVERTEX *c, int subdiv_levels) { int split12, split23, split31; float w1_times_max = a->windowCoord.w * W_RATIO_PERSP_EQ_LINEAR; float w2_times_max = b->windowCoord.w * W_RATIO_PERSP_EQ_LINEAR; float w3_times_max = c->windowCoord.w * W_RATIO_PERSP_EQ_LINEAR; if ((a->windowCoord.w < w2_times_max) && (b->windowCoord.w < w1_times_max) && (b->windowCoord.w < w3_times_max) && (c->windowCoord.w < w2_times_max) && (c->windowCoord.w < w1_times_max) && (a->windowCoord.w < w3_times_max)) { if (subdiv_levels > 1) { // this triangle result of subdivision -> must sort in y SORT_Y_ORDER(a,b,c) } __MCDFillTriangle(pRc, a, b, c, TRUE); // ready to render - linear ok } else { w1_times_max = a->windowCoord.w * MAX_W_RATIO; w2_times_max = b->windowCoord.w * MAX_W_RATIO; w3_times_max = c->windowCoord.w * MAX_W_RATIO; // determine from w ratios which (if any) edges must be subdivided EDGE_SUBDIVIDE_TEST(a,b,w2_times_max,w1_times_max,split12) EDGE_SUBDIVIDE_TEST(b,c,w3_times_max,w2_times_max,split23) EDGE_SUBDIVIDE_TEST(c,a,w1_times_max,w3_times_max,split31) // if we need to subdivide, and we're not already too many levels deep, do it // (since subdivision recursive, must limit it to prevent stack overflow in kernel mode) if ((split12 | split23 | split31) && (subdiv_levels < 4)) __MCDSubdivideTriangle(pRc, a, b, c, split12, split23, split31, subdiv_levels); else { if (subdiv_levels > 1) { // this triangle result of subdivision -> must sort in y SORT_Y_ORDER(a,b,c) } __MCDFillTriangle(pRc, a, b, c, FALSE); // ready to render - linear NOT ok } } } #define FLT_TYPE (float) #define FLOAT_TO_1616 FLT_TYPE 65536.0 #define FIXED_X_ROUND_FACTOR 0x7fff //#define INTPR(FLOATVAL) FTOL((FLOATVAL) * FLT_TYPE 1000.0) #define INTPR(FLOATVAL) 0 /********************************************************************* * Local Functions **********************************************************************/ #define RIGHT_TO_LEFT_DIR 0x80000000 #define LEFT_TO_RIGHT_DIR 0 #define EDGE_DISABLE_RIGHT_X 0x20000000 #define EDGE_DISABLE_LEFT_X 0x40000000 #define EDGE_DISABLE_BOTTOM_Y 0x20000000 #define EDGE_DISABLE_TOP_Y 0x40000000 #define EDGE_DISABLE_X EDGE_DISABLE_RIGHT_X #define EDGE_DISABLE_Y 0 // macros to convert float to precision equivalent to 16.16 representation #define PREC_FLOAT FLOAT_TO_1616 // rounding done by adding 1/2 of 1/65536, since 1/65536 is 16.16 step size #define PREC_ROUND ((FLT_TYPE 0.5) / PREC_FLOAT) #define PREC_1616(inval,outval) { \ float bias = (inval>=0) ? PREC_ROUND : -PREC_ROUND; \ outval=(float)(FTOL((inval+bias)*PREC_FLOAT)) * ((FLT_TYPE 1.0) / PREC_FLOAT); \ } // for positive values that will be used as negative, unconditionally bias it smaller // unless it's already too small #define NEG_PREC_1616(inval,outval) { \ float bias = (inval>0) ? -PREC_ROUND : 0; \ outval=(float)(FTOL((inval+bias)*PREC_FLOAT)) * ((FLT_TYPE 1.0) / PREC_FLOAT); \ } // convert from float to 16.16 long #define fix_ieee( val ) FTOL((val) * (float)65536.0) // convert from float to 8.24 long #define fix824_ieee( val ) FTOL((val) * (float)16777216.0) typedef struct { float a1, a2; float b1, b2; } QUADRATIC; VOID FASTCALL __MCDFillTriangle(DEVRC *pRc, MCDVERTEX *a, MCDVERTEX *b, MCDVERTEX *c, int linear_ok) { PDEV *ppdev; unsigned int *pdwNext; // output queue stuff... DWORD *pSrc; DWORD *pDest; DWORD *pdwStart; DWORD *pdwOrig; DWORD *pdwColor; DWORD dwOpcode; int count1, count2; float frecip_main, frecip_ortho; float fdx_main; float ftemp; float v1red,v1grn,v1blu; float fv2x,fv2y,fv3x,fv3y,fv32y; float aroundy, broundy; float fmain_adj, fwidth, fxincrement, finitwidth1, finitwidth2; float fdwidth1,fdwidth2; float awinx, awiny, bwinx, bwiny, cwinx, cwiny; int int_awiny, int_bwiny, int_cwiny; float fadjust; int xflags; // window coords are float values, and need to have // viewportadjust (MCDVIEWPORT) values subtracted to get to real screen space // color values are 0->1 floats and must be multiplied by scale values (MCDRCINFO) // to get to nbits range (scale = 0xff for 8 bit, 0x7 for 3 bit, etc.) // Z values are 0->1 floats and must be multiplied by zscale values (MCDRCINFO) // Caller has already sorted vertices so that a.y <= b.y <= c.y // Force flat-top/ flat-bottom right triangles to draw toward the center. // if Main is vertical edge, much better chance of alignment at diagonal if( a->windowCoord.y == b->windowCoord.y ) { // Flat top if( b->windowCoord.x == c->windowCoord.x ) { void *ptemp; EXCHANGE(a, b); } } else if( b->windowCoord.y == c->windowCoord.y ) { // Flat bottom if( a->windowCoord.x == b->windowCoord.x ) { void *ptemp; EXCHANGE(b, c); } } MCDTRI_PRINT("v1 = %d %d %d c1=%d %d %d",INTPR(a->windowCoord.x),INTPR(a->windowCoord.y),INTPR(a->windowCoord.z),INTPR(a->colors[0].r),INTPR(a->colors[0].g),INTPR(a->colors[0].b)); MCDTRI_PRINT("v2 = %d %d %d c2=%d %d %d",INTPR(b->windowCoord.x),INTPR(b->windowCoord.y),INTPR(b->windowCoord.z),INTPR(b->colors[0].r),INTPR(b->colors[0].g),INTPR(b->colors[0].b)); MCDTRI_PRINT("v3 = %d %d %d c3=%d %d %d",INTPR(c->windowCoord.x),INTPR(c->windowCoord.y),INTPR(c->windowCoord.z),INTPR(c->colors[0].r),INTPR(c->colors[0].g),INTPR(c->colors[0].b)); awinx = a->windowCoord.x + pRc->fxOffset; awiny = a->windowCoord.y + pRc->fyOffset; bwinx = b->windowCoord.x + pRc->fxOffset; bwiny = b->windowCoord.y + pRc->fyOffset; cwinx = c->windowCoord.x + pRc->fxOffset; cwiny = c->windowCoord.y + pRc->fyOffset; // round y's (don't ever need rounded version of c's y) aroundy = FLT_TYPE FTOL(awiny + FLT_TYPE 0.5); broundy = FLT_TYPE FTOL(bwiny + FLT_TYPE 0.5); #if 0 // Someday, may want to convert floats to 16.16 equivalent precision // I didn't find it necessary, but it's the first thing to try if // a case comes up with holes.... PREC_1616(awinx,awinx); PREC_1616(awiny,awiny); PREC_1616(bwinx,bwinx); PREC_1616(bwiny,bwiny); PREC_1616(cwinx,cwinx); PREC_1616(cwiny,cwiny); #endif MCDTRI_PRINT("v1 = %d %d ",INTPR(awinx),INTPR(awiny)); MCDTRI_PRINT("v2 = %d %d ",INTPR(bwinx),INTPR(bwiny)); MCDTRI_PRINT("v3 = %d %d ",INTPR(cwinx),INTPR(cwiny)); fv2x = bwinx - awinx; fv2y = bwiny - awiny; fv3x = cwinx - awinx; fv3y = cwiny - awiny; fv32y= cwiny - bwiny; // counts are total number of scan lines traversed // PERFORMANCE OPTIMIZATION - start divide now for main slope __MCD_FLOAT_BEGIN_DIVIDE(__MCDONE, fv3y, &frecip_main); // integer operations "free" since within fdiv latency ppdev = pRc->ppdev; pdwNext = ppdev->LL_State.pDL->pdwNext; pdwOrig = pdwNext; int_cwiny = FTOL(cwiny); int_bwiny = FTOL(bwiny); int_awiny = FTOL(awiny); count1 = int_bwiny - int_awiny; count2 = int_cwiny - int_bwiny; __MCD_FLOAT_SIMPLE_END_DIVIDE(frecip_main); if ((awiny - int_awiny) == FLT_TYPE 0.0) { // start is on whole y - so bump count to include that scanline // unless identical to b's y if (bwiny != awiny) count1++; } // check for case of adjusted A and real B being on same scan line (flat top) // even though count not 0 // ex. a.y = 79.60, b.y = 80.00 -> a will be rounded to 80.0, so really // this is a flat top triangle. In such case, set count1 = 0. // b will be counted below. Failure to do this results in scanline that // has B being part of top and bottom, so width delta's applied when // hardware steps make for some interesting artifacts (see p. 205 of MCD notes) if (count1 == 1) { if ((bwiny - int_bwiny) == FLT_TYPE 0.0) { // convert to flat top count1 = 0; } } // similarly for almost flat bottom triangles... // If b.y=124.90 and c.y=125.000, we don't want to draw the scan line at // y=125 since any pixels drawn will be outside the triangle, // so if c on exact y and count2=1, set count2=0 if (count2 == 1) { if ((cwiny - int_cwiny) == FLT_TYPE 0.0) { // convert to flat bottom count2 = 0; } } // main slope - based on precise vertices //USING MACROS TO OVERLAP DIVIDE WITH INTEGER OPERATIONS // frecip_main = FLT_TYPE 1.0/fv3y; fdx_main = fv3x * frecip_main; PREC_1616(fdx_main,fdx_main); // width at vtx b - based on precise vertices fwidth = fv2x - (fdx_main * fv2y); // make width positive, and set direction flag if (fwidth<0) { fwidth = -fwidth; xflags = RIGHT_TO_LEFT_DIR | EDGE_DISABLE_X; } else { xflags = LEFT_TO_RIGHT_DIR | EDGE_DISABLE_X; } // if triangle has a top section (i.e. not flat top).... if (count1) { fdwidth1 = fwidth / fv2y; PREC_1616(fdwidth1,fdwidth1); if (aroundy < awiny) { // rounding produced y less than original, so step to next scan line // since init width would be negative aroundy += FLT_TYPE 1.0; } // determine distance between actual a and scanline we'll start on fmain_adj = aroundy - awiny; // step width1 and x to scan line where we'll start finitwidth1 = fmain_adj * fdwidth1; fxincrement = fmain_adj * fdx_main; } #ifdef QUAKEEDGE_FIX else { // flat top... if ((bwiny - int_bwiny) == FLT_TYPE 0.0) { // if b on exact scanline, it's part of top, and is counted in count1 above, // unless this is flat top triangle - in that case, bump count2 // also, if identical to C's y, then flat bottom, so count2 should remain 0 if (cwiny != bwiny) count2++; } } #endif // QUAKEEDGE_FIX // if triangle has a bottom section (i.e. not flat bottom).... if (count2) { float mid_adjust; fdwidth2 = fwidth / fv32y; NEG_PREC_1616(fdwidth2,fdwidth2); #ifdef QUAKEEDGE_FIX // badedge.sav fix if ((broundy < bwiny) || ((broundy==bwiny) && count1)) // step to next if b.y on exact scanline, unless flat top triangle #else if (broundy < bwiny) #endif { // rounding produced y less than original, so step to next scan line mid_adjust = (broundy + (float)1.0) - bwiny; } else { // rounding produced y greater than original (i.e on scan below actual start vertex) mid_adjust = broundy - bwiny; } finitwidth2 = fwidth - (fdwidth2 * mid_adjust); // if flat top, start x/y adjustments weren't made above if (!count1) { if (aroundy < awiny) { // rounding produced y less than original, so step to next scan line aroundy += FLT_TYPE 1.0; } // determine distance between actual a and scanline we'll start on fmain_adj = aroundy - awiny; // step x to scan line where we'll start fxincrement = fmain_adj * fdx_main; } } #ifdef QUAKEEDGE_FIX // badedge2.sav fix else { // flat bottom - if bottom is on exact scanline, don't draw that last scanline // this will enforce GL restriction that bottom scanlines not drawn for polys // (special case for this setup code for case of bottom of poly being on exact y value) if ((bwiny - int_bwiny) == FLT_TYPE 0.0) { if ((cwiny == bwiny) && count1) count1--; } } #endif // badedge2.sav fix // if triangle not a horizontal line (i.e. it traverses at least 1 scan line).... if (count1 || count2) { *(pdwNext+1) = (FTOL((awinx + fxincrement)*FLOAT_TO_1616) + FIXED_X_ROUND_FACTOR) | xflags; // subtracting special offset added to y to make visual match MSFT software *(pdwNext+2) = (DWORD)( (FTOL(aroundy)-MCD_CONFORM_ADJUST) << 16 ) | EDGE_DISABLE_Y; MCDTRI_PRINT(" x, y output = %x %x, yoffset=%x",*(pdwNext+1),*(pdwNext+2),pRc->yOffset); *(pdwNext+6) = FTOL(fdx_main*FLOAT_TO_1616); // if triangle has a bottom section, decrement number of scans in top so middle // scanline is first scanline of bottom section, and has length = finitwidth2 if (!count2) { MCDTRI_PRINT(" FLATBOTTOM"); *(pdwNext+8) = ONE + FTOL(finitwidth1*FLOAT_TO_1616); *(pdwNext+10)= FTOL(fdwidth1*FLOAT_TO_1616); #ifdef FASTER_RECIP_ORTHO __MCD_FLOAT_BEGIN_DIVIDE(__MCDONE, fwidth, &frecip_ortho); #endif *(pdwNext+7) = count1-1; *(pdwNext+9) = 0; *(pdwNext+11)= 0; } else if (!count1) { MCDTRI_PRINT(" FLATTOP"); *(pdwNext+8) = ONE + FTOL(finitwidth2*FLOAT_TO_1616); *(pdwNext+10) = FTOL(FLT_TYPE -1.0*fdwidth2*FLOAT_TO_1616); #ifdef FASTER_RECIP_ORTHO __MCD_FLOAT_BEGIN_DIVIDE(__MCDONE, fwidth, &frecip_ortho); #endif *(pdwNext+7) = count2-1; *(pdwNext+9) = 0; *(pdwNext+11)= 0; } else { MCDTRI_PRINT(" GENERAL"); // sub 1 from count1, since hw adds 1 to account for first scan line *(pdwNext+8) = ONE + FTOL(finitwidth1*FLOAT_TO_1616); *(pdwNext+9) = ONE + FTOL(finitwidth2*FLOAT_TO_1616); *(pdwNext+10)= FTOL(fdwidth1*FLOAT_TO_1616); *(pdwNext+11)= FTOL(FLT_TYPE -1.0*fdwidth2*FLOAT_TO_1616); #ifdef FASTER_RECIP_ORTHO __MCD_FLOAT_BEGIN_DIVIDE(__MCDONE, fwidth, &frecip_ortho); #endif *(pdwNext+7) = (count1-1) + (count2 << 16); } MCDTRI_PRINT("dxm =%d w1=%d w2=%d dw1=%d dw2=%d", INTPR(fdx_main),INTPR(finitwidth1),INTPR(finitwidth2),INTPR(fdwidth1),INTPR(fdwidth2)); MCDTRI_PRINT(" %x %x %x %x %x %x",*(pdwNext+6),*(pdwNext+7),*(pdwNext+8),*(pdwNext+9),*(pdwNext+10),*(pdwNext+11)); pdwColor = pdwNext+3; pdwNext += 12; } else { // nothing to draw, triangle doesn't traverse any scan lines MCDTRI_PRINT(" Early return - flat top and bottom"); return; } // various integer ops to overlap with fdiv dwOpcode = pRc->dwPolyOpcode; pDest = ppdev->LL_State.pRegs + HOST_3D_DATA_PORT; pdwStart = ppdev->LL_State.pDL->pdwStartOutPtr; // do inside divide - won't slow us down unless 3D engine indeed not idle USB_TIMEOUT_FIX(ppdev) // compute 1/width, used in rgbzuv computations that follow #ifdef FASTER_RECIP_ORTHO __MCD_FLOAT_SIMPLE_END_DIVIDE(frecip_ortho); #else frecip_ortho = FLT_TYPE 1.0/fwidth; #endif PREC_1616(frecip_ortho,frecip_ortho); if (pRc->privateEnables & __MCDENABLE_SMOOTH) { // Calculate and set the color gradients, using gradients to adjust start color v1red = a->colors[0].r * pRc->rScale; v1grn = a->colors[0].g * pRc->gScale; v1blu = a->colors[0].b * pRc->bScale; ftemp = ((c->colors[0].r * pRc->rScale) - v1red) * frecip_main; *(pdwNext+0) = FTOL(ftemp); *(pdwNext+3) = FTOL(((b->colors[0].r * pRc->rScale) - (v1red + (ftemp * fv2y)) ) * frecip_ortho); // adjust v1red for start vertex's variance from vertex a *(pdwColor) = FTOL(v1red + (ftemp * fmain_adj)); ftemp = ((c->colors[0].g * pRc->gScale) - v1grn) * frecip_main; *(pdwNext+1) = FTOL(ftemp); *(pdwNext+4) = FTOL(((b->colors[0].g * pRc->gScale) - (v1grn + (ftemp * fv2y)) ) * frecip_ortho); // adjust v1grn for start vertex's variance from vertex a *(pdwColor+1) = FTOL(v1grn + (ftemp * fmain_adj)); ftemp = ((c->colors[0].b * pRc->bScale) - v1blu) * frecip_main; *(pdwNext+2) = FTOL(ftemp); *(pdwNext+5) = FTOL(((b->colors[0].b * pRc->bScale) - (v1blu + (ftemp * fv2y)) ) * frecip_ortho); // adjust v1blu for start vertex's variance from vertex a *(pdwColor+2) = FTOL(v1blu + (ftemp * fmain_adj)); MCDTRI_PRINT(" SHADE rgbout = %x %x %x",*(pdwColor),*(pdwColor+1),*(pdwColor+2)); MCDTRI_PRINT(" CSLOPES: %x %x %x %x %x %x",*pdwNext,*(pdwNext+1),*(pdwNext+2),*(pdwNext+3),*(pdwNext+4),*(pdwNext+5)); pdwNext += 6; } else { MCDCOLOR *pColor = &pRc->pvProvoking->colors[0]; // flat shaded - no adjustment of original colors needed *(pdwColor) = FTOL(pColor->r * pRc->rScale); *(pdwColor+1) = FTOL(pColor->g * pRc->gScale); *(pdwColor+2) = FTOL(pColor->b * pRc->bScale); MCDTRI_PRINT(" FLAT rgbout = %x %x %x",*(pdwColor),*(pdwColor+1),*(pdwColor+2)); } if( pRc->privateEnables & __MCDENABLE_Z) { // "NICE" Polys for Alpha blended case - see comments above in // geometry slopes calculations // Calculate and set the Z value base and gradient using floats float fdz_main = (c->windowCoord.z - a->windowCoord.z) * frecip_main; // compute adjustment - if negative z would result, set adjust so final = 0 fadjust = fdz_main * fmain_adj; if ((a->windowCoord.z + fadjust) < (float)0.0) fadjust = - a->windowCoord.z; if (pRc->MCDState.enables & MCD_POLYGON_OFFSET_FILL_ENABLE) { // APPLY Z OFFSET, and adjust for moved start vertex MCDFLOAT zOffset; if (fdz_main > 0) { zOffset = (fdz_main * pRc->MCDState.zOffsetFactor) + pRc->MCDState.zOffsetUnits; } else { zOffset = ((float)-1.0 * fdz_main * pRc->MCDState.zOffsetFactor) + pRc->MCDState.zOffsetUnits; } *(pdwNext+0) = FTOL((a->windowCoord.z + fadjust + zOffset) * FLT_TYPE 65536.0); } else { // NO Z OFFSET - just adjust for moved start vertex *(pdwNext+0) = FTOL((a->windowCoord.z + fadjust) * FLT_TYPE 65536.0); } *(pdwNext+1) = FTOL(fdz_main * FLT_TYPE 65536.0); *(pdwNext+2) = FTOL((b->windowCoord.z - a->windowCoord.z - (fdz_main * fv2y)) * FLT_TYPE 65536.0 * frecip_ortho); MCDTRI_PRINT(" Z: %x %x %x",*pdwNext,*(pdwNext+1),*(pdwNext+2)); pdwNext += 3; } if (pRc->privateEnables & __MCDENABLE_TEXTURE) { if ( (pRc->privateEnables & __MCDENABLE_PERSPECTIVE) && !linear_ok ) { TEXTURE_VERTEX vmin, vmid, vmax; QUADRATIC main, mid; TEXTURE_VERTEX i,imain,midmain,j,jmain; float del_u_i, del_v_i; float um,vm; float a1, a2, du_ortho_add; float b1, b2, dv_ortho_add; float sq, recip; float delta_sq, inv_sumw; float u1, v1; float frecip_del_x_mid = frecip_ortho; int tempi; vmin.u = a->texCoord.x * pRc->texture_width; vmin.v = a->texCoord.y * pRc->texture_height; vmin.w = a->windowCoord.w; vmid.x = fv2x; vmid.y = fv2y; vmid.u = b->texCoord.x * pRc->texture_width; vmid.v = b->texCoord.y * pRc->texture_height; vmid.w = b->windowCoord.w; vmax.x = fv3x; vmax.y = fv3y; vmax.u = c->texCoord.x * pRc->texture_width; vmax.v = c->texCoord.y * pRc->texture_height; vmax.w = c->windowCoord.w; // solve quadratic equation for main slope - we'll need exact u values // along main, and the a1/b1, a2/b2 terms computed are used to compute // du/v_main, d2u/v_main delta_sq = frecip_main * frecip_main; inv_sumw = (float)1.0/(vmin.w + vmax.w); u1 = (vmin.u*vmin.w + vmax.u*vmax.w) * inv_sumw; v1 = (vmin.v*vmin.w + vmax.v*vmax.w) * inv_sumw; main.a1 = (-3*vmin.u + 4*u1 - vmax.u) * frecip_main; main.a2 = 2*(vmin.u - 2*u1 + vmax.u) * delta_sq; main.b1 = (-3*vmin.v + 4*v1 - vmax.v) * frecip_main; main.b2 = 2*(vmin.v - 2*v1 + vmax.v) * delta_sq; i.y = (float)0.5 * vmid.y; recip = (float)1.0 / (vmin.w + vmid.w); i.u = ((vmin.u * vmin.w) + (vmid.u * vmid.w)) * recip; i.v = ((vmin.v * vmin.w) + (vmid.v * vmid.w)) * recip; sq = i.y * i.y; imain.u = main.a2*sq + main.a1*i.y + vmin.u; imain.v = main.b2*sq + main.b1*i.y + vmin.v; // vmid coordinates given, just need midmain sq = vmid.y * vmid.y; midmain.u = main.a2*sq + main.a1*vmid.y + vmin.u; midmain.v = main.b2*sq + main.b1*vmid.y + vmin.v; // j and jmain j.y = (float)0.5 * (vmax.y + vmid.y); recip = (float)1.0 / (vmid.w + vmax.w); j.u = ((vmid.u * vmid.w) + (vmax.u * vmax.w)) * recip; j.v = ((vmid.v * vmid.w) + (vmax.v * vmax.w)) * recip; sq = j.y * j.y; jmain.u = main.a2*sq + main.a1*j.y + vmin.u; jmain.v = main.b2*sq + main.b1*j.y + vmin.v; // compute intermediate parameters needed to calculate a1 del_u_i = i.u - imain.u; del_v_i = i.v - imain.v; um = j.u - jmain.u - del_u_i; vm = j.v - jmain.v - del_v_i; frecip_del_x_mid *= (float)2.0; a1 = 2*del_u_i - (float)0.5*(vmid.u - midmain.u); a1 += (vmid.y*frecip_main)*um; a1 *= frecip_del_x_mid; a2 = frecip_del_x_mid*(del_u_i*frecip_del_x_mid - a1); du_ortho_add = 2*um*frecip_del_x_mid*frecip_main; b1 = 2*del_v_i - (float)0.5*(vmid.v - midmain.v); b1 += (vmid.y*frecip_main)*vm; b1 *= frecip_del_x_mid; b2 = frecip_del_x_mid*(del_v_i*frecip_del_x_mid - b1); dv_ortho_add = 2*vm*frecip_del_x_mid*frecip_main; // rewind a1 from i scanline to top of triangle a1 -= (i.y) * du_ortho_add; b1 -= (i.y) * dv_ortho_add; // convert to forward difference terms a1 += a2; b1 += b2; a2 = 2 * a2; b2 = 2 * b2; // compute adjustment for v start - if negative would result -> no problem fadjust = ((main.b1 + main.b2) * fmain_adj) + pRc->texture_bias; *(pdwNext+0) = fix_ieee(vmin.v + fadjust) & 0x1ffffff; // v // likewise for u fadjust = ((main.a1 + main.a2) * fmain_adj) + pRc->texture_bias; *(pdwNext+1) = fix_ieee(vmin.u + fadjust) & 0x1ffffff; // u *(pdwNext+2) = fix_ieee(main.b1 + main.b2); // dv_main *(pdwNext+3) = fix_ieee(main.a1 + main.a2); // du_main *(pdwNext+4) = fix_ieee(b1); // dv_ortho *(pdwNext+5) = fix_ieee(a1); // du_ortho #if DRIVER_5465 *(pdwNext+6) = fix824_ieee(2 * main.b2); // d2v_main *(pdwNext+7) = fix824_ieee(2 * main.a2); // d2u_main *(pdwNext+8) = fix824_ieee(b2); // d2v_ortho *(pdwNext+9) = fix824_ieee(a2); // d2u_ortho *(pdwNext+10)= fix824_ieee(dv_ortho_add); // dv_ortho_add *(pdwNext+11)= fix824_ieee(du_ortho_add); // du_ortho_add #else // DRIVER_5465 // before 5465, only 16 bit fraction in second order terms *(pdwNext+6) = fix_ieee(2 * main.b2); // d2v_main *(pdwNext+7) = fix_ieee(2 * main.a2); // d2u_main *(pdwNext+8) = fix_ieee(b2); // d2v_ortho *(pdwNext+9) = fix_ieee(a2); // d2u_ortho *(pdwNext+10)= fix_ieee(dv_ortho_add); // dv_ortho_add *(pdwNext+11)= fix_ieee(du_ortho_add); // du_ortho_add #endif // DRIVER_5465 dwOpcode += 6; // 6 parms assumed (linear), add 6 since 12 total pdwNext += 12; } else // Linear texture mapping parametarization // { float v1_u, v1_v; float du_main, dv_main; dwOpcode &= ~LL_PERSPECTIVE; // turn persp bit off v1_v = a->texCoord.y * pRc->texture_height; v1_u = a->texCoord.x * pRc->texture_width; dv_main = ((c->texCoord.y * pRc->texture_height)- v1_v) * frecip_main; du_main = ((c->texCoord.x * pRc->texture_width) - v1_u) * frecip_main; // compute adjustment for v start - if negative would result -> no problem fadjust = (dv_main * fmain_adj) + pRc->texture_bias; *(pdwNext+0) = fix_ieee(v1_v + fadjust) & 0x1ffffff; // v // likewise for u... fadjust = (du_main * fmain_adj) + pRc->texture_bias; *(pdwNext+1) = fix_ieee(v1_u + fadjust) & 0x1ffffff; // u *(pdwNext+2) = fix_ieee(dv_main); *(pdwNext+3) = fix_ieee(du_main); // dv_ortho, du_ortho *(pdwNext+4) = fix_ieee(((b->texCoord.y * pRc->texture_height) - (v1_v + (dv_main * count1))) * frecip_ortho); *(pdwNext+5) = fix_ieee(((b->texCoord.x * pRc->texture_width) - (v1_u + (du_main * count1))) * frecip_ortho); MCDTRI_PRINT(" LINTEXT: %x %x %x %x %x %x",*pdwNext,*(pdwNext+1),*(pdwNext+2),*(pdwNext+3),*(pdwNext+4),*(pdwNext+5)); pdwNext += 6; } } if (pRc->privateEnables & (__MCDENABLE_BLEND|__MCDENABLE_FOG)) { float v1alp; // if CONST alpha blend, don't change alpha regs if (dwOpcode & ALPHA) { if (pRc->privateEnables & __MCDENABLE_BLEND) { if (pRc->privateEnables & __MCDENABLE_SMOOTH) { // recall that if both blending and fog active, all prims punted back to software v1alp = a->colors[0].a * pRc->aScale; ftemp = ((c->colors[0].a * pRc->aScale) - v1alp) * frecip_main; // load start alpha - adjusting for movement of start vertex from original *pdwNext = FTOL(v1alp + (ftemp * fmain_adj)); // adjustment could result in negative alpha - set to 0 if so if (*pdwNext & 0x80000000) *pdwNext = 0; *(pdwNext+1) = FTOL(ftemp); *(pdwNext+2) = FTOL(((b->colors[0].a * pRc->aScale) - (v1alp + (ftemp * count1)) ) * frecip_ortho); } else { v1alp = pRc->pvProvoking->colors[0].a * pRc->aScale; // alpha constant across triangle, so no adjustment to start *(pdwNext+0) = FTOL(v1alp) & 0x00ffff00;// bits 31->24 and 7->0 reserved *(pdwNext+1) = 0; *(pdwNext+2) = 0; } } else { // FOG... v1alp = a->fog * FLT_TYPE 16777215.0; // convert from 0->1.0 val to 0->ff.ffff val ftemp = ((c->fog * FLT_TYPE 16777215.0) - v1alp) * frecip_main; // load start alpha - adjusting for movement of start vertex from original *pdwNext = FTOL(v1alp + (ftemp * fmain_adj)); // adjustment could result in negative alpha - set to 0 if so if (*pdwNext & 0x80000000) *pdwNext = 0; *(pdwNext+1) = FTOL(ftemp); *(pdwNext+2) = FTOL(((b->fog * FLT_TYPE 16777215.0) - (v1alp + (ftemp * count1)) ) * frecip_ortho); } *(pdwNext+0) &= 0x00ffff00;// bits 31->24 and 7->0 reserved *(pdwNext+1) &= 0xffffff00;// bits 7->0 reserved *(pdwNext+2) &= 0xffffff00;// bits 7->0 reserved pdwNext += 3; } } *pdwOrig = dwOpcode; // output queued data here.... pSrc = pdwStart; while (pSrc != pdwNext) { int len = (*pSrc & 0x3F) + 1; while( len-- ) *pDest = *pSrc++; } ppdev->LL_State.pDL->pdwNext = ppdev->LL_State.pDL->pdwStartOutPtr = pdwStart; }