s_aatriangle.c revision 10f30eb43835c57c00783390a02d72daf4f78e26
1/* $Id: s_aatriangle.c,v 1.21 2001/12/17 04:54:35 brianp Exp $ */
2
3/*
4 * Mesa 3-D graphics library
5 * Version:  4.0.1
6 *
7 * Copyright (C) 1999-2001  Brian Paul   All Rights Reserved.
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
22 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
23 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 */
26
27
28/*
29 * Antialiased Triangle rasterizers
30 */
31
32
33#include "mem.h"
34#include "mmath.h"
35#include "s_aatriangle.h"
36#include "s_context.h"
37#include "s_span.h"
38
39
40/*
41 * Compute coefficients of a plane using the X,Y coords of the v0, v1, v2
42 * vertices and the given Z values.
43 * A point (x,y,z) lies on plane iff a*x+b*y+c*z+d = 0.
44 */
45static INLINE void
46compute_plane(const GLfloat v0[], const GLfloat v1[], const GLfloat v2[],
47              GLfloat z0, GLfloat z1, GLfloat z2, GLfloat plane[4])
48{
49   const GLfloat px = v1[0] - v0[0];
50   const GLfloat py = v1[1] - v0[1];
51   const GLfloat pz = z1 - z0;
52
53   const GLfloat qx = v2[0] - v0[0];
54   const GLfloat qy = v2[1] - v0[1];
55   const GLfloat qz = z2 - z0;
56
57   /* Crossproduct "(a,b,c):= dv1 x dv2" is orthogonal to plane. */
58   const GLfloat a = py * qz - pz * qy;
59   const GLfloat b = pz * qx - px * qz;
60   const GLfloat c = px * qy - py * qx;
61   /* Point on the plane = "r*(a,b,c) + w", with fixed "r" depending
62      on the distance of plane from origin and arbitrary "w" parallel
63      to the plane. */
64   /* The scalar product "(r*(a,b,c)+w)*(a,b,c)" is "r*(a^2+b^2+c^2)",
65      which is equal to "-d" below. */
66   const GLfloat d = -(a * v0[0] + b * v0[1] + c * z0);
67
68   plane[0] = a;
69   plane[1] = b;
70   plane[2] = c;
71   plane[3] = d;
72}
73
74
75/*
76 * Compute coefficients of a plane with a constant Z value.
77 */
78static INLINE void
79constant_plane(GLfloat value, GLfloat plane[4])
80{
81   plane[0] = 0.0;
82   plane[1] = 0.0;
83   plane[2] = -1.0;
84   plane[3] = value;
85}
86
87#define CONSTANT_PLANE(VALUE, PLANE)	\
88do {					\
89   PLANE[0] = 0.0F;			\
90   PLANE[1] = 0.0F;			\
91   PLANE[2] = -1.0F;			\
92   PLANE[3] = VALUE;			\
93} while (0)
94
95
96
97/*
98 * Solve plane equation for Z at (X,Y).
99 */
100static INLINE GLfloat
101solve_plane(GLfloat x, GLfloat y, const GLfloat plane[4])
102{
103   ASSERT(plane[2] != 0.0F);
104   return (plane[3] + plane[0] * x + plane[1] * y) / -plane[2];
105}
106
107
108#define SOLVE_PLANE(X, Y, PLANE) \
109   ((PLANE[3] + PLANE[0] * (X) + PLANE[1] * (Y)) / -PLANE[2])
110
111
112/*
113 * Return 1 / solve_plane().
114 */
115static INLINE GLfloat
116solve_plane_recip(GLfloat x, GLfloat y, const GLfloat plane[4])
117{
118   const GLfloat denom = plane[3] + plane[0] * x + plane[1] * y;
119   if (denom == 0.0F)
120      return 0.0F;
121   else
122      return -plane[2] / denom;
123}
124
125
126
127/*
128 * Solve plane and return clamped GLchan value.
129 */
130static INLINE GLchan
131solve_plane_chan(GLfloat x, GLfloat y, const GLfloat plane[4])
132{
133   GLfloat z = (plane[3] + plane[0] * x + plane[1] * y) / -plane[2] + 0.5F;
134   if (z < 0.0F)
135      return 0;
136   else if (z > CHAN_MAXF)
137      return (GLchan) CHAN_MAXF;
138   return (GLchan) (GLint) z;
139}
140
141
142
143/*
144 * Compute how much (area) of the given pixel is inside the triangle.
145 * Vertices MUST be specified in counter-clockwise order.
146 * Return:  coverage in [0, 1].
147 */
148static GLfloat
149compute_coveragef(const GLfloat v0[3], const GLfloat v1[3],
150                  const GLfloat v2[3], GLint winx, GLint winy)
151{
152   /* Given a position [0,3]x[0,3] return the sub-pixel sample position.
153    * Contributed by Ray Tice.
154    *
155    * Jitter sample positions -
156    * - average should be .5 in x & y for each column
157    * - each of the 16 rows and columns should be used once
158    * - the rectangle formed by the first four points
159    *   should contain the other points
160    * - the distrubition should be fairly even in any given direction
161    *
162    * The pattern drawn below isn't optimal, but it's better than a regular
163    * grid.  In the drawing, the center of each subpixel is surrounded by
164    * four dots.  The "x" marks the jittered position relative to the
165    * subpixel center.
166    */
167#define POS(a, b) (0.5+a*4+b)/16
168   static const GLfloat samples[16][2] = {
169      /* start with the four corners */
170      { POS(0, 2), POS(0, 0) },
171      { POS(3, 3), POS(0, 2) },
172      { POS(0, 0), POS(3, 1) },
173      { POS(3, 1), POS(3, 3) },
174      /* continue with interior samples */
175      { POS(1, 1), POS(0, 1) },
176      { POS(2, 0), POS(0, 3) },
177      { POS(0, 3), POS(1, 3) },
178      { POS(1, 2), POS(1, 0) },
179      { POS(2, 3), POS(1, 2) },
180      { POS(3, 2), POS(1, 1) },
181      { POS(0, 1), POS(2, 2) },
182      { POS(1, 0), POS(2, 1) },
183      { POS(2, 1), POS(2, 3) },
184      { POS(3, 0), POS(2, 0) },
185      { POS(1, 3), POS(3, 0) },
186      { POS(2, 2), POS(3, 2) }
187   };
188
189   const GLfloat x = (GLfloat) winx;
190   const GLfloat y = (GLfloat) winy;
191   const GLfloat dx0 = v1[0] - v0[0];
192   const GLfloat dy0 = v1[1] - v0[1];
193   const GLfloat dx1 = v2[0] - v1[0];
194   const GLfloat dy1 = v2[1] - v1[1];
195   const GLfloat dx2 = v0[0] - v2[0];
196   const GLfloat dy2 = v0[1] - v2[1];
197   GLint stop = 4, i;
198   GLfloat insideCount = 16.0F;
199
200#ifdef DEBUG
201   {
202      const GLfloat area = dx0 * dy1 - dx1 * dy0;
203      ASSERT(area >= 0.0);
204   }
205#endif
206
207   for (i = 0; i < stop; i++) {
208      const GLfloat sx = x + samples[i][0];
209      const GLfloat sy = y + samples[i][1];
210      const GLfloat fx0 = sx - v0[0];
211      const GLfloat fy0 = sy - v0[1];
212      const GLfloat fx1 = sx - v1[0];
213      const GLfloat fy1 = sy - v1[1];
214      const GLfloat fx2 = sx - v2[0];
215      const GLfloat fy2 = sy - v2[1];
216      /* cross product determines if sample is inside or outside each edge */
217      GLfloat cross0 = (dx0 * fy0 - dy0 * fx0);
218      GLfloat cross1 = (dx1 * fy1 - dy1 * fx1);
219      GLfloat cross2 = (dx2 * fy2 - dy2 * fx2);
220      /* Check if the sample is exactly on an edge.  If so, let cross be a
221       * positive or negative value depending on the direction of the edge.
222       */
223      if (cross0 == 0.0F)
224         cross0 = dx0 + dy0;
225      if (cross1 == 0.0F)
226         cross1 = dx1 + dy1;
227      if (cross2 == 0.0F)
228         cross2 = dx2 + dy2;
229      if (cross0 < 0.0F || cross1 < 0.0F || cross2 < 0.0F) {
230         /* point is outside triangle */
231         insideCount -= 1.0F;
232         stop = 16;
233      }
234   }
235   if (stop == 4)
236      return 1.0F;
237   else
238      return insideCount * (1.0F / 16.0F);
239}
240
241
242
243/*
244 * Compute how much (area) of the given pixel is inside the triangle.
245 * Vertices MUST be specified in counter-clockwise order.
246 * Return:  coverage in [0, 15].
247 */
248static GLint
249compute_coveragei(const GLfloat v0[3], const GLfloat v1[3],
250                  const GLfloat v2[3], GLint winx, GLint winy)
251{
252   /* NOTE: 15 samples instead of 16. */
253   static const GLfloat samples[15][2] = {
254      /* start with the four corners */
255      { POS(0, 2), POS(0, 0) },
256      { POS(3, 3), POS(0, 2) },
257      { POS(0, 0), POS(3, 1) },
258      { POS(3, 1), POS(3, 3) },
259      /* continue with interior samples */
260      { POS(1, 1), POS(0, 1) },
261      { POS(2, 0), POS(0, 3) },
262      { POS(0, 3), POS(1, 3) },
263      { POS(1, 2), POS(1, 0) },
264      { POS(2, 3), POS(1, 2) },
265      { POS(3, 2), POS(1, 1) },
266      { POS(0, 1), POS(2, 2) },
267      { POS(1, 0), POS(2, 1) },
268      { POS(2, 1), POS(2, 3) },
269      { POS(3, 0), POS(2, 0) },
270      { POS(1, 3), POS(3, 0) }
271   };
272   const GLfloat x = (GLfloat) winx;
273   const GLfloat y = (GLfloat) winy;
274   const GLfloat dx0 = v1[0] - v0[0];
275   const GLfloat dy0 = v1[1] - v0[1];
276   const GLfloat dx1 = v2[0] - v1[0];
277   const GLfloat dy1 = v2[1] - v1[1];
278   const GLfloat dx2 = v0[0] - v2[0];
279   const GLfloat dy2 = v0[1] - v2[1];
280   GLint stop = 4, i;
281   GLint insideCount = 15;
282
283#ifdef DEBUG
284   {
285      const GLfloat area = dx0 * dy1 - dx1 * dy0;
286      ASSERT(area >= 0.0);
287   }
288#endif
289
290   for (i = 0; i < stop; i++) {
291      const GLfloat sx = x + samples[i][0];
292      const GLfloat sy = y + samples[i][1];
293      const GLfloat fx0 = sx - v0[0];
294      const GLfloat fy0 = sy - v0[1];
295      const GLfloat fx1 = sx - v1[0];
296      const GLfloat fy1 = sy - v1[1];
297      const GLfloat fx2 = sx - v2[0];
298      const GLfloat fy2 = sy - v2[1];
299      /* cross product determines if sample is inside or outside each edge */
300      GLfloat cross0 = (dx0 * fy0 - dy0 * fx0);
301      GLfloat cross1 = (dx1 * fy1 - dy1 * fx1);
302      GLfloat cross2 = (dx2 * fy2 - dy2 * fx2);
303      /* Check if the sample is exactly on an edge.  If so, let cross be a
304       * positive or negative value depending on the direction of the edge.
305       */
306      if (cross0 == 0.0F)
307         cross0 = dx0 + dy0;
308      if (cross1 == 0.0F)
309         cross1 = dx1 + dy1;
310      if (cross2 == 0.0F)
311         cross2 = dx2 + dy2;
312      if (cross0 < 0.0F || cross1 < 0.0F || cross2 < 0.0F) {
313         /* point is outside triangle */
314         insideCount--;
315         stop = 15;
316      }
317   }
318   if (stop == 4)
319      return 15;
320   else
321      return insideCount;
322}
323
324
325
326static void
327rgba_aa_tri(GLcontext *ctx,
328	    const SWvertex *v0,
329	    const SWvertex *v1,
330	    const SWvertex *v2)
331{
332#define DO_Z
333#define DO_FOG
334#define DO_RGBA
335#include "s_aatritemp.h"
336}
337
338
339static void
340index_aa_tri(GLcontext *ctx,
341	     const SWvertex *v0,
342	     const SWvertex *v1,
343	     const SWvertex *v2)
344{
345#define DO_Z
346#define DO_FOG
347#define DO_INDEX
348#include "s_aatritemp.h"
349}
350
351
352/*
353 * Compute mipmap level of detail.
354 */
355static INLINE GLfloat
356compute_lambda(const GLfloat sPlane[4], const GLfloat tPlane[4],
357               GLfloat invQ, GLfloat width, GLfloat height)
358{
359   GLfloat dudx = sPlane[0] / sPlane[2] * invQ * width;
360   GLfloat dudy = sPlane[1] / sPlane[2] * invQ * width;
361   GLfloat dvdx = tPlane[0] / tPlane[2] * invQ * height;
362   GLfloat dvdy = tPlane[1] / tPlane[2] * invQ * height;
363   GLfloat r1 = dudx * dudx + dudy * dudy;
364   GLfloat r2 = dvdx * dvdx + dvdy * dvdy;
365   GLfloat rho2 = r1 + r2;
366   /* return log base 2 of rho */
367   if (rho2 == 0.0F)
368      return 0.0;
369   else
370      return (GLfloat) (log(rho2) * 1.442695 * 0.5); /* 1.442695 = 1/log(2) */
371}
372
373
374static void
375tex_aa_tri(GLcontext *ctx,
376	   const SWvertex *v0,
377	   const SWvertex *v1,
378	   const SWvertex *v2)
379{
380#define DO_Z
381#define DO_FOG
382#define DO_RGBA
383#define DO_TEX
384#include "s_aatritemp.h"
385}
386
387
388static void
389spec_tex_aa_tri(GLcontext *ctx,
390		const SWvertex *v0,
391		const SWvertex *v1,
392		const SWvertex *v2)
393{
394#define DO_Z
395#define DO_FOG
396#define DO_RGBA
397#define DO_TEX
398#define DO_SPEC
399#include "s_aatritemp.h"
400}
401
402
403static void
404multitex_aa_tri(GLcontext *ctx,
405		const SWvertex *v0,
406		const SWvertex *v1,
407		const SWvertex *v2)
408{
409#define DO_Z
410#define DO_FOG
411#define DO_RGBA
412#define DO_MULTITEX
413#include "s_aatritemp.h"
414}
415
416static void
417spec_multitex_aa_tri(GLcontext *ctx,
418		     const SWvertex *v0,
419		     const SWvertex *v1,
420		     const SWvertex *v2)
421{
422#define DO_Z
423#define DO_FOG
424#define DO_RGBA
425#define DO_MULTITEX
426#define DO_SPEC
427#include "s_aatritemp.h"
428}
429
430
431/*
432 * Examine GL state and set swrast->Triangle to an
433 * appropriate antialiased triangle rasterizer function.
434 */
435void
436_mesa_set_aa_triangle_function(GLcontext *ctx)
437{
438   ASSERT(ctx->Polygon.SmoothFlag);
439
440   if (ctx->Texture._ReallyEnabled) {
441      if (ctx->_TriangleCaps & DD_SEPARATE_SPECULAR) {
442         if (ctx->Texture._ReallyEnabled > TEXTURE0_ANY) {
443            SWRAST_CONTEXT(ctx)->Triangle = spec_multitex_aa_tri;
444         }
445         else {
446            SWRAST_CONTEXT(ctx)->Triangle = spec_tex_aa_tri;
447         }
448      }
449      else {
450         if (ctx->Texture._ReallyEnabled > TEXTURE0_ANY) {
451            SWRAST_CONTEXT(ctx)->Triangle = multitex_aa_tri;
452         }
453         else {
454            SWRAST_CONTEXT(ctx)->Triangle = tex_aa_tri;
455         }
456      }
457   }
458   else if (ctx->Visual.rgbMode) {
459      SWRAST_CONTEXT(ctx)->Triangle = rgba_aa_tri;
460   }
461   else {
462      SWRAST_CONTEXT(ctx)->Triangle = index_aa_tri;
463   }
464
465   ASSERT(SWRAST_CONTEXT(ctx)->Triangle);
466}
467