fixed bugs, added debug shaders, drawing a dungeon wall segment for testing
[vrlugburz] / src / scenefile.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <ctype.h>
4 #include <assert.h>
5 #include "cgmath/cgmath.h"
6 #include "scenefile.h"
7 #include "rbtree.h"
8
9 struct facevertex {
10         int vidx, tidx, nidx;
11 };
12
13 struct objmtl {
14         char *name;
15         cgm_vec3 ka, kd, ks, ke;
16         float shin;
17         float alpha;
18         float ior;
19         char *map_kd, *map_ke, *map_alpha;
20         struct objmtl *next;
21 };
22
23 static int proc_facevert(struct mesh *mesh, struct facevertex *fv,
24                 cgm_vec3 *varr, cgm_vec3 *narr, cgm_vec2 *tarr, struct rbtree *rbtree);
25
26 static char *cleanline(char *s);
27 static char *parse_idx(char *ptr, int *idx, int arrsz);
28 static char *parse_face_vert(char *ptr, struct facevertex *fv, int numv, int numt, int numn);
29
30 static int load_mtllib(struct scenefile *scn, const char *path_prefix, const char *mtlfname);
31 static void free_mtllist(struct material *mtl);
32 static void conv_mtl(struct material *mm, struct objmtl *om, const char *path_prefix);
33
34 static int cmp_facevert(const void *ap, const void *bp);
35 static void free_rbnode_key(struct rbnode *n, void *cls);
36
37 #define GROW_ARRAY(arr, sz)     \
38         do { \
39                 int newsz = (sz) ? (sz) * 2 : 16; \
40                 void *tmp = realloc(arr, newsz * sizeof *(arr)); \
41                 if(!tmp) { \
42                         fprintf(stderr, "failed to grow array to %d\n", newsz); \
43                         goto fail; \
44                 } \
45                 arr = tmp; \
46                 sz = newsz; \
47         } while(0)
48
49
50 int load_scenefile(struct scenefile *scn, const char *fname)
51 {
52         int i, nlines, res = -1;
53         FILE *fp;
54         char buf[256], *line, *ptr, *path_prefix;
55         int varr_size, varr_max, narr_size, narr_max, tarr_size, tarr_max;
56         cgm_vec3 v, *varr = 0, *narr = 0;
57         cgm_vec2 *tarr = 0;
58         struct facevertex fv[4];
59         struct mesh *mesh;
60         struct material *mtl = 0;
61         char *sep;
62         struct rbtree *rbtree = 0;
63
64
65         varr_size = varr_max = narr_size = narr_max = tarr_size = tarr_max = 0;
66         varr = narr = 0;
67         tarr = 0;
68
69         if(!(fp = fopen(fname, "rb"))) {
70                 fprintf(stderr, "load_scenefile: failed to open %s\n", fname);
71                 return -1;
72         }
73
74         if(!(rbtree = rb_create(cmp_facevert))) {
75                 fprintf(stderr, "load_scenefile: failed to create facevertex search tree\n");
76                 fclose(fp);
77                 return -1;
78         }
79         rb_set_delete_func(rbtree, free_rbnode_key, 0);
80
81         strcpy(buf, fname);
82         if((sep = strrchr(buf, '/'))) {
83                 sep[1] = 0;
84         } else {
85                 buf[0] = 0;
86         }
87         path_prefix = alloca(strlen(buf) + 1);
88         strcpy(path_prefix, buf);
89
90         if(!(mesh = malloc(sizeof *mesh))) {
91                 fprintf(stderr, "failed to allocate mesh\n");
92                 fclose(fp);
93                 return -1;
94         }
95         init_mesh(mesh);
96
97         scn->meshlist = 0;
98         scn->num_meshes = 0;
99
100         nlines = 0;
101         while(fgets(buf, sizeof buf, fp)) {
102                 nlines++;
103                 if(!(line = cleanline(buf))) {
104                         continue;
105                 }
106
107                 switch(line[0]) {
108                 case 'v':
109                         v.x = v.y = v.z = 0.0f;
110                         if(sscanf(line + 2, "%f %f %f", &v.x, &v.y, &v.z) < 2) {
111                                 break;
112                         }
113                         if(isspace(line[1])) {
114                                 if(varr_size >= varr_max) {
115                                         GROW_ARRAY(varr, varr_max);
116                                 }
117                                 varr[varr_size++] = v;
118                         } else if(line[1] == 't' && isspace(line[2])) {
119                                 if(tarr_size >= tarr_max) {
120                                         GROW_ARRAY(tarr, tarr_max);
121                                 }
122                                 tarr[tarr_size++] = *(cgm_vec2*)&v;
123                         } else if(line[1] == 'n' && isspace(line[2])) {
124                                 if(narr_size >= narr_max) {
125                                         GROW_ARRAY(narr, narr_max);
126                                 }
127                                 narr[narr_size++] = v;
128                         }
129                         break;
130
131                 case 'f':
132                         if(!isspace(line[1])) break;
133
134                         ptr = line + 2;
135
136                         for(i=0; i<3; i++) {
137                                 if(!(ptr = parse_face_vert(ptr, fv + i, varr_size, tarr_size, narr_size))) {
138                                         break;
139                                 }
140                                 if(proc_facevert(mesh, fv + i, varr, narr, tarr, rbtree) == -1) {
141                                         break;
142                                 }
143                         }
144
145                         if(parse_face_vert(ptr, fv + 3, varr_size, tarr_size, narr_size)) {
146                                 proc_facevert(mesh, fv, varr, narr, tarr, rbtree);
147                                 proc_facevert(mesh, fv + 2, varr, narr, tarr, rbtree);
148                                 proc_facevert(mesh, fv + 3, varr, narr, tarr, rbtree);
149                         }
150                         break;
151
152                 case 'o':
153                 case 'g':
154                         if(mesh->num_verts) {
155                                 mesh->mtl = mtl;
156                                 mesh->next = scn->meshlist;
157                                 scn->meshlist = mesh;
158                                 scn->num_meshes++;
159
160                                 if(!(mesh = malloc(sizeof *mesh))) {
161                                         fprintf(stderr, "failed to allocate mesh\n");
162                                         goto fail;
163                                 }
164                                 init_mesh(mesh);
165                         }
166                         break;
167
168                 case 'm':
169                         if(memcmp(line, "mtllib", 6) == 0 && (line = cleanline(line + 6))) {
170                                 free_mtllist(scn->mtllist);
171                                 load_mtllib(scn, path_prefix, line);
172                         }
173                         break;
174
175                 case 'u':
176                         if(memcmp(line, "usemtl", 6) == 0 && (line = cleanline(line + 6))) {
177                                 mtl = scn->mtllist;
178                                 while(mtl) {
179                                         if(strcmp(mtl->name, line) == 0) {
180                                                 break;
181                                         }
182                                         mtl = mtl->next;
183                                 }
184                         }
185                         break;
186
187                 default:
188                         break;
189                 }
190         }
191
192         if(mesh->num_verts) {
193                 mesh->mtl = mtl;
194                 mesh->next = scn->meshlist;
195                 scn->meshlist = mesh;
196                 scn->num_meshes++;
197         } else {
198                 free(mesh);
199         }
200         mesh = 0;
201
202         printf("load_scenefile: loaded %d meshes, %d vertices\n", scn->num_meshes,
203                         varr_size);
204
205         res = 0;
206
207 fail:
208         fclose(fp);
209         free(mesh);
210         free(varr);
211         free(narr);
212         free(tarr);
213         rb_free(rbtree);
214         return res;
215 }
216
217 static int proc_facevert(struct mesh *mesh, struct facevertex *fv,
218                 cgm_vec3 *varr, cgm_vec3 *narr, cgm_vec2 *tarr, struct rbtree *rbtree)
219 {
220         struct rbnode *node;
221         unsigned int idx, newidx;
222         struct facevertex *newfv;
223         struct vertex v;
224
225         if((node = rb_find(rbtree, &fv))) {
226                 idx = (unsigned int)node->data;
227                 assert(idx < mesh->num_verts);
228         } else {
229                 newidx = mesh->num_verts;
230
231                 v.pos = varr[fv->vidx];
232                 if(fv->nidx >= 0) {
233                         v.norm = narr[fv->nidx];
234                 }
235                 if(fv->tidx >= 0) {
236                         v.tex = tarr[fv->tidx];
237                 }
238                 add_mesh_vertex(mesh, &v);
239                 add_mesh_index(mesh, newidx);
240         }
241
242         if((newfv = malloc(sizeof *newfv))) {
243                 *newfv = *fv;
244         }
245         if(!newfv || rb_insert(rbtree, newfv, (void*)newidx) == -1) {
246                 fprintf(stderr, "load_scenefile: failed to insert facevertex to rbtree\n");
247                 free(newfv);
248                 return -1;
249         }
250         return 0;
251 }
252
253 void destroy_scenefile(struct scenefile *scn)
254 {
255         struct mesh *m;
256         while(scn->meshlist) {
257                 m = scn->meshlist;
258                 scn->meshlist = scn->meshlist->next;
259                 free(m);
260         }
261 }
262
263 static char *cleanline(char *s)
264 {
265         char *ptr;
266
267         if((ptr = strchr(s, '#'))) *ptr = 0;
268
269         while(*s && isspace(*s)) s++;
270         ptr = s + strlen(s) - 1;
271         while(ptr >= s && isspace(*ptr)) *ptr-- = 0;
272
273         return *s ? s : 0;
274 }
275
276 static char *parse_idx(char *ptr, int *idx, int arrsz)
277 {
278         char *endp;
279         int val = strtol(ptr, &endp, 10);
280         if(endp == ptr) return 0;
281
282         if(val < 0) {   /* convert negative indices */
283                 *idx = arrsz + val;
284         } else {
285                 *idx = val - 1; /* indices in obj are 1-based */
286         }
287         return endp;
288 }
289
290 /* possible face-vertex definitions:
291  * 1. vertex
292  * 2. vertex/texcoord
293  * 3. vertex//normal
294  * 4. vertex/texcoord/normal
295  */
296 static char *parse_face_vert(char *ptr, struct facevertex *fv, int numv, int numt, int numn)
297 {
298         fv->tidx = fv->nidx = -1;
299
300         if(!(ptr = parse_idx(ptr, &fv->vidx, numv)))
301                 return 0;
302         if(*ptr != '/') return (!*ptr || isspace(*ptr)) ? ptr : 0;
303
304         if(*++ptr == '/') {     /* no texcoord */
305                 ++ptr;
306         } else {
307                 if(!(ptr = parse_idx(ptr, &fv->tidx, numt)))
308                         return 0;
309                 if(*ptr != '/') return (!*ptr || isspace(*ptr)) ? ptr : 0;
310                 ++ptr;
311         }
312
313         if(!(ptr = parse_idx(ptr, &fv->nidx, numn)))
314                 return 0;
315         return (!*ptr || isspace(*ptr)) ? ptr : 0;
316 }
317
318 static int load_mtllib(struct scenefile *scn, const char *path_prefix, const char *mtlfname)
319 {
320         FILE *fp;
321         char buf[256], *line;
322         struct objmtl om;
323         struct material *mtl;
324
325         if(path_prefix && *path_prefix) {
326                 sprintf(buf, "%s/%s", path_prefix, mtlfname);
327         } else {
328                 strcpy(buf, mtlfname);
329         }
330
331         if(!(fp = fopen(buf, "rb"))) {
332                 return -1;
333         }
334
335         while(fgets(buf, sizeof buf, fp)) {
336                 if(!(line = cleanline(buf))) {
337                         continue;
338                 }
339
340                 if(memcmp(line, "newmtl", 6) == 0) {
341                         if(mtl) {
342                                 conv_mtl(mtl, &om, path_prefix);
343                                 mtl->next = scn->mtllist;
344                                 scn->mtllist = mtl;
345                         }
346                         mtl = calloc(1, sizeof *mtl);
347
348                         memset(&om, 0, sizeof om);
349
350                         if((line = cleanline(line + 6))) {
351                                 om.name = strdup(line);
352                         }
353
354                 } else if(memcmp(line, "Kd", 2) == 0) {
355                         sscanf(line + 3, "%f %f %f", &om.kd.x, &om.kd.y, &om.kd.z);
356                 } else if(memcmp(line, "Ks", 2) == 0) {
357                         sscanf(line + 3, "%f %f %f", &om.ks.x, &om.ks.y, &om.ks.z);
358                 } else if(memcmp(line, "Ke", 2) == 0) {
359                         sscanf(line + 3, "%f %f %f", &om.ke.x, &om.ke.y, &om.ke.z);
360                 } else if(memcmp(line, "Ni", 2) == 0) {
361                         om.ior = atof(line + 3);
362                 } else if(line[0] == 'd' && isspace(line[1])) {
363                         om.alpha = atof(line + 2);
364                 } else if(memcmp(line, "map_Kd", 6) == 0) {
365                         if((line = cleanline(line + 6))) {
366                                 om.map_kd = strdup(line);
367                         }
368                 } else if(memcmp(line, "map_Ke", 6) == 0) {
369                         if((line = cleanline(line + 6))) {
370                                 om.map_ke = strdup(line);
371                         }
372                 } else if(memcmp(line, "map_d", 5) == 0) {
373                         if((line = cleanline(line + 5))) {
374                                 om.map_alpha = strdup(line);
375                         }
376                 }
377         }
378
379         if(mtl) {
380                 conv_mtl(mtl, &om, path_prefix);
381                 mtl->next = scn->mtllist;
382                 scn->mtllist = mtl;
383         }
384
385         fclose(fp);
386         return 0;
387 }
388
389 static void free_mtllist(struct material *mtl)
390 {
391         while(mtl) {
392                 void *tmp = mtl;
393                 mtl = mtl->next;
394                 free(tmp);
395         }
396 }
397
398 static void conv_mtl(struct material *mm, struct objmtl *om, const char *path_prefix)
399 {
400         char *fname = 0, *suffix = 0;
401         int len, prefix_len, maxlen = 0;
402
403         memset(mm, 0, sizeof *mm);
404         mm->name = om->name;
405         mm->color = om->kd;
406         mm->spec = om->ks;
407         mm->shininess = om->shin;
408
409         if(om->map_kd && (len = strlen(om->map_kd)) > maxlen) maxlen = len;
410         if(om->map_ke && (len = strlen(om->map_ke)) > maxlen) maxlen = len;
411         if(om->map_alpha && (len = strlen(om->map_alpha)) > maxlen) maxlen = len;
412
413         if(maxlen) {
414                 prefix_len = strlen(path_prefix);
415                 fname = alloca(maxlen + prefix_len + 2);
416                 suffix = fname + prefix_len;
417                 strcpy(fname, path_prefix);
418         }
419
420         /*
421         if(om->map_kd) {
422                 strcpy(suffix, om->map_kd);
423                 mm->tex[TEX_DIFFUSE] = get_image(fname);
424         }
425         */
426 }
427
428 static int cmp_facevert(const void *ap, const void *bp)
429 {
430         const struct facevertex *a = ap;
431         const struct facevertex *b = bp;
432
433         if(a->vidx == b->vidx) {
434                 if(a->tidx == b->tidx) {
435                         return a->nidx - b->nidx;
436                 }
437                 return a->tidx - b->tidx;
438         }
439         return a->vidx - b->vidx;
440 }
441
442 static void free_rbnode_key(struct rbnode *n, void *cls)
443 {
444         free(n->key);
445 }