2 RetroRay - integrated standalone vintage modeller/renderer
3 Copyright (C) 2023 John Tsiombikas <nuclear@mutantstargoat.com>
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.
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.
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/>.
21 #include "cgmath/cgmath.h"
26 #include "../darray.h"
28 #define NORMALIZE(v) \
30 float len = sqrt((v)[0] * (v)[0] + (v)[1] * (v)[1] + (v)[2] * (v)[2]); \
32 float s = 1.0 / len; \
40 static void imm_flush(void);
41 static __inline void xform4_vec3(const float *mat, float *vec);
42 static __inline void xform3_vec3(const float *mat, float *vec);
43 static void shade(struct vertex *v);
45 static struct gaw_state st;
46 struct gaw_state *gaw_state;
48 static const float idmat[] = {
56 void gaw_swtnl_reset(void)
60 memset(&st, 0, sizeof st);
62 st.polymode = POLYFILL_GOURAUD;
64 for(i=0; i<NUM_MATRICES; i++) {
69 for(i=0; i<MAX_LIGHTS; i++) {
70 gaw_light_dir(i, 0, 0, 1);
71 gaw_light_color(i, 1, 1, 1, 1);
73 gaw_ambient(0.1, 0.1, 0.1);
74 gaw_mtl_diffuse(1, 1, 1, 1);
76 st.clear_depth = 0xffffff;
86 void gaw_swtnl_init(void)
91 void gaw_swtnl_destroy(void)
95 void gaw_viewport(int x, int y, int w, int h)
103 void gaw_get_viewport(int *vp)
105 memcpy(vp, st.vport, sizeof st.vport);
108 void gaw_matrix_mode(int mode)
113 void gaw_load_identity(void)
115 int top = st.mtop[st.mmode];
116 memcpy(st.mat[st.mmode][top], idmat, 16 * sizeof(float));
119 void gaw_load_matrix(const float *m)
121 int top = st.mtop[st.mmode];
122 memcpy(st.mat[st.mmode][top], m, 16 * sizeof(float));
125 #define M(i,j) (((i) << 2) + (j))
126 void gaw_mult_matrix(const float *m2)
128 int i, j, top = st.mtop[st.mmode];
130 float *dest = st.mat[st.mmode][top];
132 memcpy(m1, dest, sizeof m1);
136 *dest++ = m1[M(0,j)] * m2[M(i,0)] +
137 m1[M(1,j)] * m2[M(i,1)] +
138 m1[M(2,j)] * m2[M(i,2)] +
139 m1[M(3,j)] * m2[M(i,3)];
144 void gaw_push_matrix(void)
146 int top = st.mtop[st.mmode];
147 if(top >= STACK_SIZE) {
148 fprintf(stderr, "push_matrix overflow\n");
151 memcpy(st.mat[st.mmode][top + 1], st.mat[st.mmode][top], 16 * sizeof(float));
152 st.mtop[st.mmode] = top + 1;
155 void gaw_pop_matrix(void)
157 if(st.mtop[st.mmode] <= 0) {
158 fprintf(stderr, "pop_matrix underflow\n");
164 void gaw_get_modelview(float *m)
166 int top = st.mtop[GAW_MODELVIEW];
167 memcpy(m, st.mat[GAW_MODELVIEW][top], 16 * sizeof(float));
170 void gaw_get_projection(float *m)
172 int top = st.mtop[GAW_PROJECTION];
173 memcpy(m, st.mat[GAW_PROJECTION][top], 16 * sizeof(float));
176 void gaw_translate(float x, float y, float z)
179 m[0] = m[5] = m[10] = m[15] = 1.0f;
186 void gaw_rotate(float deg, float x, float y, float z)
190 float angle = CGM_PI * deg / 180.0f;
191 float sina = sin(angle);
192 float cosa = cos(angle);
193 float one_minus_cosa = 1.0f - cosa;
198 m[0] = nxsq + (1.0f - nxsq) * cosa;
199 m[4] = x * y * one_minus_cosa - z * sina;
200 m[8] = x * z * one_minus_cosa + y * sina;
201 m[1] = x * y * one_minus_cosa + z * sina;
202 m[5] = nysq + (1.0 - nysq) * cosa;
203 m[9] = y * z * one_minus_cosa - x * sina;
204 m[2] = x * z * one_minus_cosa - y * sina;
205 m[6] = y * z * one_minus_cosa + x * sina;
206 m[10] = nzsq + (1.0 - nzsq) * cosa;
212 void gaw_scale(float sx, float sy, float sz)
222 void gaw_ortho(float l, float r, float b, float t, float n, float f)
233 m[12] = -(r + l) / dx;
234 m[13] = -(t + b) / dy;
235 m[14] = -(f + n) / dz;
241 void gaw_frustum(float left, float right, float bottom, float top, float zn, float zf)
245 float dx = right - left;
246 float dy = top - bottom;
249 float a = (right + left) / dx;
250 float b = (top + bottom) / dy;
251 float c = -(zf + zn) / dz;
252 float d = -2.0 * zf * zn / dz;
254 m[0] = 2.0 * zn / dx;
255 m[5] = 2.0 * zn / dy;
265 void gaw_perspective(float vfov_deg, float aspect, float znear, float zfar)
269 float vfov = CGM_PI * vfov_deg / 180.0f;
270 float s = 1.0f / tan(vfov * 0.5f);
271 float range = znear - zfar;
275 m[10] = (znear + zfar) / range;
277 m[14] = 2.0f * znear * zfar / range;
284 if(st.savopt_top >= STACK_SIZE) {
287 st.savopt[st.savopt_top++] = st.opt;
290 void gaw_restore(void)
292 if(st.savopt_top <= 0) {
295 st.opt = st.savopt[--st.savopt_top];
298 void gaw_swtnl_enable(int what)
303 void gaw_swtnl_disable(int what)
305 st.opt &= ~(1 << what);
308 void gaw_depth_func(int func)
313 void gaw_blend_func(int src, int dest)
319 void gaw_alpha_func(int func, float ref)
324 void gaw_zoffset(float offs)
326 st.zoffs = offs * 0.1;
329 #define CLAMP(x, a, b) ((x) < (a) ? (a) : ((x) > (b) ? (b) : (x)))
331 void gaw_clear_color(float r, float g, float b, float a)
333 int ir = (int)(r * 255.0f);
334 int ig = (int)(g * 255.0f);
335 int ib = (int)(b * 255.0f);
337 ir = CLAMP(ir, 0, 255);
338 ig = CLAMP(ig, 0, 255);
339 ib = CLAMP(ib, 0, 255);
341 st.clear_color = PACK_RGB(ir, ig, ib);
344 void gaw_clear_depth(float z)
346 int iz = (int)(z * (float)0xffffff);
347 st.clear_depth = CLAMP(iz, 0, 0xffffff);
350 void gaw_swtnl_color_mask(int rmask, int gmask, int bmask, int amask)
355 void gaw_swtnl_depth_mask(int mask)
360 void gaw_vertex_array(int nelem, int stride, const void *ptr)
363 stride = nelem * sizeof(float);
365 st.vertex_nelem = nelem;
366 st.vertex_stride = stride;
370 void gaw_normal_array(int stride, const void *ptr)
373 stride = 3 * sizeof(float);
375 st.normal_stride = stride;
379 void gaw_texcoord_array(int nelem, int stride, const void *ptr)
382 stride = nelem * sizeof(float);
384 st.texcoord_nelem = nelem;
385 st.texcoord_stride = stride;
386 st.texcoord_ptr = ptr;
389 void gaw_color_array(int nelem, int stride, const void *ptr)
392 stride = nelem * sizeof(float);
394 st.color_nelem = nelem;
395 st.color_stride = stride;
399 void gaw_draw(int prim, int nverts)
401 gaw_draw_indexed(prim, 0, nverts);
405 #define NEED_NORMALS \
406 (st.opt & ((1 << GAW_LIGHTING) | (1 << GAW_SPHEREMAP)))
408 static int prim_vcount[] = {1, 2, 3, 4, 0};
410 void gaw_draw_indexed(int prim, const unsigned int *idxarr, int nidx)
412 int i, j, vidx, vnum, nfaces;
414 int mvtop = st.mtop[GAW_MODELVIEW];
415 int ptop = st.mtop[GAW_PROJECTION];
419 if(prim == GAW_QUAD_STRIP) return; /* TODO */
421 if(st.cur_comp >= 0) {
422 st.comp[st.cur_comp].prim = prim;
425 tmpv = alloca(prim * 6 * sizeof *tmpv);
427 /* calc the normal matrix */
429 memcpy(st.norm_mat, st.mat[GAW_MODELVIEW][mvtop], 16 * sizeof(float));
430 st.norm_mat[12] = st.norm_mat[13] = st.norm_mat[14] = 0.0f;
434 nfaces = nidx / prim_vcount[prim];
436 for(j=0; j<nfaces; j++) {
437 vnum = prim_vcount[prim]; /* reset vnum for each iteration */
439 for(i=0; i<vnum; i++) {
443 vptr = (const float*)((char*)st.vertex_ptr + vidx * st.vertex_stride);
446 v[i].z = st.vertex_nelem > 2 ? vptr[2] : 0.0f;
447 v[i].w = st.vertex_nelem > 3 ? vptr[3] : 1.0f;
450 vptr = (const float*)((char*)st.normal_ptr + vidx * st.normal_stride);
452 vptr = &st.imm_curv.nx;
458 if(st.texcoord_ptr) {
459 vptr = (const float*)((char*)st.texcoord_ptr + vidx * st.texcoord_stride);
461 vptr = &st.imm_curv.u;
467 vptr = (const float*)((char*)st.color_ptr + vidx * st.color_stride);
469 vptr = st.imm_curcol;
471 v[i].r = (int)(vptr[0] * 255.0f);
472 v[i].g = (int)(vptr[1] * 255.0f);
473 v[i].b = (int)(vptr[2] * 255.0f);
474 v[i].a = st.color_nelem > 3 ? (int)(vptr[3] * 255.0f) : 255;
478 if(st.cur_comp >= 0) {
479 /* currently compiling geometry */
480 struct comp_geom *cg = st.comp + st.cur_comp;
483 col[0] = v[i].r / 255.0f;
484 col[1] = v[i].g / 255.0f;
485 col[2] = v[i].b / 255.0f;
486 col[3] = v[i].a / 255.0f;
488 darr_push(cg->varr, &v[i].x);
489 darr_push(cg->varr, &v[i].y);
490 darr_push(cg->varr, &v[i].z);
491 darr_push(cg->narr, &v[i].nx);
492 darr_push(cg->narr, &v[i].ny);
493 darr_push(cg->narr, &v[i].nz);
494 darr_push(cg->uvarr, &v[i].u);
495 darr_push(cg->uvarr, &v[i].v);
496 darr_push(cg->carr, col);
497 darr_push(cg->carr, col + 1);
498 darr_push(cg->carr, col + 2);
499 darr_push(cg->carr, col + 3);
500 continue; /* don't transform, just skip to the next vertex */
503 xform4_vec3(st.mat[GAW_MODELVIEW][mvtop], &v[i].x);
506 xform3_vec3(st.norm_mat, &v[i].nx);
507 if(st.opt & (1 << GAW_LIGHTING)) {
510 if(st.opt & (1 << GAW_SPHEREMAP)) {
511 v[i].u = v[i].nx * 0.5 + 0.5;
512 v[i].v = 0.5 - v[i].ny * 0.5;
516 float *mat = st.mat[GAW_TEXTURE][st.mtop[GAW_TEXTURE]];
517 float x = mat[0] * v[i].u + mat[4] * v[i].v + mat[12];
518 float y = mat[1] * v[i].u + mat[5] * v[i].v + mat[13];
519 float w = mat[3] * v[i].u + mat[7] * v[i].v + mat[15];
523 xform4_vec3(st.mat[GAW_PROJECTION][ptop], &v[i].x);
526 if(st.cur_comp >= 0) {
527 /* compiling geometry, don't draw, skip to the next primitive */
533 memcpy(tmpv, v, vnum * sizeof *v);
535 if(clip_frustum(v, &vnum, tmpv, vnum, i) < 0) {
536 /* polygon completely outside of view volume. discard */
544 for(i=0; i<vnum; i++) {
545 float oow = 1.0f / v[i].w;
548 if(st.opt & (1 << GAW_POLYGON_OFFSET)) {
551 if(st.opt & (1 << GAW_DEPTH_TEST)) {
556 gaw_swtnl_drawprim(prim, v, vnum);
560 void gaw_begin(int prim)
563 st.imm_pcount = prim_vcount[st.imm_prim];
572 static void imm_flush(void)
574 int numv = st.imm_numv;
577 gaw_vertex_array(3, sizeof(struct vertex), &st.imm_vbuf->x);
578 gaw_normal_array(sizeof(struct vertex), &st.imm_vbuf->nx);
579 gaw_texcoord_array(2, sizeof(struct vertex), &st.imm_vbuf->u);
580 gaw_color_array(4, 0, st.imm_cbuf);
582 gaw_draw_indexed(st.imm_prim, 0, numv);
584 gaw_vertex_array(0, 0, 0);
585 gaw_normal_array(0, 0);
586 gaw_texcoord_array(0, 0, 0);
587 gaw_color_array(0, 0, 0);
590 void gaw_color3f(float r, float g, float b)
592 gaw_color4f(r, g, b, 1.0f);
595 void gaw_color4f(float r, float g, float b, float a)
597 st.imm_curcol[0] = r;
598 st.imm_curcol[1] = g;
599 st.imm_curcol[2] = b;
600 st.imm_curcol[3] = a;
603 void gaw_color3ub(int r, int g, int b)
605 st.imm_curcol[0] = r / 255.0f;
606 st.imm_curcol[1] = g / 255.0f;
607 st.imm_curcol[2] = b / 255.0f;
608 st.imm_curcol[3] = 1.0f;
611 void gaw_normal(float x, float y, float z)
618 void gaw_texcoord1f(float u)
621 st.imm_curv.v = 0.0f;
624 void gaw_texcoord2f(float u, float v)
630 void gaw_vertex2f(float x, float y)
632 gaw_vertex4f(x, y, 0, 1);
635 void gaw_vertex3f(float x, float y, float z)
637 gaw_vertex4f(x, y, z, 1);
640 void gaw_vertex4f(float x, float y, float z, float w)
642 float *cptr = st.imm_cbuf + st.imm_numv * 4;
643 struct vertex *vptr = st.imm_vbuf + st.imm_numv++;
650 cptr[0] = st.imm_curcol[0];
651 cptr[1] = st.imm_curcol[1];
652 cptr[2] = st.imm_curcol[2];
653 cptr[3] = st.imm_curcol[3];
655 if(!--st.imm_pcount) {
656 if(st.imm_numv >= IMM_VBUF_SIZE - prim_vcount[st.imm_prim]) {
659 st.imm_pcount = prim_vcount[st.imm_prim];
663 void gaw_rect(float x1, float y1, float x2, float y2)
665 gaw_begin(GAW_QUADS);
666 gaw_vertex2f(x1, y1);
667 gaw_vertex2f(x2, y1);
668 gaw_vertex2f(x2, y2);
669 gaw_vertex2f(x1, y2);
674 void gaw_pointsize(float sz)
679 void gaw_linewidth(float w)
684 int gaw_compile_begin(void)
689 for(i=0; i<MAX_COMPILED; i++) {
690 if(st.comp[i].varr == 0) {
695 if(st.cur_comp < 0) {
699 st.comp[i].prim = -1;
700 st.comp[i].varr = darr_alloc(0, sizeof(float));
701 st.comp[i].narr = darr_alloc(0, sizeof(float));
702 st.comp[i].uvarr = darr_alloc(0, sizeof(float));
703 st.comp[i].carr = darr_alloc(0, sizeof(float));
705 return st.cur_comp + 1;
708 void gaw_compile_end(void)
713 void gaw_draw_compiled(int id)
717 if(!st.comp[idx].varr || st.comp[idx].prim == -1) {
721 gaw_vertex_array(3, 0, st.comp[idx].varr);
722 gaw_normal_array(0, st.comp[idx].narr);
723 gaw_texcoord_array(2, 0, st.comp[idx].uvarr);
724 gaw_color_array(4, 0, st.comp[idx].carr);
726 gaw_draw(st.comp[idx].prim, darr_size(st.comp[idx].varr) / 3);
728 gaw_vertex_array(0, 0, 0);
729 gaw_normal_array(0, 0);
730 gaw_texcoord_array(0, 0, 0);
731 gaw_color_array(0, 0, 0);
734 void gaw_free_compiled(int id)
738 darr_free(st.comp[idx].varr);
739 darr_free(st.comp[idx].narr);
740 darr_free(st.comp[idx].uvarr);
741 darr_free(st.comp[idx].carr);
742 memset(st.comp + idx, 0, sizeof *st.comp);
745 void gaw_mtl_diffuse(float r, float g, float b, float a)
753 void gaw_mtl_specular(float r, float g, float b, float shin)
761 void gaw_mtl_emission(float r, float g, float b)
768 void gaw_texenv_sphmap(int enable)
771 st.opt |= 1 << GAW_SPHEREMAP;
773 st.opt &= ~(1 << GAW_SPHEREMAP);
777 void gaw_set_tex1d(unsigned int texid)
780 gaw_bind_tex1d(texid);
781 gaw_enable(GAW_TEXTURE_1D);
784 gaw_disable(GAW_TEXTURE_1D);
788 void gaw_set_tex2d(unsigned int texid)
791 gaw_bind_tex2d(texid);
792 gaw_enable(GAW_TEXTURE_2D);
795 gaw_disable(GAW_TEXTURE_2D);
799 void gaw_ambient(float r, float g, float b)
806 void gaw_light_pos(int idx, float x, float y, float z)
808 int mvtop = st.mtop[GAW_MODELVIEW];
810 st.lt[idx].type = LT_POS;
815 xform4_vec3(st.mat[GAW_MODELVIEW][mvtop], &st.lt[idx].x);
818 void gaw_light_dir(int idx, float x, float y, float z)
820 int mvtop = st.mtop[GAW_MODELVIEW];
822 st.lt[idx].type = LT_DIR;
827 /* calc the normal matrix */
828 memcpy(st.norm_mat, st.mat[GAW_MODELVIEW][mvtop], 16 * sizeof(float));
829 st.norm_mat[12] = st.norm_mat[13] = st.norm_mat[14] = 0.0f;
831 xform4_vec3(st.norm_mat, &st.lt[idx].x);
833 NORMALIZE(&st.lt[idx].x);
836 void gaw_light_color(int idx, float r, float g, float b, float s)
843 void gaw_lighting_fast(void)
847 void gaw_fog_color(float r, float g, float b)
851 void gaw_fog_linear(float z0, float z1)
855 void gaw_fog_fast(void)
860 void gaw_poly_wire(void)
862 st.polymode = POLYFILL_WIRE;
865 void gaw_poly_flat(void)
867 st.polymode = POLYFILL_FLAT;
870 void gaw_poly_gouraud(void)
872 st.polymode = POLYFILL_GOURAUD;
876 static __inline void xform4_vec3(const float *mat, float *vec)
878 float x = mat[0] * vec[0] + mat[4] * vec[1] + mat[8] * vec[2] + mat[12];
879 float y = mat[1] * vec[0] + mat[5] * vec[1] + mat[9] * vec[2] + mat[13];
880 float z = mat[2] * vec[0] + mat[6] * vec[1] + mat[10] * vec[2] + mat[14];
881 vec[3] = mat[3] * vec[0] + mat[7] * vec[1] + mat[11] * vec[2] + mat[15];
887 static __inline void xform3_vec3(const float *mat, float *vec)
889 float x = mat[0] * vec[0] + mat[4] * vec[1] + mat[8] * vec[2];
890 float y = mat[1] * vec[0] + mat[5] * vec[1] + mat[9] * vec[2];
891 vec[2] = mat[2] * vec[0] + mat[6] * vec[1] + mat[10] * vec[2];
896 static void shade(struct vertex *v)
901 color[0] = st.ambient[0] * st.mtl.kd[0];
902 color[1] = st.ambient[1] * st.mtl.kd[1];
903 color[2] = st.ambient[2] * st.mtl.kd[2];
905 for(i=0; i<MAX_LIGHTS; i++) {
909 if(!(st.opt & (GAW_LIGHT0 << i))) {
913 ldir[0] = st.lt[i].x;
914 ldir[1] = st.lt[i].y;
915 ldir[2] = st.lt[i].z;
917 if(st.lt[i].type != LT_DIR) {
924 if((ndotl = v->nx * ldir[0] + v->ny * ldir[1] + v->nz * ldir[2]) < 0.0f) {
928 color[0] += st.mtl.kd[0] * st.lt[i].r * ndotl;
929 color[1] += st.mtl.kd[1] * st.lt[i].g * ndotl;
930 color[2] += st.mtl.kd[2] * st.lt[i].b * ndotl;
933 if(st.opt & (1 << GAW_SPECULAR)) {
937 if((ndoth = v->nx * ldir[0] + v->ny * ldir[1] + v->nz * ldir[2]) < 0.0f) {
940 ndoth = pow(ndoth, st.mtl.shin);
942 color[0] += st.mtl.ks[0] * st.lt[i].r * ndoth;
943 color[1] += st.mtl.ks[1] * st.lt[i].g * ndoth;
944 color[2] += st.mtl.ks[2] * st.lt[i].b * ndoth;
949 r = cround64(color[0] * 255.0);
950 g = cround64(color[1] * 255.0);
951 b = cround64(color[2] * 255.0);
953 v->r = r > 255 ? 255 : r;
954 v->g = g > 255 ? 255 : g;
955 v->b = b > 255 ? 255 : b;