3ebaa593f85dd0568423e27a1b663473a6ba7eb5
[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[] = {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);
120                 varr += prim_vnum;
121         }
122 }
123
124 int32_t vpscale(int32_t x, int32_t s, int32_t shift);
125 #pragma aux vpscale = \
126         "add eax, 0x100" \
127         "imul ebx" \
128         "shrd eax, edx, cl" \
129         parm [eax] [ebx] [ecx] \
130         value [eax] \
131         modify [eax edx];
132
133
134 void g3d_draw_prim(int prim, struct g3d_vertex *varr)
135 {
136         int i, vcount, x, y, x1, y1;
137         struct g3d_vertex v[4];
138
139         vcount = primverts[prim];
140
141         for(i=0; i<vcount; i++) {
142                 v[i] = varr[i];
143
144                 /* transform to view space */
145                 g3d_xform(v + i, mvmat);
146
147                 /* transform to homogeneous clip space */
148                 g3d_xform(v + i, pmat);
149
150                 /* TODO clip */
151
152                 /* perspective division */
153                 if(v[i].w != 0) {
154                         v[i].x = (v[i].x << 4) / (v[i].w >> 4);
155                         v[i].y = (v[i].y << 4) / (v[i].w >> 4);
156                 } else {
157                         v[i].x >>= 8;
158                         v[i].y >>= 8;
159                 }
160
161                 /* viewport transform */
162                 v[i].x = vpscale(v[i].x, vp[2], 1) + (vp[0] << 8);
163                 v[i].y = vpscale(-v[i].y, vp[3], 1) + (vp[1] << 8);
164
165 #if defined(USE_DIRTY_CLEAR) || defined(USE_DIRTY_COPY)
166                 x = v[i].x >> 8;
167                 y = v[i].y >> 8;
168                 {
169                         struct rect *dirty = dirty_rect + cur_dirty;
170                         if(x < dirty->x0) dirty->x0 = x;
171                         if(y < dirty->y0) dirty->y0 = y;
172                         if(x + 4 > dirty->x1) dirty->x1 = x + 4;
173                         if(y > dirty->y1) dirty->y1 = y;
174                 }
175 #endif
176         }
177
178         switch(prim) {
179         case G3D_POINTS:
180                 x = v[0].x >> 8;
181                 y = v[0].y >> 8;
182                 if(x >= 0 && y >= 0 && x < XRES && y < YRES) {
183                         g3d_fbpixels[y * XRES + x] = g3d_curcidx;
184                 }
185                 break;
186
187         case G3D_LINES:
188                 break;  /* TODO */
189
190         case G3D_TRIANGLES:
191                 g3d_polyfill(v);
192                 break;
193
194         case G3D_QUADS:
195                 g3d_polyfill(v);
196                 v[1] = v[0];
197                 g3d_polyfill(v + 1);
198                 break;
199         }
200 }