better obj loading
authorJohn Tsiombikas <nuclear@member.fsf.org>
Sat, 19 Jun 2021 16:28:34 +0000 (19:28 +0300)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Sat, 19 Jun 2021 16:28:34 +0000 (19:28 +0300)
Makefile
src/main.c
src/mesh.c
src/mesh.h

index 39862c9..db3c31a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@ obj = $(src:.c=.o)
 dep = $(src:.c=.d)
 bin = cyberay
 
-opt = -O3 -ffast-math
+opt = -O3 -ffast-math -fno-strict-aliasing
 dbg = -g
 warn = -pedantic -Wall
 inc = -Ilibs
index 28a9d3e..1f4d3ac 100644 (file)
@@ -63,7 +63,7 @@ static int keymap[NUM_INPUTS][2] = {
        {' ', 0}
 };
 
-static struct mesh mesh;
+static struct scenefile scn;
 
 int main(int argc, char **argv)
 {
@@ -100,7 +100,7 @@ static int init(void)
        glEnable(GL_LIGHTING);
        glEnable(GL_LIGHT0);
 
-       if(load_mesh(&mesh, "data/testlvl.obj") == -1) {
+       if(load_scenefile(&scn, "data/testlvl.obj") == -1) {
                return -1;
        }
 
@@ -110,7 +110,7 @@ static int init(void)
 
 static void cleanup(void)
 {
-       destroy_mesh(&mesh);
+       destroy_scenefile(&scn);
 }
 
 #define WALK_SPEED 3.0f
@@ -150,6 +150,8 @@ static void update(void)
 
 static void display(void)
 {
+       struct mesh *mesh;
+
        update();
 
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@@ -157,11 +159,18 @@ static void display(void)
        glMatrixMode(GL_MODELVIEW);
        glLoadMatrixf(pxform);
 
-       glFrontFace(GL_CW);
-       glutSolidTeapot(1.0);
-       glFrontFace(GL_CCW);
-
-       draw_mesh(&mesh);
+       mesh = scn.meshlist;
+       while(mesh) {
+               float col[4];
+               col[0] = mesh->mtl.color.x;
+               col[1] = mesh->mtl.color.y;
+               col[2] = mesh->mtl.color.z;
+               col[3] = 1.0f;
+               glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, col);
+
+               draw_mesh(mesh);
+               mesh = mesh->next;
+       }
 
        glutSwapBuffers();
        assert(glGetError() == GL_NO_ERROR);
index 0e2218f..1e485e8 100644 (file)
@@ -8,6 +8,270 @@ struct facevertex {
        int vidx, tidx, nidx;
 };
 
+struct objmtl {
+       char name[64];
+       cgm_vec3 ka, kd, ks;
+       float shin;
+       float alpha;
+       float ior;
+       struct objmtl *next;
+};
+
+static void calc_face_normal(struct triangle *tri);
+static char *cleanline(char *s);
+static char *parse_idx(char *ptr, int *idx, int arrsz);
+static char *parse_face_vert(char *ptr, struct facevertex *fv, int numv, int numt, int numn);
+
+static struct objmtl *load_mtllib(const char *objfname, const char *mtlfname);
+static void free_mtllist(struct objmtl *mtl);
+static void conv_mtl(struct material *mm, struct objmtl *om);
+
+#define GROW_ARRAY(arr, sz)    \
+       do { \
+               int newsz = (sz) ? (sz) * 2 : 16; \
+               void *tmp = realloc(arr, newsz * sizeof *(arr)); \
+               if(!tmp) { \
+                       fprintf(stderr, "failed to grow array to %d\n", newsz); \
+                       goto fail; \
+               } \
+               arr = tmp; \
+               sz = newsz; \
+       } while(0)
+
+
+int load_scenefile(struct scenefile *scn, const char *fname)
+{
+       int i, nlines, total_faces = 0, res = -1;
+       FILE *fp;
+       char buf[256], *line, *ptr;
+       int varr_size, varr_max, narr_size, narr_max, tarr_size, tarr_max, max_faces;
+       cgm_vec3 v, *varr = 0, *narr = 0;
+       cgm_vec2 *tarr = 0;
+       struct facevertex fv[4];
+       int numfv;
+       struct mesh *mesh;
+       struct triangle *tri;
+       static const cgm_vec2 def_tc = {0, 0};
+       struct objmtl curmtl, *mtl, *mtllist = 0;
+
+
+       varr_size = varr_max = narr_size = narr_max = tarr_size = tarr_max = 0;
+       varr = narr = 0;
+       tarr = 0;
+
+       if(!(fp = fopen(fname, "rb"))) {
+               fprintf(stderr, "load_scenefile: failed to open %s\n", fname);
+               return -1;
+       }
+
+       if(!(mesh = calloc(1, sizeof *mesh))) {
+               fprintf(stderr, "failed to allocate mesh\n");
+               fclose(fp);
+               return -1;
+       }
+       max_faces = 0;
+
+       scn->meshlist = 0;
+       scn->num_meshes = 0;
+
+       /* default material: white diffuse */
+       memset(&curmtl, 0, sizeof curmtl);
+       cgm_vcons(&curmtl.kd, 1.0f, 1.0f, 1.0f);
+       curmtl.alpha = curmtl.ior = 1.0f;
+
+       nlines = 0;
+       while(fgets(buf, sizeof buf, fp)) {
+               nlines++;
+               if(!(line = cleanline(buf))) {
+                       continue;
+               }
+
+               switch(line[0]) {
+               case 'v':
+                       v.x = v.y = v.z = 0.0f;
+                       if(sscanf(line + 2, "%f %f %f", &v.x, &v.y, &v.z) < 2) {
+                               break;
+                       }
+                       if(isspace(line[1])) {
+                               if(varr_size >= varr_max) {
+                                       GROW_ARRAY(varr, varr_max);
+                               }
+                               varr[varr_size++] = v;
+                       } else if(line[1] == 't' && isspace(line[2])) {
+                               if(tarr_size >= tarr_max) {
+                                       GROW_ARRAY(tarr, tarr_max);
+                               }
+                               tarr[tarr_size++] = *(cgm_vec2*)&v;
+                       } else if(line[1] == 'n' && isspace(line[2])) {
+                               if(narr_size >= narr_max) {
+                                       GROW_ARRAY(narr, narr_max);
+                               }
+                               narr[narr_size++] = v;
+                       }
+                       break;
+
+               case 'f':
+                       if(!isspace(line[1])) break;
+
+                       ptr = line + 2;
+
+                       numfv = 0;
+                       for(i=0; i<4; i++) {
+                               if(!(ptr = parse_face_vert(ptr, fv + i, varr_size, tarr_size, narr_size))) {
+                                       break;
+                               }
+                               numfv++;
+                       }
+                       if(numfv < 3) break;
+
+                       if(mesh->num_faces >= max_faces - 1) {
+                               GROW_ARRAY(mesh->faces, max_faces);
+                       }
+                       tri = mesh->faces + mesh->num_faces++;
+                       tri->mtl = &mesh->mtl;
+
+                       tri->v[0].pos = varr[fv[0].vidx];
+                       tri->v[1].pos = varr[fv[1].vidx];
+                       tri->v[2].pos = varr[fv[2].vidx];
+                       calc_face_normal(tri);
+                       for(i=0; i<3; i++) {
+                               tri->v[i].norm = fv[i].nidx >= 0 ? narr[fv[i].nidx] : tri->norm;
+                               tri->v[i].tex = fv[i].tidx >= 0 ? tarr[fv[i].tidx] : def_tc;
+                       }
+
+                       if(numfv > 3) {
+                               tri++;
+                               mesh->num_faces++;
+                               tri->mtl = &mesh->mtl;
+                               tri->norm = tri[-1].norm;
+                               tri->v[0] = tri[-1].v[0];
+                               tri->v[1] = tri[-1].v[1];
+
+                               tri->v[2].pos = varr[fv[3].vidx];
+                               tri->v[2].norm = fv[3].nidx >= 0 ? narr[fv[3].nidx] : tri->norm;
+                               tri->v[2].tex = fv[3].tidx >= 0 ? tarr[fv[3].tidx] : def_tc;
+                       }
+                       break;
+
+               case 'o':
+               case 'g':
+                       if(mesh->num_faces) {
+                               conv_mtl(&mesh->mtl, &curmtl);
+                               total_faces += mesh->num_faces;
+                               mesh->next = scn->meshlist;
+                               scn->meshlist = mesh;
+                               scn->num_meshes++;
+
+                               printf("added mesh with mtl: %s\n", curmtl.name);
+
+                               if(!(mesh = calloc(1, sizeof *mesh))) {
+                                       fprintf(stderr, "failed to allocate mesh\n");
+                                       goto fail;
+                               }
+                               max_faces = 0;
+                       }
+                       break;
+
+               case 'm':
+                       if(memcmp(line, "mtllib", 6) == 0 && (line = cleanline(line + 6))) {
+                               free_mtllist(mtllist);
+                               mtllist = load_mtllib(fname, line);
+                       }
+                       break;
+
+               case 'u':
+                       if(memcmp(line, "usemtl", 6) == 0 && (line = cleanline(line + 6))) {
+                               mtl = mtllist;
+                               while(mtl) {
+                                       if(strcmp(mtl->name, line) == 0) {
+                                               curmtl = *mtl;
+                                               break;
+                                       }
+                                       mtl = mtl->next;
+                               }
+                       }
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       if(mesh->num_faces) {
+               conv_mtl(&mesh->mtl, &curmtl);
+               total_faces += mesh->num_faces;
+               mesh->next = scn->meshlist;
+               scn->meshlist = mesh;
+               scn->num_meshes++;
+
+               printf("added mesh with mtl: %s\n", curmtl.name);
+       } else {
+               free(mesh);
+       }
+       mesh = 0;
+
+       printf("load_scenefile: loaded %d meshes, %d vertices, %d triangles\n", scn->num_meshes,
+                       varr_size, total_faces);
+
+       res = 0;
+
+fail:
+       fclose(fp);
+       free(mesh);
+       free(varr);
+       free(narr);
+       free(tarr);
+       free_mtllist(mtllist);
+       return res;
+}
+
+void destroy_scenefile(struct scenefile *scn)
+{
+       struct mesh *m;
+       while(scn->meshlist) {
+               m = scn->meshlist;
+               scn->meshlist = scn->meshlist->next;
+               free(m);
+       }
+}
+
+void destroy_mesh(struct mesh *m)
+{
+       free(m->faces);
+       m->faces = 0;
+}
+
+void draw_mesh(struct mesh *m)
+{
+       int i;
+
+       glBegin(GL_TRIANGLES);
+       for(i=0; i<m->num_faces; i++) {
+               glNormal3fv((float*)&m->faces[i].v[0].norm);
+               glVertex3fv((float*)&m->faces[i].v[0].pos);
+
+               glNormal3fv((float*)&m->faces[i].v[1].norm);
+               glVertex3fv((float*)&m->faces[i].v[1].pos);
+
+               glNormal3fv((float*)&m->faces[i].v[2].norm);
+               glVertex3fv((float*)&m->faces[i].v[2].pos);
+       }
+       glEnd();
+}
+
+static void calc_face_normal(struct triangle *tri)
+{
+       cgm_vec3 va, vb;
+
+       va = tri->v[1].pos;
+       cgm_vsub(&va, &tri->v[0].pos);
+       vb = tri->v[2].pos;
+       cgm_vsub(&vb, &tri->v[0].pos);
+
+       cgm_vcross(&tri->norm, &va, &vb);
+       cgm_vnormalize(&tri->norm);
+}
+
 static char *cleanline(char *s)
 {
        char *ptr;
@@ -16,7 +280,7 @@ static char *cleanline(char *s)
 
        while(*s && isspace(*s)) s++;
        ptr = s + strlen(s) - 1;
-       while(ptr >= s && isspace(*s)) *ptr-- = 0;
+       while(ptr >= s && isspace(*ptr)) *ptr-- = 0;
 
        return *s ? s : 0;
 }
@@ -43,12 +307,13 @@ static char *parse_idx(char *ptr, int *idx, int arrsz)
  */
 static char *parse_face_vert(char *ptr, struct facevertex *fv, int numv, int numt, int numn)
 {
+       fv->tidx = fv->nidx = -1;
+
        if(!(ptr = parse_idx(ptr, &fv->vidx, numv)))
                return 0;
        if(*ptr != '/') return (!*ptr || isspace(*ptr)) ? ptr : 0;
 
        if(*++ptr == '/') {     /* no texcoord */
-               fv->tidx = -1;
                ++ptr;
        } else {
                if(!(ptr = parse_idx(ptr, &fv->tidx, numt)))
@@ -62,162 +327,67 @@ static char *parse_face_vert(char *ptr, struct facevertex *fv, int numv, int num
        return (!*ptr || isspace(*ptr)) ? ptr : 0;
 }
 
-
-#define APPEND(prefix) \
-       do { \
-               if(prefix##count >= prefix##max) { \
-                       int newsz = prefix##max ? prefix##max * 2 : 8; \
-                       void *ptr = realloc(prefix##arr, newsz * sizeof(cgm_vec3)); \
-                       if(!ptr) { \
-                               fprintf(stderr, "load_mesh: failed to resize array to %d elements\n", newsz); \
-                               return -1; \
-                       } \
-                       prefix##arr = ptr; \
-                       prefix##max = newsz; \
-               } \
-       } while(0)
-
-
-int load_mesh(struct mesh *m, const char *fname)
+static struct objmtl *load_mtllib(const char *objfname, const char *mtlfname)
 {
-       int i, num, nline, sidx, didx, res = -1;
        FILE *fp;
-       cgm_vec3 v, va, vb, fnorm;
-       cgm_vec3 *varr, *narr, *tarr;
-       int vcount, ncount, tcount, vmax, nmax, tmax, max_faces, newsz;
-       char linebuf[256], *line, *ptr;
-       struct facevertex fv[4];
-       struct triangle *tri;
-       void *tmpptr;
-       static const int quadidx[] = { 0, 1, 2, 0, 1, 3 };
-
-       varr = narr = tarr = 0;
-       vcount = ncount = tcount = vmax = nmax = tmax = 0;
+       char *sep;
+       char buf[256], *line;
+       struct objmtl *mlist = 0, *m = 0;
 
-       m->faces = 0;
-       m->num_faces = 0;
-       max_faces = 0;
+       strcpy(buf, objfname);
+       if((sep = strrchr(buf, '/'))) {
+               sep[1] = 0;
+       } else {
+               buf[0] = 0;
+       }
+       strcat(buf, mtlfname);
 
-       if(!(fp = fopen(fname, "rb"))) {
-               fprintf(stderr, "load_mesh: failed to open: %s\n", fname);
-               return -1;
+       if(!(fp = fopen(buf, "rb"))) {
+               return 0;
        }
 
-       nline = 0;
-       while(fgets(linebuf, sizeof linebuf, fp)) {
-               nline++;
-               if(!(line = cleanline(linebuf))) {
+       while(fgets(buf, sizeof buf, fp)) {
+               if(!(line = cleanline(buf))) {
                        continue;
                }
 
-               switch(line[0]) {
-               case 'v':
-                       v.y = v.z = 0;
-                       if((num = sscanf(line + 2, "%f %f %f", &v.x, &v.y, &v.z)) < 2) {
-verr:                  fprintf(stderr, "load_mesh: ignoring malformed attribute at %d: %s\n", nline, line);
-                               continue;
-                       }
-                       if(isspace(line[1])) {
-                               APPEND(v);
-                               varr[vcount++] = v;
-                       } else if(line[1] == 'n' && isspace(line[2])) {
-                               APPEND(n);
-                               narr[ncount++] = v;
-                       } else if(line[1] == 't' && isspace(line[2])) {
-                               APPEND(t);
-                               tarr[tcount].x = v.x;
-                               tarr[tcount++].y = v.y;
-                       } else {
-                               goto verr;
-                       }
-                       break;
-
-               case 'f':
-                       if(!isspace(line[1])) {
-                               break;
-                       }
-                       ptr = line + 2;
-                       for(i=0; i<4; i++) {
-                               fv[i].nidx = fv[i].tidx = -1;
-                               if(!(ptr = parse_face_vert(ptr, fv + i, vcount, tcount, ncount))) {
-                                       break;
-                               }
-                       }
-
-                       if(i < 2) {
-                               fprintf(stderr, "load_mesh: invalid face definition at %d: %s\n", nline, line);
-                               break;
+               if(memcmp(line, "newmtl", 6) == 0) {
+                       if(m) {
+                               m->next = mlist;
+                               mlist = m;
                        }
-
-                       va = varr[fv[1].vidx];
-                       cgm_vsub(&va, varr + fv[0].vidx);
-                       vb = varr[fv[2].vidx];
-                       cgm_vsub(&vb, varr + fv[0].vidx);
-                       cgm_vcross(&fnorm, &va, &vb);
-                       cgm_vnormalize(&fnorm);
-
-                       if(m->num_faces >= max_faces - 1) {
-                               newsz = max_faces ? max_faces * 2 : 16;
-                               if(!(tmpptr = realloc(m->faces, newsz * sizeof *m->faces))) {
-                                       fprintf(stderr, "load_mesh: failed to resize faces array to %d\n", newsz);
-                                       goto end;
+                       if((m = calloc(1, sizeof *m))) {
+                               if((line = cleanline(line + 6))) {
+                                       strcpy(m->name, line);
                                }
-                               m->faces = tmpptr;
-                               max_faces = newsz;
                        }
-
-                       num = i > 3 ? 6 : 3;
-                       for(i=0; i<num; i++) {
-                               if(i % 3 == 0) {
-                                       tri = m->faces + m->num_faces++;
-                                       tri->norm = fnorm;
-                                       tri->mtl = &m->mtl;
-                               }
-                               sidx = quadidx[i];
-                               didx = i >= 3 ? i - 3 : i;
-                               tri->v[didx].pos = varr[fv[sidx].vidx];
-                               tri->v[didx].norm = fv[sidx].nidx >= 0 ? varr[fv[sidx].nidx] : fnorm;
-                               if(fv[sidx].tidx >= 0) {
-                                       tri->v[didx].tex.x = tarr[fv[sidx].tidx].x;
-                                       tri->v[didx].tex.y = tarr[fv[sidx].tidx].y;
-                               } else {
-                                       tri->v[didx].tex.x = tri->v[sidx].tex.y = 0;
-                               }
+               } else if(memcmp(line, "Kd", 2) == 0) {
+                       if(m) {
+                               sscanf(line + 3, "%f %f %f", &m->kd.x, &m->kd.y, &m->kd.z);
                        }
-                       break;
                }
+       }
 
+       if(m) {
+               m->next = mlist;
+               mlist = m;
        }
 
-       res = 0;
-end:
-       free(varr);
-       free(narr);
-       free(tarr);
        fclose(fp);
-       return res;
+       return mlist;
 }
 
-void destroy_mesh(struct mesh *m)
+static void free_mtllist(struct objmtl *mtl)
 {
-       free(m->faces);
-       m->faces = 0;
+       while(mtl) {
+               void *tmp = mtl;
+               mtl = mtl->next;
+               free(tmp);
+       }
 }
 
-void draw_mesh(struct mesh *m)
+static void conv_mtl(struct material *mm, struct objmtl *om)
 {
-       int i;
-
-       glBegin(GL_TRIANGLES);
-       for(i=0; i<m->num_faces; i++) {
-               glNormal3fv(&m->faces[i].v[0].norm);
-               glVertex3fv(&m->faces[i].v[0].pos);
-
-               glNormal3fv(&m->faces[i].v[1].norm);
-               glVertex3fv(&m->faces[i].v[1].pos);
-
-               glNormal3fv(&m->faces[i].v[2].norm);
-               glVertex3fv(&m->faces[i].v[2].pos);
-       }
-       glEnd();
+       mm->color = om->kd;
+       /* TODO */
 }
index 4a51837..cf12193 100644 (file)
@@ -12,9 +12,17 @@ struct mesh {
        struct aabox aabb;
 
        struct material mtl;
+       struct mesh *next;
 };
 
-int load_mesh(struct mesh *m, const char *fname);
+struct scenefile {
+       struct mesh *meshlist;
+       int num_meshes;
+};
+
+int load_scenefile(struct scenefile *scn, const char *fname);
+void destroy_scenefile(struct scenefile *scn);
+
 void destroy_mesh(struct mesh *m);
 void draw_mesh(struct mesh *m);