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