fixed the mesh loader
[dosdemo] / tools / ropesim / src / meshload.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <ctype.h>
4 #include <assert.h>
5 #include "cmesh.h"
6
7 #ifdef USE_ASSIMP
8 #include <assimp/cimport.h>
9 #include <assimp/postprocess.h>
10 #include <assimp/mesh.h>
11 #include <assimp/scene.h>
12 #include <assimp/types.h>
13 #else
14 #include "dynarr.h"
15 #include "rbtree.h"
16 #endif
17
18
19 #ifdef USE_ASSIMP
20
21 static int add_mesh(struct cmesh *mesh, struct aiMesh *aimesh, const struct aiNode *ainode);
22 static struct aiNode *find_node(struct aiNode *root, unsigned int midx);
23
24 #define AIPPFLAGS \
25         (aiProcess_JoinIdenticalVertices | \
26          aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_FlipUVs)
27
28 int cmesh_load(struct cmesh *mesh, const char *fname)
29 {
30         int i;
31         const struct aiScene *aiscn;
32         const struct aiNode *ainode;
33
34         if(!(aiscn = aiImportFile(fname, AIPPFLAGS))) {
35                 fprintf(stderr, "failed to open mesh file: %s\n", fname);
36                 return -1;
37         }
38
39         printf("scene contains %d meshes\n", (int)aiscn->mNumMeshes);
40         for(i=0; i<(int)aiscn->mNumMeshes; i++) {
41                 if(aiscn->mRootNode->mNumChildren) {
42                         ainode = find_node(aiscn->mRootNode, i);
43                 } else {
44                         ainode = 0;
45                 }
46                 add_mesh(mesh, aiscn->mMeshes[i], ainode);
47         }
48
49         aiReleaseImport(aiscn);
50         return 0;
51 }
52
53 static int add_mesh(struct cmesh *mesh, struct aiMesh *aim, const struct aiNode *ainode)
54 {
55         int i, j, voffs, foffs;
56         const char *name;
57
58         if(ainode && ainode->mName.length > 0) {
59                 name = ainode->mName.data;
60         } else {
61                 name = aim->mName.data;
62         }
63         printf("adding mesh: %s\n", name);
64
65         voffs = cmesh_attrib_count(mesh, CMESH_ATTR_VERTEX);
66         foffs = cmesh_poly_count(mesh);
67
68         for(i=0; i<aim->mNumVertices; i++) {
69                 struct aiVector3D *v = aim->mVertices + i;
70                 cmesh_push_attrib3f(mesh, CMESH_ATTR_VERTEX, v->x, v->y, v->z);
71
72                 if(aim->mNormals) {
73                         v = aim->mNormals + i;
74                         cmesh_push_attrib3f(mesh, CMESH_ATTR_NORMAL, v->x, v->y, v->z);
75                 }
76                 if(aim->mTangents) {
77                         v = aim->mTangents + i;
78                         cmesh_push_attrib3f(mesh, CMESH_ATTR_TANGENT, v->x, v->y, v->z);
79                 }
80                 if(aim->mColors[0]) {
81                         struct aiColor4D *col = aim->mColors[0] + i;
82                         cmesh_push_attrib4f(mesh, CMESH_ATTR_COLOR, col->r, col->g, col->b, col->a);
83                 }
84                 if(aim->mTextureCoords[0]) {
85                         v = aim->mTextureCoords[0] + i;
86                         cmesh_push_attrib2f(mesh, CMESH_ATTR_TEXCOORD, v->x, v->y);
87                 }
88                 if(aim->mTextureCoords[1]) {
89                         v = aim->mTextureCoords[1] + i;
90                         cmesh_push_attrib2f(mesh, CMESH_ATTR_TEXCOORD2, v->x, v->y);
91                 }
92         }
93
94         if(aim->mFaces) {
95                 for(i=0; i<aim->mNumFaces; i++) {
96                         assert(aim->mFaces[i].mNumIndices == 3);
97                         for(j=0; j<3; j++) {
98                                 cmesh_push_index(mesh, aim->mFaces[i].mIndices[j] + voffs);
99                         }
100                 }
101                 cmesh_submesh(mesh, name, foffs, aim->mNumFaces);
102         }
103         return 0;
104 }
105
106 static struct aiNode *find_node(struct aiNode *node, unsigned int midx)
107 {
108         unsigned int i;
109         struct aiNode *n;
110
111         for(i=0; i<node->mNumMeshes; i++) {
112                 if(node->mMeshes[i] == midx) {
113                         return node;
114                 }
115         }
116
117         for(i=0; i<node->mNumChildren; i++) {
118                 if((n = find_node(node->mChildren[i], midx))) {
119                         return n;
120                 }
121         }
122         return 0;
123 }
124
125 #else
126
127 struct vertex_pos {
128         float x, y, z;
129 };
130
131 struct facevertex {
132         int vidx, tidx, nidx;
133 };
134
135 static char *clean_line(char *s);
136 static char *parse_face_vert(char *ptr, struct facevertex *fv, int numv, int numt, int numn);
137 static int cmp_facevert(const void *ap, const void *bp);
138 static void free_rbnode_key(struct rbnode *n, void *cls);
139
140 /* merge of different indices per attribute happens during face processing.
141  *
142  * A triplet of (vertex index/texcoord index/normal index) is used as the key
143  * to search in a balanced binary search tree for vertex buffer index assigned
144  * to the same triplet if it has been encountered before. That index is
145  * appended to the index buffer.
146  *
147  * If a particular triplet has not been encountered before, a new vertex is
148  * appended to the vertex buffer. The index of this new vertex is appended to
149  * the index buffer, and also inserted into the tree for future searches.
150  */
151 int cmesh_load(struct cmesh *mesh, const char *fname)
152 {
153         int i, line_num = 0, result = -1;
154         int found_quad = 0;
155         FILE *fp = 0;
156         char buf[256];
157         struct vertex_pos *varr = 0;
158         cgm_vec3 *narr = 0;
159         cgm_vec2 *tarr = 0;
160         struct rbtree *rbtree = 0;
161         char *subname = 0;
162         int substart = 0, subcount = 0;
163
164         if(!(fp = fopen(fname, "rb"))) {
165                 fprintf(stderr, "load_mesh: failed to open file: %s\n", fname);
166                 goto err;
167         }
168
169         if(!(rbtree = rb_create(cmp_facevert))) {
170                 fprintf(stderr, "load_mesh: failed to create facevertex binary search tree\n");
171                 goto err;
172         }
173         rb_set_delete_func(rbtree, free_rbnode_key, 0);
174
175         if(!(varr = dynarr_alloc(0, sizeof *varr)) ||
176                         !(narr = dynarr_alloc(0, sizeof *narr)) ||
177                         !(tarr = dynarr_alloc(0, sizeof *tarr))) {
178                 fprintf(stderr, "load_mesh: failed to allocate resizable vertex array\n");
179                 goto err;
180         }
181
182         while(fgets(buf, sizeof buf, fp)) {
183                 char *line = clean_line(buf);
184                 ++line_num;
185
186                 if(!*line) continue;
187
188                 switch(line[0]) {
189                 case 'v':
190                         if(isspace(line[1])) {
191                                 /* vertex */
192                                 struct vertex_pos v;
193                                 int num;
194
195                                 num = sscanf(line + 2, "%f %f %f", &v.x, &v.y, &v.z);
196                                 if(num < 3) {
197                                         fprintf(stderr, "%s:%d: invalid vertex definition: \"%s\"\n", fname, line_num, line);
198                                         goto err;
199                                 }
200                                 if(!(varr = dynarr_push(varr, &v))) {
201                                         fprintf(stderr, "load_mesh: failed to resize vertex buffer\n");
202                                         goto err;
203                                 }
204
205                         } else if(line[1] == 't' && isspace(line[2])) {
206                                 /* texcoord */
207                                 cgm_vec2 tc;
208                                 if(sscanf(line + 3, "%f %f", &tc.x, &tc.y) != 2) {
209                                         fprintf(stderr, "%s:%d: invalid texcoord definition: \"%s\"\n", fname, line_num, line);
210                                         goto err;
211                                 }
212                                 tc.y = 1.0f - tc.y;
213                                 if(!(tarr = dynarr_push(tarr, &tc))) {
214                                         fprintf(stderr, "load_mesh: failed to resize texcoord buffer\n");
215                                         goto err;
216                                 }
217
218                         } else if(line[1] == 'n' && isspace(line[2])) {
219                                 /* normal */
220                                 cgm_vec3 norm;
221                                 if(sscanf(line + 3, "%f %f %f", &norm.x, &norm.y, &norm.z) != 3) {
222                                         fprintf(stderr, "%s:%d: invalid normal definition: \"%s\"\n", fname, line_num, line);
223                                         goto err;
224                                 }
225                                 if(!(narr = dynarr_push(narr, &norm))) {
226                                         fprintf(stderr, "load_mesh: failed to resize normal buffer\n");
227                                         goto err;
228                                 }
229                         }
230                         break;
231
232                 case 'f':
233                         if(isspace(line[1])) {
234                                 /* face */
235                                 char *ptr = line + 2;
236                                 struct facevertex fv;
237                                 struct rbnode *node;
238                                 int vsz = dynarr_size(varr);
239                                 int tsz = dynarr_size(tarr);
240                                 int nsz = dynarr_size(narr);
241
242                                 for(i=0; i<4; i++) {
243                                         if(!(ptr = parse_face_vert(ptr, &fv, vsz, tsz, nsz))) {
244                                                 if(i < 3 || found_quad) {
245                                                         fprintf(stderr, "%s:%d: invalid face definition: \"%s\"\n", fname, line_num, line);
246                                                         goto err;
247                                                 } else {
248                                                         break;
249                                                 }
250                                         }
251
252                                         if((node = rb_find(rbtree, &fv))) {
253                                                 unsigned int idx = (unsigned int)node->data;
254                                                 assert(idx < cmesh_attrib_count(mesh, CMESH_ATTR_VERTEX));
255                                                 if(cmesh_push_index(mesh, idx) == -1) {
256                                                         fprintf(stderr, "load_mesh: failed to resize index array\n");
257                                                         goto err;
258                                                 }
259                                                 subcount++;     /* inc number of submesh indices, in case we have submeshes */
260                                         } else {
261                                                 unsigned int newidx = cmesh_attrib_count(mesh, CMESH_ATTR_VERTEX);
262                                                 struct facevertex *newfv;
263                                                 struct vertex_pos *vptr = varr + fv.vidx;
264
265                                                 if(cmesh_push_attrib3f(mesh, CMESH_ATTR_VERTEX, vptr->x, vptr->y, vptr->z) == -1) {
266                                                         fprintf(stderr, "load_mesh: failed to resize vertex array\n");
267                                                         goto err;
268                                                 }
269                                                 if(fv.nidx >= 0) {
270                                                         float nx = narr[fv.nidx].x;
271                                                         float ny = narr[fv.nidx].y;
272                                                         float nz = narr[fv.nidx].z;
273                                                         if(cmesh_push_attrib3f(mesh, CMESH_ATTR_NORMAL, nx, ny, nz) == -1) {
274                                                                 fprintf(stderr, "load_mesh: failed to resize normal array\n");
275                                                                 goto err;
276                                                         }
277                                                 }
278                                                 if(fv.tidx >= 0) {
279                                                         float tu = tarr[fv.tidx].x;
280                                                         float tv = tarr[fv.tidx].y;
281                                                         if(cmesh_push_attrib2f(mesh, CMESH_ATTR_TEXCOORD, tu, tv) == -1) {
282                                                                 fprintf(stderr, "load_mesh: failed to resize texcoord array\n");
283                                                                 goto err;
284                                                         }
285                                                 }
286
287                                                 if(cmesh_push_index(mesh, newidx) == -1) {
288                                                         fprintf(stderr, "load_mesh: failed to resize index array\n");
289                                                         goto err;
290                                                 }
291                                                 subcount++;     /* inc number of submesh indices, in case we have submeshes */
292
293                                                 if((newfv = malloc(sizeof *newfv))) {
294                                                         *newfv = fv;
295                                                 }
296                                                 if(!newfv || rb_insert(rbtree, newfv, (void*)newidx) == -1) {
297                                                         fprintf(stderr, "load_mesh: failed to insert facevertex to the binary search tree\n");
298                                                         goto err;
299                                                 }
300                                         }
301                                 }
302                                 if(i > 3) found_quad = 1;
303                         }
304                         break;
305
306                 case 'o':
307                         if(subcount > 0) {
308                                 printf("adding submesh: %s\n", subname);
309                                 cmesh_submesh(mesh, subname, substart / 3, subcount / 3);
310                         }
311                         free(subname);
312                         if((subname = malloc(strlen(line)))) {
313                                 strcpy(subname, clean_line(line + 2));
314                         }
315                         substart += subcount;
316                         subcount = 0;
317                         break;
318
319                 default:
320                         break;
321                 }
322         }
323
324         if(subcount > 0) {
325                 /* don't add the final submesh if we never found another. an obj file with a
326                  * single 'o' for the whole list of faces, is a single mesh without submeshes
327                  */
328                 if(cmesh_submesh_count(mesh) > 0) {
329                         printf("adding submesh: %s\n", subname);
330                         cmesh_submesh(mesh, subname, substart / 3, subcount / 3);
331                 } else {
332                         /* ... but use the 'o' name as the name of the mesh instead of the filename */
333                         if(subname && *subname) {
334                                 cmesh_set_name(mesh, subname);
335                         }
336                 }
337         }
338
339         result = 0;     /* success */
340
341         printf("loaded %s mesh: %s (%d submeshes): %d vertices, %d faces\n",
342                         found_quad ? "quad" : "triangle", fname, cmesh_submesh_count(mesh),
343                         cmesh_attrib_count(mesh, CMESH_ATTR_VERTEX), cmesh_poly_count(mesh));
344
345 err:
346         if(fp) fclose(fp);
347         dynarr_free(varr);
348         dynarr_free(narr);
349         dynarr_free(tarr);
350         rb_free(rbtree);
351         free(subname);
352         return result;
353 }
354
355
356 static char *clean_line(char *s)
357 {
358         char *end;
359
360         while(*s && isspace(*s)) ++s;
361         if(!*s) return 0;
362
363         end = s;
364         while(*end && *end != '#') ++end;
365         *end-- = 0;
366
367         while(end > s && isspace(*end)) {
368                 *end-- = 0;
369         }
370
371         return s;
372 }
373
374 static char *parse_idx(char *ptr, int *idx, int arrsz)
375 {
376         char *endp;
377         int val = strtol(ptr, &endp, 10);
378         if(endp == ptr) return 0;
379
380         if(val < 0) {   /* convert negative indices */
381                 *idx = arrsz + val;
382         } else {
383                 *idx = val - 1; /* indices in obj are 1-based */
384         }
385         return endp;
386 }
387
388 /* possible face-vertex definitions:
389  * 1. vertex
390  * 2. vertex/texcoord
391  * 3. vertex//normal
392  * 4. vertex/texcoord/normal
393  */
394 static char *parse_face_vert(char *ptr, struct facevertex *fv, int numv, int numt, int numn)
395 {
396         if(!(ptr = parse_idx(ptr, &fv->vidx, numv)))
397                 return 0;
398         if(*ptr != '/') return (!*ptr || isspace(*ptr)) ? ptr : 0;
399
400         if(*++ptr == '/') {     /* no texcoord */
401                 fv->tidx = -1;
402                 ++ptr;
403         } else {
404                 if(!(ptr = parse_idx(ptr, &fv->tidx, numt)))
405                         return 0;
406                 if(*ptr != '/') return (!*ptr || isspace(*ptr)) ? ptr : 0;
407                 ++ptr;
408         }
409
410         if(!(ptr = parse_idx(ptr, &fv->nidx, numn)))
411                 return 0;
412         return (!*ptr || isspace(*ptr)) ? ptr : 0;
413 }
414
415 static int cmp_facevert(const void *ap, const void *bp)
416 {
417         const struct facevertex *a = ap;
418         const struct facevertex *b = bp;
419
420         if(a->vidx == b->vidx) {
421                 if(a->tidx == b->tidx) {
422                         return a->nidx - b->nidx;
423                 }
424                 return a->tidx - b->tidx;
425         }
426         return a->vidx - b->vidx;
427 }
428
429 static void free_rbnode_key(struct rbnode *n, void *cls)
430 {
431         free(n->key);
432 }
433 #endif