commit 3434b95ff7c5bb581e116d70cc1ec4149c7bf429
parent 6377884c65418b193716b320c274f489ca5a69ef
Author: Brian Swetland <swetland@frotz.net>
Date: Mon, 14 Jan 2013 00:08:45 -0800
minimal wavefront obj loader
Diffstat:
M | Makefile | | | 11 | ++++++++--- |
A | cube.mtl | | | 7 | +++++++ |
A | cube.obj | | | 95 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | loadobj.c | | | 239 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | test3.c | | | 124 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | util.h | | | 10 | ++++++++++ |
6 files changed, 483 insertions(+), 3 deletions(-)
diff --git a/Makefile b/Makefile
@@ -8,9 +8,9 @@ SDLLIBS := $(shell $(SDLCFG) --libs)
CFLAGS := $(SDLFLAGS) -DWITH_SDL2=0 -Wall
LIBS := $(SDLLIBS) -lGL -lm -lpng
-COMMONOBJS := util.o sdlglue.o loadpng.o loadfile.o
+COMMONOBJS := util.o sdlglue.o loadpng.o loadfile.o loadobj.o
-all: test1 test2
+all: test1 test2 test3
TEST1OBJS := test1.o $(COMMONOBJS)
@@ -22,5 +22,10 @@ TEST2OBJS := test2.o $(COMMONOBJS)
test2: $(TEST2OBJS)
$(CC) -o test2 $(TEST2OBJS) $(LIBS)
+TEST3OBJS := test3.o $(COMMONOBJS)
+
+test3: $(TEST3OBJS)
+ $(CC) -o test3 $(TEST3OBJS) $(LIBS)
+
clean::
- rm -f test1 test2 *.o
+ rm -f test1 test2 test3 *.o
diff --git a/cube.mtl b/cube.mtl
@@ -0,0 +1,7 @@
+newmtl mat0
+Kd 1 1 1
+Ka 0.2 0.2 0.2
+Ks 0.2 0.2 0.2
+Ns 128
+Tr 0
+map_Kd cube-texture.png
diff --git a/cube.obj b/cube.obj
@@ -0,0 +1,95 @@
+mtllib cube.mtl
+g g0
+v -1 1 -1
+v 1 1 -1
+v 1 1 1
+v -1 1 1
+v -1 -1 1
+v 1 -1 1
+v 1 -1 -1
+v -1 -1 -1
+vn 0 -1 0
+vn 0 -1 0
+vn 0 -1 0
+vn 0 -1 0
+vn 0 -1 0
+vn 0 -1 0
+vn 0 1 0
+vn 0 1 0
+vn 0 1 0
+vn 0 1 0
+vn 0 1 0
+vn 0 1 0
+vn 0 0 1
+vn 0 0 1
+vn 0 0 1
+vn 0 0 1
+vn 0 0 1
+vn 0 0 1
+vn 0 0 -1
+vn 0 0 -1
+vn 0 0 -1
+vn 0 0 -1
+vn 0 0 -1
+vn 0 0 -1
+vn -1 0 0
+vn -1 0 0
+vn -1 0 0
+vn -1 0 0
+vn -1 0 0
+vn -1 0 0
+vn 1 0 0
+vn 1 0 0
+vn 1 0 0
+vn 1 0 0
+vn 1 0 0
+vn 1 0 0
+vt 0.25 0.25
+vt 0.5 0.5
+vt 0.25 0.5
+vt 0.5 0.5
+vt 0.25 0.25
+vt 0.5 0.25
+vt 0.5 1
+vt 0.25 0.75
+vt 0.5 0.75
+vt 0.25 0.75
+vt 0.5 1
+vt 0.25 1
+vt 0.25 0.75
+vt 0.5 0.5
+vt 0.5 0.75
+vt 0.5 0.5
+vt 0.25 0.75
+vt 0.25 0.5
+vt 1 0.5
+vt 0.75 0.75
+vt 0.75 0.5
+vt 0.75 0.75
+vt 1 0.5
+vt 1 0.75
+vt 0 0.5
+vt 0.25 0.75
+vt 0 0.75
+vt 0.25 0.75
+vt 0 0.5
+vt 0.25 0.5
+vt 0.5 0.5
+vt 0.75 0.75
+vt 0.5 0.75
+vt 0.75 0.75
+vt 0.5 0.5
+vt 0.75 0.5
+usemtl mat0
+f 8/1/1 6/2/2 5/3/3
+f 6/4/4 8/5/5 7/6/6
+f 2/7/7 4/8/8 3/9/9
+f 4/10/10 2/11/11 1/12/12
+f 4/13/13 6/14/14 3/15/15
+f 6/16/16 4/17/17 5/18/18
+f 8/19/19 2/20/20 7/21/21
+f 2/22/22 8/23/23 1/24/24
+f 8/25/25 4/26/26 1/27/27
+f 4/28/28 8/29/29 5/30/30
+f 6/31/31 2/32/32 3/33/33
+f 2/34/34 6/35/35 7/36/36
diff --git a/loadobj.c b/loadobj.c
@@ -0,0 +1,239 @@
+/* Copyright 2013 Brian Swetland <swetland@frotz.net>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+struct v3 {
+ float x;
+ float y;
+ float z;
+};
+
+struct v2 {
+ float u;
+ float v;
+};
+
+struct i3 {
+ unsigned v;
+ unsigned vt;
+ unsigned vn;
+};
+
+struct obj {
+ struct v3 *v;
+ struct v3 *vn;
+ struct v2 *vt;
+ struct i3 *f;
+ unsigned max_v, num_v;
+ unsigned max_vn, num_vn;
+ unsigned max_vt, num_vt;
+ unsigned max_f, num_f;
+};
+
+static void obj_destroy(struct obj *o) {
+ if (o->v) free(o->v);
+ if (o->vn) free(o->vn);
+ if (o->vt) free(o->vt);
+ if (o->f) free(o->f);
+ free(o);
+}
+
+static void *resize(void *array, unsigned *max, unsigned esz) {
+ void *tmp;
+ unsigned n = *max ? *max * 2 : 32;
+ tmp = realloc(array, esz * n);
+ if (!tmp)
+ free(array);
+ *max = n;
+ return tmp;
+}
+
+static int obj_add_v(struct obj *o, float x, float y, float z) {
+ unsigned n = o->num_v;
+ if (n == o->max_v)
+ if(!(o->v = resize(o->v, &o->max_v, sizeof(struct v3))))
+ return -1;
+ o->v[n].x = x;
+ o->v[n].y = y;
+ o->v[n].z = z;
+ o->num_v = n + 1;
+ return 0;
+}
+
+static int obj_add_vn(struct obj *o, float x, float y, float z) {
+ unsigned n = o->num_vn;
+ if (n == o->max_vn)
+ if(!(o->vn = resize(o->vn, &o->max_vn, sizeof(struct v3))))
+ return -1;
+ o->vn[n].x = x;
+ o->vn[n].y = y;
+ o->vn[n].z = z;
+ o->num_vn = n + 1;
+ return 0;
+}
+
+static int obj_add_vt(struct obj *o, float u, float v) {
+ unsigned n = o->num_vt;
+ if (n == o->max_vt)
+ if(!(o->vt = resize(o->vt, &o->max_vt, sizeof(struct v3))))
+ return -1;
+ o->vt[n].u = u;
+ o->vt[n].v = v;
+ o->num_vt = n + 1;
+ return 0;
+}
+
+static int obj_add_f(struct obj *o, int v, int vt, int vn) {
+ unsigned n = o->num_f;
+ if (n == o->max_f)
+ if(!(o->f = resize(o->f, &o->max_f, sizeof(struct v3))))
+ return -1;
+
+ /* XXX: range check */
+ v -= 1;
+ vt -= 1;
+ vn -= 1;
+
+ o->f[n].v = v;
+ o->f[n].vt = vt;
+ o->f[n].vn = vn;
+ o->num_f = n + 1;
+ return 0;
+}
+
+struct obj *load_obj(const char *fn) {
+ char buf[128];
+ FILE *fp;
+ struct obj *o;
+ float x, y, z;
+ int a, b, c;
+
+ if (!(fp = fopen(fn, "r")))
+ goto exit;
+
+ o = malloc(sizeof(struct obj));
+ if (!o)
+ goto close_and_exit;
+ memset(o, 0, sizeof(struct obj));
+
+ while (fgets(buf, sizeof(buf), fp) != 0) {
+ if (!strncmp(buf, "v ", 2)) {
+ sscanf(buf + 2, "%f %f %f", &x, &y, &z);
+ obj_add_v(o, x, y, z);
+ } else if (!strncmp(buf, "vn ", 3)) {
+ sscanf(buf + 3, "%f %f %f", &x, &y, &z);
+ obj_add_vn(o, x, y, z);
+ } else if (!strncmp(buf, "vt ", 3)) {
+ sscanf(buf + 3, "%f %f", &x, &y);
+ obj_add_vt(o, x, y);
+ } else if (!strncmp(buf, "f ", 2)) {
+ char *tmp = buf + 2;
+ /* XXX: handle non-triangles */
+ while (sscanf(tmp, "%d/%d/%d", &a, &b, &c) == 3) {
+ obj_add_f(o, a, b, c);
+ tmp = strstr(tmp, " ");
+ if (!tmp) break;
+ tmp++;
+ }
+ } else {
+// fprintf(stderr,"ignoring: %s", buf);
+ }
+ }
+
+close_and_exit:
+ fclose(fp);
+exit:
+ return o;
+}
+
+static struct model *obj_to_model(struct obj *o) {
+ int i, j, n;
+ struct model *m;
+
+ if(!(m = malloc(sizeof(struct model))))
+ return 0;
+ memset(m, 0, sizeof(struct model));
+
+ if (!(m->vdata = malloc(sizeof(float) * 8 * o->num_f))) {
+ free(m);
+ return 0;
+ }
+ if (!(m->idx = malloc(sizeof(short) * o->num_f))) {
+ free(m->vdata);
+ free(m);
+ return 0;
+ }
+
+ for (n = 0, i = 0; i < o->num_f; i++) {
+ float data[8];
+ data[0] = o->v[o->f[i].v].x;
+ data[1] = o->v[o->f[i].v].y;
+ data[2] = o->v[o->f[i].v].z;
+ data[3] = o->vn[o->f[i].vn].x;
+ data[4] = o->vn[o->f[i].vn].y;
+ data[5] = o->vn[o->f[i].vn].z;
+ data[6] = o->vt[o->f[i].vt].u;
+ data[7] = o->vt[o->f[i].vt].v;
+ for (j = 0; j < n; j++)
+ if (!memcmp(data, &m->vdata[8 * j], sizeof(float) * 8))
+ goto found_it;
+ memcpy(&m->vdata[8 * j], data, sizeof(float) * 8);
+ n++;
+found_it:
+ m->idx[i] = j;
+ }
+
+ m->vcount = n;
+ m->icount = o->num_f;
+
+ return m;
+}
+
+#if 0
+void model_dump(struct model *m, FILE *fp) {
+ int i;
+ fprintf(fp, "struct model M = {\n");
+ fprintf(fp, " .vdata = {\n");
+ for (i = 0; i < m->vcount; i++)
+ fprintf(fp, " %f, %f, %f,\n %f, %f, %f, %f, %f,\n",
+ m->vdata[i*8+0], m->vdata[i*8+1],
+ m->vdata[i*8+2], m->vdata[i*8+3],
+ m->vdata[i*8+4], m->vdata[i*8+5],
+ m->vdata[i*8+6], m->vdata[i*8+7]);
+ fprintf(fp, " },\n .idx[] = {\n");
+ for (i = 0; i < m->icount; i += 3)
+ fprintf(fp, " %d, %d, %d,\n",
+ m->idx[i+0], m->idx[i+1], m->idx[i+2]);
+ fprintf(fp, " },\n .vcount = %d,\n .icount = %d,\n};\n",
+ m->vcount, m->icount);
+}
+#endif
+
+struct model *load_wavefront_obj(const char *fn) {
+ struct obj *o;
+ struct model *m;
+ o = load_obj(fn);
+ if (!o)
+ return 0;
+ m = obj_to_model(o);
+ obj_destroy(o);
+ return m;
+}
+
diff --git a/test3.c b/test3.c
@@ -0,0 +1,124 @@
+/* Copyright 2013 Brian Swetland <swetland@frotz.net>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "util.h"
+#include "glue.h"
+
+#include <math.h>
+
+void *texdata;
+unsigned texw, texh;
+
+const char *vert_src, *frag_src;
+
+GLuint pgm, vshd, fshd, tex0;
+GLuint _vPosition, _vUV;
+GLuint _uMVP, _uSampler;
+
+mat4 perspective;
+mat4 MVP;
+
+float a = 0.0;
+
+struct model *m;
+
+int scene_init(struct ctxt *c) {
+ float aspect = ((float) c->width) / ((float) c->height);
+
+ if (!(texdata = load_png_rgba("cube-texture.png", &texw, &texh)))
+ return -1;
+ if (!(vert_src = load_file("test1.vertex.glsl", 0)))
+ return -1;
+ if (!(frag_src = load_file("test1.fragment.glsl", 0)))
+ return -1;
+
+ if (!(m = load_wavefront_obj("cube.obj")))
+ return -1;
+
+ mtx_identity(MVP);
+ mtx_perspective(perspective, 60.0, aspect, 1.0, 10.0);
+
+ glViewport(0, 0, c->width, c->height);
+ glClearColor(0, 0, 0, 0);
+ glClearDepth(1.0f);
+
+ if (shader_compile(vert_src, frag_src, &pgm, &vshd, &fshd))
+ return -1;
+
+ _vPosition = glGetAttribLocation(pgm, "vPosition");
+ _vUV = glGetAttribLocation(pgm, "vUV");
+ _uMVP = glGetUniformLocation(pgm, "uMVP");
+ _uSampler = glGetUniformLocation(pgm, "uSampler");
+
+ if(glGetError() != GL_NO_ERROR) fprintf(stderr,"OOPS!\n");
+
+ glEnable(GL_TEXTURE_2D);
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_CULL_FACE);
+ glFrontFace(GL_CCW);
+// glEnable(GL_BLEND);
+// glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+
+ glGenTextures(1, &tex0);
+
+ glBindTexture(GL_TEXTURE_2D, tex0);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texw, texh, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, texdata);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ return 0;
+}
+
+#include "data.h"
+
+int scene_draw(struct ctxt *c) {
+ mat4 camera;
+
+ mtx_identity(camera);
+ mtx_translate(camera, 0, 0, -5.0);
+ mtx_rotate_y(camera, a);
+ mtx_rotate_x(camera, 25.0);
+
+ mtx_mul_unsafe(MVP, camera, perspective);
+
+ a += 1.0;
+ if (a > 360.0) a = 0.0;
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glUseProgram(pgm);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, tex0);
+
+ glUniformMatrix4fv(_uMVP, 1, GL_FALSE, (void*) MVP);
+ glUniform1i(_uSampler, 0);
+
+ glVertexAttribPointer(_vPosition, 3, GL_FLOAT, GL_FALSE, 8*4, m->vdata);
+ glEnableVertexAttribArray(_vPosition);
+
+ glVertexAttribPointer(_vUV, 2, GL_FLOAT, GL_FALSE, 8*4, m->vdata + 6);
+ glEnableVertexAttribArray(_vUV);
+
+ glDrawElements(GL_TRIANGLES, m->icount, GL_UNSIGNED_SHORT, m->idx);
+
+ return 0;
+}
+
diff --git a/util.h b/util.h
@@ -52,5 +52,15 @@ void mtx_perspective(mat4 out,
void *load_png_rgba(const char *fn, unsigned *width, unsigned *height);
void *load_file(const char *fn, unsigned *sz);
+/* model helpers */
+
+struct model {
+ float *vdata; /* { vertex[3], normal[3], texcoord[2] } * vcount */
+ unsigned short *idx;
+ unsigned vcount;
+ unsigned icount;
+};
+
+struct model *load_wavefront_obj(const char *fn);
#endif