515919e29f7649b4e43afb960a537adbb723b2ab
[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
23 #define MAT_STACK_SIZE  4
24
25 static int vp[4];
26 static int32_t mat[MAT_STACK_SIZE][16];
27 static int mtop;
28
29 void xgl_init(void)
30 {
31         xgl_viewport(0, 0, 240, 160);
32         xgl_load_identity();
33 }
34
35 void xgl_viewport(int x, int y, int w, int h)
36 {
37         vp[0] = x;
38         vp[1] = y;
39         vp[2] = w;
40         vp[3] = h;
41 }
42
43 void xgl_push_matrix(void)
44 {
45         int prev;
46
47         if(mtop >= MAT_STACK_SIZE - 1) return;
48
49         prev = mtop++;
50         memcpy(mat[mtop], mat[prev], sizeof mat[0]);
51 }
52
53 void xgl_pop_matrix(void)
54 {
55         if(mtop > 0) mtop--;
56 }
57
58 static const int32_t id[] = {
59         0x10000, 0, 0, 0,
60         0, 0x10000, 0, 0,
61         0, 0, 0x10000, 0,
62         0, 0, 0, 0x10000
63 };
64
65 void xgl_load_identity(void)
66 {
67         xgl_load_matrix(id);
68 }
69
70 void xgl_load_matrix(const int32_t *m)
71 {
72         memcpy(mat[mtop], m, sizeof mat[0]);
73 }
74
75 #define M(i,j)  (((i) << 2) + (j))
76 #define XMUL(a, b)      (((a) >> 8) * ((b) >> 8))
77 void xgl_mult_matrix(const int32_t *m2)
78 {
79         int i, j;
80         int32_t m1[16];
81         int32_t *dest = mat[mtop];
82
83         memcpy(m1, dest, sizeof m1);
84
85         for(i=0; i<4; i++) {
86                 for(j=0; j<4; j++) {
87                         *dest++ = XMUL(m1[M(0, j)], m2[M(i, 0)]) +
88                                 XMUL(m1[M(1, j)], m2[M(i, 1)]) +
89                                 XMUL(m1[M(2, j)], m2[M(i, 2)]) +
90                                 XMUL(m1[M(3, j)], m2[M(i, 3)]);
91                 }
92         }
93 }
94
95 #define XSIN(x)         (int32_t)(sin(x / 65536.0f) * 65536.0f)
96 #define XCOS(x)         (int32_t)(cos(x / 65536.0f) * 65536.0f)
97
98 void xgl_translate(int32_t x, int32_t y, int32_t z)
99 {
100         int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
101         m[12] = x;
102         m[13] = y;
103         m[14] = z;
104         xgl_mult_matrix(m);
105 }
106
107 void xgl_rotate_x(int32_t angle)
108 {
109         int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
110         int32_t sa = XSIN(angle);
111         int32_t ca = XCOS(angle);
112         m[5] = ca;
113         m[6] = sa;
114         m[9] = -sa;
115         m[10] = ca;
116         xgl_mult_matrix(m);
117 }
118
119 void xgl_rotate_y(int32_t angle)
120 {
121         int32_t m[16] = {0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000, 0, 0, 0, 0, 0x10000};
122         int32_t sa = XSIN(angle);
123         int32_t ca = XCOS(angle);
124         m[0] = ca;
125         m[2] = -sa;
126         m[8] = sa;
127         m[10] = ca;
128         xgl_mult_matrix(m);
129 }
130
131 void xgl_rotate_z(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[0] = ca;
137         m[1] = sa;
138         m[4] = -sa;
139         m[5] = ca;
140         xgl_mult_matrix(m);
141 }
142
143 void xgl_scale(int32_t x, int32_t y, int32_t z)
144 {
145         int32_t m[16] = {0};
146         m[0] = x;
147         m[5] = y;
148         m[10] = z;
149         m[15] = 0x10000;
150         xgl_mult_matrix(m);
151 }
152
153 static void xform(struct xvertex *out, const struct xvertex *in, const int32_t *m)
154 {
155         out->x = XMUL(m[0], in->x) + XMUL(m[4], in->y) + XMUL(m[8], in->z) + m[12];
156         out->y = XMUL(m[1], in->x) + XMUL(m[5], in->y) + XMUL(m[9], in->z) + m[13];
157         out->z = XMUL(m[2], in->x) + XMUL(m[6], in->y) + XMUL(m[10], in->z) + m[14];
158 }
159
160 /* d = 1.0 / tan(fov/2) */
161 #define PROJ_D  2.0f
162
163 void xgl_draw(int prim, const struct xvertex *varr, int vcount)
164 {
165         int i, cidx;
166         struct xvertex xv[4];
167         struct pvertex pv[4];
168
169         while(vcount >= prim) {
170                 cidx = varr->cidx;
171                 for(i=0; i<prim; i++) {
172                         xform(xv + i, varr, mat[mtop]);
173                         varr++;
174
175                         xv[i].x = xv[i].x / (xv[i].z >> 8);     /* assume aspect: ~2 */
176                         xv[i].y = (xv[i].y << 1) / (xv[i].z >> 8);
177                         /* projection result is 24.8 */
178
179                         /* viewport */
180                         pv[i].x = (((xv[i].x + 0x100) >> 1) * vp[2]) + (vp[0] << 8);
181                         pv[i].y = (((0x100 - xv[i].y) >> 1) * vp[3]) + (vp[1] << 8);
182                 }
183                 vcount -= prim;
184
185                 polyfill_flat(pv, prim, cidx);
186         }
187 }