mesh, obj loading, sorting, shitty normals...
authorJohn Tsiombikas <nuclear@member.fsf.org>
Fri, 1 Dec 2023 12:08:05 +0000 (14:08 +0200)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Fri, 1 Dec 2023 12:08:05 +0000 (14:08 +0200)
Makefile
src/3dgfx.c
src/3dgfx.h
src/main.c
src/mesh.c [new file with mode: 0644]
src/mesh.h [new file with mode: 0644]
src/polyfill.c
src/video.asm
src/video.h
tools/packsrc [changed mode: 0755->0644]

index ab97777..26b00dd 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,10 +1,10 @@
 
 !ifdef __UNIX__
 obj = src/main.obj src/video.obj src/3dgfx.obj src/3dgfx_s.obj &
-       src/polyfill.obj src/lut.obj src/vmath.obj src/vmath_s.obj
+       src/polyfill.obj src/lut.obj src/vmath.obj src/vmath_s.obj src/mesh.obj
 !else
 obj = src\main.obj src\video.obj src\3dgfx.obj src\3dgfx_s.obj &
-       src\polyfill.obj src\lut.obj src\vmath.obj src\vmath_s.obj
+       src\polyfill.obj src\lut.obj src\vmath.obj src\vmath_s.obj src\mesh.obj
 !endif
 bin = low3d.exe
 
index 3ebaa59..e56fdcf 100644 (file)
@@ -107,7 +107,7 @@ void g3d_color(int cidx)
        g3d_curcidx = cidx;
 }
 
-static const int primverts[] = {1, 2, 3, 4};
+static const int primverts[] = {0, 1, 2, 3, 4};
 
 void g3d_draw(int prim, struct g3d_vertex *varr, int vcount)
 {
@@ -116,11 +116,24 @@ void g3d_draw(int prim, struct g3d_vertex *varr, int vcount)
        prim_vnum = primverts[prim];
 
        for(i=0; i<vcount; i+=prim_vnum) {
-               g3d_draw_prim(prim, varr);
+               g3d_draw_prim(prim, varr, 0);
                varr += prim_vnum;
        }
 }
 
+void g3d_draw_indexed(int prim, struct g3d_vertex *varr, unsigned short *idxarr,
+               int idxcount)
+{
+       int i, j, prim_vnum;
+
+       prim_vnum = primverts[prim];
+
+       for(i=0; i<idxcount; i+=prim_vnum) {
+               g3d_draw_prim(prim, varr, idxarr);
+               idxarr += prim_vnum;
+       }
+}
+
 int32_t vpscale(int32_t x, int32_t s, int32_t shift);
 #pragma aux vpscale = \
        "add eax, 0x100" \
@@ -131,7 +144,7 @@ int32_t vpscale(int32_t x, int32_t s, int32_t shift);
        modify [eax edx];
 
 
-void g3d_draw_prim(int prim, struct g3d_vertex *varr)
+void g3d_draw_prim(int prim, struct g3d_vertex *varr, unsigned short *idxarr)
 {
        int i, vcount, x, y, x1, y1;
        struct g3d_vertex v[4];
@@ -139,7 +152,7 @@ void g3d_draw_prim(int prim, struct g3d_vertex *varr)
        vcount = primverts[prim];
 
        for(i=0; i<vcount; i++) {
-               v[i] = varr[i];
+               v[i] = idxarr ? varr[idxarr[i]] : varr[i];
 
                /* transform to view space */
                g3d_xform(v + i, mvmat);
index c071c30..f3aabfc 100644 (file)
@@ -11,7 +11,7 @@ struct g3d_vertex {
 };
 
 enum {
-       G3D_POINTS,
+       G3D_POINTS = 1,
        G3D_LINES,
        G3D_TRIANGLES,
        G3D_QUADS
@@ -41,7 +41,9 @@ void g3d_xform(struct g3d_vertex *v, const int32_t *m);
 void g3d_color(int cidx);
 
 void g3d_draw(int prim, struct g3d_vertex *varr, int vcount);
-void g3d_draw_prim(int prim, struct g3d_vertex *varr);
+void g3d_draw_indexed(int prim, struct g3d_vertex *varr, unsigned short *idxarr,
+               int idxcount);
+void g3d_draw_prim(int prim, struct g3d_vertex *varr, unsigned short *idxarr);
 
 /* defined in polyfill.asm */
 void g3d_polyfill(struct g3d_vertex *verts);
index 2d271e9..51b96aa 100644 (file)
@@ -7,6 +7,7 @@
 #include <dos.h>
 #include "video.h"
 #include "3dgfx.h"
+#include "mesh.h"
 #include "vmath.h"
 #include "util.h"
 
@@ -21,11 +22,28 @@ static int quit;
 static unsigned long nframes;
 
 volatile unsigned long nticks;
-
 static void interrupt (*prev_timer_intr)();
 
+static const char *mesh_fname;
+static struct mesh mesh;
+
+#define VERT(x, y, z) { x << 16, y << 16, z << 16, 0x10000 }
+static struct g3d_vertex cube_varr[] = {
+       VERT(-1, -1, 1), VERT(1, -1, 1), VERT(1, -1, -1), VERT(-1, -1, -1),
+       VERT(-1, 1, 1), VERT(1, 1, 1), VERT(1, 1, -1), VERT(-1, 1, -1)
+};
+static struct meshface cube_faces[] = {
+       {4, {0, 1, 5, 4}},
+       {4, {1, 2, 6, 5}},
+       {4, {2, 3, 7, 6}},
+       {4, {3, 0, 4, 7}},
+       {4, {4, 5, 6, 7}},
+       {4, {3, 2, 1, 0}}
+};
+
 int main(int argc, char **argv)
 {
+       int i;
        int32_t proj[16];
        unsigned long rate, total_ticks, sec;
 
@@ -33,8 +51,30 @@ int main(int argc, char **argv)
                return 1;
        }
 
+       if(mesh_fname) {
+               if(load_mesh(&mesh, mesh_fname) == -1) {
+                       return 1;
+               }
+               flip_mesh_winding(&mesh);
+       } else {
+               mesh.varr = cube_varr;
+               mesh.nverts = sizeof cube_varr / sizeof *cube_varr;
+               mesh.faces = cube_faces;
+               mesh.nfaces = sizeof cube_faces / sizeof *cube_faces;
+       }
+       for(i=0; i<mesh.nfaces; i++) {
+               mesh.faces[i].color = (i & 7) + 9;
+       }
+
        init_video();
 
+       for(i=0; i<256; i++) {
+               int r = XCOS(i << 1) >> 5;
+               int b = (255 - r) >> 1;
+               vid_setpalent(i, r, 8, b);
+       }
+       vid_setpalent(0, 0, 0, 0);
+
        g3d_init();
        g3d_framebuffer(320, 200, 0);
 
@@ -72,21 +112,12 @@ end:
        return 0;
 }
 
-#define VERT(x, y, z) { x << 16, y << 16, z << 16, 0x10000 }
-struct g3d_vertex varr[] = {
-       VERT(-1, -1, 1), VERT(1, -1, 1), VERT(1, 1, 1), VERT(-1, 1, 1),
-       VERT(1, -1, 1), VERT(1, -1, -1), VERT(1, 1, -1), VERT(1, 1, 1),
-       VERT(1, -1, -1), VERT(-1, -1, -1), VERT(-1, 1, -1), VERT(1, 1, -1),
-       VERT(-1, -1, -1), VERT(-1, -1, 1), VERT(-1, 1, 1), VERT(-1, 1, -1),
-       VERT(-1, 1, 1), VERT(1, 1, 1), VERT(1, 1, -1), VERT(-1, 1, -1),
-       VERT(-1, -1, -1), VERT(1, -1, -1), VERT(1, -1, 1), VERT(-1, -1, 1)
-};
-
 void update(void)
 {
+       int i;
        int32_t xform[16];
 
-       mat_trans(xform, 0, 0, -0x40000);
+       mat_trans(xform, 0, 0, -0x70000);
        mat_mul_rotx(xform, nframes);
        mat_mul_roty(xform, nframes);
        g3d_modelview(xform);
@@ -102,18 +133,8 @@ void update(void)
        vid_clearfb();
 #endif
 
-       g3d_color(9);
-       g3d_draw(G3D_QUADS, varr, 4);
-       g3d_color(10);
-       g3d_draw(G3D_QUADS, varr + 4, 4);
-       g3d_color(11);
-       g3d_draw(G3D_QUADS, varr + 8, 4);
-       g3d_color(12);
-       g3d_draw(G3D_QUADS, varr + 12, 4);
-       g3d_color(13);
-       g3d_draw(G3D_QUADS, varr + 16, 4);
-       g3d_color(14);
-       g3d_draw(G3D_QUADS, varr + 20, 4);
+       sort_mesh(&mesh, xform);
+       draw_mesh_zorder(&mesh);
 
        vid_pgflip();
 }
@@ -137,7 +158,7 @@ void interrupt timer_intr()
        _chain_intr(prev_timer_intr);
 }
 
-static const char *helpfmt = "Usage: %s [options]\n"
+static const char *helpfmt = "Usage: %s [options] [mesh file]\n"
        "Options:\n"
        "  -vsync: enable vsync (default)\n"
        "  -novsync: disable vsync\n"
@@ -161,8 +182,11 @@ int parse_args(int argc, char **argv)
                                return -1;
                        }
                } else {
-                       fprintf(stderr, "unexpected argument: %s\n", argv[i]);
-                       return -1;
+                       if(mesh_fname) {
+                               fprintf(stderr, "unexpected argument: %s\n", argv[i]);
+                               return -1;
+                       }
+                       mesh_fname = argv[i];
                }
        }
 
diff --git a/src/mesh.c b/src/mesh.c
new file mode 100644 (file)
index 0000000..f7771d8
--- /dev/null
@@ -0,0 +1,356 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "mesh.h"
+
+static char *parse_facev(char *s, int *vidx, int *nidx, int *tidx,
+               int nverts, int nnorm, int nuv);
+static int resizearr(void **pptr, int *sz, int elemsz);
+static char *clean_line(char *s);
+
+int load_mesh(struct mesh *m, const char *fname)
+{
+       float fx, fy, fz;
+       FILE *fp;
+       char buf[128], *line;
+       struct g3d_vertex *vptr;
+       struct meshface *face;
+       int i, maxverts, maxfaces;
+       int vidx, tidx, nidx;
+
+       memset(m, 0, sizeof *m);
+
+       if(!(fp = fopen(fname, "rb"))) {
+               fprintf(stderr, "failed to open mesh: %s\n", fname);
+               return -1;
+       }
+
+       printf("Loading mesh: %s\n", fname);
+
+       m->nverts = 0;
+       maxverts = 32;
+       if(!(m->varr = malloc(maxverts * sizeof *m->varr))) {
+               fprintf(stderr, "failed to allocate vertex array\n");
+               goto err;
+       }
+
+       m->nfaces = 0;
+       maxfaces = 32;
+       if(!(m->faces = malloc(maxfaces * sizeof *m->faces))) {
+               fprintf(stderr, "failed to allocate mesh face array\n");
+               goto err;
+       }
+
+
+       while(fgets(buf, sizeof buf, fp)) {
+               line = clean_line(buf);
+               if(!*line) continue;
+
+               switch(line[0]) {
+               case 'v':
+                       if(!isspace(line[1])) continue;
+                       if(sscanf(line + 2, "%f %f %f", &fx, &fy, &fz) != 3) {
+                               continue;
+                       }
+                       if(m->nverts >= maxverts) {
+                               if(resizearr(&m->varr, &maxverts, sizeof *m->varr) == -1) {
+                                       goto err;
+                               }
+                       }
+                       vptr = m->varr + m->nverts++;
+                       vptr->x = (int32_t)(fx * 65536.0f);
+                       vptr->y = (int32_t)(fy * 65536.0f);
+                       vptr->z = (int32_t)(fz * 65536.0f);
+                       vptr->w = 0x10000;
+                       putchar('v'); fflush(stdout);
+                       break;
+
+               case 'f':
+                       if(m->nfaces >= maxfaces) {
+                               if(resizearr(&m->faces, &maxfaces, sizeof *m->faces) == -1) {
+                                       goto err;
+                               }
+                       }
+
+                       line += 2;
+                       face = m->faces + m->nfaces;
+                       for(i=0; i<4; i++) {
+                               if(!(line = parse_facev(line, &vidx, &nidx, &tidx, m->nverts, 0, 0))) {
+                                       if(i < 3) {
+                                               fprintf(stderr, "invalid face definition\n");
+                                               goto err;
+                                       }
+                                       break;
+                               }
+                               if(vidx < 0 || vidx >= m->nverts) {
+                                       fprintf(stderr, "invalid face definition, vertex out of range\n");
+                                       goto err;
+                               }
+                               face->vidx[i] = vidx;
+                       }
+                       face->prim = i;
+                       m->nfaces++;
+                       putchar('f'); fflush(stdout);
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       calc_face_normals(m);
+
+       for(i=0; i<m->nfaces; i++) {
+               int32_t dot;
+               face = m->faces + i;
+               dot = face->norm.z > 0 ? face->norm.z : 0;
+               face->color = dot >> 8;
+       }
+
+       putchar('\n');
+
+       fclose(fp);
+       return 0;
+
+err:
+       if(fp) fclose(fp);
+       free(m->varr);
+       free(m->faces);
+       return -1;
+}
+
+static char *parse_idx(char *s, int *idx, int arrsz)
+{
+       char *endp;
+       int val = strtol(s, &endp, 10);
+       if(endp == s) return 0;
+
+       if(val < 0) {   /* convert negative indices */
+               *idx = arrsz + val;
+       } else {
+               *idx = val - 1; /* conv to 0-based */
+       }
+       return endp;
+}
+
+static char *parse_facev(char *s, int *vidx, int *nidx, int *tidx,
+               int nverts, int nnorm, int nuv)
+{
+       if(!(s = parse_idx(s, vidx, nverts))) {
+               return 0;
+       }
+       if(*s != '/') return (!*s || isspace(*s)) ? s : 0;
+
+       if(*++s == '/') {       /* no texcoord */
+               *tidx = -1;
+               s++;
+       } else {
+               if(!(s = parse_idx(s, tidx, nuv))) {
+                       return 0;
+               }
+               if(*s != '/') return (!*s || isspace(*s)) ? s : 0;
+               s++;
+       }
+
+       if(!(s = parse_idx(s, nidx, nnorm))) {
+               return 0;
+       }
+       return (!*s || isspace(*s)) ? s : 0;
+}
+
+static char *clean_line(char *s)
+{
+       char *endp;
+
+       while(*s && isspace(*s)) s++;
+
+       if((endp = strchr(s, '#'))) {
+               *endp-- = 0;
+       } else {
+               endp = s + strlen(s) - 1;
+       }
+       while(endp > s && isspace(*endp)) {
+               *endp-- = 0;
+       }
+       return s;
+}
+
+static int resizearr(void **pptr, int *sz, int elemsz)
+{
+       int newsz = *sz ? *sz * 2 : 16;
+       void *newarr;
+
+       if(!(newarr = realloc(*pptr, newsz * elemsz))) {
+               fprintf(stderr, "failed to resize array to %d\n", newsz);
+               return -1;
+       }
+       *pptr = newarr;
+       *sz = newsz;
+       return 0;
+}
+
+
+void destroy_mesh(struct mesh *m)
+{
+       if(!m) return;
+       free(m->varr);
+       free(m->faces);
+}
+
+void flip_mesh_winding(struct mesh *m)
+{
+       int i, j;
+       unsigned short buf[4];
+       struct meshface *face = m->faces;
+
+       for(i=0; i<m->nfaces; i++) {
+               for(j=0; j<face->prim; j++) {
+                       buf[face->prim - j - 1] = face->vidx[j];
+               }
+               for(j=0; j<face->prim; j++) {
+                       face->vidx[j] = buf[j];
+               }
+               face++;
+       }
+}
+
+int conv_nonidx_mesh(struct mesh *m)
+{
+       struct g3d_vertex *varr, *vptr;
+       struct meshface *face;
+       int i, nverts;
+
+       nverts = m->nfaces * 3;
+       if(!(varr = malloc(nverts * sizeof *varr))) {
+               fprintf(stderr, "failed to allocate nonidx vertex array (%d)\n", nverts);
+               return -1;
+       }
+
+       face = m->faces;
+       vptr = varr;
+       for(i=0; i<m->nfaces; i++) {
+               vptr[0] = m->varr[face->vidx[0]];
+               vptr[1] = m->varr[face->vidx[1]];
+               vptr[2] = m->varr[face->vidx[2]];
+               face++;
+               vptr += 3;
+       }
+
+       free(m->varr);
+       m->varr = varr;
+       m->nverts = nverts;
+       return 0;
+}
+
+static float rsqrt(float x)
+{
+       float xhalf = x * 0.5f;
+       int32_t i = *(int32_t*)&x;
+       i = 0x5f3759df - (i >> 1);
+       x = *(float*)&i;
+       x = x * (1.5f - xhalf * x * x);
+       return x;
+}
+
+void calc_face_normals(struct mesh *m)
+{
+       int i;
+       struct meshface *face;
+       struct g3d_vertex *va, *vb, *vc;
+       int32_t ax, ay, az, bx, by, bz, s;
+       float xsq, ysq, zsq;
+
+       face = m->faces;
+       for(i=0; i<m->nfaces; i++) {
+               va = m->varr + face->vidx[0];
+               vb = m->varr + face->vidx[1];
+               vc = m->varr + face->vidx[2];
+
+               ax = (vc->x - va->x) >> 8;
+               ay = (vc->y - va->y) >> 8;
+               az = (vc->z - va->z) >> 8;
+               bx = (vc->x - vb->x) >> 8;
+               by = (vc->y - vb->y) >> 8;
+               bz = (vc->z - vb->z) >> 8;
+
+               face->norm.x = ay * bz - az * by;
+               face->norm.y = az * bx - ax * bz;
+               face->norm.z = ax * by - ay * bx;
+
+               xsq = (float)face->norm.x; xsq *= xsq;
+               ysq = (float)face->norm.y; ysq *= ysq;
+               zsq = (float)face->norm.z; zsq *= zsq;
+               s = (int32_t)(rsqrt(xsq + ysq + zsq) * 256.0f);
+               face->norm.x = (face->norm.x >> 8) * s;
+               face->norm.y = (face->norm.y >> 8) * s;
+               face->norm.z = (face->norm.z >> 8) * s;
+
+               face++;
+       }
+}
+
+static struct mesh *smesh;
+static const int32_t *smat;
+
+static int zsortcmp(const void *a, const void *b)
+{
+       unsigned short idxa, idxb;
+       struct meshface *fa, *fb;
+       struct g3d_vertex *va, *vb;
+       int32_t za, zb;
+
+       fa = smesh->faces + *(unsigned short*)a;
+       fb = smesh->faces + *(unsigned short*)b;
+
+       va = smesh->varr + fa->vidx[0];
+       vb = smesh->varr + fb->vidx[0];
+
+       za = (smat[8] >> 8) * (va->x >> 8) + (smat[9] >> 8) * (va->y >> 8) +
+               (smat[10] >> 8) * (va->z >> 8);
+       zb = (smat[8] >> 8) * (vb->x >> 8) + (smat[9] >> 8) * (vb->y >> 8) +
+               (smat[10] >> 8) * (vb->z >> 8);
+
+       return za - zb;
+}
+
+void sort_mesh(struct mesh *m, const int32_t *mvmat)
+{
+       int i;
+
+       if(!m->zorder) {
+               if(!(m->zorder = malloc(m->nfaces * sizeof *m->zorder))) {
+                       return;
+               }
+               for(i=0; i<m->nfaces; i++) {
+                       m->zorder[i] = i;
+               }
+       }
+
+       smesh = m;
+       smat = mvmat;
+       qsort(m->zorder, m->nfaces, sizeof *m->zorder, zsortcmp);
+}
+
+void draw_mesh(struct mesh *m)
+{
+       int i;
+       struct meshface *face = m->faces;
+       for(i=0; i<m->nfaces; i++) {
+               g3d_color(face->color);
+               g3d_draw_indexed(face->prim, m->varr, face->vidx, face->prim);
+               face++;
+       }
+}
+
+void draw_mesh_zorder(struct mesh *m)
+{
+       int i;
+       struct meshface *face;
+
+       for(i=0; i<m->nfaces; i++) {
+               face = m->faces + m->zorder[i];
+               g3d_color(face->color);
+               g3d_draw_indexed(face->prim, m->varr, face->vidx, face->prim);
+       }
+}
diff --git a/src/mesh.h b/src/mesh.h
new file mode 100644 (file)
index 0000000..0e2864d
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef MESH_H_
+#define MESH_H_
+
+#include "3dgfx.h"
+
+struct vec3 {
+       int32_t x, y, z;
+};
+struct meshface {
+       short prim;     /* 3 - triangle / 4 - quad */
+       unsigned short vidx[4];
+       unsigned char color;
+       struct vec3 norm;
+};
+
+struct mesh {
+       struct g3d_vertex *varr;
+       struct meshface *faces;
+       int nverts, nfaces;
+       unsigned short *zorder;
+};
+
+int load_mesh(struct mesh *m, const char *fname);
+void destroy_mesh(struct mesh *m);
+
+void flip_mesh_winding(struct mesh *m);
+int conv_nonidx_mesh(struct mesh *m);
+void calc_face_normals(struct mesh *m);
+
+void sort_mesh(struct mesh *m, const int32_t *mvmat);
+void draw_mesh(struct mesh *m);
+void draw_mesh_zorder(struct mesh *m);
+
+#endif /* MESH_H_ */
index c7fc500..daeac8c 100644 (file)
@@ -8,6 +8,7 @@ static void filltop(struct g3d_vertex *v0, struct g3d_vertex *v1, struct g3d_ver
 static void fillbot(struct g3d_vertex *v0, struct g3d_vertex *v1, struct g3d_vertex *v2);
 static void fillspan(unsigned char *dest, int x, int len);
 
+
 void g3d_polyfill(struct g3d_vertex *verts)
 {
        int i, topidx, botidx, mididx;
index 2de68eb..5ed2aab 100644 (file)
@@ -60,6 +60,23 @@ close_video_:
        pop ax
        ret
 
+       global vid_setpalent_
+vid_setpalent_:
+       mov ah, dl
+       mov dx, 3c8h
+       out dx, al
+       inc dx
+       mov al, ah
+       shr al, 2
+       out dx, al
+       mov al, bl
+       shr al, 2
+       out dx, al
+       mov al, cl
+       shr al, 2
+       out dx, al
+       ret
+
        ; clear the framebuffer 4 pixels at a time
        global vid_clearfb_
 vid_clearfb_:
index 3203a13..f00e95f 100644 (file)
@@ -5,6 +5,7 @@ extern unsigned char *vid_backbuf;
 
 void init_video(void);
 void close_video(void);
+void vid_setpalent(int idx, int r, int g, int b);
 
 void vid_clearfb(void);
 void vid_clearfb_rect(int x, int y, int w, int h);
old mode 100755 (executable)
new mode 100644 (file)