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