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_matrix_mode(int mode)
108 void gaw_load_identity(void)
110 int top = st.mtop[st.mmode];
111 memcpy(st.mat[st.mmode][top], idmat, 16 * sizeof(float));
114 void gaw_load_matrix(const float *m)
116 int top = st.mtop[st.mmode];
117 memcpy(st.mat[st.mmode][top], m, 16 * sizeof(float));
120 #define M(i,j) (((i) << 2) + (j))
121 void gaw_mult_matrix(const float *m2)
123 int i, j, top = st.mtop[st.mmode];
125 float *dest = st.mat[st.mmode][top];
127 memcpy(m1, dest, sizeof m1);
131 *dest++ = m1[M(0,j)] * m2[M(i,0)] +
132 m1[M(1,j)] * m2[M(i,1)] +
133 m1[M(2,j)] * m2[M(i,2)] +
134 m1[M(3,j)] * m2[M(i,3)];
139 void gaw_push_matrix(void)
141 int top = st.mtop[st.mmode];
142 if(top >= STACK_SIZE) {
143 fprintf(stderr, "push_matrix overflow\n");
146 memcpy(st.mat[st.mmode][top + 1], st.mat[st.mmode][top], 16 * sizeof(float));
147 st.mtop[st.mmode] = top + 1;
150 void gaw_pop_matrix(void)
152 if(st.mtop[st.mmode] <= 0) {
153 fprintf(stderr, "pop_matrix underflow\n");
159 void gaw_get_modelview(float *m)
161 int top = st.mtop[GAW_MODELVIEW];
162 memcpy(m, st.mat[GAW_MODELVIEW][top], 16 * sizeof(float));
165 void gaw_get_projection(float *m)
167 int top = st.mtop[GAW_PROJECTION];
168 memcpy(m, st.mat[GAW_PROJECTION][top], 16 * sizeof(float));
171 void gaw_translate(float x, float y, float z)
174 m[0] = m[5] = m[10] = m[15] = 1.0f;
181 void gaw_rotate(float deg, float x, float y, float z)
185 float angle = M_PI * deg / 180.0f;
186 float sina = sin(angle);
187 float cosa = cos(angle);
188 float one_minus_cosa = 1.0f - cosa;
193 m[0] = nxsq + (1.0f - nxsq) * cosa;
194 m[4] = x * y * one_minus_cosa - z * sina;
195 m[8] = x * z * one_minus_cosa + y * sina;
196 m[1] = x * y * one_minus_cosa + z * sina;
197 m[5] = nysq + (1.0 - nysq) * cosa;
198 m[9] = y * z * one_minus_cosa - x * sina;
199 m[2] = x * z * one_minus_cosa - y * sina;
200 m[6] = y * z * one_minus_cosa + x * sina;
201 m[10] = nzsq + (1.0 - nzsq) * cosa;
207 void gaw_scale(float sx, float sy, float sz)
217 void gaw_ortho(float l, float r, float b, float t, float n, float f)
228 m[12] = -(r + l) / dx;
229 m[13] = -(t + b) / dy;
230 m[14] = -(f + n) / dz;
236 void gaw_frustum(float left, float right, float bottom, float top, float zn, float zf)
240 float dx = right - left;
241 float dy = top - bottom;
244 float a = (right + left) / dx;
245 float b = (top + bottom) / dy;
246 float c = -(zf + zn) / dz;
247 float d = -2.0 * zf * zn / dz;
249 m[0] = 2.0 * zn / dx;
250 m[5] = 2.0 * zn / dy;
260 void gaw_perspective(float vfov_deg, float aspect, float znear, float zfar)
264 float vfov = M_PI * vfov_deg / 180.0f;
265 float s = 1.0f / tan(vfov * 0.5f);
266 float range = znear - zfar;
270 m[10] = (znear + zfar) / range;
272 m[14] = 2.0f * znear * zfar / range;
279 if(st.savopt_top >= STACK_SIZE) {
282 st.savopt[st.savopt_top++] = st.opt;
285 void gaw_restore(void)
287 if(st.savopt_top <= 0) {
290 st.opt = st.savopt[--st.savopt_top];
293 void gaw_swtnl_enable(int what)
298 void gaw_swtnl_disable(int what)
300 st.opt &= ~(1 << what);
303 void gaw_depth_func(int func)
308 void gaw_blend_func(int src, int dest)
314 void gaw_alpha_func(int func, float ref)
319 void gaw_zoffset(float offs)
324 #define CLAMP(x, a, b) ((x) < (a) ? (a) : ((x) > (b) ? (b) : (x)))
326 void gaw_clear_color(float r, float g, float b, float a)
328 int ir = (int)(r * 255.0f);
329 int ig = (int)(g * 255.0f);
330 int ib = (int)(b * 255.0f);
332 ir = CLAMP(ir, 0, 255);
333 ig = CLAMP(ig, 0, 255);
334 ib = CLAMP(ib, 0, 255);
336 st.clear_color = PACK_RGB(ir, ig, ib);
339 void gaw_clear_depth(float z)
341 int iz = (int)(z * (float)0xffffff);
342 st.clear_depth = CLAMP(iz, 0, 0xffffff);
345 void gaw_swtnl_color_mask(int rmask, int gmask, int bmask, int amask)
350 void gaw_swtnl_depth_mask(int mask)
355 void gaw_vertex_array(int nelem, int stride, const void *ptr)
358 stride = nelem * sizeof(float);
360 st.vertex_nelem = nelem;
361 st.vertex_stride = stride;
365 void gaw_normal_array(int stride, const void *ptr)
368 stride = 3 * sizeof(float);
370 st.normal_stride = stride;
374 void gaw_texcoord_array(int nelem, int stride, const void *ptr)
377 stride = nelem * sizeof(float);
379 st.texcoord_nelem = nelem;
380 st.texcoord_stride = stride;
381 st.texcoord_ptr = ptr;
384 void gaw_color_array(int nelem, int stride, const void *ptr)
387 stride = nelem * sizeof(float);
389 st.color_nelem = nelem;
390 st.color_stride = stride;
394 void gaw_draw(int prim, int nverts)
396 gaw_draw_indexed(prim, 0, nverts);
400 #define NEED_NORMALS \
401 (st.opt & ((1 << GAW_LIGHTING) | (1 << GAW_SPHEREMAP)))
403 static int prim_vcount[] = {1, 2, 3, 4, 0};
405 void gaw_draw_indexed(int prim, const unsigned int *idxarr, int nidx)
407 int i, j, vidx, vnum, nfaces;
409 int mvtop = st.mtop[GAW_MODELVIEW];
410 int ptop = st.mtop[GAW_PROJECTION];
414 if(prim == GAW_QUAD_STRIP) return; /* TODO */
416 if(st.cur_comp >= 0) {
417 st.comp[st.cur_comp].prim = prim;
420 tmpv = alloca(prim * 6 * sizeof *tmpv);
422 /* calc the normal matrix */
424 memcpy(st.norm_mat, st.mat[GAW_MODELVIEW][mvtop], 16 * sizeof(float));
425 st.norm_mat[12] = st.norm_mat[13] = st.norm_mat[14] = 0.0f;
429 nfaces = nidx / prim_vcount[prim];
431 for(j=0; j<nfaces; j++) {
432 vnum = prim_vcount[prim]; /* reset vnum for each iteration */
434 for(i=0; i<vnum; i++) {
438 vptr = (const float*)((char*)st.vertex_ptr + vidx * st.vertex_stride);
441 v[i].z = st.vertex_nelem > 2 ? vptr[2] : 0.0f;
442 v[i].w = st.vertex_nelem > 3 ? vptr[3] : 1.0f;
445 vptr = (const float*)((char*)st.normal_ptr + vidx * st.normal_stride);
447 vptr = &st.imm_curv.nx;
453 if(st.texcoord_ptr) {
454 vptr = (const float*)((char*)st.texcoord_ptr + vidx * st.texcoord_stride);
456 vptr = &st.imm_curv.u;
462 vptr = (const float*)((char*)st.color_ptr + vidx * st.color_stride);
464 vptr = st.imm_curcol;
466 v[i].r = (int)(vptr[0] * 255.0f);
467 v[i].g = (int)(vptr[1] * 255.0f);
468 v[i].b = (int)(vptr[2] * 255.0f);
469 v[i].a = st.color_nelem > 3 ? (int)(vptr[3] * 255.0f) : 255;
473 if(st.cur_comp >= 0) {
474 /* currently compiling geometry */
475 struct comp_geom *cg = st.comp + st.cur_comp;
478 col[0] = v[i].r / 255.0f;
479 col[1] = v[i].g / 255.0f;
480 col[2] = v[i].b / 255.0f;
481 col[3] = v[i].a / 255.0f;
483 darr_push(cg->varr, &v[i].x);
484 darr_push(cg->varr, &v[i].y);
485 darr_push(cg->varr, &v[i].z);
486 darr_push(cg->narr, &v[i].nx);
487 darr_push(cg->narr, &v[i].ny);
488 darr_push(cg->narr, &v[i].nz);
489 darr_push(cg->uvarr, &v[i].u);
490 darr_push(cg->uvarr, &v[i].v);
491 darr_push(cg->carr, col);
492 darr_push(cg->carr, col + 1);
493 darr_push(cg->carr, col + 2);
494 darr_push(cg->carr, col + 3);
495 continue; /* don't transform, just skip to the next vertex */
498 xform4_vec3(st.mat[GAW_MODELVIEW][mvtop], &v[i].x);
501 xform3_vec3(st.norm_mat, &v[i].nx);
502 if(st.opt & (1 << GAW_LIGHTING)) {
505 if(st.opt & (1 << GAW_SPHEREMAP)) {
506 v[i].u = v[i].nx * 0.5 + 0.5;
507 v[i].v = 0.5 - v[i].ny * 0.5;
511 float *mat = st.mat[GAW_TEXTURE][st.mtop[GAW_TEXTURE]];
512 float x = mat[0] * v[i].u + mat[4] * v[i].v + mat[12];
513 float y = mat[1] * v[i].u + mat[5] * v[i].v + mat[13];
514 float w = mat[3] * v[i].u + mat[7] * v[i].v + mat[15];
518 xform4_vec3(st.mat[GAW_PROJECTION][ptop], &v[i].x);
521 if(st.cur_comp >= 0) {
522 /* compiling geometry, don't draw, skip to the next primitive */
528 memcpy(tmpv, v, vnum * sizeof *v);
530 if(clip_frustum(v, &vnum, tmpv, vnum, i) < 0) {
531 /* polygon completely outside of view volume. discard */
539 for(i=0; i<vnum; i++) {
540 float oow = 1.0f / v[i].w;
544 if(st.opt & (1 << GAW_DEPTH_TEST)) {
549 gaw_swtnl_drawprim(prim, v, vnum);
553 void gaw_begin(int prim)
556 st.imm_pcount = prim_vcount[st.imm_prim];
565 static void imm_flush(void)
567 int numv = st.imm_numv;
570 gaw_vertex_array(3, sizeof(struct vertex), &st.imm_vbuf->x);
571 gaw_normal_array(sizeof(struct vertex), &st.imm_vbuf->nx);
572 gaw_texcoord_array(2, sizeof(struct vertex), &st.imm_vbuf->u);
573 gaw_color_array(4, 0, st.imm_cbuf);
575 gaw_draw_indexed(st.imm_prim, 0, numv);
577 gaw_vertex_array(0, 0, 0);
578 gaw_normal_array(0, 0);
579 gaw_texcoord_array(0, 0, 0);
580 gaw_color_array(0, 0, 0);
583 void gaw_color3f(float r, float g, float b)
585 gaw_color4f(r, g, b, 1.0f);
588 void gaw_color4f(float r, float g, float b, float a)
590 st.imm_curcol[0] = r;
591 st.imm_curcol[1] = g;
592 st.imm_curcol[2] = b;
593 st.imm_curcol[3] = a;
596 void gaw_color3ub(int r, int g, int b)
598 st.imm_curcol[0] = r / 255.0f;
599 st.imm_curcol[1] = g / 255.0f;
600 st.imm_curcol[2] = b / 255.0f;
601 st.imm_curcol[3] = 1.0f;
604 void gaw_normal(float x, float y, float z)
611 void gaw_texcoord1f(float u)
614 st.imm_curv.v = 0.0f;
617 void gaw_texcoord2f(float u, float v)
623 void gaw_vertex2f(float x, float y)
625 gaw_vertex4f(x, y, 0, 1);
628 void gaw_vertex3f(float x, float y, float z)
630 gaw_vertex4f(x, y, z, 1);
633 void gaw_vertex4f(float x, float y, float z, float w)
635 float *cptr = st.imm_cbuf + st.imm_numv * 4;
636 struct vertex *vptr = st.imm_vbuf + st.imm_numv++;
643 cptr[0] = st.imm_curcol[0];
644 cptr[1] = st.imm_curcol[1];
645 cptr[2] = st.imm_curcol[2];
646 cptr[3] = st.imm_curcol[3];
648 if(!--st.imm_pcount) {
649 if(st.imm_numv >= IMM_VBUF_SIZE - prim_vcount[st.imm_prim]) {
652 st.imm_pcount = prim_vcount[st.imm_prim];
656 void gaw_rect(float x1, float y1, float x2, float y2)
658 gaw_begin(GAW_QUADS);
659 gaw_vertex2f(x1, y1);
660 gaw_vertex2f(x2, y1);
661 gaw_vertex2f(x2, y2);
662 gaw_vertex2f(x1, y2);
667 void gaw_pointsize(float sz)
672 void gaw_linewidth(float w)
677 int gaw_compile_begin(void)
682 for(i=0; i<MAX_COMPILED; i++) {
683 if(st.comp[i].varr == 0) {
688 if(st.cur_comp < 0) {
692 st.comp[i].prim = -1;
693 st.comp[i].varr = darr_alloc(0, sizeof(float));
694 st.comp[i].narr = darr_alloc(0, sizeof(float));
695 st.comp[i].uvarr = darr_alloc(0, sizeof(float));
696 st.comp[i].carr = darr_alloc(0, sizeof(float));
698 return st.cur_comp + 1;
701 void gaw_compile_end(void)
706 void gaw_draw_compiled(int id)
710 if(!st.comp[idx].varr || st.comp[idx].prim == -1) {
714 gaw_vertex_array(3, 0, st.comp[idx].varr);
715 gaw_normal_array(0, st.comp[idx].narr);
716 gaw_texcoord_array(2, 0, st.comp[idx].uvarr);
717 gaw_color_array(4, 0, st.comp[idx].carr);
719 gaw_draw(st.comp[idx].prim, darr_size(st.comp[idx].varr) / 3);
721 gaw_vertex_array(0, 0, 0);
722 gaw_normal_array(0, 0);
723 gaw_texcoord_array(0, 0, 0);
724 gaw_color_array(0, 0, 0);
727 void gaw_free_compiled(int id)
731 darr_free(st.comp[idx].varr);
732 darr_free(st.comp[idx].narr);
733 darr_free(st.comp[idx].uvarr);
734 darr_free(st.comp[idx].carr);
735 memset(st.comp + idx, 0, sizeof *st.comp);
738 void gaw_mtl_diffuse(float r, float g, float b, float a)
746 void gaw_mtl_specular(float r, float g, float b, float shin)
754 void gaw_mtl_emission(float r, float g, float b)
761 void gaw_texenv_sphmap(int enable)
764 st.opt |= 1 << GAW_SPHEREMAP;
766 st.opt &= ~(1 << GAW_SPHEREMAP);
770 void gaw_set_tex1d(unsigned int texid)
773 gaw_bind_tex1d(texid);
774 gaw_enable(GAW_TEXTURE_1D);
777 gaw_disable(GAW_TEXTURE_1D);
781 void gaw_set_tex2d(unsigned int texid)
784 gaw_bind_tex2d(texid);
785 gaw_enable(GAW_TEXTURE_2D);
788 gaw_disable(GAW_TEXTURE_2D);
792 void gaw_ambient(float r, float g, float b)
799 void gaw_light_pos(int idx, float x, float y, float z)
801 int mvtop = st.mtop[GAW_MODELVIEW];
803 st.lt[idx].type = LT_POS;
808 xform4_vec3(st.mat[GAW_MODELVIEW][mvtop], &st.lt[idx].x);
811 void gaw_light_dir(int idx, float x, float y, float z)
813 int mvtop = st.mtop[GAW_MODELVIEW];
815 st.lt[idx].type = LT_DIR;
820 /* calc the normal matrix */
821 memcpy(st.norm_mat, st.mat[GAW_MODELVIEW][mvtop], 16 * sizeof(float));
822 st.norm_mat[12] = st.norm_mat[13] = st.norm_mat[14] = 0.0f;
824 xform4_vec3(st.norm_mat, &st.lt[idx].x);
826 NORMALIZE(&st.lt[idx].x);
829 void gaw_light_color(int idx, float r, float g, float b, float s)
836 void gaw_lighting_fast(void)
840 void gaw_fog_color(float r, float g, float b)
844 void gaw_fog_linear(float z0, float z1)
848 void gaw_fog_fast(void)
853 void gaw_poly_wire(void)
855 st.polymode = POLYFILL_WIRE;
858 void gaw_poly_flat(void)
860 st.polymode = POLYFILL_FLAT;
863 void gaw_poly_gouraud(void)
865 st.polymode = POLYFILL_GOURAUD;
869 static __inline void xform4_vec3(const float *mat, float *vec)
871 float x = mat[0] * vec[0] + mat[4] * vec[1] + mat[8] * vec[2] + mat[12];
872 float y = mat[1] * vec[0] + mat[5] * vec[1] + mat[9] * vec[2] + mat[13];
873 float z = mat[2] * vec[0] + mat[6] * vec[1] + mat[10] * vec[2] + mat[14];
874 vec[3] = mat[3] * vec[0] + mat[7] * vec[1] + mat[11] * vec[2] + mat[15];
880 static __inline void xform3_vec3(const float *mat, float *vec)
882 float x = mat[0] * vec[0] + mat[4] * vec[1] + mat[8] * vec[2];
883 float y = mat[1] * vec[0] + mat[5] * vec[1] + mat[9] * vec[2];
884 vec[2] = mat[2] * vec[0] + mat[6] * vec[1] + mat[10] * vec[2];
889 static void shade(struct vertex *v)
894 color[0] = st.ambient[0] * st.mtl.kd[0];
895 color[1] = st.ambient[1] * st.mtl.kd[1];
896 color[2] = st.ambient[2] * st.mtl.kd[2];
898 for(i=0; i<MAX_LIGHTS; i++) {
902 if(!(st.opt & (GAW_LIGHT0 << i))) {
906 ldir[0] = st.lt[i].x;
907 ldir[1] = st.lt[i].y;
908 ldir[2] = st.lt[i].z;
910 if(st.lt[i].type != LT_DIR) {
917 if((ndotl = v->nx * ldir[0] + v->ny * ldir[1] + v->nz * ldir[2]) < 0.0f) {
921 color[0] += st.mtl.kd[0] * st.lt[i].r * ndotl;
922 color[1] += st.mtl.kd[1] * st.lt[i].g * ndotl;
923 color[2] += st.mtl.kd[2] * st.lt[i].b * ndotl;
926 if(st.opt & (1 << GAW_SPECULAR)) {
930 if((ndoth = v->nx * ldir[0] + v->ny * ldir[1] + v->nz * ldir[2]) < 0.0f) {
933 ndoth = pow(ndoth, st.mtl.shin);
935 color[0] += st.mtl.ks[0] * st.lt[i].r * ndoth;
936 color[1] += st.mtl.ks[1] * st.lt[i].g * ndoth;
937 color[2] += st.mtl.ks[2] * st.lt[i].b * ndoth;
942 r = cround64(color[0] * 255.0);
943 g = cround64(color[1] * 255.0);
944 b = cround64(color[2] * 255.0);
946 v->r = r > 255 ? 255 : r;
947 v->g = g > 255 ? 255 : g;
948 v->b = b > 255 ? 255 : b;