setting up to improve the rasterizer
[dosdemo] / src / 3dgfx.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <math.h>
5 #include <assert.h>
6 #include "3dgfx.h"
7 #include "polyfill.h"
8 #include "inttypes.h"
9
10 #define STACK_SIZE      8
11 typedef float g3d_matrix[16];
12
13 #define MAX_VBUF_SIZE   256
14
15 struct g3d_state {
16         unsigned int opt;
17         int frontface;
18         int fill_mode;
19
20         g3d_matrix mat[G3D_NUM_MATRICES][STACK_SIZE];
21         int mtop[G3D_NUM_MATRICES];
22         int mmode;
23
24         g3d_matrix norm_mat;
25
26         int width, height;
27         void *pixels;
28 };
29
30 static void xform4_vec3(const float *mat, float *vec);
31 static void xform3_vec3(const float *mat, float *vec);
32 static void shade(struct g3d_vertex *v);
33
34 static struct g3d_state *st;
35 static const float idmat[] = {
36         1, 0, 0, 0,
37         0, 1, 0, 0,
38         0, 0, 1, 0,
39         0, 0, 0, 1
40 };
41
42 int g3d_init(void)
43 {
44         int i;
45
46         if(!(st = calloc(1, sizeof *st))) {
47                 fprintf(stderr, "failed to allocate G3D context\n");
48                 return -1;
49         }
50         st->fill_mode = POLYFILL_FLAT;
51
52         for(i=0; i<G3D_NUM_MATRICES; i++) {
53                 g3d_matrix_mode(i);
54                 g3d_load_identity();
55         }
56         return 0;
57 }
58
59 void g3d_destroy(void)
60 {
61         free(st);
62 }
63
64 void g3d_framebuffer(int width, int height, void *pixels)
65 {
66         st->width = width;
67         st->height = height;
68         st->pixels = pixels;
69
70         pimg_fb.pixels = pixels;
71         pimg_fb.width = width;
72         pimg_fb.height = height;
73 }
74
75 void g3d_enable(unsigned int opt)
76 {
77         st->opt |= opt;
78 }
79
80 void g3d_disable(unsigned int opt)
81 {
82         st->opt &= ~opt;
83 }
84
85 void g3d_setopt(unsigned int opt, unsigned int mask)
86 {
87         st->opt = (st->opt & ~mask) | (opt & mask);
88 }
89
90 unsigned int g3d_getopt(unsigned int mask)
91 {
92         return st->opt & mask;
93 }
94
95 void g3d_front_face(unsigned int order)
96 {
97         st->frontface = order;
98 }
99
100 void g3d_polygon_mode(int pmode)
101 {
102         st->fill_mode = pmode;
103 }
104
105 void g3d_matrix_mode(int mmode)
106 {
107         st->mmode = mmode;
108 }
109
110 void g3d_load_identity(void)
111 {
112         int top = st->mtop[st->mmode];
113         memcpy(st->mat[st->mmode][top], idmat, 16 * sizeof(float));
114 }
115
116 void g3d_load_matrix(const float *m)
117 {
118         int top = st->mtop[st->mmode];
119         memcpy(st->mat[st->mmode][top], m, 16 * sizeof(float));
120 }
121
122 #define M(i,j)  (((i) << 2) + (j))
123 void g3d_mult_matrix(const float *m2)
124 {
125         int i, j, top = st->mtop[st->mmode];
126         float m1[16];
127         float *dest = st->mat[st->mmode][top];
128
129         memcpy(m1, dest, sizeof m1);
130
131         for(i=0; i<4; i++) {
132                 for(j=0; j<4; j++) {
133                         *dest++ = m1[M(0,j)] * m2[M(i,0)] +
134                                 m1[M(1,j)] * m2[M(i,1)] +
135                                 m1[M(2,j)] * m2[M(i,2)] +
136                                 m1[M(3,j)] * m2[M(i,3)];
137                 }
138         }
139 }
140
141 void g3d_push_matrix(void)
142 {
143         int top = st->mtop[st->mmode];
144         if(top >= G3D_NUM_MATRICES) {
145                 fprintf(stderr, "g3d_push_matrix overflow\n");
146                 return;
147         }
148         memcpy(st->mat[st->mmode][top + 1], st->mat[st->mmode][top], 16 * sizeof(float));
149         st->mtop[st->mmode] = top + 1;
150 }
151
152 void g3d_pop_matrix(void)
153 {
154         if(st->mtop[st->mmode] <= 0) {
155                 fprintf(stderr, "g3d_pop_matrix underflow\n");
156                 return;
157         }
158         --st->mtop[st->mmode];
159 }
160
161 void g3d_translate(float x, float y, float z)
162 {
163         float m[] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1};
164         m[12] = x;
165         m[13] = y;
166         m[14] = z;
167         g3d_mult_matrix(m);
168 }
169
170 void g3d_rotate(float deg, float x, float y, float z)
171 {
172         float m[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
173
174         float angle = M_PI * deg / 180.0f;
175         float sina = sin(angle);
176         float cosa = cos(angle);
177         float one_minus_cosa = 1.0f - cosa;
178         float nxsq = x * x;
179         float nysq = y * y;
180         float nzsq = z * z;
181
182         m[0] = nxsq + (1.0f - nxsq) * cosa;
183         m[4] = x * y * one_minus_cosa - z * sina;
184         m[8] = x * z * one_minus_cosa + y * sina;
185         m[1] = x * y * one_minus_cosa + z * sina;
186         m[5] = nysq + (1.0 - nysq) * cosa;
187         m[9] = y * z * one_minus_cosa - x * sina;
188         m[2] = x * z * one_minus_cosa - y * sina;
189         m[6] = y * z * one_minus_cosa + x * sina;
190         m[10] = nzsq + (1.0 - nzsq) * cosa;
191         m[15] = 1.0f;
192
193         g3d_mult_matrix(m);
194 }
195
196 void g3d_scale(float x, float y, float z)
197 {
198         float m[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
199         m[0] = x;
200         m[5] = y;
201         m[10] = z;
202         m[15] = 1.0f;
203         g3d_mult_matrix(m);
204 }
205
206 void g3d_ortho(float left, float right, float bottom, float top, float znear, float zfar)
207 {
208         float m[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
209
210         float dx = right - left;
211         float dy = top - bottom;
212         float dz = zfar - znear;
213
214         m[0] = 2.0 / dx;
215         m[5] = 2.0 / dy;
216         m[10] = -2.0 / dz;
217         m[12] = -(right + left) / dx;
218         m[13] = -(top + bottom) / dy;
219         m[14] = -(zfar + znear) / dz;
220
221         g3d_mult_matrix(m);
222 }
223
224 void g3d_frustum(float left, float right, float bottom, float top, float nr, float fr)
225 {
226         float m[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
227
228         float dx = right - left;
229         float dy = top - bottom;
230         float dz = fr - nr;
231
232         float a = (right + left) / dx;
233         float b = (top + bottom) / dy;
234         float c = -(fr + nr) / dz;
235         float d = -2.0 * fr * nr / dz;
236
237         m[0] = 2.0 * nr / dx;
238         m[5] = 2.0 * nr / dy;
239         m[8] = a;
240         m[9] = b;
241         m[10] = c;
242         m[11] = -1.0f;
243         m[14] = d;
244
245         g3d_mult_matrix(m);
246 }
247
248 void g3d_perspective(float vfov_deg, float aspect, float znear, float zfar)
249 {
250         float m[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
251
252         float vfov = M_PI * vfov_deg / 180.0f;
253         float s = 1.0f / tan(vfov * 0.5f);
254         float range = znear - zfar;
255
256         m[0] = s / aspect;
257         m[5] = s;
258         m[10] = (znear + zfar) / range;
259         m[11] = -1.0f;
260         m[14] = 2.0f * znear * zfar / range;
261
262         g3d_mult_matrix(m);
263 }
264
265 const float *g3d_get_matrix(int which, float *m)
266 {
267         int top = st->mtop[which];
268
269         if(m) {
270                 memcpy(m, st->mat[which][top], 16 * sizeof(float));
271         }
272         return st->mat[which][top];
273 }
274
275 void g3d_draw(int prim, const struct g3d_vertex *varr, int varr_size)
276 {
277         g3d_draw_indexed(prim, varr, varr_size, 0, 0);
278 }
279
280 void g3d_draw_indexed(int prim, const struct g3d_vertex *varr, int varr_size,
281                 const int16_t *iarr, int iarr_size)
282 {
283         int i, j, nfaces;
284         struct pvertex pv[4];
285         struct g3d_vertex v[4];
286         int vnum = prim; /* primitive vertex counts correspond to enum values */
287         int mvtop = st->mtop[G3D_MODELVIEW];
288         int ptop = st->mtop[G3D_PROJECTION];
289
290         /* calc the normal matrix */
291         memcpy(st->norm_mat, st->mat[G3D_MODELVIEW][mvtop], 16 * sizeof(float));
292         st->norm_mat[12] = st->norm_mat[13] = st->norm_mat[14] = 0.0f;
293
294         nfaces = (iarr ? iarr_size : varr_size) / vnum;
295
296         for(j=0; j<nfaces; j++) {
297
298                 for(i=0; i<vnum; i++) {
299                         v[i] = iarr ? varr[*iarr++] : *varr++;
300
301                         xform4_vec3(st->mat[G3D_MODELVIEW][mvtop], &v[i].x);
302                         xform3_vec3(st->norm_mat, &v[i].nx);
303
304                         if(st->opt & G3D_LIGHTING) {
305                                 shade(v + i);
306                         }
307                         xform4_vec3(st->mat[G3D_PROJECTION][ptop], &v[i].x);
308                 }
309
310                 /* TODO clipping */
311
312                 for(i=0; i<vnum; i++) {
313                         if(v[i].w != 0.0f) {
314                                 v[i].x /= v[i].w;
315                                 v[i].y /= v[i].w;
316                                 /*v[i].z /= v[i].w;*/
317                         }
318
319                         /* viewport transformation */
320                         v[i].x = (v[i].x * 0.5f + 0.5f) * (float)st->width;
321                         v[i].y = (0.5f - v[i].y * 0.5f) * (float)st->height;
322
323                         /* convert pos to 24.8 fixed point */
324                         pv[i].x = (int32_t)(v[i].x * 256.0f);
325                         pv[i].y = (int32_t)(v[i].y * 256.0f);
326                         /* convert tex coords to 16.16 fixed point */
327                         pv[i].u = (int32_t)(v[i].u * 65536.0f);
328                         pv[i].v = (int32_t)(v[i].v * 65536.0f);
329                         /* pass the color through as is */
330                         pv[i].r = v[i].r;
331                         pv[i].g = v[i].g;
332                         pv[i].b = v[i].b;
333                 }
334
335                 /* backface culling */
336                 if(vnum > 2 && st->opt & G3D_CULL_FACE) {
337                         int32_t ax = pv[1].x - pv[0].x;
338                         int32_t ay = pv[1].y - pv[0].y;
339                         int32_t bx = pv[2].x - pv[0].x;
340                         int32_t by = pv[2].y - pv[0].y;
341                         int32_t cross_z = ax * (by >> 8) - ay * (bx >> 8);
342                         int sign = (cross_z >> 31) & 1;
343
344                         if(!(sign ^ st->frontface)) {
345                                 continue;       /* back-facing */
346                         }
347                 }
348
349                 polyfill(st->fill_mode, pv, vnum);
350         }
351 }
352
353 static void xform4_vec3(const float *mat, float *vec)
354 {
355         float x = mat[0] * vec[0] + mat[4] * vec[1] + mat[8] * vec[2] + mat[12];
356         float y = mat[1] * vec[0] + mat[5] * vec[1] + mat[9] * vec[2] + mat[13];
357         float z = mat[2] * vec[0] + mat[6] * vec[1] + mat[10] * vec[2] + mat[14];
358         float w = mat[3] * vec[0] + mat[7] * vec[1] + mat[11] * vec[2] + mat[15];
359
360         vec[0] = x;
361         vec[1] = y;
362         vec[2] = z;
363         vec[3] = w;
364 }
365
366 static void xform3_vec3(const float *mat, float *vec)
367 {
368         float x = mat[0] * vec[0] + mat[4] * vec[1] + mat[8] * vec[2];
369         float y = mat[1] * vec[0] + mat[5] * vec[1] + mat[9] * vec[2];
370         float z = mat[2] * vec[0] + mat[6] * vec[1] + mat[10] * vec[2];
371
372         vec[0] = x;
373         vec[1] = y;
374         vec[2] = z;
375 }
376
377 static void shade(struct g3d_vertex *v)
378 {
379         v->r = v->g = v->b = 255;
380 }