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