d195ff0fca548219e9b34a1c69933a17cb0d8738
[metatoy] / src / 3dgfx / polyfill.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <assert.h>
5 #include "polyfill.h"
6 #include "gfxutil.h"
7 #include "util.h"
8 #include "colormgr.h"
9
10 /*#define DEBUG_OVERDRAW        G3D_PACK_RGB(10, 10, 10)*/
11
12 #define FILL_POLY_BITS  0x03
13
14 /*void polyfill_tex_flat_new(struct pvertex *varr);*/
15
16 /* mode bits: 00-wire 01-flat 10-gouraud 11-reserved
17  *     bit 2: texture
18  *     bit 3: zbuffering
19  */
20 void (*fillfunc[])(struct pvertex*) = {
21         polyfill_wire,
22         polyfill_flat,
23         polyfill_gouraud,
24         0,
25         polyfill_tex_wire,
26         polyfill_tex_flat,
27         polyfill_tex_gouraud,
28         0,
29         0, 0, 0, 0, 0, 0, 0, 0,
30         polyfill_wire,
31         polyfill_flat_zbuf,
32         polyfill_gouraud_zbuf,
33         0,
34         polyfill_tex_wire,
35         polyfill_tex_flat_zbuf,
36         polyfill_tex_gouraud_zbuf,
37         0,
38         0, 0, 0, 0, 0, 0, 0, 0
39 };
40
41 struct pimage pfill_fb, pfill_tex;
42 uint32_t *pfill_zbuf;
43 struct pgradient pgrad;
44
45 #define EDGEPAD 8
46 static struct pvertex *edgebuf, *left, *right;
47 static int edgebuf_size;
48 static int fbheight;
49
50 /*
51 #define CHECKEDGE(x) \
52         do { \
53                 assert(x >= 0); \
54                 assert(x < fbheight); \
55         } while(0)
56 */
57 #define CHECKEDGE(x)
58
59
60 void polyfill_fbheight(int height)
61 {
62         int newsz = (height * 2 + EDGEPAD * 3) * sizeof *edgebuf;
63
64         if(newsz > edgebuf_size) {
65                 free(edgebuf);
66                 if(!(edgebuf = malloc(newsz))) {
67                         fprintf(stderr, "failed to allocate edge table buffer (%d bytes)\n", newsz);
68                         abort();
69                 }
70                 edgebuf_size = newsz;
71
72                 left = edgebuf + EDGEPAD;
73                 right = edgebuf + height + EDGEPAD * 2;
74
75 #ifndef NDEBUG
76                 memset(edgebuf, 0xaa, EDGEPAD * sizeof *edgebuf);
77                 memset(edgebuf + height + EDGEPAD, 0xaa, EDGEPAD * sizeof *edgebuf);
78                 memset(edgebuf + height * 2 + EDGEPAD * 2, 0xaa, EDGEPAD * sizeof *edgebuf);
79 #endif
80         }
81
82         fbheight = height;
83 }
84
85 void polyfill(int mode, struct pvertex *verts)
86 {
87 #ifndef NDEBUG
88         if(!fillfunc[mode]) {
89                 fprintf(stderr, "polyfill mode %d not implemented\n", mode);
90                 abort();
91         }
92 #endif
93
94         fillfunc[mode](verts);
95 }
96
97 void polyfill_wire(struct pvertex *verts)
98 {
99         int i, x0, y0, x1, y1;
100         struct pvertex *v = verts;
101         int color = find_color(v->l, v->l, v->l);
102
103         for(i=0; i<2; i++) {
104                 x0 = v->x >> 8;
105                 y0 = v->y >> 8;
106                 ++v;
107                 x1 = v->x >> 8;
108                 y1 = v->y >> 8;
109                 if(clip_line(&x0, &y0, &x1, &y1, 0, 0, pfill_fb.width, pfill_fb.height)) {
110                         draw_line(x0, y0, x1, y1, color);
111                 }
112         }
113         x0 = verts[0].x >> 8;
114         y0 = verts[0].y >> 8;
115         if(clip_line(&x1, &y1, &x0, &y0, 0, 0, pfill_fb.width, pfill_fb.height)) {
116                 draw_line(x1, y1, x0, y0, color);
117         }
118 }
119
120 void polyfill_tex_wire(struct pvertex *verts)
121 {
122         polyfill_wire(verts);   /* TODO */
123 }
124
125 void polyfill_alpha_wire(struct pvertex *verts)
126 {
127         polyfill_wire(verts);   /* TODO */
128 }
129
130 void polyfill_alpha_tex_wire(struct pvertex *verts)
131 {
132         polyfill_wire(verts);   /* TODO */
133 }
134
135 void polyfill_add_wire(struct pvertex *verts)
136 {
137         polyfill_wire(verts);   /* TODO */
138 }
139
140 void polyfill_add_tex_wire(struct pvertex *verts)
141 {
142         polyfill_wire(verts);   /* TODO */
143 }
144
145 #define VNEXT(p)        (((p) == varr + 2) ? varr : (p) + 1)
146 #define VPREV(p)        ((p) == varr ? varr + 2 : (p) - 1)
147 #define VSUCC(p, side)  ((side) == 0 ? VNEXT(p) : VPREV(p))
148
149 /* extra bits of precision to use when interpolating colors.
150  * try tweaking this if you notice strange quantization artifacts.
151  */
152 #define COLOR_SHIFT     12
153
154
155 #define POLYFILL polyfill_flat
156 #undef GOURAUD
157 #undef TEXMAP
158 #undef ZBUF
159 #include "polytmpl.h"
160 #undef POLYFILL
161
162 #define POLYFILL polyfill_gouraud
163 #define GOURAUD
164 #undef TEXMAP
165 #undef ZBUF
166 #include "polytmpl.h"
167 #undef POLYFILL
168
169 #define POLYFILL polyfill_tex_flat
170 #undef GOURAUD
171 #define TEXMAP
172 #undef ZBUF
173 #include "polytmpl.h"
174 #undef POLYFILL
175
176 #define POLYFILL polyfill_tex_gouraud
177 #define GOURAUD
178 #define TEXMAP
179 #undef ZBUF
180 #include "polytmpl.h"
181 #undef POLYFILL
182
183 /* ---- zbuffer variants ----- */
184
185 #define POLYFILL polyfill_flat_zbuf
186 #undef GOURAUD
187 #undef TEXMAP
188 #define ZBUF
189 #include "polytmpl.h"
190 #undef POLYFILL
191
192 #define POLYFILL polyfill_gouraud_zbuf
193 #define GOURAUD
194 #undef TEXMAP
195 #define ZBUF
196 #include "polytmpl.h"
197 #undef POLYFILL
198
199 #define POLYFILL polyfill_tex_flat_zbuf
200 #undef GOURAUD
201 #define TEXMAP
202 #define ZBUF
203 #include "polytmpl.h"
204 #undef POLYFILL
205
206 #define POLYFILL polyfill_tex_gouraud_zbuf
207 #define GOURAUD
208 #define TEXMAP
209 #define ZBUF
210 #include "polytmpl.h"
211 #undef POLYFILL
212
213 #if 0
214 void polyfill_tex_flat_new(struct pvertex *varr)
215 {
216         int i, line, top, bot;
217         struct pvertex *v, *vn, *tab;
218         int32_t x, y0, y1, dx, dy, slope, fx, fy;
219         int start, len;
220         g3d_pixel *fbptr, *pptr, color;
221         int32_t tu, tv, du, dv, uslope, vslope;
222         int tx, ty;
223         g3d_pixel texel;
224
225         top = pfill_fb.height;
226         bot = 0;
227
228         for(i=0; i<3; i++) {
229                 /* scan the edge between the current and next vertex */
230                 v = varr + i;
231                 vn = VNEXT(v);
232
233                 if(vn->y == v->y) continue;     /* XXX ??? */
234
235                 if(vn->y >= v->y) {
236                         /* inrementing Y: left side */
237                         tab = left;
238                 } else {
239                         /* decrementing Y: right side, flip vertices to trace bottom->up */
240                         tab = right;
241                         v = vn;
242                         vn = varr + i;
243                 }
244
245                 /* calculate edge slope */
246                 dx = vn->x - v->x;
247                 dy = vn->y - v->y;
248                 slope = (dx << 8) / dy;
249
250                 tu = v->u;
251                 tv = v->v;
252                 du = vn->u - tu;
253                 dv = vn->v - tv;
254                 uslope = (du << 8) / dy;
255                 vslope = (dv << 8) / dy;
256
257                 y0 = (v->y + 0x100) & 0xffffff00;       /* start from the next scanline */
258                 fy = y0 - v->y;                                         /* fractional part before the next scanline */
259                 fx = (fy * slope) >> 8;                         /* X adjust for the step to the next scanline */
260                 x = v->x + fx;                                          /* adjust X */
261                 y1 = vn->y & 0xffffff00;                        /* last scanline of the edge <= vn->y */
262
263                 /* also adjust other interpolated attributes */
264                 tu += (fy * uslope) >> 8;
265                 tv += (fy * vslope) >> 8;
266
267                 line = y0 >> 8;
268                 if(line < top) top = line;
269                 if((y1 >> 8) > bot) bot = y1 >> 8;
270
271                 if(line > 0) tab += line;
272
273                 while(line <= (y1 >> 8) && line < pfill_fb.height) {
274                         if(line >= 0) {
275                                 int val = x < 0 ? 0 : x >> 8;
276                                 tab->x = val < pfill_fb.width ? val : pfill_fb.width - 1;
277                                 tab->u = tu;
278                                 tab->v = tv;
279                                 tab++;
280                         }
281                         x += slope;
282                         tu += uslope;
283                         tv += vslope;
284                         line++;
285                 }
286         }
287
288         if(top < 0) top = 0;
289         if(bot >= pfill_fb.height) bot = pfill_fb.height - 1;
290
291         fbptr = pfill_fb.pixels + top * pfill_fb.width;
292         for(i=top; i<=bot; i++) {
293                 start = left[i].x;
294                 len = right[i].x - start;
295                 /* XXX we probably need more precision in left/right.x */
296
297                 dx = len == 0 ? 256 : (len << 8);
298
299                 tu = left[i].u;
300                 tv = left[i].v;
301
302                 pptr = fbptr + start;
303                 while(len-- > 0) {
304                         int cr, cg, cb;
305
306                         tx = (tu >> (16 - pfill_tex.xshift)) & pfill_tex.xmask;
307                         ty = (tv >> (16 - pfill_tex.yshift)) & pfill_tex.ymask;
308                         texel = pfill_tex.pixels[(ty << pfill_tex.xshift) + tx];
309
310                         tu += pgrad.dudx;
311                         tv += pgrad.dvdx;
312
313                         cr = varr[0].r;
314                         cg = varr[0].g;
315                         cb = varr[0].b;
316
317                         /* This is not correct, should be /255, but it's much faster
318                          * to shift by 8 (/256), and won't make a huge difference
319                          */
320                         cr = (cr * G3D_UNPACK_R(texel)) >> 8;
321                         cg = (cg * G3D_UNPACK_G(texel)) >> 8;
322                         cb = (cb * G3D_UNPACK_B(texel)) >> 8;
323
324                         if(cr >= 255) cr = 255;
325                         if(cg >= 255) cg = 255;
326                         if(cb >= 255) cb = 255;
327                         color = G3D_PACK_RGB(cr, cg, cb);
328                         *pptr++ = color;
329                 }
330                 fbptr += pfill_fb.width;
331         }
332 }
333 #endif