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