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