integrating software renderer
[retroray] / src / gaw / gaw_sw.c
1 /*
2 RetroRay - integrated standalone vintage modeller/renderer
3 Copyright (C) 2023  John Tsiombikas <nuclear@mutantstargoat.com>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 #include <string.h>
19 #include "gaw.h"
20 #include "gawswtnl.h"
21 #include "polyfill.h"
22
23 static struct pimage textures[MAX_TEXTURES];
24
25 void gaw_sw_reset(void)
26 {
27         gaw_swtnl_reset();
28
29         free(pfill_zbuf);
30 }
31
32 void gaw_sw_init(void)
33 {
34         gaw_swtnl_init();
35
36         gaw_sw_reset();
37 }
38
39 void gaw_sw_destroy(void)
40 {
41         gaw_swtnl_destroy();
42
43         free(pfill_zbuf);
44 }
45
46 void gaw_sw_framebuffer(int width, int height, void *pixels)
47 {
48         static int max_height;
49         static int max_npixels;
50         int npixels = width * height;
51
52         if(npixels > max_npixels) {
53                 free(pfill_zbuf);
54                 pfill_zbuf = malloc_nf(npixels * sizeof *pfill_zbuf);
55                 max_npixels = npixels;
56         }
57
58         if(height > max_height) {
59                 polyfill_fbheight(height);
60                 max_height = height;
61         }
62
63         ST->width = width;
64         ST->height = height;
65
66         pfill_fb.pixels = pixels;
67         pfill_fb.width = width;
68         pfill_fb.height = height;
69
70         gaw_viewport(0, 0, width, height);
71 }
72
73 /* set the framebuffer pointer, without resetting the size */
74 void gaw_sw_framebuffer_addr(void *pixels)
75 {
76         pfill_fb.pixels = pixels;
77 }
78
79 void gaw_enable(int what)
80 {
81         gaw_swtnl_enable(what);
82 }
83
84 void gaw_disable(int what)
85 {
86         gaw_swtnl_disable(what);
87 }
88
89 void gaw_clear(unsigned int flags)
90 {
91         int i, npix = pfill_fb.width * pfill_fb.height;
92
93         if(flags & GAW_COLORBUF) {
94                 for(i=0; i<npix; i++) {
95                         pfill_fb.pixels[i] = ST->clear_color;
96                 }
97         }
98
99         if(flags & GAW_DEPTHBUF) {
100                 for(i=0; i<npix; i++) {
101                         pfill_zbuf[i] = ST->clear_depth;
102                 }
103         }
104 }
105
106 void gaw_color_mask(int rmask, int gmask, int bmask, int amask)
107 {
108         gaw_swtnl_color_mask(rmask, gmask, bmask, amask);
109 }
110
111 void gaw_depth_mask(int mask)
112 {
113         gaw_swtnl_depth_mask(mask);
114 }
115
116 static int alloc_tex(void)
117 {
118         int i;
119         for(i=0; i<MAX_TEXTURES; i++) {
120                 if(ST->textypes[i] == 0) {
121                         return i;
122                 }
123         }
124         return -1;
125 }
126
127 unsigned int gaw_create_tex1d(int texfilter)
128 {
129         int idx;
130         if((idx = alloc_tex()) == -1) {
131                 return 0;
132         }
133         ST->textypes[idx] = 1;
134
135         memset(textures + idx, 0, sizeof *textures);
136         ST->cur_tex = idx;
137         return idx + 1;
138 }
139
140 unsigned int gaw_create_tex2d(int texfilter)
141 {
142         int idx;
143         if((idx = alloc_tex()) == -1) {
144                 return 0;
145         }
146         ST->textypes[idx] = 2;
147
148         memset(textures + idx, 0, sizeof *textures);
149         ST->cur_tex = idx;
150         return idx + 1;
151 }
152
153 void gaw_destroy_tex(unsigned int texid)
154 {
155         int idx = texid - 1;
156
157         if(!ST->textypes[idx]) return;
158
159         free(textures[idx].pixels);
160         ST->textypes[idx] = 0;
161 }
162
163 void gaw_texfilter1d(int texfilter)
164 {
165 }
166
167 void gaw_texfilter2d(int texfilter)
168 {
169 }
170
171 void gaw_texwrap1d(int wrap)
172 {
173 }
174
175 void gaw_texwrap2d(int uwrap, int vwrap)
176 {
177 }
178
179
180 static __inline int calc_shift(unsigned int x)
181 {
182         int res = -1;
183         while(x) {
184                 x >>= 1;
185                 ++res;
186         }
187         return res;
188 }
189
190 void gaw_tex1d(int ifmt, int xsz, int fmt, void *pix)
191 {
192         gaw_tex2d(ifmt, xsz, 1, fmt, pix);
193 }
194
195 void gaw_tex2d(int ifmt, int xsz, int ysz, int fmt, void *pix)
196 {
197         int npix;
198         struct pimage *img;
199
200         if(ST->cur_tex < 0) return;
201         img = textures + ST->cur_tex;
202
203         npix = xsz * ysz;
204
205         free(img->pixels);
206         img->pixels = malloc_nf(npix * sizeof *img->pixels);
207         img->width = xsz;
208         img->height = ysz;
209
210         img->xmask = xsz - 1;
211         img->ymask = ysz - 1;
212         img->xshift = calc_shift(xsz);
213         img->yshift = calc_shift(ysz);
214
215         gaw_subtex2d(0, 0, 0, xsz, ysz, fmt, pix);
216 }
217
218 void gaw_subtex2d(int lvl, int x, int y, int xsz, int ysz, int fmt, void *pix)
219 {
220         int i, j, r, g, b, val;
221         uint32_t *dest;
222         unsigned char *src;
223         struct pimage *img;
224
225         if(ST->cur_tex < 0) return;
226         img = textures + ST->cur_tex;
227
228         dest = img->pixels + (y << img->xshift) + x;
229         src = pix;
230
231         switch(fmt) {
232         case GAW_LUMINANCE:
233                 for(i=0; i<ysz; i++) {
234                         for(j=0; j<xsz; j++) {
235                                 val = *src++;
236                                 dest[j] = PACK_RGBA(val, val, val, 255);
237                         }
238                         dest += img->width;
239                 }
240                 break;
241
242         case GAW_RGB:
243                 for(i=0; i<ysz; i++) {
244                         for(j=0; j<xsz; j++) {
245                                 b = src[0];
246                                 g = src[1];
247                                 r = src[2];
248                                 src += 3;
249                                 dest[j] = PACK_RGBA(r, g, b, 255);
250                         }
251                         dest += img->width;
252                 }
253                 break;
254
255         case GAW_RGBA:
256                 for(i=0; i<ysz; i++) {
257                         for(j=0; j<xsz; j++) {
258                                 dest[j] = *((uint32_t*)src);
259                                 src += 4;
260                         }
261                         dest += img->width;
262                 }
263                 break;
264
265         default:
266                 break;
267         }
268 }
269
270 void gaw_bind_tex1d(int tex)
271 {
272         ST->cur_tex = (int)tex - 1;
273         pfill_tex = textures[ST->cur_tex];
274 }
275
276 void gaw_bind_tex2d(int tex)
277 {
278         ST->cur_tex = (int)tex - 1;
279         pfill_tex = textures[ST->cur_tex];
280 }
281
282 static void dump_texture(struct pimage *img, const char *fname)
283 {
284         int i, npix = img->width * img->height;
285         FILE *fp = fopen(fname, "wb");
286         if(!fp) return;
287
288         fprintf(fp, "P6\n%d %d\n255\n", img->width, img->height);
289         for(i=0; i<npix; i++) {
290                 int r = UNPACK_R(img->pixels[i]);
291                 int g = UNPACK_G(img->pixels[i]);
292                 int b = UNPACK_B(img->pixels[i]);
293                 fputc(r, fp);
294                 fputc(g, fp);
295                 fputc(b, fp);
296         }
297         fclose(fp);
298 }
299
300 void gaw_sw_dump_textures(void)
301 {
302         int i;
303         char buf[64];
304
305         for(i=0; i<MAX_TEXTURES; i++) {
306                 if(ST->textypes[i] <= 0) continue;
307
308                 sprintf(buf, "tex%04d.ppm", i);
309                 printf("dumping %s ...\n", buf);
310                 dump_texture(textures + i, buf);
311         }
312 }
313
314 void gaw_swtnl_drawprim(int prim, struct vertex *v, int vnum)
315 {
316         int i, fill_mode;
317         struct pvertex pv[16];
318
319         for(i=0; i<vnum; i++) {
320                 /* viewport transformation */
321                 v[i].x = (v[i].x * 0.5f + 0.5f) * (float)ST->vport[2] + ST->vport[0];
322                 v[i].y = (v[i].y * 0.5f + 0.5f) * (float)ST->vport[3] + ST->vport[1];
323                 v[i].y = pfill_fb.height - v[i].y - 1;
324
325                 /* convert pos to 24.8 fixed point */
326                 pv[i].x = cround64(v[i].x * 256.0f);
327                 pv[i].y = cround64(v[i].y * 256.0f);
328
329                 if(ST->opt & (1 << GAW_DEPTH_TEST)) {
330                         /* after div/w z is in [-1, 1], remap it to [0, 0xffffff] */
331                         pv[i].z = cround64(v[i].z * 8388607.5f + 8388607.5f);
332                 }
333
334                 /* convert tex coords to 16.16 fixed point */
335                 pv[i].u = cround64(v[i].u * 65536.0f);
336                 pv[i].v = cround64(v[i].v * 65536.0f);
337                 /* pass the color through as is */
338                 pv[i].r = v[i].r;
339                 pv[i].g = v[i].g;
340                 pv[i].b = v[i].b;
341                 pv[i].a = v[i].a;
342         }
343
344         /* backface culling */
345 #if 0   /* TODO fix culling */
346         if(vnum > 2 && (ST->opt & (1 << GAW_CULL_FACE))) {
347                 int32_t ax = pv[1].x - pv[0].x;
348                 int32_t ay = pv[1].y - pv[0].y;
349                 int32_t bx = pv[2].x - pv[0].x;
350                 int32_t by = pv[2].y - pv[0].y;
351                 int32_t cross_z = (ax >> 4) * (by >> 4) - (ay >> 4) * (bx >> 4);
352                 int sign = (cross_z >> 31) & 1;
353
354                 if(!(sign ^ ST->frontface)) {
355                         continue;       /* back-facing */
356                 }
357         }
358 #endif
359
360         switch(prim) {
361         case GAW_POINTS:
362                 break;
363
364         case GAW_LINES:
365                 break;
366
367         default:
368                 fill_mode = ST->polymode;
369                 if(ST->opt & ((1 << GAW_TEXTURE_2D) | (1 << GAW_TEXTURE_1D))) {
370                         fill_mode |= POLYFILL_TEX_BIT;
371                 }
372                 if((ST->opt & (1 << GAW_BLEND)) && (ST->bsrc == GAW_SRC_ALPHA)) {
373                         fill_mode |= POLYFILL_ALPHA_BIT;
374                 } else if((ST->opt & (1 << GAW_BLEND)) && (ST->bsrc == GAW_ONE)) {
375                         fill_mode |= POLYFILL_ADD_BIT;
376                 }
377                 if(ST->opt & (1 << GAW_DEPTH_TEST)) {
378                         fill_mode |= POLYFILL_ZBUF_BIT;
379                 }
380                 polyfill(fill_mode, pv, vnum);
381         }
382 }
383