1/*
2 * Mesa 3-D graphics library
3 *
4 * Copyright 2008 VMware, Inc.
5 * Copyright (C) 2010 LunarG Inc.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 *
25 * Authors:
26  *   Keith Whitwell <keithw@vmware.com>
27 *    Chia-I Wu <olv@lunarg.com>
28 */
29
30/* these macros are optional */
31#ifndef LOCAL_VARS
32#define LOCAL_VARS
33#endif
34#ifndef FUNC_ENTER
35#define FUNC_ENTER do {} while (0)
36#endif
37#ifndef FUNC_EXIT
38#define FUNC_EXIT do {} while (0)
39#endif
40#ifndef LINE_ADJ
41#define LINE_ADJ(flags, a0, i0, i1, a1) LINE(flags, i0, i1)
42#endif
43#ifndef TRIANGLE_ADJ
44#define TRIANGLE_ADJ(flags, i0, a0, i1, a1, i2, a2) TRIANGLE(flags, i0, i1, i2)
45#endif
46
47static void
48FUNC(FUNC_VARS)
49{
50   unsigned idx[6], i;
51   ushort flags;
52   LOCAL_VARS
53
54   FUNC_ENTER;
55
56   /* prim, prim_flags, count, and last_vertex_last should have been defined */
57   if (0) {
58      debug_printf("%s: prim 0x%x, prim_flags 0x%x, count %d, last_vertex_last %d\n",
59            __FUNCTION__, prim, prim_flags, count, last_vertex_last);
60   }
61
62   switch (prim) {
63   case PIPE_PRIM_POINTS:
64      for (i = 0; i < count; i++) {
65         idx[0] = GET_ELT(i);
66         POINT(idx[0]);
67      }
68      break;
69
70   case PIPE_PRIM_LINES:
71      flags = DRAW_PIPE_RESET_STIPPLE;
72      for (i = 0; i + 1 < count; i += 2) {
73         idx[0] = GET_ELT(i);
74         idx[1] = GET_ELT(i + 1);
75         LINE(flags, idx[0], idx[1]);
76      }
77      break;
78
79   case PIPE_PRIM_LINE_LOOP:
80   case PIPE_PRIM_LINE_STRIP:
81      if (count >= 2) {
82         flags = (prim_flags & DRAW_SPLIT_BEFORE) ? 0 : DRAW_PIPE_RESET_STIPPLE;
83         idx[1] = GET_ELT(0);
84         idx[2] = idx[1];
85
86         for (i = 1; i < count; i++, flags = 0) {
87            idx[0] = idx[1];
88            idx[1] = GET_ELT(i);
89            LINE(flags, idx[0], idx[1]);
90         }
91         /* close the loop */
92         if (prim == PIPE_PRIM_LINE_LOOP && !prim_flags)
93            LINE(flags, idx[1], idx[2]);
94      }
95      break;
96
97   case PIPE_PRIM_TRIANGLES:
98      flags = DRAW_PIPE_RESET_STIPPLE | DRAW_PIPE_EDGE_FLAG_ALL;
99      for (i = 0; i + 2 < count; i += 3) {
100         idx[0] = GET_ELT(i);
101         idx[1] = GET_ELT(i + 1);
102         idx[2] = GET_ELT(i + 2);
103         TRIANGLE(flags, idx[0], idx[1], idx[2]);
104      }
105      break;
106
107   case PIPE_PRIM_TRIANGLE_STRIP:
108      if (count >= 3) {
109         flags = DRAW_PIPE_RESET_STIPPLE | DRAW_PIPE_EDGE_FLAG_ALL;
110         idx[1] = GET_ELT(0);
111         idx[2] = GET_ELT(1);
112
113         if (last_vertex_last) {
114            for (i = 0; i + 2 < count; i++) {
115               idx[0] = idx[1];
116               idx[1] = idx[2];
117               idx[2] = GET_ELT(i + 2);
118               /* always emit idx[2] last */
119               if (i & 1)
120                  TRIANGLE(flags, idx[1], idx[0], idx[2]);
121               else
122                  TRIANGLE(flags, idx[0], idx[1], idx[2]);
123            }
124         }
125         else {
126            for (i = 0; i + 2 < count; i++) {
127               idx[0] = idx[1];
128               idx[1] = idx[2];
129               idx[2] = GET_ELT(i + 2);
130               /* always emit idx[0] first */
131               if (i & 1)
132                  TRIANGLE(flags, idx[0], idx[2], idx[1]);
133               else
134                  TRIANGLE(flags, idx[0], idx[1], idx[2]);
135            }
136         }
137      }
138      break;
139
140   case PIPE_PRIM_TRIANGLE_FAN:
141      if (count >= 3) {
142         flags = DRAW_PIPE_RESET_STIPPLE | DRAW_PIPE_EDGE_FLAG_ALL;
143         idx[0] = GET_ELT(0);
144         idx[2] = GET_ELT(1);
145
146         /* idx[0] is neither the first nor the last vertex */
147         if (last_vertex_last) {
148            for (i = 0; i + 2 < count; i++) {
149               idx[1] = idx[2];
150               idx[2] = GET_ELT(i + 2);
151               /* always emit idx[2] last */
152               TRIANGLE(flags, idx[0], idx[1], idx[2]);
153            }
154         }
155         else {
156            for (i = 0; i + 2 < count; i++) {
157               idx[1] = idx[2];
158               idx[2] = GET_ELT(i + 2);
159               /* always emit idx[1] first */
160               TRIANGLE(flags, idx[1], idx[2], idx[0]);
161            }
162         }
163      }
164      break;
165
166   case PIPE_PRIM_QUADS:
167      if (last_vertex_last) {
168         for (i = 0; i + 3 < count; i += 4) {
169            idx[0] = GET_ELT(i);
170            idx[1] = GET_ELT(i + 1);
171            idx[2] = GET_ELT(i + 2);
172            idx[3] = GET_ELT(i + 3);
173#ifdef PASS_QUADS
174            QUAD(0, idx[0], idx[1],
175                  idx[2], idx[3]);
176#else
177            flags = DRAW_PIPE_RESET_STIPPLE |
178                    DRAW_PIPE_EDGE_FLAG_0 |
179                    DRAW_PIPE_EDGE_FLAG_2;
180            /* always emit idx[3] last */
181            TRIANGLE(flags, idx[0], idx[1], idx[3]);
182
183            flags = DRAW_PIPE_EDGE_FLAG_0 |
184                    DRAW_PIPE_EDGE_FLAG_1;
185            TRIANGLE(flags, idx[1], idx[2], idx[3]);
186#endif
187         }
188      }
189      else {
190         for (i = 0; i + 3 < count; i += 4) {
191            idx[0] = GET_ELT(i);
192            idx[1] = GET_ELT(i + 1);
193            idx[2] = GET_ELT(i + 2);
194            idx[3] = GET_ELT(i + 3);
195#ifdef PASS_QUADS
196            QUAD(0, idx[0], idx[1],
197                  idx[2], idx[3]);
198#else
199            flags = DRAW_PIPE_RESET_STIPPLE |
200                    DRAW_PIPE_EDGE_FLAG_0 |
201                    DRAW_PIPE_EDGE_FLAG_1;
202            /* always emit idx[3] / idx[0] first */
203            if (quads_flatshade_last)
204               TRIANGLE(flags, idx[3], idx[0], idx[1]);
205            else
206               TRIANGLE(flags, idx[0], idx[1], idx[2]);
207
208            flags = DRAW_PIPE_EDGE_FLAG_1 |
209                    DRAW_PIPE_EDGE_FLAG_2;
210            if (quads_flatshade_last)
211               TRIANGLE(flags, idx[3], idx[1], idx[2]);
212            else
213               TRIANGLE(flags, idx[0], idx[2], idx[3]);
214#endif
215         }
216      }
217      break;
218
219   case PIPE_PRIM_QUAD_STRIP:
220      if (count >= 4) {
221         idx[2] = GET_ELT(0);
222         idx[3] = GET_ELT(1);
223
224         if (last_vertex_last) {
225            for (i = 0; i + 3 < count; i += 2) {
226               idx[0] = idx[2];
227               idx[1] = idx[3];
228               idx[2] = GET_ELT(i + 2);
229               idx[3] = GET_ELT(i + 3);
230
231#ifdef PASS_QUADS
232               QUAD(0, idx[2], idx[0],
233                    idx[1], idx[3]);
234#else
235               /* always emit idx[3] last */
236               flags = DRAW_PIPE_RESET_STIPPLE |
237                       DRAW_PIPE_EDGE_FLAG_0 |
238                       DRAW_PIPE_EDGE_FLAG_2;
239               TRIANGLE(flags, idx[2], idx[0], idx[3]);
240
241               flags = DRAW_PIPE_EDGE_FLAG_0 |
242                       DRAW_PIPE_EDGE_FLAG_1;
243               TRIANGLE(flags, idx[0], idx[1], idx[3]);
244#endif
245            }
246         }
247         else {
248            for (i = 0; i + 3 < count; i += 2) {
249               idx[0] = idx[2];
250               idx[1] = idx[3];
251               idx[2] = GET_ELT(i + 2);
252               idx[3] = GET_ELT(i + 3);
253
254#ifdef PASS_QUADS
255               QUAD(0, idx[3], idx[2],
256                    idx[0], idx[1]);
257#else
258               flags = DRAW_PIPE_RESET_STIPPLE |
259                       DRAW_PIPE_EDGE_FLAG_0 |
260                       DRAW_PIPE_EDGE_FLAG_1;
261               /* always emit idx[3] / idx[0 first */
262               if (quads_flatshade_last)
263                  TRIANGLE(flags, idx[3], idx[2], idx[0]);
264               else
265                  TRIANGLE(flags, idx[0], idx[3], idx[2]);
266
267               flags = DRAW_PIPE_EDGE_FLAG_1 |
268                       DRAW_PIPE_EDGE_FLAG_2;
269               if (quads_flatshade_last)
270                  TRIANGLE(flags, idx[3], idx[0], idx[1]);
271               else
272                  TRIANGLE(flags, idx[0], idx[1], idx[3]);
273#endif
274            }
275         }
276      }
277      break;
278
279   case PIPE_PRIM_POLYGON:
280      if (count >= 3) {
281         ushort edge_next, edge_finish;
282
283         if (last_vertex_last) {
284            flags = (DRAW_PIPE_RESET_STIPPLE |
285                     DRAW_PIPE_EDGE_FLAG_0);
286            if (!(prim_flags & DRAW_SPLIT_BEFORE))
287               flags |= DRAW_PIPE_EDGE_FLAG_2;
288
289            edge_next = DRAW_PIPE_EDGE_FLAG_0;
290            edge_finish =
291               (prim_flags & DRAW_SPLIT_AFTER) ? 0 : DRAW_PIPE_EDGE_FLAG_1;
292         }
293         else {
294            flags = (DRAW_PIPE_RESET_STIPPLE |
295                     DRAW_PIPE_EDGE_FLAG_1);
296            if (!(prim_flags & DRAW_SPLIT_BEFORE))
297               flags |= DRAW_PIPE_EDGE_FLAG_0;
298
299            edge_next = DRAW_PIPE_EDGE_FLAG_1;
300            edge_finish =
301               (prim_flags & DRAW_SPLIT_AFTER) ? 0 : DRAW_PIPE_EDGE_FLAG_2;
302         }
303
304         idx[0] = GET_ELT(0);
305         idx[2] = GET_ELT(1);
306
307         for (i = 0; i + 2 < count; i++, flags = edge_next) {
308            idx[1] = idx[2];
309            idx[2] = GET_ELT(i + 2);
310
311            if (i + 3 == count)
312               flags |= edge_finish;
313
314            /* idx[0] is both the first and the last vertex */
315            if (last_vertex_last)
316               TRIANGLE(flags, idx[1], idx[2], idx[0]);
317            else
318               TRIANGLE(flags, idx[0], idx[1], idx[2]);
319         }
320      }
321      break;
322
323   case PIPE_PRIM_LINES_ADJACENCY:
324      flags = DRAW_PIPE_RESET_STIPPLE;
325      for (i = 0; i + 3 < count; i += 4) {
326         idx[0] = GET_ELT(i);
327         idx[1] = GET_ELT(i + 1);
328         idx[2] = GET_ELT(i + 2);
329         idx[3] = GET_ELT(i + 3);
330         LINE_ADJ(flags, idx[0], idx[1], idx[2], idx[3]);
331      }
332      break;
333
334   case PIPE_PRIM_LINE_STRIP_ADJACENCY:
335      if (count >= 4) {
336         flags = (prim_flags & DRAW_SPLIT_BEFORE) ? 0 : DRAW_PIPE_RESET_STIPPLE;
337         idx[1] = GET_ELT(0);
338         idx[2] = GET_ELT(1);
339         idx[3] = GET_ELT(2);
340
341         for (i = 1; i + 2 < count; i++, flags = 0) {
342            idx[0] = idx[1];
343            idx[1] = idx[2];
344            idx[2] = idx[3];
345            idx[3] = GET_ELT(i + 2);
346            LINE_ADJ(flags, idx[0], idx[1], idx[2], idx[3]);
347         }
348      }
349      break;
350
351   case PIPE_PRIM_TRIANGLES_ADJACENCY:
352      flags = DRAW_PIPE_RESET_STIPPLE | DRAW_PIPE_EDGE_FLAG_ALL;
353      for (i = 0; i + 5 < count; i += 6) {
354         idx[0] = GET_ELT(i);
355         idx[1] = GET_ELT(i + 1);
356         idx[2] = GET_ELT(i + 2);
357         idx[3] = GET_ELT(i + 3);
358         idx[4] = GET_ELT(i + 4);
359         idx[5] = GET_ELT(i + 5);
360         TRIANGLE_ADJ(flags, idx[0], idx[1], idx[2], idx[3], idx[4], idx[5]);
361      }
362      break;
363
364   case PIPE_PRIM_TRIANGLE_STRIP_ADJACENCY:
365      if (count >= 6) {
366         flags = DRAW_PIPE_RESET_STIPPLE | DRAW_PIPE_EDGE_FLAG_ALL;
367         idx[0] = GET_ELT(1);
368         idx[2] = GET_ELT(0);
369         idx[4] = GET_ELT(2);
370         idx[3] = GET_ELT(4);
371
372         /*
373          * The vertices of the i-th triangle are stored in
374          * idx[0,2,4] = { 2*i, 2*i+2, 2*i+4 };
375          *
376          * The adjacent vertices are stored in
377          * idx[1,3,5] = { 2*i-2, 2*i+6, 2*i+3 }.
378          *
379          * However, there are two exceptions:
380          *
381          * For the first triangle, idx[1] = 1;
382          * For the  last triangle, idx[3] = 2*i+5.
383          */
384         if (last_vertex_last) {
385            for (i = 0; i + 5 < count; i += 2) {
386               idx[1] = idx[0];
387
388               idx[0] = idx[2];
389               idx[2] = idx[4];
390               idx[4] = idx[3];
391
392               idx[3] = GET_ELT(i + ((i + 7 < count) ? 6 : 5));
393               idx[5] = GET_ELT(i + 3);
394
395               /*
396                * alternate the first two vertices (idx[0] and idx[2]) and the
397                * corresponding adjacent vertices (idx[3] and idx[5]) to have
398                * the correct orientation
399                */
400               if (i & 2) {
401                  TRIANGLE_ADJ(flags,
402                        idx[2], idx[1], idx[0], idx[5], idx[4], idx[3]);
403               }
404               else {
405                  TRIANGLE_ADJ(flags,
406                        idx[0], idx[1], idx[2], idx[3], idx[4], idx[5]);
407               }
408            }
409         }
410         else {
411            for (i = 0; i + 5 < count; i += 2) {
412               idx[1] = idx[0];
413
414               idx[0] = idx[2];
415               idx[2] = idx[4];
416               idx[4] = idx[3];
417
418               idx[3] = GET_ELT(i + ((i + 7 < count) ? 6 : 5));
419               idx[5] = GET_ELT(i + 3);
420
421               /*
422                * alternate the last two vertices (idx[2] and idx[4]) and the
423                * corresponding adjacent vertices (idx[1] and idx[5]) to have
424                * the correct orientation
425                */
426               if (i & 2) {
427                  TRIANGLE_ADJ(flags,
428                        idx[0], idx[5], idx[4], idx[3], idx[2], idx[1]);
429               }
430               else {
431                  TRIANGLE_ADJ(flags,
432                        idx[0], idx[1], idx[2], idx[3], idx[4], idx[5]);
433               }
434            }
435         }
436      }
437      break;
438
439   default:
440      assert(0);
441      break;
442   }
443
444   FUNC_EXIT;
445}
446
447#undef LOCAL_VARS
448#undef FUNC_ENTER
449#undef FUNC_EXIT
450#undef LINE_ADJ
451#undef TRIANGLE_ADJ
452
453#undef FUNC
454#undef FUNC_VARS
455#undef GET_ELT
456#undef POINT
457#undef LINE
458#undef TRIANGLE
459