software rendering
[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 <stdio.h>
19 #include <string.h>
20 #include "gaw.h"
21 #include "gawswtnl.h"
22 #include "polyfill.h"
23
24 static struct pimage textures[MAX_TEXTURES];
25
26 void gaw_sw_reset(void)
27 {
28         gaw_swtnl_reset();
29
30         free(pfill_zbuf);
31 }
32
33 void gaw_sw_init(void)
34 {
35         gaw_swtnl_init();
36
37         gaw_sw_reset();
38 }
39
40 void gaw_sw_destroy(void)
41 {
42         gaw_swtnl_destroy();
43
44         free(pfill_zbuf);
45 }
46
47 void gaw_sw_framebuffer(int width, int height, void *pixels)
48 {
49         static int max_height;
50         static int max_npixels;
51         int npixels = width * height;
52
53         if(npixels > max_npixels) {
54                 free(pfill_zbuf);
55                 pfill_zbuf = malloc_nf(npixels * sizeof *pfill_zbuf);
56                 max_npixels = npixels;
57         }
58
59         if(height > max_height) {
60                 polyfill_fbheight(height);
61                 max_height = height;
62         }
63
64         ST->width = width;
65         ST->height = height;
66
67         pfill_fb.pixels = pixels;
68         pfill_fb.width = width;
69         pfill_fb.height = height;
70
71         gaw_viewport(0, 0, width, height);
72 }
73
74 /* set the framebuffer pointer, without resetting the size */
75 void gaw_sw_framebuffer_addr(void *pixels)
76 {
77         pfill_fb.pixels = pixels;
78 }
79
80 void gaw_enable(int what)
81 {
82         gaw_swtnl_enable(what);
83 }
84
85 void gaw_disable(int what)
86 {
87         gaw_swtnl_disable(what);
88 }
89
90 void gaw_clear(unsigned int flags)
91 {
92         int i, npix = pfill_fb.width * pfill_fb.height;
93
94         if(flags & GAW_COLORBUF) {
95                 for(i=0; i<npix; i++) {
96                         pfill_fb.pixels[i] = ST->clear_color;
97                 }
98         }
99
100         if(flags & GAW_DEPTHBUF) {
101                 for(i=0; i<npix; i++) {
102                         pfill_zbuf[i] = ST->clear_depth;
103                 }
104         }
105 }
106
107 void gaw_color_mask(int rmask, int gmask, int bmask, int amask)
108 {
109         gaw_swtnl_color_mask(rmask, gmask, bmask, amask);
110 }
111
112 void gaw_depth_mask(int mask)
113 {
114         gaw_swtnl_depth_mask(mask);
115 }
116
117 static int alloc_tex(void)
118 {
119         int i;
120         for(i=0; i<MAX_TEXTURES; i++) {
121                 if(ST->textypes[i] == 0) {
122                         return i;
123                 }
124         }
125         return -1;
126 }
127
128 unsigned int gaw_create_tex1d(int texfilter)
129 {
130         int idx;
131         if((idx = alloc_tex()) == -1) {
132                 return 0;
133         }
134         ST->textypes[idx] = 1;
135
136         memset(textures + idx, 0, sizeof *textures);
137         ST->cur_tex = idx;
138         return idx + 1;
139 }
140
141 unsigned int gaw_create_tex2d(int texfilter)
142 {
143         int idx;
144         if((idx = alloc_tex()) == -1) {
145                 return 0;
146         }
147         ST->textypes[idx] = 2;
148
149         memset(textures + idx, 0, sizeof *textures);
150         ST->cur_tex = idx;
151         return idx + 1;
152 }
153
154 void gaw_destroy_tex(unsigned int texid)
155 {
156         int idx = texid - 1;
157
158         if(!ST->textypes[idx]) return;
159
160         free(textures[idx].pixels);
161         ST->textypes[idx] = 0;
162 }
163
164 void gaw_texfilter1d(int texfilter)
165 {
166 }
167
168 void gaw_texfilter2d(int texfilter)
169 {
170 }
171
172 void gaw_texwrap1d(int wrap)
173 {
174 }
175
176 void gaw_texwrap2d(int uwrap, int vwrap)
177 {
178 }
179
180
181 static __inline int calc_shift(unsigned int x)
182 {
183         int res = -1;
184         while(x) {
185                 x >>= 1;
186                 ++res;
187         }
188         return res;
189 }
190
191 void gaw_tex1d(int ifmt, int xsz, int fmt, void *pix)
192 {
193         gaw_tex2d(ifmt, xsz, 1, fmt, pix);
194 }
195
196 void gaw_tex2d(int ifmt, int xsz, int ysz, int fmt, void *pix)
197 {
198         int npix;
199         struct pimage *img;
200
201         if(ST->cur_tex < 0) return;
202         img = textures + ST->cur_tex;
203
204         npix = xsz * ysz;
205
206         free(img->pixels);
207         img->pixels = malloc_nf(npix * sizeof *img->pixels);
208         img->width = xsz;
209         img->height = ysz;
210
211         img->xmask = xsz - 1;
212         img->ymask = ysz - 1;
213         img->xshift = calc_shift(xsz);
214         img->yshift = calc_shift(ysz);
215
216         gaw_subtex2d(0, 0, 0, xsz, ysz, fmt, pix);
217 }
218
219 void gaw_subtex2d(int lvl, int x, int y, int xsz, int ysz, int fmt, void *pix)
220 {
221         int i, j, r, g, b, val;
222         uint32_t *dest;
223         unsigned char *src;
224         struct pimage *img;
225
226         if(ST->cur_tex < 0) return;
227         img = textures + ST->cur_tex;
228
229         dest = img->pixels + (y << img->xshift) + x;
230         src = pix;
231
232         switch(fmt) {
233         case GAW_LUMINANCE:
234                 for(i=0; i<ysz; i++) {
235                         for(j=0; j<xsz; j++) {
236                                 val = *src++;
237                                 dest[j] = PACK_RGBA(val, val, val, 255);
238                         }
239                         dest += img->width;
240                 }
241                 break;
242
243         case GAW_RGB:
244                 for(i=0; i<ysz; i++) {
245                         for(j=0; j<xsz; j++) {
246                                 b = src[0];
247                                 g = src[1];
248                                 r = src[2];
249                                 src += 3;
250                                 dest[j] = PACK_RGBA(r, g, b, 255);
251                         }
252                         dest += img->width;
253                 }
254                 break;
255
256         case GAW_RGBA:
257                 for(i=0; i<ysz; i++) {
258                         for(j=0; j<xsz; j++) {
259                                 dest[j] = *((uint32_t*)src);
260                                 src += 4;
261                         }
262                         dest += img->width;
263                 }
264                 break;
265
266         default:
267                 break;
268         }
269 }
270
271 void gaw_bind_tex1d(int tex)
272 {
273         ST->cur_tex = (int)tex - 1;
274         pfill_tex = textures[ST->cur_tex];
275 }
276
277 void gaw_bind_tex2d(int tex)
278 {
279         ST->cur_tex = (int)tex - 1;
280         pfill_tex = textures[ST->cur_tex];
281 }
282
283 static void dump_texture(struct pimage *img, const char *fname)
284 {
285         int i, npix = img->width * img->height;
286         FILE *fp = fopen(fname, "wb");
287         if(!fp) return;
288
289         fprintf(fp, "P6\n%d %d\n255\n", img->width, img->height);
290         for(i=0; i<npix; i++) {
291                 int r = UNPACK_R(img->pixels[i]);
292                 int g = UNPACK_G(img->pixels[i]);
293                 int b = UNPACK_B(img->pixels[i]);
294                 fputc(r, fp);
295                 fputc(g, fp);
296                 fputc(b, fp);
297         }
298         fclose(fp);
299 }
300
301 void gaw_sw_dump_textures(void)
302 {
303         int i;
304         char buf[64];
305
306         for(i=0; i<MAX_TEXTURES; i++) {
307                 if(ST->textypes[i] <= 0) continue;
308
309                 sprintf(buf, "tex%04d.ppm", i);
310                 printf("dumping %s ...\n", buf);
311                 dump_texture(textures + i, buf);
312         }
313 }
314
315 void gaw_swtnl_drawprim(int prim, struct vertex *v, int vnum)
316 {
317         int i, fill_mode;
318         struct pvertex pv[16];
319
320         for(i=0; i<vnum; i++) {
321                 /* viewport transformation */
322                 v[i].x = (v[i].x * 0.5f + 0.5f) * (float)ST->vport[2] + ST->vport[0];
323                 v[i].y = (v[i].y * -0.5f + 0.5f) * (float)ST->vport[3] + ST->vport[1] - 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                 draw_line(pv);
366                 break;
367
368         default:
369                 fill_mode = ST->polymode;
370                 if(ST->opt & ((1 << GAW_TEXTURE_2D) | (1 << GAW_TEXTURE_1D))) {
371                         fill_mode |= POLYFILL_TEX_BIT;
372                 }
373                 if((ST->opt & (1 << GAW_BLEND)) && (ST->bsrc == GAW_SRC_ALPHA)) {
374                         fill_mode |= POLYFILL_ALPHA_BIT;
375                 } else if((ST->opt & (1 << GAW_BLEND)) && (ST->bsrc == GAW_ONE)) {
376                         fill_mode |= POLYFILL_ADD_BIT;
377                 }
378                 if(ST->opt & (1 << GAW_DEPTH_TEST)) {
379                         fill_mode |= POLYFILL_ZBUF_BIT;
380                 }
381                 polyfill(fill_mode, pv, vnum);
382         }
383 }
384