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