half-assed 3D graphics attempt underway
[regis] / x3d.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <math.h>
4 #include "x3d.h"
5 #include "fixed.h"
6 #include "sincos.h"
7 #include "regis.h"
8
9 typedef struct pvec3 {
10         int32_t x, y, z;
11 } pvec3;
12
13 typedef struct pvec2 {
14         int32_t x, y;
15 } pvec2;
16
17
18 #define MAT_STACK_SIZE  4
19
20 struct matrix {
21         int32_t m[12];
22 };
23
24 static void proc_vertex(const int32_t *vin, pvec3 *vout);
25
26 void draw_poly(int num, const pvec3 *verts, int color);
27 void draw_point(const pvec3 *v, int color);
28
29
30 static int32_t proj_fov = M_PI_X16;
31 static int32_t proj_aspect = 65536;
32 static int32_t inv_proj_aspect = 65536;
33 static int32_t proj_near = ftox16(0.5);
34 static int32_t proj_far = 500 << 16;
35 static int32_t inv_tan_half_xfov, inv_tan_half_yfov;
36
37 #define ID_INIT {65536, 0, 0, 0, 0, 65536, 0, 0, 0, 0, 65536, 0}
38
39 static struct matrix identity = { ID_INIT };
40
41 static short mtop;
42 static struct matrix mstack[MAT_STACK_SIZE] = { {ID_INIT}, {ID_INIT} };
43
44 static const int32_t *vertex_array;
45 static unsigned short vertex_count;
46
47 static uint8_t im_color_index;
48
49
50 void x3d_projection(int fov, int32_t aspect, int32_t nearz, int32_t farz)
51 {
52         proj_fov = (M_PI_X16 * fov) / 180;
53         proj_aspect = aspect;
54         inv_proj_aspect = x16div(65536, proj_aspect);
55         proj_near = nearz;
56         proj_far = farz;
57
58         inv_tan_half_yfov = (int32_t)(65536.0 / tan(0.5 * proj_fov / 65536.0));
59         inv_tan_half_xfov = x16mul(inv_tan_half_yfov, aspect);
60 }
61
62 int x3d_push_matrix(void)
63 {
64         short newtop = mtop + 1;
65         if(newtop >= MAT_STACK_SIZE) {
66                 return -1;
67         }
68         memcpy(mstack + newtop, mstack + mtop, sizeof *mstack);
69         mtop = newtop;
70         return 0;
71 }
72
73 int x3d_pop_matrix(void)
74 {
75         if(mtop <= 0) {
76                 return -1;
77         }
78         --mtop;
79         return 0;
80 }
81
82 void x3d_load_matrix(int32_t *m)
83 {
84         memcpy(mstack[mtop].m, m, sizeof *mstack);
85 }
86
87
88 #define M(i,j)  (((i) << 2) + (j))
89 void x3d_mult_matrix(int32_t *m)
90 {
91         int i, j;
92         struct matrix tmp;
93
94         memcpy(tmp.m, mstack[mtop].m, sizeof tmp);
95
96         for(i=0; i<3; i++) {
97                 for(j=0; j<4; j++) {
98                         mstack[mtop].m[M(i, j)] =
99                                 x16mul(m[M(0, j)], tmp.m[M(i, 0)]) +
100                                 x16mul(m[M(1, j)], tmp.m[M(i, 1)]) +
101                                 x16mul(m[M(2, j)], tmp.m[M(i, 2)]);
102                 }
103                 mstack[mtop].m[M(i, 3)] += tmp.m[M(i, 3)];
104         }
105 }
106
107 void x3d_load_identity(void)
108 {
109         memcpy(mstack[mtop].m, identity.m, sizeof identity);
110 }
111
112 void x3d_translate(int32_t x, int32_t y, int32_t z)
113 {
114         int32_t m[] = ID_INIT;
115         m[3] = x;
116         m[7] = y;
117         m[11] = z;
118
119         x3d_mult_matrix(m);
120 }
121
122 void x3d_rotate(int32_t deg, int32_t x, int32_t y, int32_t z)
123 {
124         int32_t xform[] = ID_INIT;
125
126         int32_t angle = x16mul(M_PI_X16, deg) / 180;
127         int32_t sina = sin_x16(angle);
128         int32_t cosa = cos_x16(angle);
129         int32_t one_minus_cosa = 65536 - cosa;
130         int32_t nxsq = x16sq(x);
131         int32_t nysq = x16sq(y);
132         int32_t nzsq = x16sq(z);
133
134         xform[0] = nxsq + x16mul(65536 - nxsq, cosa);
135         xform[4] = x16mul(x16mul(x, y), one_minus_cosa) - x16mul(z, sina);
136         xform[8] = x16mul(x16mul(x, z), one_minus_cosa) + x16mul(y, sina);
137         xform[1] = x16mul(x16mul(x, y), one_minus_cosa) + x16mul(z, sina);
138         xform[5] = nysq + x16mul(65536 - nysq, cosa);
139         xform[9] = x16mul(x16mul(y, z), one_minus_cosa) - x16mul(x, sina);
140         xform[2] = x16mul(x16mul(x, z), one_minus_cosa) - x16mul(y, sina);
141         xform[6] = x16mul(x16mul(y, z), one_minus_cosa) + x16mul(x, sina);
142         xform[10] = nzsq + x16mul(65536 - nzsq, cosa);
143
144         x3d_mult_matrix(xform);
145 }
146
147 void x3d_scale(int32_t x, int32_t y, int32_t z)
148 {
149         int32_t m[] = ID_INIT;
150
151         m[0] = x;
152         m[5] = y;
153         m[10] = z;
154
155         x3d_mult_matrix(m);
156 }
157
158 void x3d_vertex_array(int count, const int32_t *ptr)
159 {
160         vertex_array = ptr;
161         vertex_count = count;
162 }
163
164 int x3d_draw(int prim, int vnum)
165 {
166         int i, j, pverts = prim;
167         const int32_t *vptr = vertex_array;
168         uint16_t color;
169
170         if(!vertex_array) return -1;
171
172         if(vnum > vertex_count) {
173                 vnum = vertex_count;
174         }
175
176         for(i=0; i<vnum; i+=pverts) {
177                 /* process vertices */
178                 pvec3 vpos[4];
179
180                 for(j=0; j<pverts; j++) {
181                         proc_vertex(vptr, vpos + j);
182
183                         if(vpos[j].z <= proj_near) {
184                                 goto skip_prim;
185                         }
186
187                         vptr += 3;
188                 }
189
190                 color = im_color_index;
191
192                 /* project & viewport */
193                 for(j=0; j<pverts; j++) {
194                         int32_t x, y;
195
196                         x = x16mul(vpos[j].x, inv_tan_half_xfov);
197                         x = x16div(x, vpos[j].z);
198                         vpos[j].x = (x16mul(x, inv_proj_aspect) + 65536) * (WIDTH / 2);
199
200                         y = x16mul(vpos[j].y, inv_tan_half_yfov);
201                         y = x16div(y, vpos[j].z);
202                         vpos[j].y = (65536 - y) * (HEIGHT / 2);
203                 }
204
205                 switch(pverts) {
206                 case X3D_POINTS:
207                         draw_point(vpos, color);
208                         break;
209
210                 case X3D_LINES:
211                         break;
212
213                 case X3D_TRIANGLES:
214                 case X3D_QUADS:
215                         draw_poly(pverts, vpos, im_color_index);
216                         break;
217                 }
218 skip_prim: ;
219         }
220
221         return 0;
222 }
223
224 static void proc_vertex(const int32_t *vin, pvec3 *vout)
225 {
226         int i;
227         int32_t tvert[3];
228         int32_t *mvmat = mstack[mtop].m;
229
230         /* transform vertex with current matrix */
231         for(i=0; i<3; i++) {
232                 tvert[i] = x16mul(mvmat[0], vin[0]) +
233                         x16mul(mvmat[1], vin[1]) +
234                         x16mul(mvmat[2], vin[2]) +
235                         mvmat[3];
236                 mvmat += 4;
237         }
238
239         vout->x = tvert[0];
240         vout->y = tvert[1];
241         vout->z = tvert[2];
242 }
243
244 void x3d_color_index(int cidx)
245 {
246         im_color_index = cidx;
247 }
248
249
250 void draw_poly(int num, const pvec3 *verts, int color)
251 {
252         int i;
253         regis_abspos(verts[0].x, verts[0].y);
254         regis_begin_vector(REGIS_BOUNDED);
255
256         for(i=0; i<num-1; i++) {
257                 ++verts;
258                 regis_absv(verts->x, verts->y);
259         }
260         regis_end_vector();
261 }
262
263 void draw_point(const pvec3 *v, int color)
264 {
265 }