panning
[gba_blender] / src / xgl.c
1 /*
2 blender for the Gameboy Advance
3 Copyright (C) 2021  John Tsiombikas <nuclear@member.fsf.org>
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
24 #define MAT_STACK_SIZE  4
25
26 static int vp[4];
27 static int32_t mat[MAT_STACK_SIZE][16];
28 static int mtop;
29 static unsigned int opt;
30 static int32_t ldir[3];
31
32 static void draw_ptlines(int prim, const struct xvertex *varr, int vcount);
33
34
35 void xgl_init(void)
36 {
37         xgl_viewport(0, 0, 240, 160);
38         xgl_load_identity();
39
40         ldir[0] = ldir[1] = 0;
41         ldir[2] = -0x100;
42 }
43
44 void xgl_enable(unsigned int o)
45 {
46         opt |= o;
47 }
48
49 void xgl_disable(unsigned int o)
50 {
51         opt &= ~o;
52 }
53
54 void xgl_viewport(int x, int y, int w, int h)
55 {
56         vp[0] = x;
57         vp[1] = y;
58         vp[2] = w;
59         vp[3] = h;
60 }
61
62 void xgl_push_matrix(void)
63 {
64         int prev;
65
66         if(mtop >= MAT_STACK_SIZE - 1) return;
67
68         prev = mtop++;
69         memcpy(mat[mtop], mat[prev], sizeof mat[0]);
70 }
71
72 void xgl_pop_matrix(void)
73 {
74         if(mtop > 0) mtop--;
75 }
76
77 static int32_t id[] = {
78         0x10000, 0, 0, 0,
79         0, 0x10000, 0, 0,
80         0, 0, 0x10000, 0,
81         0, 0, 0, 0x10000
82 };
83
84 void xgl_load_identity(void)
85 {
86         memcpy(mat[mtop], id, sizeof mat[0]);
87 }
88
89 void xgl_load_matrix(const int32_t *m)
90 {
91         memcpy(mat[mtop], m, sizeof mat[0]);
92 }
93
94 void xgl_get_matrix(int32_t *m)
95 {
96         memcpy(m, mat[mtop], sizeof mat[0]);
97 }
98
99 #define M(i,j)  (((i) << 2) + (j))
100 #define XMUL(a, b)      (((a) >> 8) * ((b) >> 8))
101 void xgl_mult_matrix(const int32_t *m2)
102 {
103         int i, j;
104         int32_t m1[16];
105         int32_t *dest = mat[mtop];
106
107         memcpy(m1, dest, sizeof m1);
108
109         for(i=0; i<4; i++) {
110                 for(j=0; j<4; j++) {
111                         *dest++ = XMUL(m1[M(0, j)], m2[M(i, 0)]) +
112                                 XMUL(m1[M(1, j)], m2[M(i, 1)]) +
113                                 XMUL(m1[M(2, j)], m2[M(i, 2)]) +
114                                 XMUL(m1[M(3, j)], m2[M(i, 3)]);
115                 }
116         }
117 }
118
119 #define XSIN(x)         (int32_t)(sin(x / 65536.0f) * 65536.0f)
120 #define XCOS(x)         (int32_t)(cos(x / 65536.0f) * 65536.0f)
121
122 void xgl_translate(int32_t x, int32_t y, int32_t z)
123 {
124         int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
125         m[12] = x;
126         m[13] = y;
127         m[14] = z;
128         xgl_mult_matrix(m);
129 }
130
131 void xgl_rotate_x(int32_t angle)
132 {
133         int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
134         int32_t sa = XSIN(angle);
135         int32_t ca = XCOS(angle);
136         m[5] = ca;
137         m[6] = sa;
138         m[9] = -sa;
139         m[10] = ca;
140         xgl_mult_matrix(m);
141 }
142
143 void xgl_rotate_y(int32_t angle)
144 {
145         int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
146         int32_t sa = XSIN(angle);
147         int32_t ca = XCOS(angle);
148         m[0] = ca;
149         m[2] = -sa;
150         m[8] = sa;
151         m[10] = ca;
152         xgl_mult_matrix(m);
153 }
154
155 void xgl_rotate_z(int32_t angle)
156 {
157         int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
158         int32_t sa = XSIN(angle);
159         int32_t ca = XCOS(angle);
160         m[0] = ca;
161         m[1] = sa;
162         m[4] = -sa;
163         m[5] = ca;
164         xgl_mult_matrix(m);
165 }
166
167 void xgl_scale(int32_t x, int32_t y, int32_t z)
168 {
169         int32_t m[16] = {0};
170         m[0] = x;
171         m[5] = y;
172         m[10] = z;
173         m[15] = 0x10000;
174         xgl_mult_matrix(m);
175 }
176
177 static void xform(struct xvertex *out, const struct xvertex *in, const int32_t *m)
178 {
179         out->x = XMUL(m[0], in->x) + XMUL(m[4], in->y) + XMUL(m[8], in->z) + m[12];
180         out->y = XMUL(m[1], in->x) + XMUL(m[5], in->y) + XMUL(m[9], in->z) + m[13];
181         out->z = XMUL(m[2], in->x) + XMUL(m[6], in->y) + XMUL(m[10], in->z) + m[14];
182 }
183
184 static void xform_norm(struct xvertex *out, const struct xvertex *in, const int32_t *m)
185 {
186         out->nx = XMUL(m[0], in->nx) + XMUL(m[4], in->ny) + XMUL(m[8], in->nz);
187         out->ny = XMUL(m[1], in->nx) + XMUL(m[5], in->ny) + XMUL(m[9], in->nz);
188         out->nz = XMUL(m[2], in->nx) + XMUL(m[6], in->ny) + XMUL(m[10], in->nz);
189 }
190
191 /* d = 1.0 / tan(fov/2) */
192 #define PROJ_D  0x20000
193
194 void xgl_draw(int prim, const struct xvertex *varr, int vcount)
195 {
196         int i, cidx;
197         struct xvertex xv[4];
198         struct pvertex pv[4];
199         int32_t ndotl;
200
201         if(prim < 3) {
202                 draw_ptlines(prim, varr, vcount);
203                 return;
204         }
205
206         while(vcount >= prim) {
207                 cidx = varr->cidx;
208
209                 xform(xv, varr, mat[mtop]);
210                 xform_norm(xv, varr, mat[mtop]);
211
212                 if(xv->nz > 0) {
213                         /* backface */
214                         varr += prim;
215                         vcount -= prim;
216                         continue;
217                 }
218
219                 if(opt & XGL_LIGHTING) {
220                         ndotl = (xv->nx >> 8) * ldir[0] + (xv->ny >> 8) * ldir[1] + (xv->nz >> 8) * ldir[2];
221                         if(ndotl < 0) ndotl = 0;
222                         cidx = 128 + (ndotl >> 9);
223                         if(cidx > 255) cidx = 255;
224                 }
225
226                 xv->x = (xv->x << 1) / (xv->z >> 8);    /* assume aspect: ~2 */
227                 xv->y = (xv->y << 2) / (xv->z >> 8);    /* the shift is * PROJ_D */
228                 /* projection result is 24.8 */
229                 /* viewport */
230                 pv->x = (((xv->x + 0x100) >> 1) * vp[2]) + (vp[0] << 8);
231                 pv->y = (((0x100 - xv->y) >> 1) * vp[3]) + (vp[1] << 8);
232                 varr++;
233
234                 for(i=1; i<prim; i++) {
235                         xform(xv + i, varr, mat[mtop]);
236
237                         xv[i].x = (xv[i].x << 1) / (xv[i].z >> 8);      /* assume aspect: ~2 */
238                         xv[i].y = (xv[i].y << 2) / (xv[i].z >> 8);      /* the shift is * PROJ_D */
239                         /* projection result is 24.8 */
240                         /* viewport */
241                         pv[i].x = (((xv[i].x + 0x100) >> 1) * vp[2]) + (vp[0] << 8);
242                         pv[i].y = (((0x100 - xv[i].y) >> 1) * vp[3]) + (vp[1] << 8);
243                         varr++;
244                 }
245                 vcount -= prim;
246
247                 polyfill_flat(pv, prim, cidx);
248         }
249 }
250
251 void xgl_transform(const struct xvertex *vin, int *x, int *y)
252 {
253         struct xvertex v;
254         xform(&v, vin, mat[mtop]);
255
256         v.x = (v.x << 1) / (v.z >> 8);  /* assume aspect: ~2 */
257         v.y = (v.y << 2) / (v.z >> 8);  /* the shift is * PROJ_D */
258         /* projection result is 24.8 */
259         /* viewport */
260         *x = ((((v.x + 0x100) >> 1) * vp[2]) >> 8) + vp[0];
261         *y = ((((0x100 - v.y) >> 1) * vp[3]) >> 8) + vp[1];
262 }
263
264 static void draw_ptlines(int prim, const struct xvertex *varr, int vcount)
265 {
266         int i;
267         struct xvertex xv[2];
268
269         while(vcount >= prim) {
270                 for(i=0; i<prim; i++) {
271                         xform(xv + i, varr, mat[mtop]);
272
273                         xv[i].x = (xv[i].x << 1) / (xv[i].z >> 8);      /* assume aspect: ~2 */
274                         xv[i].y = (xv[i].y << 2) / (xv[i].z >> 8);      /* the shift is * PROJ_D */
275                         /* projection result is 24.8 */
276                         /* viewport */
277                         xv[i].x = ((((xv[i].x + 0x100) >> 1) * vp[2]) >> 8) + vp[0];
278                         xv[i].y = ((((0x100 - xv[i].y) >> 1) * vp[3]) >> 8) + vp[1];
279                         varr++;
280                 }
281                 vcount -= prim;
282
283                 /* line clipping */
284 #ifndef ALT_LCLIP
285                 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);
286 #endif
287                 draw_line(xv[0].x, xv[0].y, xv[1].x, xv[1].y, varr[-2].cidx);
288         }
289 }
290
291 void xgl_xyzzy(void)
292 {
293         mat[mtop][12] = mat[mtop][13] = 0;
294 }