3d cube and dirty drawing
[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 "util.h"
8
9 static int32_t mvmat[16];
10 static int32_t pmat[16];
11 static int vp[4];
12 static int dirty_x0, dirty_x1, dirty_y0, dirty_y1;
13
14 unsigned char *g3d_fbpixels;
15 int g3d_width, g3d_height;
16 int g3d_curcidx;
17
18
19
20 int g3d_init(void)
21 {
22         memset(mvmat, 0, sizeof mvmat);
23         memset(pmat, 0, sizeof pmat);
24         mvmat[0] = mvmat[5] = mvmat[10] = mvmat[15] = 0x10000;
25         pmat[0] = pmat[5] = pmat[10] = pmat[15] = 0x10000;
26         g3d_curcidx = 0xff;
27         return 0;
28 }
29
30 void g3d_shutdown(void)
31 {
32 }
33
34 void g3d_framebuffer(int width, int height, void *fb)
35 {
36         g3d_fbpixels = fb;
37         g3d_width = width;
38         g3d_height = height;
39
40         vp[0] = vp[1] = 0;
41         vp[2] = width;
42         vp[3] = height;
43
44         g3d_reset_dirty();
45 }
46
47 void g3d_reset_dirty(void)
48 {
49         dirty_x0 = XRES;
50         dirty_y0 = YRES;
51         dirty_x1 = 0;
52         dirty_y1 = 0;
53 }
54
55 void blkclear(void *p, int len, int col);
56 #pragma aux blkclear = \
57         "mov ah, al" \
58         "shl eax, 8" \
59         "mov al, ah" \
60         "shl eax, 8" \
61         "mov al, ah" \
62         "shr ecx, 2" \
63         "rep stosd" \
64         parm [edi] [ecx] [eax] \
65         modify [eax ecx edi];
66
67 extern volatile long nticks;
68 #define COL     0
69
70 void g3d_clear_dirty(void)
71 {
72         unsigned char *ptr;
73         int i, count, nlines;
74
75         if(dirty_x0 < 0) dirty_x0 = 0;
76         if(dirty_y0 < 0) dirty_y0 = 0;
77         if(dirty_x1 >= XRES) dirty_x1 = XRES - 1;
78         if(dirty_y1 >= YRES) dirty_y1 = YRES - 1;
79
80         nlines = dirty_y1 - dirty_y0;
81         if(dirty_y1 <= 0 || nlines >= YRES) {
82                 blkclear(g3d_fbpixels, XRES * YRES, COL);
83                 goto end;
84         }
85
86         ptr = g3d_fbpixels + dirty_y0 * XRES;
87         if(dirty_x1 > XRES - 4) {
88                 blkclear(ptr, nlines * XRES, COL);
89                 goto end;
90         }
91
92         ptr = (unsigned char*)((uintptr_t)(ptr + dirty_x0) & 0xfffffffc);
93         count = dirty_x1 + 3 - dirty_x0;
94         for(i=0; i<nlines; i++) {
95                 blkclear(ptr, count, COL);
96                 ptr += XRES;
97         }
98 end:
99         g3d_reset_dirty();
100 }
101
102
103 void vmemcopy(long fboffs, void *p, int len);
104 #pragma aux vmemcopy = \
105         "mov edi, 0xa0000" \
106         "add edi, eax" \
107         "shr ecx, 2" \
108         "rep movsd" \
109         parm [eax] [esi] [ecx] \
110         modify [ecx edi esi];
111
112 void g3d_copy_dirty(void)
113 {
114         int i, count, nlines;
115         unsigned long fboffs;
116
117         if(dirty_x0 < 0) dirty_x0 = 0;
118         if(dirty_y0 < 0) dirty_y0 = 0;
119         if(dirty_x1 >= XRES) dirty_x1 = XRES - 1;
120         if(dirty_y1 >= YRES) dirty_y1 = YRES - 1;
121
122         nlines = dirty_y1 - dirty_y0;
123         if(dirty_y1 <= 0 || nlines >= YRES) {
124                 vmemcopy(0, g3d_fbpixels, XRES * YRES);
125                 return;
126         }
127
128         fboffs = dirty_y0 * XRES;
129         if(dirty_x1 > XRES - 4) {
130                 vmemcopy(fboffs, g3d_fbpixels + fboffs, nlines * XRES);
131                 return;
132         }
133
134         fboffs += dirty_x0 & 0xfffffffc;
135         count = dirty_x1 + 3 - dirty_x0;
136         for(i=0; i<nlines; i++) {
137                 vmemcopy(fboffs, g3d_fbpixels + fboffs, count);
138                 fboffs += XRES;
139         }
140 }
141
142 void g3d_modelview(const int32_t *m)
143 {
144         memcpy(mvmat, m, sizeof mvmat);
145 }
146
147 void g3d_projection(const int32_t *m)
148 {
149         memcpy(pmat, m, sizeof pmat);
150 }
151
152 void g3d_color(int cidx)
153 {
154         g3d_curcidx = cidx;
155 }
156
157 static const int primverts[] = {1, 2, 3, 4};
158
159 void g3d_draw(int prim, struct g3d_vertex *varr, int vcount)
160 {
161         int i, prim_vnum;
162
163         prim_vnum = primverts[prim];
164
165         for(i=0; i<vcount; i+=prim_vnum) {
166                 g3d_draw_prim(prim, varr);
167                 varr += prim_vnum;
168         }
169 }
170
171 int32_t vpscale(int32_t x, int32_t s, int32_t shift);
172 #pragma aux vpscale = \
173         "add eax, 0x100" \
174         "imul ebx" \
175         "shrd eax, edx, cl" \
176         parm [eax] [ebx] [ecx] \
177         value [eax] \
178         modify [eax edx];
179
180
181 void g3d_draw_prim(int prim, struct g3d_vertex *varr)
182 {
183         int i, vcount, x, y, x1, y1;
184         struct g3d_vertex v[4];
185
186         vcount = primverts[prim];
187
188         for(i=0; i<vcount; i++) {
189                 v[i] = varr[i];
190
191                 /* transform to view space */
192                 g3d_xform(v + i, mvmat);
193
194                 /* transform to homogeneous clip space */
195                 g3d_xform(v + i, pmat);
196
197                 /* TODO clip */
198
199                 /* perspective division */
200                 if(v[i].w != 0) {
201                         v[i].x = (v[i].x << 4) / (v[i].w >> 4);
202                         v[i].y = (v[i].y << 4) / (v[i].w >> 4);
203                 } else {
204                         v[i].x >>= 8;
205                         v[i].y >>= 8;
206                 }
207
208                 /* viewport transform */
209                 v[i].x = vpscale(v[i].x, vp[2], 1) + (vp[0] << 8);
210                 v[i].y = vpscale(-v[i].y, vp[3], 1) + (vp[1] << 8);
211
212 #if defined(USE_DIRTY_CLEAR) || defined(USE_DIRTY_COPY)
213                 x = v[i].x >> 8;
214                 y = v[i].y >> 8;
215                 if(x - 4 < dirty_x0) dirty_x0 = x - 4;
216                 if(y - 4 < dirty_y0) dirty_y0 = y - 4;
217                 if(x + 8 > dirty_x1) dirty_x1 = x + 8;
218                 if(y + 8 > dirty_y1) dirty_y1 = y + 8;
219 #endif
220         }
221
222         switch(prim) {
223         case G3D_POINTS:
224                 x = v[0].x >> 8;
225                 y = v[0].y >> 8;
226                 if(x >= 0 && y >= 0 && x < XRES && y < YRES) {
227                         g3d_fbpixels[y * XRES + x] = g3d_curcidx;
228                 }
229                 break;
230
231         case G3D_LINES:
232                 break;  /* TODO */
233
234         case G3D_TRIANGLES:
235                 g3d_polyfill(v);
236                 break;
237
238         case G3D_QUADS:
239                 g3d_polyfill(v);
240                 v[1] = v[0];
241                 g3d_polyfill(v + 1);
242                 break;
243         }
244 }