| /* |
| * Mesa 3-D graphics library |
| * |
| * Copyright (C) 1999-2007 Brian Paul All Rights Reserved. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included |
| * in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR |
| * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
| * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
| * OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| |
| /* |
| * Antialiased Triangle Rasterizer Template |
| * |
| * This file is #include'd to generate custom AA triangle rasterizers. |
| * NOTE: this code hasn't been optimized yet. That'll come after it |
| * works correctly. |
| * |
| * The following macros may be defined to indicate what auxillary information |
| * must be copmuted across the triangle: |
| * DO_Z - if defined, compute Z values |
| * DO_ATTRIBS - if defined, compute texcoords, varying, etc. |
| */ |
| |
| /*void triangle( struct gl_context *ctx, GLuint v0, GLuint v1, GLuint v2, GLuint pv )*/ |
| { |
| const SWcontext *swrast = SWRAST_CONTEXT(ctx); |
| const GLfloat *p0 = v0->attrib[VARYING_SLOT_POS]; |
| const GLfloat *p1 = v1->attrib[VARYING_SLOT_POS]; |
| const GLfloat *p2 = v2->attrib[VARYING_SLOT_POS]; |
| const SWvertex *vMin, *vMid, *vMax; |
| GLint iyMin, iyMax; |
| GLfloat yMin, yMax; |
| GLboolean ltor; |
| GLfloat majDx, majDy; /* major (i.e. long) edge dx and dy */ |
| |
| SWspan span; |
| |
| #ifdef DO_Z |
| GLfloat zPlane[4]; |
| #endif |
| GLfloat rPlane[4], gPlane[4], bPlane[4], aPlane[4]; |
| #if defined(DO_ATTRIBS) |
| GLfloat attrPlane[VARYING_SLOT_MAX][4][4]; |
| GLfloat wPlane[4]; /* win[3] */ |
| #endif |
| GLfloat bf = SWRAST_CONTEXT(ctx)->_BackfaceCullSign; |
| |
| (void) swrast; |
| |
| INIT_SPAN(span, GL_POLYGON); |
| span.arrayMask = SPAN_COVERAGE; |
| |
| /* determine bottom to top order of vertices */ |
| { |
| GLfloat y0 = v0->attrib[VARYING_SLOT_POS][1]; |
| GLfloat y1 = v1->attrib[VARYING_SLOT_POS][1]; |
| GLfloat y2 = v2->attrib[VARYING_SLOT_POS][1]; |
| if (y0 <= y1) { |
| if (y1 <= y2) { |
| vMin = v0; vMid = v1; vMax = v2; /* y0<=y1<=y2 */ |
| } |
| else if (y2 <= y0) { |
| vMin = v2; vMid = v0; vMax = v1; /* y2<=y0<=y1 */ |
| } |
| else { |
| vMin = v0; vMid = v2; vMax = v1; bf = -bf; /* y0<=y2<=y1 */ |
| } |
| } |
| else { |
| if (y0 <= y2) { |
| vMin = v1; vMid = v0; vMax = v2; bf = -bf; /* y1<=y0<=y2 */ |
| } |
| else if (y2 <= y1) { |
| vMin = v2; vMid = v1; vMax = v0; bf = -bf; /* y2<=y1<=y0 */ |
| } |
| else { |
| vMin = v1; vMid = v2; vMax = v0; /* y1<=y2<=y0 */ |
| } |
| } |
| } |
| |
| majDx = vMax->attrib[VARYING_SLOT_POS][0] - vMin->attrib[VARYING_SLOT_POS][0]; |
| majDy = vMax->attrib[VARYING_SLOT_POS][1] - vMin->attrib[VARYING_SLOT_POS][1]; |
| |
| /* front/back-face determination and cullling */ |
| { |
| const GLfloat botDx = vMid->attrib[VARYING_SLOT_POS][0] - vMin->attrib[VARYING_SLOT_POS][0]; |
| const GLfloat botDy = vMid->attrib[VARYING_SLOT_POS][1] - vMin->attrib[VARYING_SLOT_POS][1]; |
| const GLfloat area = majDx * botDy - botDx * majDy; |
| /* Do backface culling */ |
| if (area * bf < 0 || area == 0 || IS_INF_OR_NAN(area)) |
| return; |
| ltor = (GLboolean) (area < 0.0F); |
| |
| span.facing = area * swrast->_BackfaceSign > 0.0F; |
| } |
| |
| /* Plane equation setup: |
| * We evaluate plane equations at window (x,y) coordinates in order |
| * to compute color, Z, fog, texcoords, etc. This isn't terribly |
| * efficient but it's easy and reliable. |
| */ |
| #ifdef DO_Z |
| compute_plane(p0, p1, p2, p0[2], p1[2], p2[2], zPlane); |
| span.arrayMask |= SPAN_Z; |
| #endif |
| if (ctx->Light.ShadeModel == GL_SMOOTH) { |
| compute_plane(p0, p1, p2, v0->color[RCOMP], v1->color[RCOMP], v2->color[RCOMP], rPlane); |
| compute_plane(p0, p1, p2, v0->color[GCOMP], v1->color[GCOMP], v2->color[GCOMP], gPlane); |
| compute_plane(p0, p1, p2, v0->color[BCOMP], v1->color[BCOMP], v2->color[BCOMP], bPlane); |
| compute_plane(p0, p1, p2, v0->color[ACOMP], v1->color[ACOMP], v2->color[ACOMP], aPlane); |
| } |
| else { |
| constant_plane(v2->color[RCOMP], rPlane); |
| constant_plane(v2->color[GCOMP], gPlane); |
| constant_plane(v2->color[BCOMP], bPlane); |
| constant_plane(v2->color[ACOMP], aPlane); |
| } |
| span.arrayMask |= SPAN_RGBA; |
| #if defined(DO_ATTRIBS) |
| { |
| const GLfloat invW0 = v0->attrib[VARYING_SLOT_POS][3]; |
| const GLfloat invW1 = v1->attrib[VARYING_SLOT_POS][3]; |
| const GLfloat invW2 = v2->attrib[VARYING_SLOT_POS][3]; |
| compute_plane(p0, p1, p2, invW0, invW1, invW2, wPlane); |
| span.attrStepX[VARYING_SLOT_POS][3] = plane_dx(wPlane); |
| span.attrStepY[VARYING_SLOT_POS][3] = plane_dy(wPlane); |
| ATTRIB_LOOP_BEGIN |
| GLuint c; |
| if (swrast->_InterpMode[attr] == GL_FLAT) { |
| for (c = 0; c < 4; c++) { |
| constant_plane(v2->attrib[attr][c] * invW2, attrPlane[attr][c]); |
| } |
| } |
| else { |
| for (c = 0; c < 4; c++) { |
| const GLfloat a0 = v0->attrib[attr][c] * invW0; |
| const GLfloat a1 = v1->attrib[attr][c] * invW1; |
| const GLfloat a2 = v2->attrib[attr][c] * invW2; |
| compute_plane(p0, p1, p2, a0, a1, a2, attrPlane[attr][c]); |
| } |
| } |
| for (c = 0; c < 4; c++) { |
| span.attrStepX[attr][c] = plane_dx(attrPlane[attr][c]); |
| span.attrStepY[attr][c] = plane_dy(attrPlane[attr][c]); |
| } |
| ATTRIB_LOOP_END |
| } |
| #endif |
| |
| /* Begin bottom-to-top scan over the triangle. |
| * The long edge will either be on the left or right side of the |
| * triangle. We always scan from the long edge toward the shorter |
| * edges, stopping when we find that coverage = 0. If the long edge |
| * is on the left we scan left-to-right. Else, we scan right-to-left. |
| */ |
| yMin = vMin->attrib[VARYING_SLOT_POS][1]; |
| yMax = vMax->attrib[VARYING_SLOT_POS][1]; |
| iyMin = (GLint) yMin; |
| iyMax = (GLint) yMax + 1; |
| |
| if (ltor) { |
| /* scan left to right */ |
| const GLfloat *pMin = vMin->attrib[VARYING_SLOT_POS]; |
| const GLfloat *pMid = vMid->attrib[VARYING_SLOT_POS]; |
| const GLfloat *pMax = vMax->attrib[VARYING_SLOT_POS]; |
| const GLfloat dxdy = majDx / majDy; |
| const GLfloat xAdj = dxdy < 0.0F ? -dxdy : 0.0F; |
| GLint iy; |
| #ifdef _OPENMP |
| #pragma omp parallel for schedule(dynamic) private(iy) firstprivate(span) |
| #endif |
| for (iy = iyMin; iy < iyMax; iy++) { |
| GLfloat x = pMin[0] - (yMin - iy) * dxdy; |
| GLint ix, startX = (GLint) (x - xAdj); |
| GLuint count; |
| GLfloat coverage = 0.0F; |
| |
| #ifdef _OPENMP |
| /* each thread needs to use a different (global) SpanArrays variable */ |
| span.array = SWRAST_CONTEXT(ctx)->SpanArrays + omp_get_thread_num(); |
| #endif |
| /* skip over fragments with zero coverage */ |
| while (startX < SWRAST_MAX_WIDTH) { |
| coverage = compute_coveragef(pMin, pMid, pMax, startX, iy); |
| if (coverage > 0.0F) |
| break; |
| startX++; |
| } |
| |
| /* enter interior of triangle */ |
| ix = startX; |
| |
| #if defined(DO_ATTRIBS) |
| /* compute attributes at left-most fragment */ |
| span.attrStart[VARYING_SLOT_POS][3] = solve_plane(ix + 0.5F, iy + 0.5F, wPlane); |
| ATTRIB_LOOP_BEGIN |
| GLuint c; |
| for (c = 0; c < 4; c++) { |
| span.attrStart[attr][c] = solve_plane(ix + 0.5F, iy + 0.5F, attrPlane[attr][c]); |
| } |
| ATTRIB_LOOP_END |
| #endif |
| |
| count = 0; |
| while (coverage > 0.0F) { |
| /* (cx,cy) = center of fragment */ |
| const GLfloat cx = ix + 0.5F, cy = iy + 0.5F; |
| SWspanarrays *array = span.array; |
| array->coverage[count] = coverage; |
| #ifdef DO_Z |
| array->z[count] = (GLuint) solve_plane(cx, cy, zPlane); |
| #endif |
| array->rgba[count][RCOMP] = solve_plane_chan(cx, cy, rPlane); |
| array->rgba[count][GCOMP] = solve_plane_chan(cx, cy, gPlane); |
| array->rgba[count][BCOMP] = solve_plane_chan(cx, cy, bPlane); |
| array->rgba[count][ACOMP] = solve_plane_chan(cx, cy, aPlane); |
| ix++; |
| count++; |
| coverage = compute_coveragef(pMin, pMid, pMax, ix, iy); |
| } |
| |
| if (ix > startX) { |
| span.x = startX; |
| span.y = iy; |
| span.end = (GLuint) ix - (GLuint) startX; |
| _swrast_write_rgba_span(ctx, &span); |
| } |
| } |
| } |
| else { |
| /* scan right to left */ |
| const GLfloat *pMin = vMin->attrib[VARYING_SLOT_POS]; |
| const GLfloat *pMid = vMid->attrib[VARYING_SLOT_POS]; |
| const GLfloat *pMax = vMax->attrib[VARYING_SLOT_POS]; |
| const GLfloat dxdy = majDx / majDy; |
| const GLfloat xAdj = dxdy > 0 ? dxdy : 0.0F; |
| GLint iy; |
| #ifdef _OPENMP |
| #pragma omp parallel for schedule(dynamic) private(iy) firstprivate(span) |
| #endif |
| for (iy = iyMin; iy < iyMax; iy++) { |
| GLfloat x = pMin[0] - (yMin - iy) * dxdy; |
| GLint ix, left, startX = (GLint) (x + xAdj); |
| GLuint count, n; |
| GLfloat coverage = 0.0F; |
| |
| #ifdef _OPENMP |
| /* each thread needs to use a different (global) SpanArrays variable */ |
| span.array = SWRAST_CONTEXT(ctx)->SpanArrays + omp_get_thread_num(); |
| #endif |
| /* make sure we're not past the window edge */ |
| if (startX >= ctx->DrawBuffer->_Xmax) { |
| startX = ctx->DrawBuffer->_Xmax - 1; |
| } |
| |
| /* skip fragments with zero coverage */ |
| while (startX > 0) { |
| coverage = compute_coveragef(pMin, pMax, pMid, startX, iy); |
| if (coverage > 0.0F) |
| break; |
| startX--; |
| } |
| |
| /* enter interior of triangle */ |
| ix = startX; |
| count = 0; |
| while (coverage > 0.0F) { |
| /* (cx,cy) = center of fragment */ |
| const GLfloat cx = ix + 0.5F, cy = iy + 0.5F; |
| SWspanarrays *array = span.array; |
| assert(ix >= 0); |
| array->coverage[ix] = coverage; |
| #ifdef DO_Z |
| array->z[ix] = (GLuint) solve_plane(cx, cy, zPlane); |
| #endif |
| array->rgba[ix][RCOMP] = solve_plane_chan(cx, cy, rPlane); |
| array->rgba[ix][GCOMP] = solve_plane_chan(cx, cy, gPlane); |
| array->rgba[ix][BCOMP] = solve_plane_chan(cx, cy, bPlane); |
| array->rgba[ix][ACOMP] = solve_plane_chan(cx, cy, aPlane); |
| ix--; |
| count++; |
| coverage = compute_coveragef(pMin, pMax, pMid, ix, iy); |
| } |
| |
| #if defined(DO_ATTRIBS) |
| /* compute attributes at left-most fragment */ |
| span.attrStart[VARYING_SLOT_POS][3] = solve_plane(ix + 1.5F, iy + 0.5F, wPlane); |
| ATTRIB_LOOP_BEGIN |
| GLuint c; |
| for (c = 0; c < 4; c++) { |
| span.attrStart[attr][c] = solve_plane(ix + 1.5F, iy + 0.5F, attrPlane[attr][c]); |
| } |
| ATTRIB_LOOP_END |
| #endif |
| |
| if (startX > ix) { |
| n = (GLuint) startX - (GLuint) ix; |
| |
| left = ix + 1; |
| |
| /* shift all values to the left */ |
| /* XXX this is temporary */ |
| { |
| SWspanarrays *array = span.array; |
| GLint j; |
| for (j = 0; j < (GLint) n; j++) { |
| array->coverage[j] = array->coverage[j + left]; |
| COPY_CHAN4(array->rgba[j], array->rgba[j + left]); |
| #ifdef DO_Z |
| array->z[j] = array->z[j + left]; |
| #endif |
| } |
| } |
| |
| span.x = left; |
| span.y = iy; |
| span.end = n; |
| _swrast_write_rgba_span(ctx, &span); |
| } |
| } |
| } |
| } |
| |
| |
| #undef DO_Z |
| #undef DO_ATTRIBS |
| #undef DO_OCCLUSION_TEST |