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