suzanne
[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 void xgl_init(void)
33 {
34         xgl_viewport(0, 0, 240, 160);
35         xgl_load_identity();
36
37         ldir[0] = ldir[1] = 0;
38         ldir[2] = -0x100;
39 }
40
41 void xgl_enable(unsigned int o)
42 {
43         opt |= o;
44 }
45
46 void xgl_disable(unsigned int o)
47 {
48         opt &= ~o;
49 }
50
51 void xgl_viewport(int x, int y, int w, int h)
52 {
53         vp[0] = x;
54         vp[1] = y;
55         vp[2] = w;
56         vp[3] = h;
57 }
58
59 void xgl_push_matrix(void)
60 {
61         int prev;
62
63         if(mtop >= MAT_STACK_SIZE - 1) return;
64
65         prev = mtop++;
66         memcpy(mat[mtop], mat[prev], sizeof mat[0]);
67 }
68
69 void xgl_pop_matrix(void)
70 {
71         if(mtop > 0) mtop--;
72 }
73
74 static const int32_t id[] = {
75         0x10000, 0, 0, 0,
76         0, 0x10000, 0, 0,
77         0, 0, 0x10000, 0,
78         0, 0, 0, 0x10000
79 };
80
81 void xgl_load_identity(void)
82 {
83         xgl_load_matrix(id);
84 }
85
86 void xgl_load_matrix(const int32_t *m)
87 {
88         memcpy(mat[mtop], m, sizeof mat[0]);
89 }
90
91 #define M(i,j)  (((i) << 2) + (j))
92 #define XMUL(a, b)      (((a) >> 8) * ((b) >> 8))
93 void xgl_mult_matrix(const int32_t *m2)
94 {
95         int i, j;
96         int32_t m1[16];
97         int32_t *dest = mat[mtop];
98
99         memcpy(m1, dest, sizeof m1);
100
101         for(i=0; i<4; i++) {
102                 for(j=0; j<4; j++) {
103                         *dest++ = XMUL(m1[M(0, j)], m2[M(i, 0)]) +
104                                 XMUL(m1[M(1, j)], m2[M(i, 1)]) +
105                                 XMUL(m1[M(2, j)], m2[M(i, 2)]) +
106                                 XMUL(m1[M(3, j)], m2[M(i, 3)]);
107                 }
108         }
109 }
110
111 #define XSIN(x)         (int32_t)(sin(x / 65536.0f) * 65536.0f)
112 #define XCOS(x)         (int32_t)(cos(x / 65536.0f) * 65536.0f)
113
114 void xgl_translate(int32_t x, int32_t y, int32_t z)
115 {
116         int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
117         m[12] = x;
118         m[13] = y;
119         m[14] = z;
120         xgl_mult_matrix(m);
121 }
122
123 void xgl_rotate_x(int32_t angle)
124 {
125         int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
126         int32_t sa = XSIN(angle);
127         int32_t ca = XCOS(angle);
128         m[5] = ca;
129         m[6] = sa;
130         m[9] = -sa;
131         m[10] = ca;
132         xgl_mult_matrix(m);
133 }
134
135 void xgl_rotate_y(int32_t angle)
136 {
137         int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
138         int32_t sa = XSIN(angle);
139         int32_t ca = XCOS(angle);
140         m[0] = ca;
141         m[2] = -sa;
142         m[8] = sa;
143         m[10] = ca;
144         xgl_mult_matrix(m);
145 }
146
147 void xgl_rotate_z(int32_t angle)
148 {
149         int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
150         int32_t sa = XSIN(angle);
151         int32_t ca = XCOS(angle);
152         m[0] = ca;
153         m[1] = sa;
154         m[4] = -sa;
155         m[5] = ca;
156         xgl_mult_matrix(m);
157 }
158
159 void xgl_scale(int32_t x, int32_t y, int32_t z)
160 {
161         int32_t m[16] = {0};
162         m[0] = x;
163         m[5] = y;
164         m[10] = z;
165         m[15] = 0x10000;
166         xgl_mult_matrix(m);
167 }
168
169 static void xform(struct xvertex *out, const struct xvertex *in, const int32_t *m)
170 {
171         out->x = XMUL(m[0], in->x) + XMUL(m[4], in->y) + XMUL(m[8], in->z) + m[12];
172         out->y = XMUL(m[1], in->x) + XMUL(m[5], in->y) + XMUL(m[9], in->z) + m[13];
173         out->z = XMUL(m[2], in->x) + XMUL(m[6], in->y) + XMUL(m[10], in->z) + m[14];
174 }
175
176 static void xform_norm(struct xvertex *out, const struct xvertex *in, const int32_t *m)
177 {
178         out->nx = XMUL(m[0], in->nx) + XMUL(m[4], in->ny) + XMUL(m[8], in->nz);
179         out->ny = XMUL(m[1], in->nx) + XMUL(m[5], in->ny) + XMUL(m[9], in->nz);
180         out->nz = XMUL(m[2], in->nx) + XMUL(m[6], in->ny) + XMUL(m[10], in->nz);
181 }
182
183 /* d = 1.0 / tan(fov/2) */
184 #define PROJ_D  0x20000
185
186 void xgl_draw(int prim, const struct xvertex *varr, int vcount)
187 {
188         int i, cidx;
189         struct xvertex xv[4];
190         struct pvertex pv[4];
191         int32_t ndotl;
192
193         while(vcount >= prim) {
194                 cidx = varr->cidx;
195
196                 xform(xv, varr, mat[mtop]);
197                 xform_norm(xv, varr, mat[mtop]);
198
199                 if(xv->nz > 0) {
200                         /* backface */
201                         varr += prim;
202                         vcount -= prim;
203                         continue;
204                 }
205
206                 if(opt & XGL_LIGHTING) {
207                         ndotl = (xv->nx >> 8) * ldir[0] + (xv->ny >> 8) * ldir[1] + (xv->nz >> 8) * ldir[2];
208                         if(ndotl < 0) ndotl = 0;
209                         cidx = 128 + (ndotl >> 9);
210                         if(cidx > 255) cidx = 255;
211                 }
212
213                 xv->x = (xv->x << 1) / (xv->z >> 8);    /* assume aspect: ~2 */
214                 xv->y = (xv->y << 2) / (xv->z >> 8);    /* the shift is * PROJ_D */
215                 /* projection result is 24.8 */
216                 /* viewport */
217                 pv->x = (((xv->x + 0x100) >> 1) * vp[2]) + (vp[0] << 8);
218                 pv->y = (((0x100 - xv->y) >> 1) * vp[3]) + (vp[1] << 8);
219                 varr++;
220
221                 for(i=1; i<prim; i++) {
222                         xform(xv + i, varr, mat[mtop]);
223
224                         xv[i].x = (xv[i].x << 1) / (xv[i].z >> 8);      /* assume aspect: ~2 */
225                         xv[i].y = (xv[i].y << 2) / (xv[i].z >> 8);      /* the shift is * PROJ_D */
226                         /* projection result is 24.8 */
227                         /* viewport */
228                         pv[i].x = (((xv[i].x + 0x100) >> 1) * vp[2]) + (vp[0] << 8);
229                         pv[i].y = (((0x100 - xv[i].y) >> 1) * vp[3]) + (vp[1] << 8);
230                         varr++;
231                 }
232                 vcount -= prim;
233
234                 polyfill_flat(pv, prim, cidx);
235         }
236 }