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