writing obj loader
[dosdemo] / src / mesh.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <math.h>
5 #include "mesh.h"
6 #include "3dgfx.h"
7
8 static struct {
9         struct g3d_vertex *varr;
10         const float *xform;
11 } zsort_cls;
12
13 static int zsort_cmp(const void *aptr, const void *bptr)
14 {
15         const float *m = zsort_cls.xform;
16
17         const struct g3d_vertex *va = (const struct g3d_vertex*)aptr;
18         const struct g3d_vertex *vb = (const struct g3d_vertex*)bptr;
19
20         float za = m[2] * va->x + m[6] * va->y + m[10] * va->z + m[14];
21         float zb = m[2] * vb->x + m[6] * vb->y + m[10] * vb->z + m[14];
22
23         ++va;
24         ++vb;
25
26         za += m[2] * va->x + m[6] * va->y + m[10] * va->z + m[14];
27         zb += m[2] * vb->x + m[6] * vb->y + m[10] * vb->z + m[14];
28
29         return za - zb;
30 }
31
32 static int zsort_indexed_cmp(const void *aptr, const void *bptr)
33 {
34         const int16_t *a = (const int16_t*)aptr;
35         const int16_t *b = (const int16_t*)bptr;
36
37         const float *m = zsort_cls.xform;
38
39         const struct g3d_vertex *va = zsort_cls.varr + a[0];
40         const struct g3d_vertex *vb = zsort_cls.varr + b[0];
41
42         float za = m[2] * va->x + m[6] * va->y + m[10] * va->z + m[14];
43         float zb = m[2] * vb->x + m[6] * vb->y + m[10] * vb->z + m[14];
44
45         va = zsort_cls.varr + a[2];
46         vb = zsort_cls.varr + b[2];
47
48         za += m[2] * va->x + m[6] * va->y + m[10] * va->z + m[14];
49         zb += m[2] * vb->x + m[6] * vb->y + m[10] * vb->z + m[14];
50
51         return za - zb;
52 }
53
54
55 void zsort_mesh(struct g3d_mesh *m)
56 {
57         zsort_cls.varr = m->varr;
58         zsort_cls.xform = g3d_get_matrix(G3D_MODELVIEW, 0);
59
60         if(m->iarr) {
61                 int nfaces = m->icount / m->prim;
62                 qsort(m->iarr, nfaces, m->prim * sizeof *m->iarr, zsort_indexed_cmp);
63         } else {
64                 int nfaces = m->vcount / m->prim;
65                 qsort(m->varr, nfaces, m->prim * sizeof *m->varr, zsort_cmp);
66         }
67 }
68
69
70 void draw_mesh(struct g3d_mesh *mesh)
71 {
72         if(mesh->iarr) {
73                 g3d_draw_indexed(mesh->prim, mesh->varr, mesh->vcount, mesh->iarr, mesh->icount);
74         } else {
75                 g3d_draw(mesh->prim, mesh->varr, mesh->vcount);
76         }
77 }
78
79 void apply_mesh_xform(struct g3d_mesh *mesh, const float *xform)
80 {
81         int i;
82         struct g3d_vertex *v = mesh->varr;
83
84         for(i=0; i<mesh->vcount; i++) {
85                 float x = xform[0] * v->x + xform[4] * v->y + xform[8] * v->z + xform[12];
86                 float y = xform[1] * v->x + xform[5] * v->y + xform[9] * v->z + xform[13];
87                 v->z = xform[2] * v->x + xform[6] * v->y + xform[10] * v->z + xform[14];
88                 v->x = x;
89                 v->y = y;
90                 x = xform[0] * v->nx + xform[4] * v->ny + xform[8] * v->nz;
91                 y = xform[1] * v->nx + xform[5] * v->ny + xform[9] * v->nz;
92                 v->nz = xform[2] * v->nx + xform[6] * v->ny + xform[10] * v->nz;
93                 v->nx = x;
94                 v->ny = y;
95                 ++v;
96         }
97 }
98
99 int append_mesh(struct g3d_mesh *ma, struct g3d_mesh *mb)
100 {
101         int i, new_vcount, new_icount;
102         void *tmp;
103         int16_t *iptr;
104
105         if(ma->prim != mb->prim) {
106                 fprintf(stderr, "append_mesh failed, primitive mismatch\n");
107                 return -1;
108         }
109
110         if(ma->iarr || mb->iarr) {
111                 if(!ma->iarr) {
112                         if(indexify_mesh(ma) == -1) {
113                                 return -1;
114                         }
115                 } else if(!mb->iarr) {
116                         if(indexify_mesh(mb) == -1) {
117                                 return -1;
118                         }
119                 }
120
121                 new_icount = ma->icount + mb->icount;
122                 if(!(iptr = realloc(ma->iarr, new_icount * sizeof *iptr))) {
123                         fprintf(stderr, "append_mesh: failed to allocate combined index buffer (%d indices)\n", new_icount);
124                         return -1;
125                 }
126                 ma->iarr = iptr;
127
128                 iptr += ma->icount;
129                 for(i=0; i<mb->icount; i++) {
130                         *iptr++ = mb->iarr[i] + ma->vcount;
131                 }
132                 ma->icount = new_icount;
133         }
134
135         new_vcount = ma->vcount + mb->vcount;
136         if(!(tmp = realloc(ma->varr, new_vcount * sizeof *ma->varr))) {
137                 fprintf(stderr, "append_mesh: failed to allocate combined vertex buffer (%d verts)\n", new_vcount);
138                 return -1;
139         }
140         ma->varr = tmp;
141         memcpy(ma->varr + ma->vcount, mb->varr, mb->vcount * sizeof *ma->varr);
142         ma->vcount = new_vcount;
143         return 0;
144 }
145
146 #define FEQ(a, b)       ((a) - (b) < 1e-5 && (b) - (a) < 1e-5)
147 static int cmp_vertex(struct g3d_vertex *a, struct g3d_vertex *b)
148 {
149         if(!FEQ(a->x, b->x) || !FEQ(a->y, b->y) || !FEQ(a->z, b->z) || !FEQ(a->w, b->w))
150                 return -1;
151         if(!FEQ(a->nx, b->nx) || !FEQ(a->ny, b->ny) || !FEQ(a->nz, b->nz))
152                 return -1;
153         if(!FEQ(a->u, b->u) || !FEQ(a->v, b->v))
154                 return -1;
155         if(a->r != b->r || a->g != b->g || a->b != b->b || a->a != b->a)
156                 return -1;
157         return 0;
158 }
159
160 static int find_existing(struct g3d_vertex *v, struct g3d_vertex *varr, int vcount)
161 {
162         int i;
163         for(i=0; i<vcount; i++) {
164                 if(cmp_vertex(v, varr++) == 0) {
165                         return i;
166                 }
167         }
168         return -1;
169 }
170
171 int indexify_mesh(struct g3d_mesh *mesh)
172 {
173         int i, j, nfaces, max_icount, idx;
174         int out_vcount = 0;
175         struct g3d_vertex *vin, *vout;
176         int16_t *iout;
177
178         if(mesh->iarr) {
179                 fprintf(stderr, "indexify_mesh failed: already indexed\n");
180                 return -1;
181         }
182
183         nfaces = mesh->vcount / mesh->prim;
184         max_icount = mesh->vcount;
185
186         if(!(mesh->iarr = malloc(max_icount * sizeof *mesh->iarr))) {
187                 fprintf(stderr, "indexify_mesh failed to allocate index buffer of %d indices\n", max_icount);
188                 return -1;
189         }
190
191         vin = vout = mesh->varr;
192         iout = mesh->iarr;
193
194         for(i=0; i<nfaces; i++) {
195                 for(j=0; j<mesh->prim; j++) {
196                         if((idx = find_existing(vin, mesh->varr, out_vcount)) >= 0) {
197                                 *iout++ = idx;
198                         } else {
199                                 *iout++ = out_vcount++;
200                                 if(vin != vout) {
201                                         *vout++ = *vin;
202                                 }
203                         }
204                         ++vin;
205                 }
206         }
207
208         /* XXX also shrink buffers? I'll just leave them to max size for now */
209         return 0;
210 }
211
212 int gen_plane_mesh(struct g3d_mesh *m, float width, float height, int usub, int vsub)
213 {
214         int i, j;
215         int nfaces, nverts, nidx, uverts, vverts;
216         float x, y, u, v, du, dv;
217         struct g3d_vertex *vptr;
218         int16_t *iptr;
219
220         if(usub < 1) usub = 1;
221         if(vsub < 1) vsub = 1;
222
223         nfaces = usub * vsub;
224         uverts = usub + 1;
225         vverts = vsub + 1;
226         du = (float)width / (float)usub;
227         dv = (float)height / (float)vsub;
228
229         nverts = uverts * vverts;
230         nidx = nfaces * 4;
231
232         if(!(m->varr = malloc(nverts * sizeof *m->varr))) {
233                 fprintf(stderr, "gen_plane_mesh: failed to allocate vertex buffer (%d vertices)\n", nverts);
234                 return -1;
235         }
236         if(!(m->iarr = malloc(nidx * sizeof *m->iarr))) {
237                 fprintf(stderr, "gen_plane_mesh: failed to allocate index buffer (%d indices)\n", nidx);
238                 free(m->varr);
239                 m->varr = 0;
240                 return -1;
241         }
242
243         m->prim = G3D_QUADS;
244         m->vcount = nverts;
245         m->icount = nidx;
246
247         vptr = m->varr;
248         iptr = m->iarr;
249
250         v = 0.0f;
251         for(i=0; i<vverts; i++) {
252                 y = (v - 0.5) * height;
253                 u = 0.0f;
254
255                 for(j=0; j<uverts; j++) {
256                         x = (u - 0.5) * width;
257
258                         vptr->x = x;
259                         vptr->y = y;
260                         vptr->z = 0.0f;
261                         vptr->w = 1.0f;
262                         vptr->nx = 0.0f;
263                         vptr->ny = 0.0f;
264                         vptr->nz = 1.0f;
265                         vptr->u = u;
266                         vptr->v = v;
267                         vptr->r = vptr->g = vptr->b = vptr->a = 255;
268                         ++vptr;
269
270                         u += du;
271                 }
272                 v += dv;
273         }
274
275         for(i=0; i<vsub; i++) {
276                 for(j=0; j<usub; j++) {
277                         int idx = i * uverts + j;
278                         *iptr++ = idx;
279                         *iptr++ = idx + 1;
280                         *iptr++ = idx + uverts + 1;
281                         *iptr++ = idx + uverts;
282                 }
283         }
284         return 0;
285 }
286
287 int gen_cube_mesh(struct g3d_mesh *mesh, float sz, int sub)
288 {
289         int i;
290         struct g3d_mesh *m;
291         struct g3d_mesh tmpmesh;
292         float xform[16];
293         static float rotface[][4] = {
294                 {0, 0, 1, 0},
295                 {90, 0, 1, 0},
296                 {180, 0, 1, 0},
297                 {270, 0, 1, 0},
298                 {90, 1, 0, 0},
299                 {-90, 1, 0, 0}
300         };
301
302         g3d_matrix_mode(G3D_MODELVIEW);
303         g3d_push_matrix();
304
305         for(i=0; i<6; i++) {
306                 m = i > 0 ? &tmpmesh : mesh;
307                 if(gen_plane_mesh(m, sz, sz, sub, sub) == -1)
308                         return -1;
309                 g3d_load_identity();
310                 g3d_rotate(rotface[i][0], rotface[i][1], rotface[i][2], rotface[i][3]);
311                 g3d_translate(0, 0, sz / 2.0f);
312                 g3d_get_matrix(G3D_MODELVIEW, xform);
313                 apply_mesh_xform(m, xform);
314                 if(i > 0) {
315                         if(append_mesh(mesh, m) == -1) {
316                                 return -1;
317                         }
318                 }
319         }
320
321         g3d_pop_matrix();
322         return 0;
323 }
324
325 static void torusvec(float *res, float theta, float phi, float mr, float rr)
326 {
327         float rx, ry, rz;
328         theta = -theta;
329
330         rx = -cos(phi) * rr + mr;
331         ry = sin(phi) * rr;
332         rz = 0.0f;
333
334         res[0] = rx * sin(theta) + rz * cos(theta);
335         res[1] = ry;
336         res[2] = -rx * cos(theta) + rz * sin(theta);
337 }
338
339 int gen_torus_mesh(struct g3d_mesh *mesh, float rad, float ringrad, int usub, int vsub)
340 {
341         int i, j;
342         int nfaces, uverts, vverts;
343         struct g3d_vertex *vptr;
344         int16_t *iptr;
345
346         mesh->prim = G3D_QUADS;
347
348         if(usub < 4) usub = 4;
349         if(vsub < 2) vsub = 2;
350
351         uverts = usub + 1;
352         vverts = vsub + 1;
353
354         mesh->vcount = uverts * vverts;
355         nfaces = usub * vsub;
356         mesh->icount = nfaces * 4;
357
358         printf("generating torus with %d faces (%d vertices)\n", nfaces, mesh->vcount);
359
360         if(!(mesh->varr = malloc(mesh->vcount * sizeof *mesh->varr))) {
361                 return -1;
362         }
363         if(!(mesh->iarr = malloc(mesh->icount * sizeof *mesh->iarr))) {
364                 return -1;
365         }
366         vptr = mesh->varr;
367         iptr = mesh->iarr;
368
369         for(i=0; i<uverts; i++) {
370                 float u = (float)i / (float)(uverts - 1);
371                 float theta = u * 2.0 * M_PI;
372                 float rcent[3];
373
374                 torusvec(rcent, theta, 0, rad, 0);
375
376                 for(j=0; j<vverts; j++) {
377                         float v = (float)j / (float)(vverts - 1);
378                         float phi = v * 2.0 * M_PI;
379                         int chess = (i & 1) == (j & 1);
380
381                         torusvec(&vptr->x, theta, phi, rad, ringrad);
382
383                         vptr->nx = (vptr->x - rcent[0]) / ringrad;
384                         vptr->ny = (vptr->y - rcent[1]) / ringrad;
385                         vptr->nz = (vptr->z - rcent[2]) / ringrad;
386                         vptr->u = u;
387                         vptr->v = v;
388                         vptr->r = chess ? 255 : 64;
389                         vptr->g = 128;
390                         vptr->b = chess ? 64 : 255;
391                         ++vptr;
392
393                         if(i < usub && j < vsub) {
394                                 int idx = i * vverts + j;
395                                 *iptr++ = idx;
396                                 *iptr++ = idx + 1;
397                                 *iptr++ = idx + vverts + 1;
398                                 *iptr++ = idx + vverts;
399                         }
400                 }
401         }
402         return 0;
403 }
404