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