2 gbajam22 entry for the Gameboy Advance
3 Copyright (C) 2022 John Tsiombikas <nuclear@mutantstargoat.com>
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/>.
25 #define MAT_STACK_SIZE 4
28 static int32_t mat[MAT_STACK_SIZE][16];
30 static unsigned int opt;
31 static int32_t ldir[3];
35 static void draw_ptlines(int prim, const struct xvertex *varr, int vcount);
40 xgl_viewport(0, 0, 240, 160);
43 ldir[0] = ldir[1] = 0;
47 void xgl_enable(unsigned int o)
52 void xgl_disable(unsigned int o)
57 void xgl_viewport(int x, int y, int w, int h)
65 void xgl_push_matrix(void)
69 if(mtop >= MAT_STACK_SIZE - 1) return;
72 memcpy(mat[mtop], mat[prev], sizeof mat[0]);
75 void xgl_pop_matrix(void)
80 static int32_t id[] = {
87 void xgl_load_identity(void)
89 memcpy(mat[mtop], id, sizeof mat[0]);
92 void xgl_load_matrix(const int32_t *m)
94 memcpy(mat[mtop], m, sizeof mat[0]);
97 void xgl_get_matrix(int32_t *m)
99 memcpy(m, mat[mtop], sizeof mat[0]);
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)
108 int32_t *dest = mat[mtop];
110 memcpy(m1, dest, sizeof m1);
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)]);
123 #define XSIN(x) (int32_t)(sin(x / 65536.0f) * 65536.0f)
124 #define XCOS(x) (int32_t)(cos(x / 65536.0f) * 65536.0f)
126 #define XSIN(x) SIN(((x) << 8) / (X_2PI >> 8))
127 #define XCOS(x) COS(((x) << 8) / (X_2PI >> 8))
130 void xgl_translate(int32_t x, int32_t y, int32_t z)
132 int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
139 void xgl_rotate_x(int32_t angle)
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);
151 void xgl_rotate_y(int32_t angle)
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);
163 void xgl_rotate_z(int32_t angle)
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);
175 void xgl_scale(int32_t x, int32_t y, int32_t z)
185 void xgl_index(int cidx)
190 static inline void xform(struct xvertex *out, const struct xvertex *in, const int32_t *m)
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];
197 static inline void xform_norm(struct xvertex *out, const struct xvertex *in, const int32_t *m)
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);
204 /* d = 1.0 / tan(fov/2) */
205 #define PROJ_D 0x20000
207 #define NEAR_Z 0x18000
210 void xgl_draw(int prim, const struct xvertex *varr, int vcount)
212 int i, cidx, clipnum;
213 struct xvertex xv[4], xvclip[8];
214 struct pvertex pv[4];
218 draw_ptlines(prim, varr, vcount);
222 while(vcount >= prim) {
223 cidx = cur_cidx;//varr->cidx;
225 xform(xv, varr, mat[mtop]);
226 xform_norm(xv, varr, mat[mtop]);
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;
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]);
242 /* clip against near plane */
243 xgl_clip_near(xvclip, &clipnum, xv, prim);
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 */
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);
254 polyfill_flat(pv, clipnum, cidx);
261 void xgl_transform(const struct xvertex *vin, int *x, int *y)
264 xform(&v, vin, mat[mtop]);
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 */
270 *x = ((((v.x + 0x100) >> 1) * vp[2]) >> 8) + vp[0];
271 *y = ((((0x100 - v.y) >> 1) * vp[3]) >> 8) + vp[1];
274 static void draw_ptlines(int prim, const struct xvertex *varr, int vcount)
277 struct xvertex xv[2];
279 while(vcount >= prim) {
280 for(i=0; i<prim; i++) {
281 xform(xv + i, varr, mat[mtop]);
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 */
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];
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);
297 draw_line(xv[0].x, xv[0].y, xv[1].x, xv[1].y, 0xff);
303 mat[mtop][12] = mat[mtop][13] = 0;
306 #define ISECT_NEAR(v0, v1) ((((v0)->z - NEAR_Z) << 8) / ((v0)->z - (v1)->z))
308 #define LERP_VATTR(res, v0, v1, t) \
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); \
322 static int clip_edge_near(struct xvertex *poly, int *vnumptr, struct xvertex *v0, struct xvertex *v1)
327 struct xvertex *vptr;
329 in0 = v0->z >= NEAR_Z ? 1 : 0;
330 in1 = v1->z >= NEAR_Z ? 1 : 0;
336 poly[vnum++] = *v1; /* append v1 */
342 t = ISECT_NEAR(v0, v1); /* 24:8 */
343 LERP_VATTR(vptr, v0, v1, t);
344 ++vnum; /* append new vertex on the intersection point */
351 t = ISECT_NEAR(v0, v1);
352 LERP_VATTR(vptr, v0, v1, t);
353 ++vnum; /* append new vertex ... */
366 /* special case near-plane clipper */
368 int xgl_clip_near(struct xvertex *vout, int *voutnum, struct xvertex *vin, int vnum)
371 int edges_clipped = 0;
375 for(i=0; i<vnum; i++) {
377 if(nextidx >= vnum) nextidx = 0;
378 res = clip_edge_near(vout, voutnum, vin + i, vin + nextidx);
384 if(*voutnum <= 0) return -1;
385 return edges_clipped > 0 ? 0 : 1;