2 blender for the Gameboy Advance
3 Copyright (C) 2021 John Tsiombikas <nuclear@member.fsf.org>
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.
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.
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/>.
24 #define MAT_STACK_SIZE 4
27 static int32_t mat[MAT_STACK_SIZE][16];
29 static unsigned int opt;
30 static int32_t ldir[3];
34 xgl_viewport(0, 0, 240, 160);
37 ldir[0] = ldir[1] = 0;
41 void xgl_enable(unsigned int o)
46 void xgl_disable(unsigned int o)
51 void xgl_viewport(int x, int y, int w, int h)
59 void xgl_push_matrix(void)
63 if(mtop >= MAT_STACK_SIZE - 1) return;
66 memcpy(mat[mtop], mat[prev], sizeof mat[0]);
69 void xgl_pop_matrix(void)
74 static const int32_t id[] = {
81 void xgl_load_identity(void)
86 void xgl_load_matrix(const int32_t *m)
88 memcpy(mat[mtop], m, sizeof mat[0]);
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)
97 int32_t *dest = mat[mtop];
99 memcpy(m1, dest, sizeof m1);
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)]);
111 #define XSIN(x) (int32_t)(sin(x / 65536.0f) * 65536.0f)
112 #define XCOS(x) (int32_t)(cos(x / 65536.0f) * 65536.0f)
114 void xgl_translate(int32_t x, int32_t y, int32_t z)
116 int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
123 void xgl_rotate_x(int32_t angle)
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);
135 void xgl_rotate_y(int32_t angle)
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);
147 void xgl_rotate_z(int32_t angle)
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);
159 void xgl_scale(int32_t x, int32_t y, int32_t z)
169 static void xform(struct xvertex *out, const struct xvertex *in, const int32_t *m)
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];
176 static void xform_norm(struct xvertex *out, const struct xvertex *in, const int32_t *m)
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);
183 /* d = 1.0 / tan(fov/2) */
184 #define PROJ_D 0x20000
186 void xgl_draw(int prim, const struct xvertex *varr, int vcount)
189 struct xvertex xv[4];
190 struct pvertex pv[4];
193 while(vcount >= prim) {
196 xform(xv, varr, mat[mtop]);
197 xform_norm(xv, varr, mat[mtop]);
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;
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 */
217 pv->x = (((xv->x + 0x100) >> 1) * vp[2]) + (vp[0] << 8);
218 pv->y = (((0x100 - xv->y) >> 1) * vp[3]) + (vp[1] << 8);
221 for(i=1; i<prim; i++) {
222 xform(xv + i, varr, mat[mtop]);
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 */
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);
234 polyfill_flat(pv, prim, cidx);