| /* |
| * 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/framebuffer.h" |
| #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 < MAX_TEXTURE_COORD_UNITS); |
| if (ctx->Point.CoordReplace & (1u << 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 (_mesa_is_multisample_enabled(ctx)) { |
| 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; |
| } |
| } |