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