--- /dev/null
+#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);
+ }
+}