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