mesh, obj loading, sorting, shitty normals...
[dos_low3d] / src / 3dgfx.c
1 #include "config.h"
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include "3dgfx.h"
7 #include "video.h"
8 #include "util.h"
9
10 struct rect {
11         int x0, y0, x1, y1;
12 };
13
14 static void reset_dirty(struct rect *r);
15
16 static int32_t mvmat[16];
17 static int32_t pmat[16];
18 static int vp[4];
19 static struct rect dirty_rect[2];
20 static unsigned int cur_dirty;
21
22 unsigned char *g3d_fbpixels;
23 int g3d_width, g3d_height;
24 int g3d_curcidx;
25
26
27
28 int g3d_init(void)
29 {
30         memset(mvmat, 0, sizeof mvmat);
31         memset(pmat, 0, sizeof pmat);
32         mvmat[0] = mvmat[5] = mvmat[10] = mvmat[15] = 0x10000;
33         pmat[0] = pmat[5] = pmat[10] = pmat[15] = 0x10000;
34         g3d_curcidx = 0xff;
35
36         cur_dirty = 0;
37         return 0;
38 }
39
40 void g3d_shutdown(void)
41 {
42 }
43
44 void g3d_framebuffer(int width, int height, void *fb)
45 {
46         g3d_fbpixels = fb ? fb : vid_backbuf;
47         g3d_width = width;
48         g3d_height = height;
49
50         vp[0] = vp[1] = 0;
51         vp[2] = width;
52         vp[3] = height;
53
54         reset_dirty(dirty_rect);
55         reset_dirty(dirty_rect + 1);
56 }
57
58 void g3d_framebuf_addr(void *fb)
59 {
60         g3d_fbpixels = vid_backbuf;
61 }
62
63 static void reset_dirty(struct rect *r)
64 {
65         r->x0 = XRES;
66         r->y0 = YRES;
67         r->x1 = 0;
68         r->y1 = 0;
69 }
70
71 void g3d_clear_dirty(void)
72 {
73         int width, height;
74         struct rect *dirty;
75
76         /* we need to clear based not on the last current dirty region, but the one
77          * before that. Then we need to reset the same one we used, and increment
78          * cur_dirty in preperation of this frames drawing
79          */
80         cur_dirty = (cur_dirty + 1) & 1;
81         dirty = dirty_rect + cur_dirty;
82
83         if(dirty->x0 < 0) dirty->x0 = 0;
84         if(dirty->y0 < 0) dirty->y0 = 0;
85         if(dirty->x1 >= XRES) dirty->x1 = XRES - 1;
86         if(dirty->y1 >= YRES) dirty->y1 = YRES - 1;
87
88         width = dirty->x1 + 1 - dirty->x0;
89         height = dirty->y1 + 1 - dirty->y0;
90
91         vid_clearfb_rect(dirty->x0, dirty->y0, width, height);
92         reset_dirty(dirty);
93 }
94
95 void g3d_modelview(const int32_t *m)
96 {
97         memcpy(mvmat, m, sizeof mvmat);
98 }
99
100 void g3d_projection(const int32_t *m)
101 {
102         memcpy(pmat, m, sizeof pmat);
103 }
104
105 void g3d_color(int cidx)
106 {
107         g3d_curcidx = cidx;
108 }
109
110 static const int primverts[] = {0, 1, 2, 3, 4};
111
112 void g3d_draw(int prim, struct g3d_vertex *varr, int vcount)
113 {
114         int i, prim_vnum;
115
116         prim_vnum = primverts[prim];
117
118         for(i=0; i<vcount; i+=prim_vnum) {
119                 g3d_draw_prim(prim, varr, 0);
120                 varr += prim_vnum;
121         }
122 }
123
124 void g3d_draw_indexed(int prim, struct g3d_vertex *varr, unsigned short *idxarr,
125                 int idxcount)
126 {
127         int i, j, prim_vnum;
128
129         prim_vnum = primverts[prim];
130
131         for(i=0; i<idxcount; i+=prim_vnum) {
132                 g3d_draw_prim(prim, varr, idxarr);
133                 idxarr += prim_vnum;
134         }
135 }
136
137 int32_t vpscale(int32_t x, int32_t s, int32_t shift);
138 #pragma aux vpscale = \
139         "add eax, 0x100" \
140         "imul ebx" \
141         "shrd eax, edx, cl" \
142         parm [eax] [ebx] [ecx] \
143         value [eax] \
144         modify [eax edx];
145
146
147 void g3d_draw_prim(int prim, struct g3d_vertex *varr, unsigned short *idxarr)
148 {
149         int i, vcount, x, y, x1, y1;
150         struct g3d_vertex v[4];
151
152         vcount = primverts[prim];
153
154         for(i=0; i<vcount; i++) {
155                 v[i] = idxarr ? varr[idxarr[i]] : varr[i];
156
157                 /* transform to view space */
158                 g3d_xform(v + i, mvmat);
159
160                 /* transform to homogeneous clip space */
161                 g3d_xform(v + i, pmat);
162
163                 /* TODO clip */
164
165                 /* perspective division */
166                 if(v[i].w != 0) {
167                         v[i].x = (v[i].x << 4) / (v[i].w >> 4);
168                         v[i].y = (v[i].y << 4) / (v[i].w >> 4);
169                 } else {
170                         v[i].x >>= 8;
171                         v[i].y >>= 8;
172                 }
173
174                 /* viewport transform */
175                 v[i].x = vpscale(v[i].x, vp[2], 1) + (vp[0] << 8);
176                 v[i].y = vpscale(-v[i].y, vp[3], 1) + (vp[1] << 8);
177
178 #if defined(USE_DIRTY_CLEAR) || defined(USE_DIRTY_COPY)
179                 x = v[i].x >> 8;
180                 y = v[i].y >> 8;
181                 {
182                         struct rect *dirty = dirty_rect + cur_dirty;
183                         if(x < dirty->x0) dirty->x0 = x;
184                         if(y < dirty->y0) dirty->y0 = y;
185                         if(x + 4 > dirty->x1) dirty->x1 = x + 4;
186                         if(y > dirty->y1) dirty->y1 = y;
187                 }
188 #endif
189         }
190
191         switch(prim) {
192         case G3D_POINTS:
193                 x = v[0].x >> 8;
194                 y = v[0].y >> 8;
195                 if(x >= 0 && y >= 0 && x < XRES && y < YRES) {
196                         g3d_fbpixels[y * XRES + x] = g3d_curcidx;
197                 }
198                 break;
199
200         case G3D_LINES:
201                 break;  /* TODO */
202
203         case G3D_TRIANGLES:
204                 g3d_polyfill(v);
205                 break;
206
207         case G3D_QUADS:
208                 g3d_polyfill(v);
209                 v[1] = v[0];
210                 g3d_polyfill(v + 1);
211                 break;
212         }
213 }