/* * 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. */ #include "main/glheader.h" #include "main/macros.h" #include "s_context.h" #include "s_feedback.h" #include "s_points.h" #include "s_span.h" /** * Used to cull points with invalid coords */ #define CULL_INVALID(V) \ do { \ float tmp = (V)->attrib[VARYING_SLOT_POS][0] \ + (V)->attrib[VARYING_SLOT_POS][1]; \ if (IS_INF_OR_NAN(tmp)) \ return; \ } while(0) /** * Get/compute the point size. * The size may come from a vertex shader, or computed with attentuation * or just the glPointSize value. * Must also clamp to user-defined range and implmentation limits. */ static inline GLfloat get_size(const struct gl_context *ctx, const SWvertex *vert, GLboolean smoothed) { GLfloat size; if (ctx->Point._Attenuated || ctx->VertexProgram.PointSizeEnabled) { /* use vertex's point size */ size = vert->pointSize; } else { /* use constant point size */ size = ctx->Point.Size; } /* always clamp to user-specified limits */ size = CLAMP(size, ctx->Point.MinSize, ctx->Point.MaxSize); /* clamp to implementation limits */ if (smoothed) size = CLAMP(size, ctx->Const.MinPointSizeAA, ctx->Const.MaxPointSizeAA); else size = CLAMP(size, ctx->Const.MinPointSize, ctx->Const.MaxPointSize); return size; } /** * Draw a point sprite */ static void sprite_point(struct gl_context *ctx, const SWvertex *vert) { SWcontext *swrast = SWRAST_CONTEXT(ctx); SWspan span; GLfloat size; GLuint tCoords[MAX_TEXTURE_COORD_UNITS + 1]; GLuint numTcoords = 0; GLfloat t0, dtdy; CULL_INVALID(vert); /* z coord */ if (ctx->DrawBuffer->Visual.depthBits <= 16) span.z = FloatToFixed(vert->attrib[VARYING_SLOT_POS][2] + 0.5F); else span.z = (GLuint) (vert->attrib[VARYING_SLOT_POS][2] + 0.5F); span.zStep = 0; size = get_size(ctx, vert, GL_FALSE); /* span init */ INIT_SPAN(span, GL_POINT); span.interpMask = SPAN_Z | SPAN_RGBA; span.facing = swrast->PointLineFacing; span.red = ChanToFixed(vert->color[0]); span.green = ChanToFixed(vert->color[1]); span.blue = ChanToFixed(vert->color[2]); span.alpha = ChanToFixed(vert->color[3]); span.redStep = 0; span.greenStep = 0; span.blueStep = 0; span.alphaStep = 0; /* need these for fragment programs */ span.attrStart[VARYING_SLOT_POS][3] = 1.0F; span.attrStepX[VARYING_SLOT_POS][3] = 0.0F; span.attrStepY[VARYING_SLOT_POS][3] = 0.0F; { GLfloat s, r, dsdx; /* texcoord / pointcoord interpolants */ s = 0.0F; dsdx = 1.0F / size; if (ctx->Point.SpriteOrigin == GL_LOWER_LEFT) { dtdy = 1.0F / size; t0 = 0.5F * dtdy; } else { /* GL_UPPER_LEFT */ dtdy = -1.0F / size; t0 = 1.0F + 0.5F * dtdy; } ATTRIB_LOOP_BEGIN if (attr >= VARYING_SLOT_TEX0 && attr <= VARYING_SLOT_TEX7) { /* a texcoord attribute */ const GLuint u = attr - VARYING_SLOT_TEX0; assert(u < ARRAY_SIZE(ctx->Point.CoordReplace)); if (ctx->Point.CoordReplace[u]) { tCoords[numTcoords++] = attr; if (ctx->Point.SpriteRMode == GL_ZERO) r = 0.0F; else if (ctx->Point.SpriteRMode == GL_S) r = vert->attrib[attr][0]; else /* GL_R */ r = vert->attrib[attr][2]; span.attrStart[attr][0] = s; span.attrStart[attr][1] = 0.0; /* overwritten below */ span.attrStart[attr][2] = r; span.attrStart[attr][3] = 1.0; span.attrStepX[attr][0] = dsdx; span.attrStepX[attr][1] = 0.0; span.attrStepX[attr][2] = 0.0; span.attrStepX[attr][3] = 0.0; span.attrStepY[attr][0] = 0.0; span.attrStepY[attr][1] = dtdy; span.attrStepY[attr][2] = 0.0; span.attrStepY[attr][3] = 0.0; continue; } } else if (attr == VARYING_SLOT_PNTC) { /* GLSL gl_PointCoord.xy (.zw undefined) */ span.attrStart[VARYING_SLOT_PNTC][0] = 0.0; span.attrStart[VARYING_SLOT_PNTC][1] = 0.0; /* t0 set below */ span.attrStepX[VARYING_SLOT_PNTC][0] = dsdx; span.attrStepX[VARYING_SLOT_PNTC][1] = 0.0; span.attrStepY[VARYING_SLOT_PNTC][0] = 0.0; span.attrStepY[VARYING_SLOT_PNTC][1] = dtdy; tCoords[numTcoords++] = VARYING_SLOT_PNTC; continue; } /* use vertex's texcoord/attrib */ COPY_4V(span.attrStart[attr], vert->attrib[attr]); ASSIGN_4V(span.attrStepX[attr], 0, 0, 0, 0); ASSIGN_4V(span.attrStepY[attr], 0, 0, 0, 0); ATTRIB_LOOP_END; } /* compute pos, bounds and render */ { const GLfloat x = vert->attrib[VARYING_SLOT_POS][0]; const GLfloat y = vert->attrib[VARYING_SLOT_POS][1]; GLint iSize = (GLint) (size + 0.5F); GLint xmin, xmax, ymin, ymax, iy; GLint iRadius; GLfloat tcoord = t0; iSize = MAX2(1, iSize); iRadius = iSize / 2; if (iSize & 1) { /* odd size */ xmin = (GLint) (x - iRadius); xmax = (GLint) (x + iRadius); ymin = (GLint) (y - iRadius); ymax = (GLint) (y + iRadius); } else { /* even size */ /* 0.501 factor allows conformance to pass */ xmin = (GLint) (x + 0.501F) - iRadius; xmax = xmin + iSize - 1; ymin = (GLint) (y + 0.501F) - iRadius; ymax = ymin + iSize - 1; } /* render spans */ for (iy = ymin; iy <= ymax; iy++) { GLuint i; /* setup texcoord T for this row */ for (i = 0; i < numTcoords; i++) { span.attrStart[tCoords[i]][1] = tcoord; } /* these might get changed by span clipping */ span.x = xmin; span.y = iy; span.end = xmax - xmin + 1; _swrast_write_rgba_span(ctx, &span); tcoord += dtdy; } } } /** * Draw smooth/antialiased point. RGB or CI mode. */ static void smooth_point(struct gl_context *ctx, const SWvertex *vert) { SWcontext *swrast = SWRAST_CONTEXT(ctx); SWspan span; GLfloat size, alphaAtten; CULL_INVALID(vert); /* z coord */ if (ctx->DrawBuffer->Visual.depthBits <= 16) span.z = FloatToFixed(vert->attrib[VARYING_SLOT_POS][2] + 0.5F); else span.z = (GLuint) (vert->attrib[VARYING_SLOT_POS][2] + 0.5F); span.zStep = 0; size = get_size(ctx, vert, GL_TRUE); /* alpha attenuation / fade factor */ if (ctx->Multisample._Enabled) { if (vert->pointSize >= ctx->Point.Threshold) { alphaAtten = 1.0F; } else { GLfloat dsize = vert->pointSize / ctx->Point.Threshold; alphaAtten = dsize * dsize; } } else { alphaAtten = 1.0; } (void) alphaAtten; /* not used */ /* span init */ INIT_SPAN(span, GL_POINT); span.interpMask = SPAN_Z | SPAN_RGBA; span.arrayMask = SPAN_COVERAGE | SPAN_MASK; span.facing = swrast->PointLineFacing; span.red = ChanToFixed(vert->color[0]); span.green = ChanToFixed(vert->color[1]); span.blue = ChanToFixed(vert->color[2]); span.alpha = ChanToFixed(vert->color[3]); span.redStep = 0; span.greenStep = 0; span.blueStep = 0; span.alphaStep = 0; /* need these for fragment programs */ span.attrStart[VARYING_SLOT_POS][3] = 1.0F; span.attrStepX[VARYING_SLOT_POS][3] = 0.0F; span.attrStepY[VARYING_SLOT_POS][3] = 0.0F; ATTRIB_LOOP_BEGIN COPY_4V(span.attrStart[attr], vert->attrib[attr]); ASSIGN_4V(span.attrStepX[attr], 0, 0, 0, 0); ASSIGN_4V(span.attrStepY[attr], 0, 0, 0, 0); ATTRIB_LOOP_END /* compute pos, bounds and render */ { const GLfloat x = vert->attrib[VARYING_SLOT_POS][0]; const GLfloat y = vert->attrib[VARYING_SLOT_POS][1]; const GLfloat radius = 0.5F * size; const GLfloat rmin = radius - 0.7071F; /* 0.7071 = sqrt(2)/2 */ const GLfloat rmax = radius + 0.7071F; const GLfloat rmin2 = MAX2(0.0F, rmin * rmin); const GLfloat rmax2 = rmax * rmax; const GLfloat cscale = 1.0F / (rmax2 - rmin2); const GLint xmin = (GLint) (x - radius); const GLint xmax = (GLint) (x + radius); const GLint ymin = (GLint) (y - radius); const GLint ymax = (GLint) (y + radius); GLint ix, iy; for (iy = ymin; iy <= ymax; iy++) { /* these might get changed by span clipping */ span.x = xmin; span.y = iy; span.end = xmax - xmin + 1; /* compute coverage for each pixel in span */ for (ix = xmin; ix <= xmax; ix++) { const GLfloat dx = ix - x + 0.5F; const GLfloat dy = iy - y + 0.5F; const GLfloat dist2 = dx * dx + dy * dy; GLfloat coverage; if (dist2 < rmax2) { if (dist2 >= rmin2) { /* compute partial coverage */ coverage = 1.0F - (dist2 - rmin2) * cscale; } else { /* full coverage */ coverage = 1.0F; } span.array->mask[ix - xmin] = 1; } else { /* zero coverage - fragment outside the radius */ coverage = 0.0; span.array->mask[ix - xmin] = 0; } span.array->coverage[ix - xmin] = coverage; } /* render span */ _swrast_write_rgba_span(ctx, &span); } } } /** * Draw large (size >= 1) non-AA point. RGB or CI mode. */ static void large_point(struct gl_context *ctx, const SWvertex *vert) { SWcontext *swrast = SWRAST_CONTEXT(ctx); SWspan span; GLfloat size; CULL_INVALID(vert); /* z coord */ if (ctx->DrawBuffer->Visual.depthBits <= 16) span.z = FloatToFixed(vert->attrib[VARYING_SLOT_POS][2] + 0.5F); else span.z = (GLuint) (vert->attrib[VARYING_SLOT_POS][2] + 0.5F); span.zStep = 0; size = get_size(ctx, vert, GL_FALSE); /* span init */ INIT_SPAN(span, GL_POINT); span.arrayMask = SPAN_XY; span.facing = swrast->PointLineFacing; span.interpMask = SPAN_Z | SPAN_RGBA; span.red = ChanToFixed(vert->color[0]); span.green = ChanToFixed(vert->color[1]); span.blue = ChanToFixed(vert->color[2]); span.alpha = ChanToFixed(vert->color[3]); span.redStep = 0; span.greenStep = 0; span.blueStep = 0; span.alphaStep = 0; /* need these for fragment programs */ span.attrStart[VARYING_SLOT_POS][3] = 1.0F; span.attrStepX[VARYING_SLOT_POS][3] = 0.0F; span.attrStepY[VARYING_SLOT_POS][3] = 0.0F; ATTRIB_LOOP_BEGIN COPY_4V(span.attrStart[attr], vert->attrib[attr]); ASSIGN_4V(span.attrStepX[attr], 0, 0, 0, 0); ASSIGN_4V(span.attrStepY[attr], 0, 0, 0, 0); ATTRIB_LOOP_END /* compute pos, bounds and render */ { const GLfloat x = vert->attrib[VARYING_SLOT_POS][0]; const GLfloat y = vert->attrib[VARYING_SLOT_POS][1]; GLint iSize = (GLint) (size + 0.5F); GLint xmin, xmax, ymin, ymax, ix, iy; GLint iRadius; iSize = MAX2(1, iSize); iRadius = iSize / 2; if (iSize & 1) { /* odd size */ xmin = (GLint) (x - iRadius); xmax = (GLint) (x + iRadius); ymin = (GLint) (y - iRadius); ymax = (GLint) (y + iRadius); } else { /* even size */ /* 0.501 factor allows conformance to pass */ xmin = (GLint) (x + 0.501F) - iRadius; xmax = xmin + iSize - 1; ymin = (GLint) (y + 0.501F) - iRadius; ymax = ymin + iSize - 1; } /* generate fragments */ span.end = 0; for (iy = ymin; iy <= ymax; iy++) { for (ix = xmin; ix <= xmax; ix++) { span.array->x[span.end] = ix; span.array->y[span.end] = iy; span.end++; } } assert(span.end <= SWRAST_MAX_WIDTH); _swrast_write_rgba_span(ctx, &span); } } /** * Draw size=1, single-pixel point */ static void pixel_point(struct gl_context *ctx, const SWvertex *vert) { SWcontext *swrast = SWRAST_CONTEXT(ctx); /* * Note that unlike the other functions, we put single-pixel points * into a special span array in order to render as many points as * possible with a single _swrast_write_rgba_span() call. */ SWspan *span = &(swrast->PointSpan); GLuint count; CULL_INVALID(vert); /* Span init */ span->interpMask = 0; span->arrayMask = SPAN_XY | SPAN_Z; span->arrayMask |= SPAN_RGBA; /*span->arrayMask |= SPAN_LAMBDA;*/ span->arrayAttribs = swrast->_ActiveAttribMask; /* we'll produce these vals */ /* need these for fragment programs */ span->attrStart[VARYING_SLOT_POS][3] = 1.0F; span->attrStepX[VARYING_SLOT_POS][3] = 0.0F; span->attrStepY[VARYING_SLOT_POS][3] = 0.0F; /* check if we need to flush */ if (span->end >= SWRAST_MAX_WIDTH || (swrast->_RasterMask & (BLEND_BIT | LOGIC_OP_BIT | MASKING_BIT)) || span->facing != swrast->PointLineFacing) { if (span->end > 0) { _swrast_write_rgba_span(ctx, span); span->end = 0; } } count = span->end; span->facing = swrast->PointLineFacing; /* fragment attributes */ span->array->rgba[count][RCOMP] = vert->color[0]; span->array->rgba[count][GCOMP] = vert->color[1]; span->array->rgba[count][BCOMP] = vert->color[2]; span->array->rgba[count][ACOMP] = vert->color[3]; ATTRIB_LOOP_BEGIN COPY_4V(span->array->attribs[attr][count], vert->attrib[attr]); ATTRIB_LOOP_END /* fragment position */ span->array->x[count] = (GLint) vert->attrib[VARYING_SLOT_POS][0]; span->array->y[count] = (GLint) vert->attrib[VARYING_SLOT_POS][1]; span->array->z[count] = (GLint) (vert->attrib[VARYING_SLOT_POS][2] + 0.5F); span->end = count + 1; assert(span->end <= SWRAST_MAX_WIDTH); } /** * Add specular color to primary color, draw point, restore original * primary color. */ void _swrast_add_spec_terms_point(struct gl_context *ctx, const SWvertex *v0) { SWvertex *ncv0 = (SWvertex *) v0; /* cast away const */ GLfloat rSum, gSum, bSum; GLchan cSave[4]; /* save */ COPY_CHAN4(cSave, ncv0->color); /* sum */ rSum = CHAN_TO_FLOAT(ncv0->color[0]) + ncv0->attrib[VARYING_SLOT_COL1][0]; gSum = CHAN_TO_FLOAT(ncv0->color[1]) + ncv0->attrib[VARYING_SLOT_COL1][1]; bSum = CHAN_TO_FLOAT(ncv0->color[2]) + ncv0->attrib[VARYING_SLOT_COL1][2]; UNCLAMPED_FLOAT_TO_CHAN(ncv0->color[0], rSum); UNCLAMPED_FLOAT_TO_CHAN(ncv0->color[1], gSum); UNCLAMPED_FLOAT_TO_CHAN(ncv0->color[2], bSum); /* draw */ SWRAST_CONTEXT(ctx)->SpecPoint(ctx, ncv0); /* restore */ COPY_CHAN4(ncv0->color, cSave); } /** * Examine current state to determine which point drawing function to use. */ void _swrast_choose_point(struct gl_context *ctx) { SWcontext *swrast = SWRAST_CONTEXT(ctx); const GLfloat size = CLAMP(ctx->Point.Size, ctx->Point.MinSize, ctx->Point.MaxSize); if (ctx->RenderMode == GL_RENDER) { if (ctx->Point.PointSprite) { swrast->Point = sprite_point; } else if (ctx->Point.SmoothFlag) { swrast->Point = smooth_point; } else if (size > 1.0F || ctx->Point._Attenuated || ctx->VertexProgram.PointSizeEnabled) { swrast->Point = large_point; } else { swrast->Point = pixel_point; } } else if (ctx->RenderMode == GL_FEEDBACK) { swrast->Point = _swrast_feedback_point; } else { /* GL_SELECT mode */ swrast->Point = _swrast_select_point; } }