4 #include <cgmath/cgmath.h>
20 static void calc_face_normal(struct triangle *tri);
21 static char *cleanline(char *s);
22 static char *parse_idx(char *ptr, int *idx, int arrsz);
23 static char *parse_face_vert(char *ptr, struct facevertex *fv, int numv, int numt, int numn);
25 static struct objmtl *load_mtllib(const char *objfname, const char *mtlfname);
26 static void free_mtllist(struct objmtl *mtl);
27 static void conv_mtl(struct material *mm, struct objmtl *om);
29 #define GROW_ARRAY(arr, sz) \
31 int newsz = (sz) ? (sz) * 2 : 16; \
32 void *tmp = realloc(arr, newsz * sizeof *(arr)); \
34 fprintf(stderr, "failed to grow array to %d\n", newsz); \
42 int load_scenefile(struct scenefile *scn, const char *fname)
44 int i, nlines, total_faces = 0, res = -1;
46 char buf[256], *line, *ptr;
47 int varr_size, varr_max, narr_size, narr_max, tarr_size, tarr_max, max_faces;
48 cgm_vec3 v, *varr = 0, *narr = 0;
50 struct facevertex fv[4];
54 static const cgm_vec2 def_tc = {0, 0};
55 struct objmtl curmtl, *mtl, *mtllist = 0;
58 varr_size = varr_max = narr_size = narr_max = tarr_size = tarr_max = 0;
62 if(!(fp = fopen(fname, "rb"))) {
63 fprintf(stderr, "load_scenefile: failed to open %s\n", fname);
67 if(!(mesh = calloc(1, sizeof *mesh))) {
68 fprintf(stderr, "failed to allocate mesh\n");
77 /* default material: white diffuse */
78 memset(&curmtl, 0, sizeof curmtl);
79 cgm_vcons(&curmtl.kd, 1.0f, 1.0f, 1.0f);
80 curmtl.alpha = curmtl.ior = 1.0f;
83 while(fgets(buf, sizeof buf, fp)) {
85 if(!(line = cleanline(buf))) {
91 v.x = v.y = v.z = 0.0f;
92 if(sscanf(line + 2, "%f %f %f", &v.x, &v.y, &v.z) < 2) {
95 if(isspace(line[1])) {
96 if(varr_size >= varr_max) {
97 GROW_ARRAY(varr, varr_max);
99 varr[varr_size++] = v;
100 } else if(line[1] == 't' && isspace(line[2])) {
101 if(tarr_size >= tarr_max) {
102 GROW_ARRAY(tarr, tarr_max);
104 tarr[tarr_size++] = *(cgm_vec2*)&v;
105 } else if(line[1] == 'n' && isspace(line[2])) {
106 if(narr_size >= narr_max) {
107 GROW_ARRAY(narr, narr_max);
109 narr[narr_size++] = v;
114 if(!isspace(line[1])) break;
120 if(!(ptr = parse_face_vert(ptr, fv + i, varr_size, tarr_size, narr_size))) {
127 if(mesh->num_faces >= max_faces - 1) {
128 GROW_ARRAY(mesh->faces, max_faces);
130 tri = mesh->faces + mesh->num_faces++;
131 tri->mtl = &mesh->mtl;
133 tri->v[0].pos = varr[fv[0].vidx];
134 tri->v[1].pos = varr[fv[1].vidx];
135 tri->v[2].pos = varr[fv[2].vidx];
136 calc_face_normal(tri);
138 tri->v[i].norm = fv[i].nidx >= 0 ? narr[fv[i].nidx] : tri->norm;
139 tri->v[i].tex = fv[i].tidx >= 0 ? tarr[fv[i].tidx] : def_tc;
145 tri->mtl = &mesh->mtl;
146 tri->norm = tri[-1].norm;
147 tri->v[0] = tri[-1].v[0];
148 tri->v[1] = tri[-1].v[1];
150 tri->v[2].pos = varr[fv[3].vidx];
151 tri->v[2].norm = fv[3].nidx >= 0 ? narr[fv[3].nidx] : tri->norm;
152 tri->v[2].tex = fv[3].tidx >= 0 ? tarr[fv[3].tidx] : def_tc;
158 if(mesh->num_faces) {
159 conv_mtl(&mesh->mtl, &curmtl);
160 total_faces += mesh->num_faces;
161 mesh->next = scn->meshlist;
162 scn->meshlist = mesh;
165 if(!(mesh = calloc(1, sizeof *mesh))) {
166 fprintf(stderr, "failed to allocate mesh\n");
174 if(memcmp(line, "mtllib", 6) == 0 && (line = cleanline(line + 6))) {
175 free_mtllist(mtllist);
176 mtllist = load_mtllib(fname, line);
181 if(memcmp(line, "usemtl", 6) == 0 && (line = cleanline(line + 6))) {
184 if(strcmp(mtl->name, line) == 0) {
198 if(mesh->num_faces) {
199 conv_mtl(&mesh->mtl, &curmtl);
200 total_faces += mesh->num_faces;
201 mesh->next = scn->meshlist;
202 scn->meshlist = mesh;
205 printf("added mesh with mtl: %s\n", curmtl.name);
211 printf("load_scenefile: loaded %d meshes, %d vertices, %d triangles\n", scn->num_meshes,
212 varr_size, total_faces);
222 free_mtllist(mtllist);
226 void destroy_scenefile(struct scenefile *scn)
229 while(scn->meshlist) {
231 scn->meshlist = scn->meshlist->next;
236 void destroy_mesh(struct mesh *m)
242 void draw_mesh(struct mesh *m)
246 glBegin(GL_TRIANGLES);
247 for(i=0; i<m->num_faces; i++) {
248 glNormal3fv((float*)&m->faces[i].v[0].norm);
249 glVertex3fv((float*)&m->faces[i].v[0].pos);
251 glNormal3fv((float*)&m->faces[i].v[1].norm);
252 glVertex3fv((float*)&m->faces[i].v[1].pos);
254 glNormal3fv((float*)&m->faces[i].v[2].norm);
255 glVertex3fv((float*)&m->faces[i].v[2].pos);
260 static void calc_face_normal(struct triangle *tri)
265 cgm_vsub(&va, &tri->v[0].pos);
267 cgm_vsub(&vb, &tri->v[0].pos);
269 cgm_vcross(&tri->norm, &va, &vb);
270 cgm_vnormalize(&tri->norm);
273 static char *cleanline(char *s)
277 if((ptr = strchr(s, '#'))) *ptr = 0;
279 while(*s && isspace(*s)) s++;
280 ptr = s + strlen(s) - 1;
281 while(ptr >= s && isspace(*ptr)) *ptr-- = 0;
286 static char *parse_idx(char *ptr, int *idx, int arrsz)
289 int val = strtol(ptr, &endp, 10);
290 if(endp == ptr) return 0;
292 if(val < 0) { /* convert negative indices */
295 *idx = val - 1; /* indices in obj are 1-based */
300 /* possible face-vertex definitions:
304 * 4. vertex/texcoord/normal
306 static char *parse_face_vert(char *ptr, struct facevertex *fv, int numv, int numt, int numn)
308 fv->tidx = fv->nidx = -1;
310 if(!(ptr = parse_idx(ptr, &fv->vidx, numv)))
312 if(*ptr != '/') return (!*ptr || isspace(*ptr)) ? ptr : 0;
314 if(*++ptr == '/') { /* no texcoord */
317 if(!(ptr = parse_idx(ptr, &fv->tidx, numt)))
319 if(*ptr != '/') return (!*ptr || isspace(*ptr)) ? ptr : 0;
323 if(!(ptr = parse_idx(ptr, &fv->nidx, numn)))
325 return (!*ptr || isspace(*ptr)) ? ptr : 0;
328 static struct objmtl *load_mtllib(const char *objfname, const char *mtlfname)
332 char buf[256], *line;
333 struct objmtl *mlist = 0, *m = 0;
335 strcpy(buf, objfname);
336 if((sep = strrchr(buf, '/'))) {
341 strcat(buf, mtlfname);
343 if(!(fp = fopen(buf, "rb"))) {
347 while(fgets(buf, sizeof buf, fp)) {
348 if(!(line = cleanline(buf))) {
352 if(memcmp(line, "newmtl", 6) == 0) {
357 if((m = calloc(1, sizeof *m))) {
358 if((line = cleanline(line + 6))) {
359 strcpy(m->name, line);
362 } else if(memcmp(line, "Kd", 2) == 0) {
364 sscanf(line + 3, "%f %f %f", &m->kd.x, &m->kd.y, &m->kd.z);
378 static void free_mtllist(struct objmtl *mtl)
387 static void conv_mtl(struct material *mm, struct objmtl *om)