writing obj loader
[dosdemo] / src / meshload.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <ctype.h>
4 #include "mesh.h"
5 #include "dynarr.h"
6 #include "rbtree.h"
7 #include "vmath.h"
8 #include "3dgfx.h"
9
10
11 struct facevertex {
12         int vidx, tidx, nidx;
13         int myidx;
14 };
15
16 static char *clean_line(char *s);
17 static char *parse_face_vert(char *ptr, struct facevertex *fv, int numv, int numt, int numn);
18 static int cmp_facevert(const void *ap, const void *bp);
19 static void free_rbnode_key(struct rbnode *n, void *cls);
20
21
22 int load_mesh(struct g3d_mesh *mesh, const char *fname)
23 {
24         int i, line_num = 0, result = -1;
25         int found_quad = 0;
26         FILE *fp;
27         char buf[256];
28         vec3_t *varr = 0, *narr = 0;
29         vec2_t *tarr = 0;
30         struct rbtree *rbtree;
31
32         if(!(fp = fopen(fname, "rb"))) {
33                 fprintf(stderr, "load_mesh: failed to open file: %s\n", fname);
34                 return -1;
35         }
36
37         if(!(rbtree = rb_create(cmp_facevert))) {
38                 fprintf(stderr, "load_mesh: failed to create facevertex binary search tree\n");
39                 goto err;
40         }
41         rb_set_delete_func(rbtree, free_rbnode_key, 0);
42
43         if(!(mesh->varr = dynarr_alloc(0, sizeof *mesh->varr)) ||
44                         !(mesh->iarr = dynarr_alloc(0, sizeof *mesh->iarr))) {
45                 fprintf(stderr, "load_mesh: failed to allocate resizable mesh arrays\n");
46                 goto err;
47         }
48         if(!(varr = dynarr_alloc(0, sizeof *varr)) ||
49                         !(narr = dynarr_alloc(0, sizeof *narr)) ||
50                         !(tarr = dynarr_alloc(0, sizeof *tarr))) {
51                 fprintf(stderr, "load_mesh: failed to allocate resizable vertex array\n");
52                 goto err;
53         }
54
55         while(fgets(buf, sizeof buf, fp)) {
56                 char *line = clean_line(buf);
57                 ++line_num;
58
59                 if(!*line) continue;
60
61                 switch(line[0]) {
62                 case 'v':
63                         if(isspace(line[1])) {
64                                 /* vertex */
65                                 vec3_t v;
66                                 if(sscanf(line + 2, "%f %f %f", &v.x, &v.y, &v.z) != 3) {
67                                         fprintf(stderr, "%s:%d: invalid vertex definition: \"%s\"\n", fname, line_num, line);
68                                         goto err;
69                                 }
70                                 if(!(varr = dynarr_push(varr, &v))) {
71                                         fprintf(stderr, "load_mesh: failed to resize vertex buffer\n");
72                                         goto err;
73                                 }
74
75                         } else if(line[1] == 't' && isspace(line[2])) {
76                                 /* texcoord */
77                                 vec2_t tc;
78                                 if(sscanf(line + 3, "%f %f", &tc.x, &tc.y) != 2) {
79                                         fprintf(stderr, "%s:%d: invalid texcoord definition: \"%s\"\n", fname, line_num, line);
80                                         goto err;
81                                 }
82                                 if(!(tarr = dynarr_push(tarr, &tc))) {
83                                         fprintf(stderr, "load_mesh: failed to resize texcoord buffer\n");
84                                         goto err;
85                                 }
86
87                         } else if(line[1] == 'n' && isspace(line[2])) {
88                                 /* normal */
89                                 vec3_t norm;
90                                 if(sscanf(line + 3, "%f %f %f", &norm.x, &norm.y, &norm.z) != 3) {
91                                         fprintf(stderr, "%s:%d: invalid normal definition: \"%s\"\n", fname, line_num, line);
92                                         goto err;
93                                 }
94                                 if(!(narr = dynarr_push(narr, &norm))) {
95                                         fprintf(stderr, "load_mesh: failed to resize normal buffer\n");
96                                         goto err;
97                                 }
98                         }
99                         break;
100
101                 case 'f':
102                         if(isspace(line[1])) {
103                                 /* face */
104                                 char *ptr = line + 2;
105                                 struct facevertex fv;
106                                 struct rbnode *node;
107                                 int vsz = dynarr_size(varr);
108                                 int tsz = dynarr_size(tarr);
109                                 int nsz = dynarr_size(narr);
110
111                                 for(i=0; i<4; i++) {
112                                         if(!(ptr = parse_face_vert(ptr, &fv, vsz, tsz, nsz))) {
113                                                 if(i < 3 || found_quad) {
114                                                         fprintf(stderr, "%s:%d: invalid face definition: \"%s\"\n", fname, line_num, line);
115                                                         goto err;
116                                                 } else {
117                                                         break;
118                                                 }
119                                         }
120
121                                         if((node = rb_find(rbtree, &fv))) {
122                                                 uint16_t idx = (int)(intptr_t)node->data;
123                                                 if(!(mesh->iarr = dynarr_push(mesh->iarr, &idx))) {
124                                                         fprintf(stderr, "load_mesh: failed to resize index array\n");
125                                                         goto err;
126                                                 }
127                                         } else {
128                                                 uint16_t idx = dynarr_size(mesh->varr);
129                                                 struct g3d_vertex v;
130                                                 struct facevertex *newfv;
131
132                                                 v.x = varr[fv.vidx].x;
133                                                 v.y = varr[fv.vidx].y;
134                                                 v.z = varr[fv.vidx].z;
135                                                 v.w = 1.0f;
136                                                 if(fv.tidx >= 0) {
137                                                         v.u = tarr[fv.tidx].x;
138                                                         v.v = tarr[fv.tidx].y;
139                                                 } else {
140                                                         v.u = v.x;
141                                                         v.v = v.y;
142                                                 }
143                                                 if(fv.nidx >= 0) {
144                                                         v.nx = narr[fv.nidx].x;
145                                                         v.ny = narr[fv.nidx].y;
146                                                         v.nz = narr[fv.nidx].z;
147                                                 } else {
148                                                         v.nx = v.ny = 0.0f;
149                                                         v.nz = 1.0f;
150                                                 }
151                                                 v.r = v.g = v.b = v.a = 255;
152
153                                                 if(!(mesh->varr = dynarr_push(mesh->varr, &v))) {
154                                                         fprintf(stderr, "load_mesh: failed to resize combined vertex array\n");
155                                                         goto err;
156                                                 }
157                                                 if(!(mesh->iarr = dynarr_push(mesh->iarr, &idx))) {
158                                                         fprintf(stderr, "load_mesh: failed to resize index array\n");
159                                                         goto err;
160                                                 }
161
162                                                 if((newfv = malloc(sizeof *newfv))) {
163                                                         *newfv = fv;
164                                                 }
165                                                 if(!newfv || rb_insert(rbtree, newfv, &idx) == -1) {
166                                                         fprintf(stderr, "load_mesh: failed to insert facevertex to the binary search tree\n");
167                                                         goto err;
168                                                 }
169                                         }
170                                 }
171                                 if(i >= 3) found_quad = 1;
172                         }
173                         break;
174
175                 default:
176                         break;
177                 }
178         }
179
180         mesh->prim = found_quad ? G3D_QUADS : G3D_TRIANGLES;
181         mesh->vcount = dynarr_size(mesh->varr);
182         mesh->icount = dynarr_size(mesh->iarr);
183         mesh->varr = dynarr_finalize(mesh->varr);
184         mesh->iarr = dynarr_finalize(mesh->iarr);
185         result = 0;     /* success */
186
187         printf("loaded %s mesh: %s: %d vertices, %d faces\n", found_quad ? "quad" : "triangle",
188                         fname, mesh->vcount, mesh->icount / mesh->prim);
189
190 err:
191         if(fp) fclose(fp);
192         dynarr_free(varr);
193         dynarr_free(narr);
194         dynarr_free(tarr);
195         if(result == -1) {
196                 dynarr_free(mesh->varr);
197                 dynarr_free(mesh->iarr);
198         }
199         rb_free(rbtree);
200         return result;
201 }
202
203 static char *clean_line(char *s)
204 {
205         char *end;
206
207         while(*s && isspace(*s)) ++s;
208         if(!*s) return 0;
209
210         end = s;
211         while(*end && *end != '#') ++end;
212         *end = 0;
213
214         while(end > s && isspace(*end)) --end;
215         *end = 0;
216
217         return s;
218 }
219
220 static char *parse_idx(char *ptr, int *idx, int arrsz)
221 {
222         char *endp;
223         int val = strtol(ptr, &endp, 10);
224         if(endp == ptr) return 0;
225
226         if(val < 0) {   /* convert negative indices */
227                 *idx = arrsz + val;
228         } else {
229                 *idx = val - 1; /* indices in obj are 1-based */
230         }
231         return endp;
232 }
233
234 /* possible face-vertex definitions:
235  * 1. vertex
236  * 2. vertex/texcoord
237  * 3. vertex//normal
238  * 4. vertex/texcoord/normal
239  */
240 static char *parse_face_vert(char *ptr, struct facevertex *fv, int numv, int numt, int numn)
241 {
242         if(!(ptr = parse_idx(ptr, &fv->vidx, numv)))
243                 return 0;
244         if(*ptr != '/') return (!*ptr || isspace(*ptr)) ? ptr : 0;
245
246         if(*++ptr == '/') {     /* no texcoord */
247                 fv->tidx = -1;
248                 ++ptr;
249         } else {
250                 if(!(ptr = parse_idx(ptr, &fv->tidx, numt)))
251                         return 0;
252                 if(*ptr != '/') return (!*ptr || isspace(*ptr)) ? ptr : 0;
253                 ++ptr;
254         }
255
256         if(!(ptr = parse_idx(ptr, &fv->nidx, numn)))
257                 return 0;
258         return (!*ptr || isspace(*ptr)) ? ptr : 0;
259 }
260
261 static int cmp_facevert(const void *ap, const void *bp)
262 {
263         const struct facevertex *a = ap;
264         const struct facevertex *b = bp;
265
266         if(a->vidx == b->vidx) {
267                 if(a->tidx == b->tidx) {
268                         return a->nidx - b->nidx;
269                 }
270                 return a->tidx - b->tidx;
271         }
272         return a->vidx - b->vidx;
273 }
274
275 static void free_rbnode_key(struct rbnode *n, void *cls)
276 {
277         free(n->key);
278 }