implementing build_bvh_sah
[cyberay] / src / mesh.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <ctype.h>
4 #include <cgmath/cgmath.h>
5 #include "mesh.h"
6
7 struct facevertex {
8         int vidx, tidx, nidx;
9 };
10
11 struct objmtl {
12         char name[64];
13         cgm_vec3 ka, kd, ks;
14         float shin;
15         float alpha;
16         float ior;
17         struct objmtl *next;
18 };
19
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);
24
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);
28
29 #define GROW_ARRAY(arr, sz)     \
30         do { \
31                 int newsz = (sz) ? (sz) * 2 : 16; \
32                 void *tmp = realloc(arr, newsz * sizeof *(arr)); \
33                 if(!tmp) { \
34                         fprintf(stderr, "failed to grow array to %d\n", newsz); \
35                         goto fail; \
36                 } \
37                 arr = tmp; \
38                 sz = newsz; \
39         } while(0)
40
41
42 int load_scenefile(struct scenefile *scn, const char *fname)
43 {
44         int i, nlines, total_faces = 0, res = -1;
45         FILE *fp;
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;
49         cgm_vec2 *tarr = 0;
50         struct facevertex fv[4];
51         int numfv;
52         struct mesh *mesh;
53         struct triangle *tri;
54         static const cgm_vec2 def_tc = {0, 0};
55         struct objmtl curmtl, *mtl, *mtllist = 0;
56
57
58         varr_size = varr_max = narr_size = narr_max = tarr_size = tarr_max = 0;
59         varr = narr = 0;
60         tarr = 0;
61
62         if(!(fp = fopen(fname, "rb"))) {
63                 fprintf(stderr, "load_scenefile: failed to open %s\n", fname);
64                 return -1;
65         }
66
67         if(!(mesh = calloc(1, sizeof *mesh))) {
68                 fprintf(stderr, "failed to allocate mesh\n");
69                 fclose(fp);
70                 return -1;
71         }
72         max_faces = 0;
73
74         scn->meshlist = 0;
75         scn->num_meshes = 0;
76
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;
81
82         nlines = 0;
83         while(fgets(buf, sizeof buf, fp)) {
84                 nlines++;
85                 if(!(line = cleanline(buf))) {
86                         continue;
87                 }
88
89                 switch(line[0]) {
90                 case 'v':
91                         v.x = v.y = v.z = 0.0f;
92                         if(sscanf(line + 2, "%f %f %f", &v.x, &v.y, &v.z) < 2) {
93                                 break;
94                         }
95                         if(isspace(line[1])) {
96                                 if(varr_size >= varr_max) {
97                                         GROW_ARRAY(varr, varr_max);
98                                 }
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);
103                                 }
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);
108                                 }
109                                 narr[narr_size++] = v;
110                         }
111                         break;
112
113                 case 'f':
114                         if(!isspace(line[1])) break;
115
116                         ptr = line + 2;
117
118                         numfv = 0;
119                         for(i=0; i<4; i++) {
120                                 if(!(ptr = parse_face_vert(ptr, fv + i, varr_size, tarr_size, narr_size))) {
121                                         break;
122                                 }
123                                 numfv++;
124                         }
125                         if(numfv < 3) break;
126
127                         if(mesh->num_faces >= max_faces - 1) {
128                                 GROW_ARRAY(mesh->faces, max_faces);
129                         }
130                         tri = mesh->faces + mesh->num_faces++;
131                         tri->mtl = &mesh->mtl;
132
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);
137                         for(i=0; i<3; i++) {
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;
140                         }
141
142                         if(numfv > 3) {
143                                 tri++;
144                                 mesh->num_faces++;
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];
149
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;
153                         }
154                         break;
155
156                 case 'o':
157                 case 'g':
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;
163                                 scn->num_meshes++;
164
165                                 if(!(mesh = calloc(1, sizeof *mesh))) {
166                                         fprintf(stderr, "failed to allocate mesh\n");
167                                         goto fail;
168                                 }
169                                 max_faces = 0;
170                         }
171                         break;
172
173                 case 'm':
174                         if(memcmp(line, "mtllib", 6) == 0 && (line = cleanline(line + 6))) {
175                                 free_mtllist(mtllist);
176                                 mtllist = load_mtllib(fname, line);
177                         }
178                         break;
179
180                 case 'u':
181                         if(memcmp(line, "usemtl", 6) == 0 && (line = cleanline(line + 6))) {
182                                 mtl = mtllist;
183                                 while(mtl) {
184                                         if(strcmp(mtl->name, line) == 0) {
185                                                 curmtl = *mtl;
186                                                 break;
187                                         }
188                                         mtl = mtl->next;
189                                 }
190                         }
191                         break;
192
193                 default:
194                         break;
195                 }
196         }
197
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;
203                 scn->num_meshes++;
204         } else {
205                 free(mesh);
206         }
207         mesh = 0;
208
209         printf("load_scenefile: loaded %d meshes, %d vertices, %d triangles\n", scn->num_meshes,
210                         varr_size, total_faces);
211
212         res = 0;
213
214 fail:
215         fclose(fp);
216         free(mesh);
217         free(varr);
218         free(narr);
219         free(tarr);
220         free_mtllist(mtllist);
221         return res;
222 }
223
224 void destroy_scenefile(struct scenefile *scn)
225 {
226         struct mesh *m;
227         while(scn->meshlist) {
228                 m = scn->meshlist;
229                 scn->meshlist = scn->meshlist->next;
230                 free(m);
231         }
232 }
233
234 void destroy_mesh(struct mesh *m)
235 {
236         free(m->faces);
237         m->faces = 0;
238 }
239
240 void draw_mesh(struct mesh *m)
241 {
242         int i;
243
244         glBegin(GL_TRIANGLES);
245         for(i=0; i<m->num_faces; i++) {
246                 glNormal3fv((float*)&m->faces[i].v[0].norm);
247                 glVertex3fv((float*)&m->faces[i].v[0].pos);
248
249                 glNormal3fv((float*)&m->faces[i].v[1].norm);
250                 glVertex3fv((float*)&m->faces[i].v[1].pos);
251
252                 glNormal3fv((float*)&m->faces[i].v[2].norm);
253                 glVertex3fv((float*)&m->faces[i].v[2].pos);
254         }
255         glEnd();
256 }
257
258 static void calc_face_normal(struct triangle *tri)
259 {
260         cgm_vec3 va, vb;
261
262         va = tri->v[1].pos;
263         cgm_vsub(&va, &tri->v[0].pos);
264         vb = tri->v[2].pos;
265         cgm_vsub(&vb, &tri->v[0].pos);
266
267         cgm_vcross(&tri->norm, &va, &vb);
268         cgm_vnormalize(&tri->norm);
269 }
270
271 static char *cleanline(char *s)
272 {
273         char *ptr;
274
275         if((ptr = strchr(s, '#'))) *ptr = 0;
276
277         while(*s && isspace(*s)) s++;
278         ptr = s + strlen(s) - 1;
279         while(ptr >= s && isspace(*ptr)) *ptr-- = 0;
280
281         return *s ? s : 0;
282 }
283
284 static char *parse_idx(char *ptr, int *idx, int arrsz)
285 {
286         char *endp;
287         int val = strtol(ptr, &endp, 10);
288         if(endp == ptr) return 0;
289
290         if(val < 0) {   /* convert negative indices */
291                 *idx = arrsz + val;
292         } else {
293                 *idx = val - 1; /* indices in obj are 1-based */
294         }
295         return endp;
296 }
297
298 /* possible face-vertex definitions:
299  * 1. vertex
300  * 2. vertex/texcoord
301  * 3. vertex//normal
302  * 4. vertex/texcoord/normal
303  */
304 static char *parse_face_vert(char *ptr, struct facevertex *fv, int numv, int numt, int numn)
305 {
306         fv->tidx = fv->nidx = -1;
307
308         if(!(ptr = parse_idx(ptr, &fv->vidx, numv)))
309                 return 0;
310         if(*ptr != '/') return (!*ptr || isspace(*ptr)) ? ptr : 0;
311
312         if(*++ptr == '/') {     /* no texcoord */
313                 ++ptr;
314         } else {
315                 if(!(ptr = parse_idx(ptr, &fv->tidx, numt)))
316                         return 0;
317                 if(*ptr != '/') return (!*ptr || isspace(*ptr)) ? ptr : 0;
318                 ++ptr;
319         }
320
321         if(!(ptr = parse_idx(ptr, &fv->nidx, numn)))
322                 return 0;
323         return (!*ptr || isspace(*ptr)) ? ptr : 0;
324 }
325
326 static struct objmtl *load_mtllib(const char *objfname, const char *mtlfname)
327 {
328         FILE *fp;
329         char *sep;
330         char buf[256], *line;
331         struct objmtl *mlist = 0, *m = 0;
332
333         strcpy(buf, objfname);
334         if((sep = strrchr(buf, '/'))) {
335                 sep[1] = 0;
336         } else {
337                 buf[0] = 0;
338         }
339         strcat(buf, mtlfname);
340
341         if(!(fp = fopen(buf, "rb"))) {
342                 return 0;
343         }
344
345         while(fgets(buf, sizeof buf, fp)) {
346                 if(!(line = cleanline(buf))) {
347                         continue;
348                 }
349
350                 if(memcmp(line, "newmtl", 6) == 0) {
351                         if(m) {
352                                 m->next = mlist;
353                                 mlist = m;
354                         }
355                         if((m = calloc(1, sizeof *m))) {
356                                 if((line = cleanline(line + 6))) {
357                                         strcpy(m->name, line);
358                                 }
359                         }
360                 } else if(memcmp(line, "Kd", 2) == 0) {
361                         if(m) {
362                                 sscanf(line + 3, "%f %f %f", &m->kd.x, &m->kd.y, &m->kd.z);
363                         }
364                 }
365         }
366
367         if(m) {
368                 m->next = mlist;
369                 mlist = m;
370         }
371
372         fclose(fp);
373         return mlist;
374 }
375
376 static void free_mtllist(struct objmtl *mtl)
377 {
378         while(mtl) {
379                 void *tmp = mtl;
380                 mtl = mtl->next;
381                 free(tmp);
382         }
383 }
384
385 static void conv_mtl(struct material *mm, struct objmtl *om)
386 {
387         mm->color = om->kd;
388         /* TODO */
389 }