initial commit
[meshfrac] / src / cmesh.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdint.h>
4 #include <limits.h>
5 #include <float.h>
6 #include <assert.h>
7 #include "opengl.h"
8 #include "cmesh.h"
9
10
11 struct cmesh_vattrib {
12         int nelem;      /* num elements per attribute [1, 4] */
13         float *data;
14         unsigned int count;
15         unsigned int vbo;
16         int vbo_valid, data_valid;
17 };
18
19 /* istart,icount are valid only when the mesh is indexed, otherwise icount is 0.
20  * vstart,vcount are define the submesh for non-indexed meshes.
21  * For indexed meshes, vstart,vcount denote the range of vertices used by each
22  * submesh.
23  */
24 struct submesh {
25         char *name;
26         int nfaces;     /* derived from either icount or vcount */
27         int istart, icount;
28         int vstart, vcount;
29         struct submesh *next;
30 };
31
32 struct cmesh {
33         char *name;
34         unsigned int nverts, nfaces;
35
36         struct submesh *sublist;
37         int subcount;
38
39         /* current value for each attribute for the immediate mode interface */
40         cgm_vec4 cur_val[CMESH_NUM_ATTR];
41
42         unsigned int buffer_objects[CMESH_NUM_ATTR + 1];
43         struct cmesh_vattrib vattr[CMESH_NUM_ATTR];
44
45         unsigned int *idata;
46         unsigned int icount;
47         unsigned int ibo;
48         int ibo_valid, idata_valid;
49
50         /* index buffer for wireframe rendering (constructed on demand) */
51         unsigned int wire_ibo;
52         int wire_ibo_valid;
53
54         /* axis-aligned bounding box */
55         cgm_vec3 aabb_min, aabb_max;
56         int aabb_valid;
57         /* bounding sphere */
58         cgm_vec3 bsph_center;
59         float bsph_radius;
60         int bsph_valid;
61 };
62
63
64 static int clone(struct cmesh *cmdest, struct cmesh *cmsrc, struct submesh *sub);
65 static int pre_draw(struct cmesh *cm);
66 static void post_draw(struct cmesh *cm, int cur_sdr);
67 static void update_buffers(struct cmesh *cm);
68 static void update_wire_ibo(struct cmesh *cm);
69 static void calc_aabb(struct cmesh *cm);
70 static void calc_bsph(struct cmesh *cm);
71
72 static int def_nelem[CMESH_NUM_ATTR] = {3, 3, 3, 2, 4, 4, 4, 2};
73
74 static int sdr_loc[CMESH_NUM_ATTR] = {0, 1, 2, 3, 4, 5, 6, 7};
75 static int use_custom_sdr_attr;
76
77
78 /* global state */
79 void cmesh_set_attrib_sdrloc(int attr, int loc)
80 {
81         sdr_loc[attr] = loc;
82 }
83
84 int cmesh_get_attrib_sdrloc(int attr)
85 {
86         return sdr_loc[attr];
87 }
88
89 void cmesh_clear_attrib_sdrloc(void)
90 {
91         int i;
92         for(i=0; i<CMESH_NUM_ATTR; i++) {
93                 sdr_loc[i] = -1;
94         }
95 }
96
97 /* mesh functions */
98 struct cmesh *cmesh_alloc(void)
99 {
100         struct cmesh *cm;
101
102         if(!(cm = malloc(sizeof *cm))) {
103                 return 0;
104         }
105         if(cmesh_init(cm) == -1) {
106                 free(cm);
107                 return 0;
108         }
109         return cm;
110 }
111
112 void cmesh_free(struct cmesh *cm)
113 {
114         cmesh_destroy(cm);
115         free(cm);
116 }
117
118 int cmesh_init(struct cmesh *cm)
119 {
120         int i;
121
122         memset(cm, 0, sizeof *cm);
123         cgm_wcons(cm->cur_val + CMESH_ATTR_COLOR, 1, 1, 1, 1);
124
125         glGenBuffers(CMESH_NUM_ATTR + 1, cm->buffer_objects);
126
127         for(i=0; i<CMESH_NUM_ATTR; i++) {
128                 cm->vattr[i].vbo = cm->buffer_objects[i];
129         }
130
131         cm->ibo = cm->buffer_objects[CMESH_NUM_ATTR];
132         return 0;
133 }
134
135 void cmesh_destroy(struct cmesh *cm)
136 {
137         int i;
138
139         free(cm->name);
140
141         for(i=0; i<CMESH_NUM_ATTR; i++) {
142                 free(cm->vattr[i].data);
143         }
144         free(cm->idata);
145
146         cmesh_clear_submeshes(cm);
147
148         glDeleteBuffers(CMESH_NUM_ATTR + 1, cm->buffer_objects);
149         if(cm->wire_ibo) {
150                 glDeleteBuffers(1, &cm->wire_ibo);
151         }
152 }
153
154 void cmesh_clear(struct cmesh *cm)
155 {
156         int i;
157
158         for(i=0; i<CMESH_NUM_ATTR; i++) {
159                 cm->vattr[i].nelem = 0;
160                 cm->vattr[i].vbo_valid = 0;
161                 cm->vattr[i].data_valid = 0;
162                 free(cm->vattr[i].data);
163                 cm->vattr[i].data = 0;
164                 cm->vattr[i].count = 0;
165         }
166         cm->ibo_valid = cm->idata_valid = 0;
167         free(cm->idata);
168         cm->idata = 0;
169         cm->icount = 0;
170
171         cm->wire_ibo_valid = 0;
172         cm->nverts = cm->nfaces = 0;
173
174         cm->bsph_valid = cm->aabb_valid = 0;
175
176         cmesh_clear_submeshes(cm);
177 }
178
179 int cmesh_clone(struct cmesh *cmdest, struct cmesh *cmsrc)
180 {
181         return clone(cmdest, cmsrc, 0);
182 }
183
184 static int clone(struct cmesh *cmdest, struct cmesh *cmsrc, struct submesh *sub)
185 {
186         int i, nelem, vstart, vcount, istart, icount;
187         char *srcname, *name = 0;
188         float *varr[CMESH_NUM_ATTR] = {0};
189         float *vptr;
190         unsigned int *iptr, *iarr = 0;
191
192         /* try do anything that can fail first, before making any changes to cmdest
193          * so we have the option of recovering gracefuly
194          */
195
196         srcname = sub ? sub->name : cmsrc->name;
197         if(srcname) {
198                 if(!(name = malloc(strlen(srcname) + 1))) {
199                         return -1;
200                 }
201                 strcpy(name, srcname);
202         }
203
204         if(sub) {
205                 vstart = sub->vstart;
206                 vcount = sub->vcount;
207                 istart = sub->istart;
208                 icount = sub->icount;
209         } else {
210                 vstart = istart = 0;
211                 vcount = cmsrc->nverts;
212                 icount = cmsrc->icount;
213         }
214
215         if(cmesh_indexed(cmsrc)) {
216                 if(!(iarr = malloc(icount * sizeof *iarr))) {
217                         free(name);
218                         return -1;
219                 }
220         }
221
222         for(i=0; i<CMESH_NUM_ATTR; i++) {
223                 if(cmesh_has_attrib(cmsrc, i)) {
224                         nelem = cmsrc->vattr[i].nelem;
225                         if(!(varr[i] = malloc(vcount * nelem * sizeof(float)))) {
226                                 while(--i >= 0) {
227                                         free(varr[i]);
228                                 }
229                                 free(iarr);
230                                 free(name);
231                                 return -1;
232                         }
233                 }
234         }
235
236         /* from this point forward nothing can fail */
237         cmesh_clear(cmdest);
238
239         for(i=0; i<CMESH_NUM_ATTR; i++) {
240                 free(cmdest->vattr[i].data);
241
242                 if(cmesh_has_attrib(cmsrc, i)) {
243                         cmesh_attrib(cmsrc, i); /* force validation of the actual data on the source mesh */
244
245                         nelem = cmsrc->vattr[i].nelem;
246                         cmdest->vattr[i].nelem = nelem;
247                         cmdest->vattr[i].data = varr[i];
248                         cmdest->vattr[i].count = vcount;
249                         vptr = cmsrc->vattr[i].data + vstart * nelem;
250                         memcpy(cmdest->vattr[i].data, vptr, vcount * nelem * sizeof(float));
251                         cmdest->vattr[i].data_valid = 1;
252                         cmdest->vattr[i].vbo_valid = 0;
253                 } else {
254                         memset(cmdest->vattr + i, 0, sizeof cmdest->vattr[i]);
255                 }
256         }
257
258         if(cmesh_indexed(cmsrc)) {
259                 cmesh_index(cmsrc);     /* force validation .... */
260
261                 cmdest->idata = iarr;
262                 cmdest->icount = icount;
263                 if(sub) {
264                         /* need to offset all vertex indices by -vstart */
265                         iptr = cmsrc->idata + istart;
266                         for(i=0; i<icount; i++) {
267                                 cmdest->idata[i] = *iptr++ - vstart;
268                         }
269                 } else {
270                         memcpy(cmdest->idata, cmsrc->idata + istart, icount * sizeof *cmdest->idata);
271                 }
272                 cmdest->idata_valid = 1;
273         } else {
274                 cmdest->idata = 0;
275                 cmdest->idata_valid = cmdest->ibo_valid = 0;
276         }
277
278         free(cmdest->name);
279         cmdest->name = name;
280
281         cmdest->nverts = cmsrc->nverts;
282         cmdest->nfaces = sub ? sub->nfaces : cmsrc->nfaces;
283
284         memcpy(cmdest->cur_val, cmsrc->cur_val, sizeof cmdest->cur_val);
285
286         cmdest->aabb_min = cmsrc->aabb_min;
287         cmdest->aabb_max = cmsrc->aabb_max;
288         cmdest->aabb_valid = cmsrc->aabb_valid;
289         cmdest->bsph_center = cmsrc->bsph_center;
290         cmdest->bsph_radius = cmsrc->bsph_radius;
291         cmdest->bsph_valid = cmsrc->bsph_valid;
292
293         /* copy sublist only if we're not cloning a submesh */
294         if(!sub) {
295                 struct submesh *sm, *n, *head = 0, *tail = 0;
296
297                 sm = cmsrc->sublist;
298                 while(sm) {
299                         if(!(n = malloc(sizeof *n)) || !(name = malloc(strlen(sm->name) + 1))) {
300                                 free(n);
301                                 sm = sm->next;
302                                 continue;
303                         }
304                         strcpy(name, sm->name);
305                         *n = *sm;
306                         n->name = name;
307                         n->next = 0;
308
309                         if(head) {
310                                 tail->next = n;
311                                 tail = n;
312                         } else {
313                                 head = tail = n;
314                         }
315
316                         sm = sm->next;
317                 }
318
319                 cmdest->sublist = head;
320                 cmdest->subcount = cmsrc->subcount;
321         }
322
323         return 0;
324 }
325
326 int cmesh_set_name(struct cmesh *cm, const char *name)
327 {
328         int len = strlen(name);
329         char *tmp = malloc(len + 1);
330         if(!tmp) return -1;
331         free(cm->name);
332         cm->name = tmp;
333         memcpy(cm->name, name, len + 1);
334         return 0;
335 }
336
337 const char *cmesh_name(struct cmesh *cm)
338 {
339         return cm->name;
340 }
341
342 int cmesh_has_attrib(struct cmesh *cm, int attr)
343 {
344         if(attr < 0 || attr >= CMESH_NUM_ATTR) {
345                 return 0;
346         }
347         return cm->vattr[attr].vbo_valid | cm->vattr[attr].data_valid;
348 }
349
350 int cmesh_indexed(struct cmesh *cm)
351 {
352         return cm->ibo_valid | cm->idata_valid;
353 }
354
355 /* vdata can be 0, in which case only memory is allocated
356  * returns pointer to the attribute array
357  */
358 float *cmesh_set_attrib(struct cmesh *cm, int attr, int nelem, unsigned int num,
359                 const float *vdata)
360 {
361         float *newarr;
362
363         if(attr < 0 || attr >= CMESH_NUM_ATTR) {
364                 return 0;
365         }
366         if(cm->nverts && num != cm->nverts) {
367                 return 0;
368         }
369
370         if(!(newarr = malloc(num * nelem * sizeof *newarr))) {
371                 return 0;
372         }
373         if(vdata) {
374                 memcpy(newarr, vdata, num * nelem * sizeof *newarr);
375         }
376
377         cm->nverts = num;
378
379         free(cm->vattr[attr].data);
380         cm->vattr[attr].data = newarr;
381         cm->vattr[attr].count = num * nelem;
382         cm->vattr[attr].nelem = nelem;
383         cm->vattr[attr].data_valid = 1;
384         cm->vattr[attr].vbo_valid = 0;
385         return newarr;
386 }
387
388 float *cmesh_attrib(struct cmesh *cm, int attr)
389 {
390         if(attr < 0 || attr >= CMESH_NUM_ATTR) {
391                 return 0;
392         }
393         cm->vattr[attr].vbo_valid = 0;
394         return (float*)cmesh_attrib_ro(cm, attr);
395 }
396
397 const float *cmesh_attrib_ro(struct cmesh *cm, int attr)
398 {
399         void *tmp;
400         int nelem;
401
402         if(attr < 0 || attr >= CMESH_NUM_ATTR) {
403                 return 0;
404         }
405
406         if(!cm->vattr[attr].data_valid) {
407 #if GL_ES_VERSION_2_0
408                 return 0;
409 #else
410                 if(!cm->vattr[attr].vbo_valid) {
411                         return 0;
412                 }
413
414                 /* local data copy unavailable, grab the data from the vbo */
415                 nelem = cm->vattr[attr].nelem;
416                 if(!(cm->vattr[attr].data = malloc(cm->nverts * nelem * sizeof(float)))) {
417                         return 0;
418                 }
419                 cm->vattr[attr].count = cm->nverts * nelem;
420
421                 glBindBuffer(GL_ARRAY_BUFFER, cm->vattr[attr].vbo);
422                 tmp = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
423                 memcpy(cm->vattr[attr].data, tmp, cm->nverts * nelem * sizeof(float));
424                 glUnmapBuffer(GL_ARRAY_BUFFER);
425
426                 cm->vattr[attr].data_valid = 1;
427 #endif
428         }
429         return cm->vattr[attr].data;
430 }
431
432 float *cmesh_attrib_at(struct cmesh *cm, int attr, int idx)
433 {
434         float *vptr = cmesh_attrib(cm, attr);
435         return vptr ? vptr + idx * cm->vattr[attr].nelem : 0;
436 }
437
438 const float *cmesh_attrib_at_ro(struct cmesh *cm, int attr, int idx)
439 {
440         const float *vptr = cmesh_attrib_ro(cm, attr);
441         return vptr ? vptr + idx * cm->vattr[attr].nelem : 0;
442 }
443
444 int cmesh_attrib_count(struct cmesh *cm, int attr)
445 {
446         return cmesh_has_attrib(cm, attr) ? cm->nverts : 0;
447 }
448
449 int cmesh_push_attrib(struct cmesh *cm, int attr, float *v)
450 {
451         float *vptr;
452         int i, cursz, newsz;
453
454         if(!cm->vattr[attr].nelem) {
455                 cm->vattr[attr].nelem = def_nelem[attr];
456         }
457
458         cursz = cm->vattr[attr].count;
459         newsz = cursz + cm->vattr[attr].nelem;
460         if(!(vptr = realloc(cm->vattr[attr].data, newsz * sizeof(float)))) {
461                 return -1;
462         }
463         cm->vattr[attr].data = vptr;
464         cm->vattr[attr].count = newsz;
465         vptr += cursz;
466
467         for(i=0; i<cm->vattr[attr].nelem; i++) {
468                 *vptr++ = *v++;
469         }
470         cm->vattr[attr].data_valid = 1;
471         cm->vattr[attr].vbo_valid = 0;
472
473         if(attr == CMESH_ATTR_VERTEX) {
474                 cm->nverts = newsz / cm->vattr[attr].nelem;
475         }
476         return 0;
477 }
478
479 int cmesh_push_attrib1f(struct cmesh *cm, int attr, float x)
480 {
481         float v[4];
482         v[0] = x;
483         v[1] = v[2] = 0.0f;
484         v[3] = 1.0f;
485         return cmesh_push_attrib(cm, attr, v);
486 }
487
488 int cmesh_push_attrib2f(struct cmesh *cm, int attr, float x, float y)
489 {
490         float v[4];
491         v[0] = x;
492         v[1] = y;
493         v[2] = 0.0f;
494         v[3] = 1.0f;
495         return cmesh_push_attrib(cm, attr, v);
496 }
497
498 int cmesh_push_attrib3f(struct cmesh *cm, int attr, float x, float y, float z)
499 {
500         float v[4];
501         v[0] = x;
502         v[1] = y;
503         v[2] = z;
504         v[3] = 1.0f;
505         return cmesh_push_attrib(cm, attr, v);
506 }
507
508 int cmesh_push_attrib4f(struct cmesh *cm, int attr, float x, float y, float z, float w)
509 {
510         float v[4];
511         v[0] = x;
512         v[1] = y;
513         v[2] = z;
514         v[3] = w;
515         return cmesh_push_attrib(cm, attr, v);
516 }
517
518 /* indices can be 0, in which case only memory is allocated
519  * returns pointer to the index array
520  */
521 unsigned int *cmesh_set_index(struct cmesh *cm, int num, const unsigned int *indices)
522 {
523         unsigned int *tmp;
524         int nidx = cm->nfaces * 3;
525
526         if(nidx && num != nidx) {
527                 return 0;
528         }
529
530         if(!(tmp = malloc(num * sizeof *tmp))) {
531                 return 0;
532         }
533         if(indices) {
534                 memcpy(tmp, indices, num * sizeof *tmp);
535         }
536
537         free(cm->idata);
538         cm->idata = tmp;
539         cm->icount = num;
540         cm->nfaces = num / 3;
541         cm->idata_valid = 1;
542         cm->ibo_valid = 0;
543         return tmp;
544 }
545
546 unsigned int *cmesh_index(struct cmesh *cm)
547 {
548         cm->ibo_valid = 0;
549         return (unsigned int*)cmesh_index_ro(cm);
550 }
551
552 const unsigned int *cmesh_index_ro(struct cmesh *cm)
553 {
554         int nidx;
555         unsigned int *tmp;
556
557         if(!cm->idata_valid) {
558 #if GL_ES_VERSION_2_0
559                 return 0;
560 #else
561                 if(!cm->ibo_valid) {
562                         return 0;
563                 }
564
565                 /* local copy is unavailable, grab the data from the ibo */
566                 nidx = cm->nfaces * 3;
567                 if(!(tmp = malloc(nidx * sizeof *cm->idata))) {
568                         return 0;
569                 }
570                 free(cm->idata);
571                 cm->idata = tmp;
572                 cm->icount = nidx;
573
574                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cm->ibo);
575                 tmp = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY);
576                 memcpy(cm->idata, tmp, nidx * sizeof *cm->idata);
577                 glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
578
579                 cm->idata_valid = 1;
580 #endif
581         }
582         return cm->idata;
583 }
584
585 int cmesh_index_count(struct cmesh *cm)
586 {
587         return cm->nfaces * 3;
588 }
589
590 int cmesh_push_index(struct cmesh *cm, unsigned int idx)
591 {
592         unsigned int *iptr;
593         unsigned int cur_sz = cm->icount;
594         if(!(iptr = realloc(cm->idata, (cur_sz + 1) * sizeof *iptr))) {
595                 return -1;
596         }
597         iptr[cur_sz] = idx;
598         cm->idata = iptr;
599         cm->icount = cur_sz + 1;
600         cm->idata_valid = 1;
601         cm->ibo_valid = 0;
602
603         cm->nfaces = cm->icount / 3;
604         return 0;
605 }
606
607 int cmesh_poly_count(struct cmesh *cm)
608 {
609         if(cm->nfaces) {
610                 return cm->nfaces;
611         }
612         if(cm->nverts) {
613                 return cm->nverts / 3;
614         }
615         return 0;
616 }
617
618 /* attr can be -1 to invalidate all attributes */
619 void cmesh_invalidate_vbo(struct cmesh *cm, int attr)
620 {
621         int i;
622
623         if(attr >= CMESH_NUM_ATTR) {
624                 return;
625         }
626
627         if(attr < 0) {
628                 for(i=0; i<CMESH_NUM_ATTR; i++) {
629                         cm->vattr[i].vbo_valid = 0;
630                 }
631         } else {
632                 cm->vattr[attr].vbo_valid = 0;
633         }
634 }
635
636 void cmesh_invalidate_index(struct cmesh *cm)
637 {
638         cm->ibo_valid = 0;
639 }
640
641 int cmesh_append(struct cmesh *cmdest, struct cmesh *cmsrc)
642 {
643         int i, nelem, newsz, origsz, srcsz;
644         float *vptr;
645         unsigned int *iptr;
646         unsigned int idxoffs;
647
648         if(!cmdest->nverts) {
649                 return cmesh_clone(cmdest, cmsrc);
650         }
651
652         for(i=0; i<CMESH_NUM_ATTR; i++) {
653                 if(cmesh_has_attrib(cmdest, i) && cmesh_has_attrib(cmsrc, i)) {
654                         /* force validation of the data arrays */
655                         cmesh_attrib(cmdest, i);
656                         cmesh_attrib_ro(cmsrc, i);
657
658                         assert(cmdest->vattr[i].nelem == cmsrc->vattr[i].nelem);
659                         nelem = cmdest->vattr[i].nelem;
660                         origsz = cmdest->nverts * nelem;
661                         newsz = cmdest->nverts + cmsrc->nverts * nelem;
662
663                         if(!(vptr = realloc(cmdest->vattr[i].data, newsz * sizeof *vptr))) {
664                                 return -1;
665                         }
666                         memcpy(vptr + origsz, cmsrc->vattr[i].data, cmsrc->nverts * nelem * sizeof(float));
667                         cmdest->vattr[i].data = vptr;
668                         cmdest->vattr[i].count = newsz;
669                 }
670         }
671
672         if(cmesh_indexed(cmdest)) {
673                 assert(cmesh_indexed(cmsrc));
674                 /* force validation ... */
675                 cmesh_index(cmdest);
676                 cmesh_index_ro(cmsrc);
677
678                 idxoffs = cmdest->nverts;
679                 origsz = cmdest->icount;
680                 srcsz = cmsrc->icount;
681                 newsz = origsz + srcsz;
682
683                 if(!(iptr = realloc(cmdest->idata, newsz * sizeof *iptr))) {
684                         return -1;
685                 }
686                 cmdest->idata = iptr;
687                 cmdest->icount = newsz;
688
689                 /* copy and fixup all the new indices */
690                 iptr += origsz;
691                 for(i=0; i<srcsz; i++) {
692                         *iptr++ = cmsrc->idata[i] + idxoffs;
693                 }
694         }
695
696         cmdest->wire_ibo_valid = 0;
697         cmdest->aabb_valid = 0;
698         cmdest->bsph_valid = 0;
699         return 0;
700 }
701
702 void cmesh_clear_submeshes(struct cmesh *cm)
703 {
704         struct submesh *sm;
705
706         while(cm->sublist) {
707                 sm = cm->sublist;
708                 cm->sublist = cm->sublist->next;
709                 free(sm->name);
710                 free(sm);
711         }
712         cm->subcount = 0;
713 }
714
715 int cmesh_submesh(struct cmesh *cm, const char *name, int fstart, int fcount)
716 {
717         int i;
718         unsigned int minv = UINT_MAX, maxv = 0;
719         unsigned int *iptr;
720         struct submesh *sm;
721
722         if(fstart < 0 || fcount < 1 || fstart + fcount > cm->nfaces) {
723                 return -1;
724         }
725
726         if(!(sm = malloc(sizeof *sm)) || !(sm->name = malloc(strlen(name) + 1))) {
727                 free(sm);
728                 return -1;
729         }
730         strcpy(sm->name, name);
731         sm->nfaces = fcount;
732
733         if(cmesh_indexed(cm)) {
734                 sm->istart = fstart * 3;
735                 sm->icount = fcount * 3;
736
737                 /* find out which vertices are used by this submesh */
738                 iptr = cm->idata + sm->istart;
739                 for(i=0; i<sm->icount; i++) {
740                         unsigned int vidx = *iptr++;
741                         if(vidx < minv) minv = vidx;
742                         if(vidx > maxv) maxv = vidx;
743                 }
744                 sm->vstart = minv;
745                 sm->vcount = maxv - minv + 1;
746         } else {
747                 sm->istart = sm->icount = 0;
748                 sm->vstart = fstart * 3;
749                 sm->vcount = fcount * 3;
750         }
751
752         sm->next = cm->sublist;
753         cm->sublist = sm;
754         cm->subcount++;
755         return 0;
756 }
757
758 int cmesh_remove_submesh(struct cmesh *cm, int idx)
759 {
760         struct submesh dummy;
761         struct submesh *prev, *sm;
762
763         if(idx >= cm->subcount) {
764                 return -1;
765         }
766
767         dummy.next = cm->sublist;
768         prev = &dummy;
769
770         while(prev->next && idx-- > 0) {
771                 prev = prev->next;
772         }
773
774         if(!(sm = prev->next)) return -1;
775
776         prev->next = sm->next;
777         free(sm->name);
778         free(sm);
779
780         cm->subcount--;
781         assert(cm->subcount >= 0);
782
783         cm->sublist = dummy.next;
784         return 0;
785 }
786
787 int cmesh_find_submesh(struct cmesh *cm, const char *name)
788 {
789         int idx = 0;
790         struct submesh *sm = cm->sublist;
791         while(sm) {
792                 if(strcmp(sm->name, name) == 0) {
793                         assert(idx <= cm->subcount);
794                         return idx;
795                 }
796                 idx++;
797                 sm = sm->next;
798         }
799         return -1;
800 }
801
802 int cmesh_submesh_count(struct cmesh *cm)
803 {
804         return cm->subcount;
805 }
806
807 static struct submesh *get_submesh(struct cmesh *m, int idx)
808 {
809         struct submesh *sm = m->sublist;
810         while(sm && --idx >= 0) {
811                 sm = sm->next;
812         }
813         return sm;
814 }
815
816 int cmesh_clone_submesh(struct cmesh *cmdest, struct cmesh *cm, int subidx)
817 {
818         struct submesh *sub;
819
820         if(!(sub = get_submesh(cm, subidx))) {
821                 return -1;
822         }
823         return clone(cmdest, cm, sub);
824 }
825
826
827 /* assemble a complete vertex by adding all the useful attributes */
828 int cmesh_vertex(struct cmesh *cm, float x, float y, float z)
829 {
830         int i, j;
831
832         cgm_wcons(cm->cur_val + CMESH_ATTR_VERTEX, x, y, z, 1.0f);
833         cm->vattr[CMESH_ATTR_VERTEX].data_valid = 1;
834         cm->vattr[CMESH_ATTR_VERTEX].nelem = 3;
835
836         for(i=0; i<CMESH_NUM_ATTR; i++) {
837                 if(cm->vattr[i].data_valid) {
838                         int newsz = cm->vattr[i].count + cm->vattr[i].nelem;
839                         float *tmp = realloc(cm->vattr[i].data, newsz * sizeof *tmp);
840                         if(!tmp) return -1;
841                         tmp += cm->vattr[i].count;
842
843                         cm->vattr[i].data = tmp;
844                         cm->vattr[i].count = newsz;
845
846                         for(j=0; j<cm->vattr[i].nelem; j++) {
847                                 *tmp++ = *(&cm->cur_val[i].x + j);
848                         }
849                 }
850                 cm->vattr[i].vbo_valid = 0;
851                 cm->vattr[i].data_valid = 1;
852         }
853
854         if(cm->idata_valid) {
855                 free(cm->idata);
856                 cm->idata = 0;
857                 cm->icount = 0;
858         }
859         cm->ibo_valid = cm->idata_valid = 0;
860         return 0;
861 }
862
863 void cmesh_normal(struct cmesh *cm, float nx, float ny, float nz)
864 {
865         cgm_wcons(cm->cur_val + CMESH_ATTR_NORMAL, nx, ny, nz, 1.0f);
866         cm->vattr[CMESH_ATTR_NORMAL].nelem = 3;
867 }
868
869 void cmesh_tangent(struct cmesh *cm, float tx, float ty, float tz)
870 {
871         cgm_wcons(cm->cur_val + CMESH_ATTR_TANGENT, tx, ty, tz, 1.0f);
872         cm->vattr[CMESH_ATTR_TANGENT].nelem = 3;
873 }
874
875 void cmesh_texcoord(struct cmesh *cm, float u, float v, float w)
876 {
877         cgm_wcons(cm->cur_val + CMESH_ATTR_TEXCOORD, u, v, w, 1.0f);
878         cm->vattr[CMESH_ATTR_TEXCOORD].nelem = 3;
879 }
880
881 void cmesh_boneweights(struct cmesh *cm, float w1, float w2, float w3, float w4)
882 {
883         cgm_wcons(cm->cur_val + CMESH_ATTR_BONEWEIGHTS, w1, w2, w3, w4);
884         cm->vattr[CMESH_ATTR_BONEWEIGHTS].nelem = 4;
885 }
886
887 void cmesh_boneidx(struct cmesh *cm, int idx1, int idx2, int idx3, int idx4)
888 {
889         cgm_wcons(cm->cur_val + CMESH_ATTR_BONEIDX, idx1, idx2, idx3, idx4);
890         cm->vattr[CMESH_ATTR_BONEIDX].nelem = 4;
891 }
892
893 static float *get_vec4(struct cmesh *cm, int attr, int idx, cgm_vec4 *res)
894 {
895         int i;
896         float *sptr, *dptr;
897         cgm_wcons(res, 0, 0, 0, 1);
898         if(!(sptr = cmesh_attrib_at(cm, attr, idx))) {
899                 return 0;
900         }
901         dptr = &res->x;
902
903         for(i=0; i<cm->vattr[attr].nelem; i++) {
904                 *dptr++ = sptr[i];
905         }
906         return sptr;
907 }
908
909 static float *get_vec3(struct cmesh *cm, int attr, int idx, cgm_vec3 *res)
910 {
911         int i;
912         float *sptr, *dptr;
913         cgm_vcons(res, 0, 0, 0);
914         if(!(sptr = cmesh_attrib_at(cm, attr, idx))) {
915                 return 0;
916         }
917         dptr = &res->x;
918
919         for(i=0; i<cm->vattr[attr].nelem; i++) {
920                 *dptr++ = sptr[i];
921         }
922         return sptr;
923 }
924
925 /* dir_xform can be null, in which case it's calculated from xform */
926 void cmesh_apply_xform(struct cmesh *cm, float *xform, float *dir_xform)
927 {
928         unsigned int i;
929         int j;
930         cgm_vec4 v;
931         cgm_vec3 n, t;
932         float *vptr;
933
934         for(i=0; i<cm->nverts; i++) {
935                 if(!(vptr = get_vec4(cm, CMESH_ATTR_VERTEX, i, &v))) {
936                         return;
937                 }
938                 cgm_wmul_m4v4(&v, xform);
939                 for(j=0; j<cm->vattr[CMESH_ATTR_VERTEX].nelem; j++) {
940                         *vptr++ = (&v.x)[j];
941                 }
942
943                 if(cmesh_has_attrib(cm, CMESH_ATTR_NORMAL)) {
944                         if((vptr = get_vec3(cm, CMESH_ATTR_NORMAL, i, &n))) {
945                                 cgm_vmul_m3v3(&n, dir_xform);
946                                 for(j=0; j<cm->vattr[CMESH_ATTR_NORMAL].nelem; j++) {
947                                         *vptr++ = (&n.x)[j];
948                                 }
949                         }
950                 }
951                 if(cmesh_has_attrib(cm, CMESH_ATTR_TANGENT)) {
952                         if((vptr = get_vec3(cm, CMESH_ATTR_TANGENT, i, &t))) {
953                                 cgm_vmul_m3v3(&t, dir_xform);
954                                 for(j=0; j<cm->vattr[CMESH_ATTR_TANGENT].nelem; j++) {
955                                         *vptr++ = (&t.x)[j];
956                                 }
957                         }
958                 }
959         }
960 }
961
962 void cmesh_flip(struct cmesh *cm)
963 {
964         cmesh_flip_faces(cm);
965         cmesh_flip_normals(cm);
966 }
967
968 void cmesh_flip_faces(struct cmesh *cm)
969 {
970         int i, j, idxnum, vnum, nelem;
971         unsigned int *indices;
972         float *verts, *vptr;
973
974         if(cmesh_indexed(cm)) {
975                 if(!(indices = cmesh_index(cm))) {
976                         return;
977                 }
978                 idxnum = cmesh_index_count(cm);
979                 for(i=0; i<idxnum; i+=3) {
980                         unsigned int tmp = indices[i + 2];
981                         indices[i + 2] = indices[i + 1];
982                         indices[i + 1] = tmp;
983                 }
984         } else {
985                 if(!(verts = cmesh_attrib(cm, CMESH_ATTR_VERTEX))) {
986                         return;
987                 }
988                 vnum = cmesh_attrib_count(cm, CMESH_ATTR_VERTEX);
989                 nelem = cm->vattr[CMESH_ATTR_VERTEX].nelem;
990                 for(i=0; i<vnum; i+=3) {
991                         for(j=0; j<nelem; j++) {
992                                 vptr = verts + (i + 1) * nelem + j;
993                                 float tmp = vptr[nelem];
994                                 vptr[nelem] = vptr[0];
995                                 vptr[0] = tmp;
996                         }
997                 }
998         }
999 }
1000 void cmesh_flip_normals(struct cmesh *cm)
1001 {
1002         int i, num;
1003         float *nptr = cmesh_attrib(cm, CMESH_ATTR_NORMAL);
1004         if(!nptr) return;
1005
1006         num = cm->nverts * cm->vattr[CMESH_ATTR_NORMAL].nelem;
1007         for(i=0; i<num; i++) {
1008                 *nptr = -*nptr;
1009                 nptr++;
1010         }
1011 }
1012
1013 int cmesh_explode(struct cmesh *cm)
1014 {
1015         int i, j, k, idxnum, nnverts;
1016         unsigned int *indices;
1017
1018         if(!cmesh_indexed(cm)) return 0;
1019
1020         indices = cmesh_index(cm);
1021         assert(indices);
1022
1023         idxnum = cmesh_index_count(cm);
1024         nnverts = idxnum;
1025
1026         for(i=0; i<CMESH_NUM_ATTR; i++) {
1027                 const float *srcbuf;
1028                 float *tmpbuf, *dstptr;
1029
1030                 if(!cmesh_has_attrib(cm, i)) continue;
1031
1032                 srcbuf = cmesh_attrib(cm, i);
1033                 if(!(tmpbuf = malloc(nnverts * cm->vattr[i].nelem * sizeof(float)))) {
1034                         return -1;
1035                 }
1036                 dstptr = tmpbuf;
1037
1038                 for(j=0; j<idxnum; j++) {
1039                         unsigned int idx = indices[j];
1040                         const float *srcptr = srcbuf + idx * cm->vattr[i].nelem;
1041
1042                         for(k=0; k<cm->vattr[i].nelem; k++) {
1043                                 *dstptr++ = *srcptr++;
1044                         }
1045                 }
1046
1047                 free(cm->vattr[i].data);
1048                 cm->vattr[i].data = tmpbuf;
1049                 cm->vattr[i].count = nnverts * cm->vattr[i].nelem;
1050                 cm->vattr[i].data_valid = 1;
1051         }
1052
1053         cm->ibo_valid = 0;
1054         cm->idata_valid = 0;
1055         free(cm->idata);
1056         cm->idata = 0;
1057         cm->icount = 0;
1058
1059         cm->nverts = nnverts;
1060         cm->nfaces = idxnum / 3;
1061         return 0;
1062 }
1063
1064 void cmesh_calc_face_normals(struct cmesh *cm)
1065 {
1066         /* TODO */
1067 }
1068
1069 static int pre_draw(struct cmesh *cm)
1070 {
1071         int i, loc, cur_sdr;
1072
1073         glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr);
1074
1075         update_buffers(cm);
1076
1077         if(!cm->vattr[CMESH_ATTR_VERTEX].vbo_valid) {
1078                 return -1;
1079         }
1080
1081         if(cur_sdr && use_custom_sdr_attr) {
1082                 if(sdr_loc[CMESH_ATTR_VERTEX] == -1) {
1083                         return -1;
1084                 }
1085
1086                 for(i=0; i<CMESH_NUM_ATTR; i++) {
1087                         loc = sdr_loc[i];
1088                         if(loc >= 0 && cm->vattr[i].vbo_valid) {
1089                                 glBindBuffer(GL_ARRAY_BUFFER, cm->vattr[i].vbo);
1090                                 glVertexAttribPointer(loc, cm->vattr[i].nelem, GL_FLOAT, GL_FALSE, 0, 0);
1091                                 glEnableVertexAttribArray(loc);
1092                         }
1093                 }
1094         } else {
1095 #ifndef GL_ES_VERSION_2_0
1096                 glBindBuffer(GL_ARRAY_BUFFER, cm->vattr[CMESH_ATTR_VERTEX].vbo);
1097                 glVertexPointer(cm->vattr[CMESH_ATTR_VERTEX].nelem, GL_FLOAT, 0, 0);
1098                 glEnableClientState(GL_VERTEX_ARRAY);
1099
1100                 if(cm->vattr[CMESH_ATTR_NORMAL].vbo_valid) {
1101                         glBindBuffer(GL_ARRAY_BUFFER, cm->vattr[CMESH_ATTR_NORMAL].vbo);
1102                         glNormalPointer(GL_FLOAT, 0, 0);
1103                         glEnableClientState(GL_NORMAL_ARRAY);
1104                 }
1105                 if(cm->vattr[CMESH_ATTR_TEXCOORD].vbo_valid) {
1106                         glBindBuffer(GL_ARRAY_BUFFER, cm->vattr[CMESH_ATTR_TEXCOORD].vbo);
1107                         glTexCoordPointer(cm->vattr[CMESH_ATTR_TEXCOORD].nelem, GL_FLOAT, 0, 0);
1108                         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1109                 }
1110                 if(cm->vattr[CMESH_ATTR_COLOR].vbo_valid) {
1111                         glBindBuffer(GL_ARRAY_BUFFER, cm->vattr[CMESH_ATTR_COLOR].vbo);
1112                         glColorPointer(cm->vattr[CMESH_ATTR_COLOR].nelem, GL_FLOAT, 0, 0);
1113                         glEnableClientState(GL_COLOR_ARRAY);
1114                 }
1115                 if(cm->vattr[CMESH_ATTR_TEXCOORD2].vbo_valid) {
1116                         glClientActiveTexture(GL_TEXTURE1);
1117                         glBindBuffer(GL_ARRAY_BUFFER, cm->vattr[CMESH_ATTR_TEXCOORD2].vbo);
1118                         glTexCoordPointer(cm->vattr[CMESH_ATTR_TEXCOORD2].nelem, GL_FLOAT, 0, 0);
1119                         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1120                         glClientActiveTexture(GL_TEXTURE0);
1121                 }
1122 #endif  /* GL_ES_VERSION_2_0 */
1123         }
1124         glBindBuffer(GL_ARRAY_BUFFER, 0);
1125         return cur_sdr;
1126 }
1127
1128 void cmesh_draw(struct cmesh *cm)
1129 {
1130         int cur_sdr;
1131
1132         if((cur_sdr = pre_draw(cm)) == -1) {
1133                 return;
1134         }
1135
1136         if(cm->ibo_valid) {
1137                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cm->ibo);
1138                 glDrawElements(GL_TRIANGLES, cm->nfaces * 3, GL_UNSIGNED_INT, 0);
1139                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1140         } else {
1141                 glDrawArrays(GL_TRIANGLES, 0, cm->nverts);
1142         }
1143
1144         post_draw(cm, cur_sdr);
1145 }
1146
1147 void cmesh_draw_range(struct cmesh *cm, int start, int count)
1148 {
1149         int cur_sdr;
1150
1151         if((cur_sdr = pre_draw(cm)) == -1) {
1152                 return;
1153         }
1154
1155         if(cm->ibo_valid) {
1156                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cm->ibo);
1157                 glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, (void*)(intptr_t)(start * 4));
1158                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1159         } else {
1160                 glDrawArrays(GL_TRIANGLES, start, count);
1161         }
1162
1163         post_draw(cm, cur_sdr);
1164 }
1165
1166 void cmesh_draw_submesh(struct cmesh *cm, int subidx)
1167 {
1168         struct submesh *sm = cm->sublist;
1169
1170         while(sm && subidx-- > 0) {
1171                 sm = sm->next;
1172         }
1173         if(!sm) return;
1174
1175         if(sm->icount) {
1176                 cmesh_draw_range(cm, sm->istart, sm->icount);
1177         } else {
1178                 cmesh_draw_range(cm, sm->vstart, sm->vcount);
1179         }
1180 }
1181
1182 static void post_draw(struct cmesh *cm, int cur_sdr)
1183 {
1184         int i;
1185
1186         if(cur_sdr && use_custom_sdr_attr) {
1187                 for(i=0; i<CMESH_NUM_ATTR; i++) {
1188                         int loc = sdr_loc[i];
1189                         if(loc >= 0 && cm->vattr[i].vbo_valid) {
1190                                 glDisableVertexAttribArray(loc);
1191                         }
1192                 }
1193         } else {
1194 #ifndef GL_ES_VERSION_2_0
1195                 glDisableClientState(GL_VERTEX_ARRAY);
1196                 if(cm->vattr[CMESH_ATTR_NORMAL].vbo_valid) {
1197                         glDisableClientState(GL_NORMAL_ARRAY);
1198                 }
1199                 if(cm->vattr[CMESH_ATTR_TEXCOORD].vbo_valid) {
1200                         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1201                 }
1202                 if(cm->vattr[CMESH_ATTR_COLOR].vbo_valid) {
1203                         glDisableClientState(GL_COLOR_ARRAY);
1204                 }
1205                 if(cm->vattr[CMESH_ATTR_TEXCOORD2].vbo_valid) {
1206                         glClientActiveTexture(GL_TEXTURE1);
1207                         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1208                         glClientActiveTexture(GL_TEXTURE0);
1209                 }
1210 #endif  /* GL_ES_VERSION_2_0 */
1211         }
1212 }
1213
1214 void cmesh_draw_wire(struct cmesh *cm, float linesz)
1215 {
1216         int cur_sdr, nfaces;
1217
1218         if((cur_sdr = pre_draw(cm)) == -1) {
1219                 return;
1220         }
1221         update_wire_ibo(cm);
1222
1223         nfaces = cmesh_poly_count(cm);
1224         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cm->wire_ibo);
1225         glDrawElements(GL_LINES, nfaces * 6, GL_UNSIGNED_INT, 0);
1226         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1227
1228         post_draw(cm, cur_sdr);
1229 }
1230
1231 void cmesh_draw_vertices(struct cmesh *cm, float ptsz)
1232 {
1233         int cur_sdr;
1234         if((cur_sdr = pre_draw(cm)) == -1) {
1235                 return;
1236         }
1237
1238         glPushAttrib(GL_POINT_BIT);
1239         glPointSize(ptsz);
1240         glDrawArrays(GL_POINTS, 0, cm->nverts);
1241         glPopAttrib();
1242
1243         post_draw(cm, cur_sdr);
1244 }
1245
1246 void cmesh_draw_normals(struct cmesh *cm, float len)
1247 {
1248 #ifndef GL_ES_VERSION_2_0
1249         int i, cur_sdr, vert_nelem, norm_nelem;
1250         int loc = -1;
1251         const float *varr, *norm;
1252
1253         varr = cmesh_attrib_ro(cm, CMESH_ATTR_VERTEX);
1254         norm = cmesh_attrib_ro(cm, CMESH_ATTR_NORMAL);
1255         if(!varr || !norm) return;
1256
1257         vert_nelem = cm->vattr[CMESH_ATTR_VERTEX].nelem;
1258         norm_nelem = cm->vattr[CMESH_ATTR_NORMAL].nelem;
1259
1260         glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr);
1261         if(cur_sdr && use_custom_sdr_attr) {
1262                 if((loc = sdr_loc[CMESH_ATTR_VERTEX]) < 0) {
1263                         return;
1264                 }
1265         }
1266
1267         glBegin(GL_LINES);
1268         for(i=0; i<cm->nverts; i++) {
1269                 float x, y, z, endx, endy, endz;
1270
1271                 x = varr[i * vert_nelem];
1272                 y = varr[i * vert_nelem + 1];
1273                 z = varr[i * vert_nelem + 2];
1274                 endx = x + norm[i * norm_nelem] * len;
1275                 endy = y + norm[i * norm_nelem + 1] * len;
1276                 endz = z + norm[i * norm_nelem + 2] * len;
1277
1278                 if(loc == -1) {
1279                         glVertex3f(x, y, z);
1280                         glVertex3f(endx, endy, endz);
1281                 } else {
1282                         glVertexAttrib3f(loc, x, y, z);
1283                         glVertexAttrib3f(loc, endx, endy, endz);
1284                 }
1285         }
1286         glEnd();
1287 #endif  /* GL_ES_VERSION_2_0 */
1288 }
1289
1290 void cmesh_draw_tangents(struct cmesh *cm, float len)
1291 {
1292 #ifndef GL_ES_VERSION_2_0
1293         int i, cur_sdr, vert_nelem, tang_nelem;
1294         int loc = -1;
1295         const float *varr, *tang;
1296
1297         varr = cmesh_attrib_ro(cm, CMESH_ATTR_VERTEX);
1298         tang = cmesh_attrib_ro(cm, CMESH_ATTR_TANGENT);
1299         if(!varr || !tang) return;
1300
1301         vert_nelem = cm->vattr[CMESH_ATTR_VERTEX].nelem;
1302         tang_nelem = cm->vattr[CMESH_ATTR_TANGENT].nelem;
1303
1304         glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr);
1305         if(cur_sdr && use_custom_sdr_attr) {
1306                 if((loc = sdr_loc[CMESH_ATTR_VERTEX]) < 0) {
1307                         return;
1308                 }
1309         }
1310
1311         glBegin(GL_LINES);
1312         for(i=0; i<cm->nverts; i++) {
1313                 float x, y, z, endx, endy, endz;
1314
1315                 x = varr[i * vert_nelem];
1316                 y = varr[i * vert_nelem + 1];
1317                 z = varr[i * vert_nelem + 2];
1318                 endx = x + tang[i * tang_nelem] * len;
1319                 endy = y + tang[i * tang_nelem + 1] * len;
1320                 endz = z + tang[i * tang_nelem + 2] * len;
1321
1322                 if(loc == -1) {
1323                         glVertex3f(x, y, z);
1324                         glVertex3f(endx, endy, endz);
1325                 } else {
1326                         glVertexAttrib3f(loc, x, y, z);
1327                         glVertexAttrib3f(loc, endx, endy, endz);
1328                 }
1329         }
1330         glEnd();
1331 #endif  /* GL_ES_VERSION_2_0 */
1332 }
1333
1334 static void update_buffers(struct cmesh *cm)
1335 {
1336         int i;
1337
1338         for(i=0; i<CMESH_NUM_ATTR; i++) {
1339                 if(cmesh_has_attrib(cm, i) && !cm->vattr[i].vbo_valid) {
1340                         glBindBuffer(GL_ARRAY_BUFFER, cm->vattr[i].vbo);
1341                         glBufferData(GL_ARRAY_BUFFER, cm->nverts * cm->vattr[i].nelem * sizeof(float),
1342                                         cm->vattr[i].data, GL_STATIC_DRAW);
1343                         cm->vattr[i].vbo_valid = 1;
1344                 }
1345         }
1346         glBindBuffer(GL_ARRAY_BUFFER, 0);
1347
1348         if(cm->idata_valid && !cm->ibo_valid) {
1349                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cm->ibo);
1350                 glBufferData(GL_ELEMENT_ARRAY_BUFFER, cm->nfaces * 3 * sizeof(unsigned int),
1351                                 cm->idata, GL_STATIC_DRAW);
1352                 cm->ibo_valid = 1;
1353                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1354         }
1355 }
1356
1357 static void update_wire_ibo(struct cmesh *cm)
1358 {
1359         int i, num_faces;
1360         unsigned int *wire_idxarr, *dest;
1361
1362         update_buffers(cm);
1363
1364         if(cm->wire_ibo_valid) return;
1365
1366         if(!cm->wire_ibo) {
1367                 glGenBuffers(1, &cm->wire_ibo);
1368         }
1369         num_faces = cmesh_poly_count(cm);
1370
1371         if(!(wire_idxarr = malloc(num_faces * 6 * sizeof *wire_idxarr))) {
1372                 return;
1373         }
1374         dest = wire_idxarr;
1375
1376         if(cm->ibo_valid) {
1377                 /* we're dealing with an indexed mesh */
1378                 const unsigned int *idxarr = cmesh_index_ro(cm);
1379
1380                 for(i=0; i<num_faces; i++) {
1381                         *dest++ = idxarr[0];
1382                         *dest++ = idxarr[1];
1383                         *dest++ = idxarr[1];
1384                         *dest++ = idxarr[2];
1385                         *dest++ = idxarr[2];
1386                         *dest++ = idxarr[0];
1387                         idxarr += 3;
1388                 }
1389         } else {
1390                 /* not an indexed mesh */
1391                 for(i=0; i<num_faces; i++) {
1392                         int vidx = i * 3;
1393                         *dest++ = vidx;
1394                         *dest++ = vidx + 1;
1395                         *dest++ = vidx + 1;
1396                         *dest++ = vidx + 2;
1397                         *dest++ = vidx + 2;
1398                         *dest++ = vidx;
1399                 }
1400         }
1401
1402         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cm->wire_ibo);
1403         glBufferData(GL_ELEMENT_ARRAY_BUFFER, num_faces * 6 * sizeof(unsigned int),
1404                         wire_idxarr, GL_STATIC_DRAW);
1405         free(wire_idxarr);
1406         cm->wire_ibo_valid = 1;
1407         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1408 }
1409
1410 static void calc_aabb(struct cmesh *cm)
1411 {
1412         int i, j;
1413
1414         if(!cmesh_attrib_ro(cm, CMESH_ATTR_VERTEX)) {
1415                 return;
1416         }
1417
1418         cgm_vcons(&cm->aabb_min, FLT_MAX, FLT_MAX, FLT_MAX);
1419         cgm_vcons(&cm->aabb_max, -FLT_MAX, -FLT_MAX, -FLT_MAX);
1420
1421         for(i=0; i<cm->nverts; i++) {
1422                 const float *v = cmesh_attrib_at_ro(cm, CMESH_ATTR_VERTEX, i);
1423                 for(j=0; j<3; j++) {
1424                         if(v[j] < (&cm->aabb_min.x)[j]) {
1425                                 (&cm->aabb_min.x)[j] = v[j];
1426                         }
1427                         if(v[j] > (&cm->aabb_max.x)[j]) {
1428                                 (&cm->aabb_max.x)[j] = v[j];
1429                         }
1430                 }
1431         }
1432         cm->aabb_valid = 1;
1433 }
1434
1435 void cmesh_aabbox(struct cmesh *cm, cgm_vec3 *vmin, cgm_vec3 *vmax)
1436 {
1437         if(!cm->aabb_valid) {
1438                 calc_aabb(cm);
1439         }
1440         *vmin = cm->aabb_min;
1441         *vmax = cm->aabb_max;
1442 }
1443
1444 static void calc_bsph(struct cmesh *cm)
1445 {
1446         int i;
1447         float s, dist_sq;
1448
1449         if(!cmesh_attrib_ro(cm, CMESH_ATTR_VERTEX)) {
1450                 return;
1451         }
1452
1453         cgm_vcons(&cm->bsph_center, 0, 0, 0);
1454
1455         /* first find the center */
1456         for(i=0; i<cm->nverts; i++) {
1457                 const float *v = cmesh_attrib_at_ro(cm, CMESH_ATTR_VERTEX, i);
1458                 cm->bsph_center.x += v[0];
1459                 cm->bsph_center.y += v[1];
1460                 cm->bsph_center.z += v[2];
1461         }
1462         s = 1.0f / (float)cm->nverts;
1463         cm->bsph_center.x *= s;
1464         cm->bsph_center.y *= s;
1465         cm->bsph_center.z *= s;
1466
1467         cm->bsph_radius = 0.0f;
1468         for(i=0; i<cm->nverts; i++) {
1469                 const cgm_vec3 *v = (const cgm_vec3*)cmesh_attrib_at_ro(cm, CMESH_ATTR_VERTEX, i);
1470                 if((dist_sq = cgm_vdist_sq(v, &cm->bsph_center)) > cm->bsph_radius) {
1471                         cm->bsph_radius = dist_sq;
1472                 }
1473         }
1474         cm->bsph_radius = sqrt(cm->bsph_radius);
1475         cm->bsph_valid = 1;
1476 }
1477
1478 float cmesh_bsphere(struct cmesh *cm, cgm_vec3 *center, float *rad)
1479 {
1480         if(!cm->bsph_valid) {
1481                 calc_bsph(cm);
1482         }
1483         *center = cm->bsph_center;
1484         *rad = cm->bsph_radius;
1485         return cm->bsph_radius;
1486 }
1487
1488 /* TODO */
1489 void cmesh_texcoord_apply_xform(struct cmesh *cm, float *xform);
1490 void cmesh_texcoord_gen_plane(struct cmesh *cm, cgm_vec3 *norm, cgm_vec3 *tang);
1491 void cmesh_texcoord_gen_box(struct cmesh *cm);
1492 void cmesh_texcoord_gen_cylinder(struct cmesh *cm);
1493
1494 int cmesh_dump(struct cmesh *cm, const char *fname)
1495 {
1496         FILE *fp = fopen(fname, "wb");
1497         if(fp) {
1498                 int res = cmesh_dump_file(cm, fp);
1499                 fclose(fp);
1500                 return res;
1501         }
1502         return -1;
1503 }
1504
1505 int cmesh_dump_file(struct cmesh *cm, FILE *fp)
1506 {
1507         static const char *label[] = { "pos", "nor", "tan", "tex", "col", "bw", "bid", "tex2" };
1508         static const char *elemfmt[] = { 0, " %s(%g)", " %s(%g, %g)", " %s(%g, %g, %g)", " %s(%g, %g, %g, %g)", 0 };
1509         int i, j;
1510
1511         if(!cmesh_has_attrib(cm, CMESH_ATTR_VERTEX)) {
1512                 return -1;
1513         }
1514
1515         fprintf(fp, "VERTEX ATTRIBUTES\n");
1516
1517         for(i=0; i<cm->nverts; i++) {
1518                 fprintf(fp, "%5u:", i);
1519                 for(j=0; j<CMESH_NUM_ATTR; j++) {
1520                         if(cmesh_has_attrib(cm, j)) {
1521                                 const float *v = cmesh_attrib_at_ro(cm, j, i);
1522                                 int nelem = cm->vattr[j].nelem;
1523                                 fprintf(fp, elemfmt[nelem], label[j], v[0], nelem > 1 ? v[1] : 0.0f,
1524                                                 nelem > 2 ? v[2] : 0.0f, nelem > 3 ? v[3] : 0.0f);
1525                         }
1526                 }
1527                 fputc('\n', fp);
1528         }
1529
1530         if(cmesh_indexed(cm)) {
1531                 const unsigned int *idx = cmesh_index_ro(cm);
1532                 int numidx = cmesh_index_count(cm);
1533                 int numtri = numidx / 3;
1534                 assert(numidx % 3 == 0);
1535
1536                 fprintf(fp, "FACES\n");
1537
1538                 for(i=0; i<numtri; i++) {
1539                         fprintf(fp, "%5d: %d %d %d\n", i, idx[0], idx[1], idx[2]);
1540                         idx += 3;
1541                 }
1542         }
1543         return 0;
1544 }
1545
1546 int cmesh_dump_obj(struct cmesh *cm, const char *fname)
1547 {
1548         FILE *fp = fopen(fname, "wb");
1549         if(fp) {
1550                 int res = cmesh_dump_obj_file(cm, fp, 0);
1551                 fclose(fp);
1552                 return res;
1553         }
1554         return -1;
1555 }
1556
1557 #define HAS_VN  1
1558 #define HAS_VT  2
1559
1560 int cmesh_dump_obj_file(struct cmesh *cm, FILE *fp, int voffs)
1561 {
1562         static const char *fmtstr[] = {" %u", " %u//%u", " %u/%u", " %u/%u/%u"};
1563         int i, j, num, nelem;
1564         unsigned int aflags = 0;
1565
1566         if(!cmesh_has_attrib(cm, CMESH_ATTR_VERTEX)) {
1567                 return -1;
1568         }
1569
1570
1571         nelem = cm->vattr[CMESH_ATTR_VERTEX].nelem;
1572         if((num = cm->vattr[CMESH_ATTR_VERTEX].count) != cm->nverts * nelem) {
1573                 fprintf(stderr, "vertex array size (%d) != nverts (%d)\n", num, cm->nverts);
1574         }
1575         for(i=0; i<cm->nverts; i++) {
1576                 const float *v = cmesh_attrib_at_ro(cm, CMESH_ATTR_VERTEX, i);
1577                 fprintf(fp, "v %f %f %f\n", v[0], nelem > 1 ? v[1] : 0.0f, nelem > 2 ? v[2] : 0.0f);
1578         }
1579
1580         if(cmesh_has_attrib(cm, CMESH_ATTR_NORMAL)) {
1581                 aflags |= HAS_VN;
1582                 nelem = cm->vattr[CMESH_ATTR_NORMAL].nelem;
1583                 if((num = cm->vattr[CMESH_ATTR_NORMAL].count) != cm->nverts * nelem) {
1584                         fprintf(stderr, "normal array size (%d) != nverts (%d)\n", num, cm->nverts);
1585                 }
1586                 for(i=0; i<cm->nverts; i++) {
1587                         const float *v = cmesh_attrib_at_ro(cm, CMESH_ATTR_NORMAL, i);
1588                         fprintf(fp, "vn %f %f %f\n", v[0], nelem > 1 ? v[1] : 0.0f, nelem > 2 ? v[2] : 0.0f);
1589                 }
1590         }
1591
1592         if(cmesh_has_attrib(cm, CMESH_ATTR_TEXCOORD)) {
1593                 aflags |= HAS_VT;
1594                 nelem = cm->vattr[CMESH_ATTR_TEXCOORD].nelem;
1595                 if((num = cm->vattr[CMESH_ATTR_TEXCOORD].count) != cm->nverts * nelem) {
1596                         fprintf(stderr, "texcoord array size (%d) != nverts (%d)\n", num, cm->nverts);
1597                 }
1598                 for(i=0; i<cm->nverts; i++) {
1599                         const float *v = cmesh_attrib_at_ro(cm, CMESH_ATTR_TEXCOORD, i);
1600                         fprintf(fp, "vt %f %f\n", v[0], nelem > 1 ? v[1] : 0.0f);
1601                 }
1602         }
1603
1604         if(cmesh_indexed(cm)) {
1605                 const unsigned int *idxptr = cmesh_index_ro(cm);
1606                 int numidx = cmesh_index_count(cm);
1607                 int numtri = numidx / 3;
1608                 assert(numidx % 3 == 0);
1609
1610                 for(i=0; i<numtri; i++) {
1611                         fputc('f', fp);
1612                         for(j=0; j<3; j++) {
1613                                 unsigned int idx = *idxptr++ + 1 + voffs;
1614                                 fprintf(fp, fmtstr[aflags], idx, idx, idx);
1615                         }
1616                         fputc('\n', fp);
1617                 }
1618         } else {
1619                 int numtri = cm->nverts / 3;
1620                 unsigned int idx = 1 + voffs;
1621                 for(i=0; i<numtri; i++) {
1622                         fputc('f', fp);
1623                         for(j=0; j<3; j++) {
1624                                 fprintf(fp, fmtstr[aflags], idx, idx, idx);
1625                                 ++idx;
1626                         }
1627                         fputc('\n', fp);
1628                 }
1629         }
1630         return 0;
1631 }