mesh, obj loading, sorting, shitty normals...
[dos_low3d] / src / mesh.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include "mesh.h"
6
7 static char *parse_facev(char *s, int *vidx, int *nidx, int *tidx,
8                 int nverts, int nnorm, int nuv);
9 static int resizearr(void **pptr, int *sz, int elemsz);
10 static char *clean_line(char *s);
11
12 int load_mesh(struct mesh *m, const char *fname)
13 {
14         float fx, fy, fz;
15         FILE *fp;
16         char buf[128], *line;
17         struct g3d_vertex *vptr;
18         struct meshface *face;
19         int i, maxverts, maxfaces;
20         int vidx, tidx, nidx;
21
22         memset(m, 0, sizeof *m);
23
24         if(!(fp = fopen(fname, "rb"))) {
25                 fprintf(stderr, "failed to open mesh: %s\n", fname);
26                 return -1;
27         }
28
29         printf("Loading mesh: %s\n", fname);
30
31         m->nverts = 0;
32         maxverts = 32;
33         if(!(m->varr = malloc(maxverts * sizeof *m->varr))) {
34                 fprintf(stderr, "failed to allocate vertex array\n");
35                 goto err;
36         }
37
38         m->nfaces = 0;
39         maxfaces = 32;
40         if(!(m->faces = malloc(maxfaces * sizeof *m->faces))) {
41                 fprintf(stderr, "failed to allocate mesh face array\n");
42                 goto err;
43         }
44
45
46         while(fgets(buf, sizeof buf, fp)) {
47                 line = clean_line(buf);
48                 if(!*line) continue;
49
50                 switch(line[0]) {
51                 case 'v':
52                         if(!isspace(line[1])) continue;
53                         if(sscanf(line + 2, "%f %f %f", &fx, &fy, &fz) != 3) {
54                                 continue;
55                         }
56                         if(m->nverts >= maxverts) {
57                                 if(resizearr(&m->varr, &maxverts, sizeof *m->varr) == -1) {
58                                         goto err;
59                                 }
60                         }
61                         vptr = m->varr + m->nverts++;
62                         vptr->x = (int32_t)(fx * 65536.0f);
63                         vptr->y = (int32_t)(fy * 65536.0f);
64                         vptr->z = (int32_t)(fz * 65536.0f);
65                         vptr->w = 0x10000;
66                         putchar('v'); fflush(stdout);
67                         break;
68
69                 case 'f':
70                         if(m->nfaces >= maxfaces) {
71                                 if(resizearr(&m->faces, &maxfaces, sizeof *m->faces) == -1) {
72                                         goto err;
73                                 }
74                         }
75
76                         line += 2;
77                         face = m->faces + m->nfaces;
78                         for(i=0; i<4; i++) {
79                                 if(!(line = parse_facev(line, &vidx, &nidx, &tidx, m->nverts, 0, 0))) {
80                                         if(i < 3) {
81                                                 fprintf(stderr, "invalid face definition\n");
82                                                 goto err;
83                                         }
84                                         break;
85                                 }
86                                 if(vidx < 0 || vidx >= m->nverts) {
87                                         fprintf(stderr, "invalid face definition, vertex out of range\n");
88                                         goto err;
89                                 }
90                                 face->vidx[i] = vidx;
91                         }
92                         face->prim = i;
93                         m->nfaces++;
94                         putchar('f'); fflush(stdout);
95                         break;
96
97                 default:
98                         break;
99                 }
100         }
101
102         calc_face_normals(m);
103
104         for(i=0; i<m->nfaces; i++) {
105                 int32_t dot;
106                 face = m->faces + i;
107                 dot = face->norm.z > 0 ? face->norm.z : 0;
108                 face->color = dot >> 8;
109         }
110
111         putchar('\n');
112
113         fclose(fp);
114         return 0;
115
116 err:
117         if(fp) fclose(fp);
118         free(m->varr);
119         free(m->faces);
120         return -1;
121 }
122
123 static char *parse_idx(char *s, int *idx, int arrsz)
124 {
125         char *endp;
126         int val = strtol(s, &endp, 10);
127         if(endp == s) return 0;
128
129         if(val < 0) {   /* convert negative indices */
130                 *idx = arrsz + val;
131         } else {
132                 *idx = val - 1; /* conv to 0-based */
133         }
134         return endp;
135 }
136
137 static char *parse_facev(char *s, int *vidx, int *nidx, int *tidx,
138                 int nverts, int nnorm, int nuv)
139 {
140         if(!(s = parse_idx(s, vidx, nverts))) {
141                 return 0;
142         }
143         if(*s != '/') return (!*s || isspace(*s)) ? s : 0;
144
145         if(*++s == '/') {       /* no texcoord */
146                 *tidx = -1;
147                 s++;
148         } else {
149                 if(!(s = parse_idx(s, tidx, nuv))) {
150                         return 0;
151                 }
152                 if(*s != '/') return (!*s || isspace(*s)) ? s : 0;
153                 s++;
154         }
155
156         if(!(s = parse_idx(s, nidx, nnorm))) {
157                 return 0;
158         }
159         return (!*s || isspace(*s)) ? s : 0;
160 }
161
162 static char *clean_line(char *s)
163 {
164         char *endp;
165
166         while(*s && isspace(*s)) s++;
167
168         if((endp = strchr(s, '#'))) {
169                 *endp-- = 0;
170         } else {
171                 endp = s + strlen(s) - 1;
172         }
173         while(endp > s && isspace(*endp)) {
174                 *endp-- = 0;
175         }
176         return s;
177 }
178
179 static int resizearr(void **pptr, int *sz, int elemsz)
180 {
181         int newsz = *sz ? *sz * 2 : 16;
182         void *newarr;
183
184         if(!(newarr = realloc(*pptr, newsz * elemsz))) {
185                 fprintf(stderr, "failed to resize array to %d\n", newsz);
186                 return -1;
187         }
188         *pptr = newarr;
189         *sz = newsz;
190         return 0;
191 }
192
193
194 void destroy_mesh(struct mesh *m)
195 {
196         if(!m) return;
197         free(m->varr);
198         free(m->faces);
199 }
200
201 void flip_mesh_winding(struct mesh *m)
202 {
203         int i, j;
204         unsigned short buf[4];
205         struct meshface *face = m->faces;
206
207         for(i=0; i<m->nfaces; i++) {
208                 for(j=0; j<face->prim; j++) {
209                         buf[face->prim - j - 1] = face->vidx[j];
210                 }
211                 for(j=0; j<face->prim; j++) {
212                         face->vidx[j] = buf[j];
213                 }
214                 face++;
215         }
216 }
217
218 int conv_nonidx_mesh(struct mesh *m)
219 {
220         struct g3d_vertex *varr, *vptr;
221         struct meshface *face;
222         int i, nverts;
223
224         nverts = m->nfaces * 3;
225         if(!(varr = malloc(nverts * sizeof *varr))) {
226                 fprintf(stderr, "failed to allocate nonidx vertex array (%d)\n", nverts);
227                 return -1;
228         }
229
230         face = m->faces;
231         vptr = varr;
232         for(i=0; i<m->nfaces; i++) {
233                 vptr[0] = m->varr[face->vidx[0]];
234                 vptr[1] = m->varr[face->vidx[1]];
235                 vptr[2] = m->varr[face->vidx[2]];
236                 face++;
237                 vptr += 3;
238         }
239
240         free(m->varr);
241         m->varr = varr;
242         m->nverts = nverts;
243         return 0;
244 }
245
246 static float rsqrt(float x)
247 {
248         float xhalf = x * 0.5f;
249         int32_t i = *(int32_t*)&x;
250         i = 0x5f3759df - (i >> 1);
251         x = *(float*)&i;
252         x = x * (1.5f - xhalf * x * x);
253         return x;
254 }
255
256 void calc_face_normals(struct mesh *m)
257 {
258         int i;
259         struct meshface *face;
260         struct g3d_vertex *va, *vb, *vc;
261         int32_t ax, ay, az, bx, by, bz, s;
262         float xsq, ysq, zsq;
263
264         face = m->faces;
265         for(i=0; i<m->nfaces; i++) {
266                 va = m->varr + face->vidx[0];
267                 vb = m->varr + face->vidx[1];
268                 vc = m->varr + face->vidx[2];
269
270                 ax = (vc->x - va->x) >> 8;
271                 ay = (vc->y - va->y) >> 8;
272                 az = (vc->z - va->z) >> 8;
273                 bx = (vc->x - vb->x) >> 8;
274                 by = (vc->y - vb->y) >> 8;
275                 bz = (vc->z - vb->z) >> 8;
276
277                 face->norm.x = ay * bz - az * by;
278                 face->norm.y = az * bx - ax * bz;
279                 face->norm.z = ax * by - ay * bx;
280
281                 xsq = (float)face->norm.x; xsq *= xsq;
282                 ysq = (float)face->norm.y; ysq *= ysq;
283                 zsq = (float)face->norm.z; zsq *= zsq;
284                 s = (int32_t)(rsqrt(xsq + ysq + zsq) * 256.0f);
285                 face->norm.x = (face->norm.x >> 8) * s;
286                 face->norm.y = (face->norm.y >> 8) * s;
287                 face->norm.z = (face->norm.z >> 8) * s;
288
289                 face++;
290         }
291 }
292
293 static struct mesh *smesh;
294 static const int32_t *smat;
295
296 static int zsortcmp(const void *a, const void *b)
297 {
298         unsigned short idxa, idxb;
299         struct meshface *fa, *fb;
300         struct g3d_vertex *va, *vb;
301         int32_t za, zb;
302
303         fa = smesh->faces + *(unsigned short*)a;
304         fb = smesh->faces + *(unsigned short*)b;
305
306         va = smesh->varr + fa->vidx[0];
307         vb = smesh->varr + fb->vidx[0];
308
309         za = (smat[8] >> 8) * (va->x >> 8) + (smat[9] >> 8) * (va->y >> 8) +
310                 (smat[10] >> 8) * (va->z >> 8);
311         zb = (smat[8] >> 8) * (vb->x >> 8) + (smat[9] >> 8) * (vb->y >> 8) +
312                 (smat[10] >> 8) * (vb->z >> 8);
313
314         return za - zb;
315 }
316
317 void sort_mesh(struct mesh *m, const int32_t *mvmat)
318 {
319         int i;
320
321         if(!m->zorder) {
322                 if(!(m->zorder = malloc(m->nfaces * sizeof *m->zorder))) {
323                         return;
324                 }
325                 for(i=0; i<m->nfaces; i++) {
326                         m->zorder[i] = i;
327                 }
328         }
329
330         smesh = m;
331         smat = mvmat;
332         qsort(m->zorder, m->nfaces, sizeof *m->zorder, zsortcmp);
333 }
334
335 void draw_mesh(struct mesh *m)
336 {
337         int i;
338         struct meshface *face = m->faces;
339         for(i=0; i<m->nfaces; i++) {
340                 g3d_color(face->color);
341                 g3d_draw_indexed(face->prim, m->varr, face->vidx, face->prim);
342                 face++;
343         }
344 }
345
346 void draw_mesh_zorder(struct mesh *m)
347 {
348         int i;
349         struct meshface *face;
350
351         for(i=0; i<m->nfaces; i++) {
352                 face = m->faces + m->zorder[i];
353                 g3d_color(face->color);
354                 g3d_draw_indexed(face->prim, m->varr, face->vidx, face->prim);
355         }
356 }