add missing tools/pngdump to the repo
[gbajam22] / src / xgl.c
1 /*
2 gbajam22 entry for the Gameboy Advance
3 Copyright (C) 2022  John Tsiombikas <nuclear@mutantstargoat.com>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 #include <string.h>
19 #include <math.h>
20 #include "xgl.h"
21 #include "polyfill.h"
22 #include "debug.h"
23 #include "util.h"
24
25 #define MAT_STACK_SIZE  4
26
27 static int vp[4];
28 static int32_t mat[MAT_STACK_SIZE][16];
29 static int mtop;
30 static unsigned int opt;
31 static int32_t ldir[3];
32
33 static int cur_cidx;
34
35 static void draw_ptlines(int prim, const struct xvertex *varr, int vcount);
36
37
38 void xgl_init(void)
39 {
40         xgl_viewport(0, 0, 240, 160);
41         xgl_load_identity();
42
43         ldir[0] = ldir[1] = 0;
44         ldir[2] = -0x100;
45 }
46
47 void xgl_enable(unsigned int o)
48 {
49         opt |= o;
50 }
51
52 void xgl_disable(unsigned int o)
53 {
54         opt &= ~o;
55 }
56
57 void xgl_viewport(int x, int y, int w, int h)
58 {
59         vp[0] = x;
60         vp[1] = y;
61         vp[2] = w;
62         vp[3] = h;
63 }
64
65 void xgl_push_matrix(void)
66 {
67         int prev;
68
69         if(mtop >= MAT_STACK_SIZE - 1) return;
70
71         prev = mtop++;
72         memcpy(mat[mtop], mat[prev], sizeof mat[0]);
73 }
74
75 void xgl_pop_matrix(void)
76 {
77         if(mtop > 0) mtop--;
78 }
79
80 static int32_t id[] = {
81         0x10000, 0, 0, 0,
82         0, 0x10000, 0, 0,
83         0, 0, 0x10000, 0,
84         0, 0, 0, 0x10000
85 };
86
87 void xgl_load_identity(void)
88 {
89         memcpy(mat[mtop], id, sizeof mat[0]);
90 }
91
92 void xgl_load_matrix(const int32_t *m)
93 {
94         memcpy(mat[mtop], m, sizeof mat[0]);
95 }
96
97 void xgl_get_matrix(int32_t *m)
98 {
99         memcpy(m, mat[mtop], sizeof mat[0]);
100 }
101
102 #define M(i,j)  (((i) << 2) + (j))
103 #define XMUL(a, b)      (((a) >> 8) * ((b) >> 8))
104 void xgl_mult_matrix(const int32_t *m2)
105 {
106         int i, j;
107         int32_t m1[16];
108         int32_t *dest = mat[mtop];
109
110         memcpy(m1, dest, sizeof m1);
111
112         for(i=0; i<4; i++) {
113                 for(j=0; j<4; j++) {
114                         *dest++ = XMUL(m1[M(0, j)], m2[M(i, 0)]) +
115                                 XMUL(m1[M(1, j)], m2[M(i, 1)]) +
116                                 XMUL(m1[M(2, j)], m2[M(i, 2)]) +
117                                 XMUL(m1[M(3, j)], m2[M(i, 3)]);
118                 }
119         }
120 }
121
122 #if 0
123 #define XSIN(x)         (int32_t)(sin(x / 65536.0f) * 65536.0f)
124 #define XCOS(x)         (int32_t)(cos(x / 65536.0f) * 65536.0f)
125 #else
126 #define XSIN(x)         SIN(((x) << 8) / (X_2PI >> 8))
127 #define XCOS(x)         COS(((x) << 8) / (X_2PI >> 8))
128 #endif
129
130 void xgl_translate(int32_t x, int32_t y, int32_t z)
131 {
132         int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
133         m[12] = x;
134         m[13] = y;
135         m[14] = z;
136         xgl_mult_matrix(m);
137 }
138
139 void xgl_rotate_x(int32_t angle)
140 {
141         int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
142         int32_t sa = XSIN(angle);
143         int32_t ca = XCOS(angle);
144         m[5] = ca;
145         m[6] = sa;
146         m[9] = -sa;
147         m[10] = ca;
148         xgl_mult_matrix(m);
149 }
150
151 void xgl_rotate_y(int32_t angle)
152 {
153         int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
154         int32_t sa = XSIN(angle);
155         int32_t ca = XCOS(angle);
156         m[0] = ca;
157         m[2] = -sa;
158         m[8] = sa;
159         m[10] = ca;
160         xgl_mult_matrix(m);
161 }
162
163 void xgl_rotate_z(int32_t angle)
164 {
165         int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
166         int32_t sa = XSIN(angle);
167         int32_t ca = XCOS(angle);
168         m[0] = ca;
169         m[1] = sa;
170         m[4] = -sa;
171         m[5] = ca;
172         xgl_mult_matrix(m);
173 }
174
175 void xgl_scale(int32_t x, int32_t y, int32_t z)
176 {
177         int32_t m[16] = {0};
178         m[0] = x;
179         m[5] = y;
180         m[10] = z;
181         m[15] = 0x10000;
182         xgl_mult_matrix(m);
183 }
184
185 void xgl_index(int cidx)
186 {
187         cur_cidx = cidx;
188 }
189
190 static inline void xform(struct xvertex *out, const struct xvertex *in, const int32_t *m)
191 {
192         out->x = XMUL(m[0], in->x) + XMUL(m[4], in->y) + XMUL(m[8], in->z) + m[12];
193         out->y = XMUL(m[1], in->x) + XMUL(m[5], in->y) + XMUL(m[9], in->z) + m[13];
194         out->z = XMUL(m[2], in->x) + XMUL(m[6], in->y) + XMUL(m[10], in->z) + m[14];
195 }
196
197 static inline void xform_norm(struct xvertex *out, const struct xvertex *in, const int32_t *m)
198 {
199         out->nx = XMUL(m[0], in->nx) + XMUL(m[4], in->ny) + XMUL(m[8], in->nz);
200         out->ny = XMUL(m[1], in->nx) + XMUL(m[5], in->ny) + XMUL(m[9], in->nz);
201         out->nz = XMUL(m[2], in->nx) + XMUL(m[6], in->ny) + XMUL(m[10], in->nz);
202 }
203
204 /* d = 1.0 / tan(fov/2) */
205 #define PROJ_D  0x20000
206 /* near Z = 0.5 */
207 #define NEAR_Z  0x18000
208
209 ARM_IWRAM
210 void xgl_draw(int prim, const struct xvertex *varr, int vcount)
211 {
212         int i, cidx, clipnum;
213         struct xvertex xv[4], xvclip[8];
214         struct pvertex pv[4];
215         int32_t ndotl;
216
217         if(prim < 3) {
218                 draw_ptlines(prim, varr, vcount);
219                 return;
220         }
221
222         while(vcount >= prim) {
223                 cidx = cur_cidx;//varr->cidx;
224
225                 xform(xv, varr, mat[mtop]);
226                 xform_norm(xv, varr, mat[mtop]);
227
228                 /*
229                 if(opt & XGL_LIGHTING) {
230                         ndotl = (xv->nx >> 8) * ldir[0] + (xv->ny >> 8) * ldir[1] + (xv->nz >> 8) * ldir[2];
231                         if(ndotl < 0) ndotl = 0;
232                         cidx = 128 + (ndotl >> 9);
233                         if(cidx > 255) cidx = 255;
234                 }
235                 */
236
237                 /* transform the rest of the vertices to view space */
238                 for(i=1; i<prim; i++) {
239                         xform(xv + i, varr + i, mat[mtop]);
240                 }
241
242                 /* clip against near plane */
243                 xgl_clip_near(xvclip, &clipnum, xv, prim);
244
245                 for(i=0; i<clipnum; i++) {
246                         xvclip[i].x = (xvclip[i].x << 1) / (xvclip[i].z >> 8);  /* assume aspect: ~2 */
247                         xvclip[i].y = (xvclip[i].y << 2) / (xvclip[i].z >> 8);  /* the shift is * PROJ_D */
248                         /* transform result is 24.8 */
249                         /* viewport */
250                         pv[i].x = (((xvclip[i].x + 0x100) >> 1) * vp[2]) + (vp[0] << 8);
251                         pv[i].y = (((0x100 - xvclip[i].y) >> 1) * vp[3]) + (vp[1] << 8);
252                 }
253
254                 polyfill_flat(pv, clipnum, cidx);
255 skip_poly:
256                 varr += prim;
257                 vcount -= prim;
258         }
259 }
260
261 void xgl_transform(const struct xvertex *vin, int *x, int *y)
262 {
263         struct xvertex v;
264         xform(&v, vin, mat[mtop]);
265
266         v.x = (v.x << 1) / (v.z >> 8);  /* assume aspect: ~2 */
267         v.y = (v.y << 2) / (v.z >> 8);  /* the shift is * PROJ_D */
268         /* projection result is 24.8 */
269         /* viewport */
270         *x = ((((v.x + 0x100) >> 1) * vp[2]) >> 8) + vp[0];
271         *y = ((((0x100 - v.y) >> 1) * vp[3]) >> 8) + vp[1];
272 }
273
274 static void draw_ptlines(int prim, const struct xvertex *varr, int vcount)
275 {
276         int i;
277         struct xvertex xv[2];
278
279         while(vcount >= prim) {
280                 for(i=0; i<prim; i++) {
281                         xform(xv + i, varr, mat[mtop]);
282
283                         xv[i].x = (xv[i].x << 1) / (xv[i].z >> 8);      /* assume aspect: ~2 */
284                         xv[i].y = (xv[i].y << 2) / (xv[i].z >> 8);      /* the shift is * PROJ_D */
285                         /* projection result is 24.8 */
286                         /* viewport */
287                         xv[i].x = ((((xv[i].x + 0x100) >> 1) * vp[2]) >> 8) + vp[0];
288                         xv[i].y = ((((0x100 - xv[i].y) >> 1) * vp[3]) >> 8) + vp[1];
289                         varr++;
290                 }
291                 vcount -= prim;
292
293                 /* line clipping */
294 #ifndef ALT_LCLIP
295                 clip_line((int*)&xv[0].x, (int*)&xv[0].y, (int*)&xv[1].x, (int*)&xv[1].y, vp[0], vp[1], vp[2] - 1, vp[3] - 1);
296 #endif
297                 draw_line(xv[0].x, xv[0].y, xv[1].x, xv[1].y, 0xff);
298         }
299 }
300
301 void xgl_xyzzy(void)
302 {
303         mat[mtop][12] = mat[mtop][13] = 0;
304 }
305
306 #define ISECT_NEAR(v0, v1)      ((((v0)->z - NEAR_Z) << 8) / ((v0)->z - (v1)->z))
307
308 #define LERP_VATTR(res, v0, v1, t) \
309         do { \
310                 (res)->x = (v0)->x + (((v1)->x - (v0)->x) >> 8) * (t);  \
311                 (res)->y = (v0)->y + (((v1)->y - (v0)->y) >> 8) * (t);  \
312                 (res)->z = (v0)->z + (((v1)->z - (v0)->z) >> 8) * (t);  \
313                 (res)->nx = (v0)->nx + (((v1)->nx - (v0)->nx) >> 8) * (t);      \
314                 (res)->ny = (v0)->ny + (((v1)->ny - (v0)->ny) >> 8) * (t);      \
315                 (res)->nz = (v0)->nz + (((v1)->nz - (v0)->nz) >> 8) * (t);      \
316                 (res)->tx = (v0)->tx + (((v1)->tx - (v0)->tx) >> 8) * (t);      \
317                 (res)->ty = (v0)->ty + (((v1)->ty - (v0)->ty) >> 8) * (t);      \
318                 (res)->lit = (v0)->lit + (((v1)->lit - (v0)->lit) >> 8) * (t); \
319         } while(0)
320
321 ARM_IWRAM
322 static int clip_edge_near(struct xvertex *poly, int *vnumptr, struct xvertex *v0, struct xvertex *v1)
323 {
324         int vnum = *vnumptr;
325         int in0, in1;
326         int32_t t;
327         struct xvertex *vptr;
328
329         in0 = v0->z >= NEAR_Z ? 1 : 0;
330         in1 = v1->z >= NEAR_Z ? 1 : 0;
331
332         if(in0) {
333                 /* start inside */
334                 if(in1) {
335                         /* all inside */
336                         poly[vnum++] = *v1;     /* append v1 */
337                         *vnumptr = vnum;
338                         return 1;
339                 } else {
340                         /* going out */
341                         vptr = poly + vnum;
342                         t = ISECT_NEAR(v0, v1); /* 24:8 */
343                         LERP_VATTR(vptr, v0, v1, t);
344                         ++vnum;         /* append new vertex on the intersection point */
345                 }
346         } else {
347                 /* start outside */
348                 if(in1) {
349                         /* going in */
350                         vptr = poly + vnum;
351                         t = ISECT_NEAR(v0, v1);
352                         LERP_VATTR(vptr, v0, v1, t);
353                         ++vnum;         /* append new vertex ... */
354                         /* then append v1 */
355                         poly[vnum++] = *v1;
356                 } else {
357                         /* all outside */
358                         return -1;
359                 }
360         }
361
362         *vnumptr = vnum;
363         return 0;
364 }
365
366 /* special case near-plane clipper */
367 ARM_IWRAM
368 int xgl_clip_near(struct xvertex *vout, int *voutnum, struct xvertex *vin, int vnum)
369 {
370         int i, nextidx, res;
371         int edges_clipped = 0;
372
373         *voutnum = 0;
374
375         for(i=0; i<vnum; i++) {
376                 nextidx = i + 1;
377                 if(nextidx >= vnum) nextidx = 0;
378                 res = clip_edge_near(vout, voutnum, vin + i, vin + nextidx);
379                 if(res == 0) {
380                         ++edges_clipped;
381                 }
382         }
383
384         if(*voutnum <= 0) return -1;
385         return edges_clipped > 0 ? 0 : 1;
386 }