foo
[retroray] / src / cmesh.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdint.h>
4 #include <limits.h>
5 #include <float.h>
6 #include <assert.h>
7 #include "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                                 vptr = verts + (i + 1) * nelem + j;
1122                                 float tmp = vptr[nelem];
1123                                 vptr[nelem] = vptr[0];
1124                                 vptr[0] = tmp;
1125                         }
1126                 }
1127         }
1128 }
1129 void cmesh_flip_normals(struct cmesh *cm)
1130 {
1131         int i, num;
1132         float *nptr = cmesh_attrib(cm, CMESH_ATTR_NORMAL);
1133         if(!nptr) return;
1134
1135         num = cm->nverts * cm->vattr[CMESH_ATTR_NORMAL].nelem;
1136         for(i=0; i<num; i++) {
1137                 *nptr = -*nptr;
1138                 nptr++;
1139         }
1140 }
1141
1142 int cmesh_explode(struct cmesh *cm)
1143 {
1144         int i, j, k, idxnum, nnverts;
1145         unsigned int *indices;
1146
1147         if(!cmesh_indexed(cm)) return 0;
1148
1149         indices = cmesh_index(cm);
1150         assert(indices);
1151
1152         idxnum = cmesh_index_count(cm);
1153         nnverts = idxnum;
1154
1155         for(i=0; i<CMESH_NUM_ATTR; i++) {
1156                 const float *srcbuf;
1157                 float *tmpbuf, *dstptr;
1158
1159                 if(!cmesh_has_attrib(cm, i)) continue;
1160
1161                 srcbuf = cmesh_attrib(cm, i);
1162                 if(!(tmpbuf = malloc(nnverts * cm->vattr[i].nelem * sizeof(float)))) {
1163                         return -1;
1164                 }
1165                 dstptr = tmpbuf;
1166
1167                 for(j=0; j<idxnum; j++) {
1168                         unsigned int idx = indices[j];
1169                         const float *srcptr = srcbuf + idx * cm->vattr[i].nelem;
1170
1171                         for(k=0; k<cm->vattr[i].nelem; k++) {
1172                                 *dstptr++ = *srcptr++;
1173                         }
1174                 }
1175
1176                 free(cm->vattr[i].data);
1177                 cm->vattr[i].data = tmpbuf;
1178                 cm->vattr[i].count = nnverts * cm->vattr[i].nelem;
1179                 cm->vattr[i].data_valid = 1;
1180         }
1181
1182 #ifdef USE_VBO
1183         cm->ibo_valid = 0;
1184 #endif
1185 #ifdef USE_DLIST
1186         if(cm->dlist) {
1187                 gaw_free_compiled(cm->dlist);
1188                 cm->dlist = 0;
1189         }
1190 #endif
1191         cm->idata_valid = 0;
1192         free(cm->idata);
1193         cm->idata = 0;
1194         cm->icount = 0;
1195
1196         cm->nverts = nnverts;
1197         cm->nfaces = idxnum / 3;
1198         return 0;
1199 }
1200
1201 void cmesh_calc_face_normals(struct cmesh *cm)
1202 {
1203         /* TODO */
1204 }
1205
1206 static int pre_draw(const struct cmesh *cm, int start)
1207 {
1208         int cur_sdr;
1209
1210 #ifdef USE_SDR
1211         int i, loc;
1212         glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr);
1213 #else
1214         cur_sdr = 0;
1215 #endif
1216
1217         update_buffers((struct cmesh*)cm);
1218
1219 #ifdef USE_VBO
1220         if(!cm->vattr[CMESH_ATTR_VERTEX].vbo_valid) {
1221                 return -1;
1222         }
1223
1224 #ifdef USE_SDR
1225         if(cur_sdr && use_custom_sdr_attr) {
1226                 if(sdr_loc[CMESH_ATTR_VERTEX] == -1) {
1227                         return -1;
1228                 }
1229
1230                 for(i=0; i<CMESH_NUM_ATTR; i++) {
1231                         loc = sdr_loc[i];
1232                         if(loc >= 0 && cm->vattr[i].vbo_valid) {
1233                                 glBindBuffer(GL_ARRAY_BUFFER, cm->vattr[i].vbo);
1234                                 glVertexAttribPointer(loc, cm->vattr[i].nelem, GL_FLOAT, GL_FALSE, 0, 0);
1235                                 glEnableVertexAttribArray(loc);
1236                         }
1237                 }
1238         } else {
1239 #endif  /* USE_SDR */
1240 #ifndef GL_ES_VERSION_2_0
1241                 glBindBuffer(GL_ARRAY_BUFFER, cm->vattr[CMESH_ATTR_VERTEX].vbo);
1242                 glVertexPointer(cm->vattr[CMESH_ATTR_VERTEX].nelem, GL_FLOAT, 0, 0);
1243                 glEnableClientState(GL_VERTEX_ARRAY);
1244
1245                 if(cm->vattr[CMESH_ATTR_NORMAL].vbo_valid) {
1246                         glBindBuffer(GL_ARRAY_BUFFER, cm->vattr[CMESH_ATTR_NORMAL].vbo);
1247                         glNormalPointer(GL_FLOAT, 0, 0);
1248                         glEnableClientState(GL_NORMAL_ARRAY);
1249                 }
1250                 if(cm->vattr[CMESH_ATTR_TEXCOORD].vbo_valid) {
1251                         glBindBuffer(GL_ARRAY_BUFFER, cm->vattr[CMESH_ATTR_TEXCOORD].vbo);
1252                         glTexCoordPointer(cm->vattr[CMESH_ATTR_TEXCOORD].nelem, GL_FLOAT, 0, 0);
1253                         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1254                 }
1255                 if(cm->vattr[CMESH_ATTR_COLOR].vbo_valid) {
1256                         glBindBuffer(GL_ARRAY_BUFFER, cm->vattr[CMESH_ATTR_COLOR].vbo);
1257                         glColorPointer(cm->vattr[CMESH_ATTR_COLOR].nelem, GL_FLOAT, 0, 0);
1258                         glEnableClientState(GL_COLOR_ARRAY);
1259                 }
1260                 if(cm->vattr[CMESH_ATTR_TEXCOORD2].vbo_valid) {
1261                         glClientActiveTexture(GL_TEXTURE1);
1262                         glBindBuffer(GL_ARRAY_BUFFER, cm->vattr[CMESH_ATTR_TEXCOORD2].vbo);
1263                         glTexCoordPointer(cm->vattr[CMESH_ATTR_TEXCOORD2].nelem, GL_FLOAT, 0, 0);
1264                         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1265                         glClientActiveTexture(GL_TEXTURE0);
1266                 }
1267 #endif  /* GL_ES_VERSION_2_0 */
1268 #ifdef USE_SDR
1269         }
1270 #endif
1271         glBindBuffer(GL_ARRAY_BUFFER, 0);
1272
1273 #else   /* !USE_VBO */
1274
1275 #ifdef USE_DLIST
1276         if(cm->dlist && !start) {
1277                 return cur_sdr;
1278         }
1279 #endif
1280         {
1281                 const struct cmesh_vattrib *vattr;
1282
1283                 vattr = cm->vattr + CMESH_ATTR_VERTEX;
1284                 gaw_vertex_array(vattr->nelem, 0, vattr->data + start * vattr->nelem);
1285
1286                 vattr = cm->vattr + CMESH_ATTR_NORMAL;
1287                 if(vattr->data_valid) {
1288                         gaw_normal_array(0, vattr->data + start * 3);
1289                 }
1290
1291                 vattr = cm->vattr + CMESH_ATTR_TEXCOORD;
1292                 if(vattr->data_valid) {
1293                         gaw_texcoord_array(vattr->nelem, 0, vattr->data + start * vattr->nelem);
1294                 }
1295
1296                 vattr = cm->vattr + CMESH_ATTR_COLOR;
1297                 if(vattr->data_valid) {
1298                         gaw_color_array(vattr->nelem, 0, vattr->data + start * vattr->nelem);
1299                 }
1300         }
1301 #endif  /* !USE_VBO */
1302         return cur_sdr;
1303 }
1304
1305 void cmesh_draw(const struct cmesh *cm)
1306 {
1307         int cur_sdr;
1308
1309         if((cur_sdr = pre_draw(cm, 0)) == -1) {
1310                 return;
1311         }
1312
1313 #ifdef USE_VBO
1314         if(cm->ibo_valid) {
1315                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cm->ibo);
1316                 glDrawElements(GL_TRIANGLES, cm->nfaces * 3, GL_UNSIGNED_INT, 0);
1317                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1318         } else {
1319                 glDrawArrays(GL_TRIANGLES, 0, cm->nverts);
1320         }
1321 #else
1322 #ifdef USE_DLIST
1323         if(cm->dlist) {
1324                 gaw_draw_compiled(cm->dlist);
1325         } else
1326 #endif
1327         if(cm->idata_valid) {
1328                 /*glDrawElements(GL_TRIANGLES, cm->nfaces * 3, GL_UNSIGNED_INT, cm->idata);*/
1329                 gaw_draw_indexed(GAW_TRIANGLES, cm->idata, cm->nfaces * 3);
1330         } else {
1331                 /*glDrawArrays(GL_TRIANGLES, 0, cm->nverts);*/
1332                 gaw_draw(GAW_TRIANGLES, cm->nverts);
1333         }
1334 #endif
1335
1336         post_draw(cm, cur_sdr);
1337 }
1338
1339 void cmesh_draw_range(const struct cmesh *cm, int start, int count)
1340 {
1341         int cur_sdr;
1342
1343         if((cur_sdr = pre_draw(cm, start)) == -1) {
1344                 return;
1345         }
1346
1347 #ifdef USE_VBO
1348         if(cm->ibo_valid) {
1349                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cm->ibo);
1350                 glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, (void*)(intptr_t)(start * 4));
1351                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1352         } else {
1353                 glDrawArrays(GL_TRIANGLES, start, count);
1354         }
1355 #else
1356         if(cm->idata_valid) {
1357                 gaw_draw_indexed(GAW_TRIANGLES, cm->idata + start, count);
1358         } else {
1359                 gaw_draw(GAW_TRIANGLES, count);
1360         }
1361 #endif
1362
1363         post_draw(cm, cur_sdr);
1364 }
1365
1366 void cmesh_draw_submesh(const struct cmesh *cm, int subidx)
1367 {
1368         struct submesh *sm = cm->sublist;
1369
1370         while(sm && subidx-- > 0) {
1371                 sm = sm->next;
1372         }
1373         if(!sm) return;
1374
1375         if(sm->icount) {
1376                 cmesh_draw_range(cm, sm->istart, sm->icount);
1377         } else {
1378                 cmesh_draw_range(cm, sm->vstart, sm->vcount);
1379         }
1380 }
1381
1382 static void post_draw(const struct cmesh *cm, int cur_sdr)
1383 {
1384 #ifdef USE_VBO
1385 #ifdef USE_SDR
1386         if(cur_sdr && use_custom_sdr_attr) {
1387                 int i;
1388                 for(i=0; i<CMESH_NUM_ATTR; i++) {
1389                         int loc = sdr_loc[i];
1390                         if(loc >= 0 && cm->vattr[i].vbo_valid) {
1391                                 glDisableVertexAttribArray(loc);
1392                         }
1393                 }
1394         } else {
1395 #endif  /* USE_SDR */
1396 #ifndef GL_ES_VERSION_2_0
1397                 glDisableClientState(GL_VERTEX_ARRAY);
1398                 if(cm->vattr[CMESH_ATTR_NORMAL].vbo_valid) {
1399                         glDisableClientState(GL_NORMAL_ARRAY);
1400                 }
1401                 if(cm->vattr[CMESH_ATTR_TEXCOORD].vbo_valid) {
1402                         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1403                 }
1404                 if(cm->vattr[CMESH_ATTR_COLOR].vbo_valid) {
1405                         glDisableClientState(GL_COLOR_ARRAY);
1406                 }
1407                 if(cm->vattr[CMESH_ATTR_TEXCOORD2].vbo_valid) {
1408                         glClientActiveTexture(GL_TEXTURE1);
1409                         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1410                         glClientActiveTexture(GL_TEXTURE0);
1411                 }
1412 #endif  /* GL_ES_VERSION_2_0 */
1413 #ifdef USE_SDR
1414         }
1415 #endif
1416 #else   /* !USE_VBO */
1417         gaw_vertex_array(0, 0, 0);
1418         gaw_normal_array(0, 0);
1419         gaw_texcoord_array(0, 0, 0);
1420         gaw_color_array(0, 0, 0);
1421 #endif
1422 }
1423
1424 void cmesh_draw_wire(const struct cmesh *cm, float linesz)
1425 {
1426 #ifdef USE_VBO
1427         int cur_sdr, nfaces;
1428
1429         if((cur_sdr = pre_draw(cm, 0)) == -1) {
1430                 return;
1431         }
1432         update_wire_ibo((struct cmesh*)cm);
1433
1434         nfaces = cmesh_poly_count(cm);
1435         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cm->wire_ibo);
1436         glDrawElements(GL_LINES, nfaces * 6, GL_UNSIGNED_INT, 0);
1437         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1438
1439         post_draw(cm, cur_sdr);
1440 #endif
1441 }
1442
1443 void cmesh_draw_vertices(const struct cmesh *cm, float ptsz)
1444 {
1445         int cur_sdr;
1446         if((cur_sdr = pre_draw(cm, 0)) == -1) {
1447                 return;
1448         }
1449
1450         /*
1451         glPushAttrib(GL_POINT_BIT);
1452         glPointSize(ptsz);
1453         glDrawArrays(GL_POINTS, 0, cm->nverts);
1454         glPopAttrib();
1455         */
1456         gaw_draw(GAW_POINTS, cm->nverts);
1457
1458         post_draw(cm, cur_sdr);
1459 }
1460
1461 void cmesh_draw_normals(const struct cmesh *cm, float len)
1462 {
1463 #ifndef GL_ES_VERSION_2_0
1464         int i, vert_nelem, norm_nelem;
1465 #ifdef USE_SDR
1466         int cur_sdr, loc = -1;
1467 #endif
1468         const float *varr, *norm;
1469
1470         varr = cmesh_attrib_ro(cm, CMESH_ATTR_VERTEX);
1471         norm = cmesh_attrib_ro(cm, CMESH_ATTR_NORMAL);
1472         if(!varr || !norm) return;
1473
1474         vert_nelem = cm->vattr[CMESH_ATTR_VERTEX].nelem;
1475         norm_nelem = cm->vattr[CMESH_ATTR_NORMAL].nelem;
1476
1477 #ifdef USE_SDR
1478         glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr);
1479         if(cur_sdr && use_custom_sdr_attr) {
1480                 if((loc = sdr_loc[CMESH_ATTR_VERTEX]) < 0) {
1481                         return;
1482                 }
1483         }
1484 #endif
1485
1486         gaw_begin(GAW_LINES);
1487         for(i=0; i<cm->nverts; i++) {
1488                 float x, y, z, endx, endy, endz;
1489
1490                 x = varr[i * vert_nelem];
1491                 y = varr[i * vert_nelem + 1];
1492                 z = varr[i * vert_nelem + 2];
1493                 endx = x + norm[i * norm_nelem] * len;
1494                 endy = y + norm[i * norm_nelem + 1] * len;
1495                 endz = z + norm[i * norm_nelem + 2] * len;
1496
1497 #ifdef USE_SDR
1498                 if(loc == -1) {
1499 #endif
1500                         gaw_vertex3f(x, y, z);
1501                         gaw_vertex3f(endx, endy, endz);
1502 #ifdef USE_SDR
1503                 } else {
1504                         glVertexAttrib3f(loc, x, y, z);
1505                         glVertexAttrib3f(loc, endx, endy, endz);
1506                 }
1507 #endif
1508         }
1509         gaw_end();
1510 #endif  /* GL_ES_VERSION_2_0 */
1511 }
1512
1513 void cmesh_draw_tangents(const struct cmesh *cm, float len)
1514 {
1515 #ifndef GL_ES_VERSION_2_0
1516         int i, vert_nelem, tang_nelem;
1517 #ifdef USE_SDR
1518         int cur_sdr, loc = -1;
1519 #endif
1520         const float *varr, *tang;
1521
1522         varr = cmesh_attrib_ro(cm, CMESH_ATTR_VERTEX);
1523         tang = cmesh_attrib_ro(cm, CMESH_ATTR_TANGENT);
1524         if(!varr || !tang) return;
1525
1526         vert_nelem = cm->vattr[CMESH_ATTR_VERTEX].nelem;
1527         tang_nelem = cm->vattr[CMESH_ATTR_TANGENT].nelem;
1528
1529 #ifdef USE_SDR
1530         glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr);
1531         if(cur_sdr && use_custom_sdr_attr) {
1532                 if((loc = sdr_loc[CMESH_ATTR_VERTEX]) < 0) {
1533                         return;
1534                 }
1535         }
1536 #endif
1537
1538         gaw_begin(GAW_LINES);
1539         for(i=0; i<cm->nverts; i++) {
1540                 float x, y, z, endx, endy, endz;
1541
1542                 x = varr[i * vert_nelem];
1543                 y = varr[i * vert_nelem + 1];
1544                 z = varr[i * vert_nelem + 2];
1545                 endx = x + tang[i * tang_nelem] * len;
1546                 endy = y + tang[i * tang_nelem + 1] * len;
1547                 endz = z + tang[i * tang_nelem + 2] * len;
1548
1549 #ifdef USE_SDR
1550                 if(loc == -1) {
1551 #endif
1552                         gaw_vertex3f(x, y, z);
1553                         gaw_vertex3f(endx, endy, endz);
1554 #ifdef USE_SDR
1555                 } else {
1556                         glVertexAttrib3f(loc, x, y, z);
1557                         glVertexAttrib3f(loc, endx, endy, endz);
1558                 }
1559 #endif
1560         }
1561         gaw_end();
1562 #endif  /* GL_ES_VERSION_2_0 */
1563 }
1564
1565 static void update_buffers(struct cmesh *cm)
1566 {
1567 #ifdef USE_VBO
1568         int i;
1569         for(i=0; i<CMESH_NUM_ATTR; i++) {
1570                 if(cmesh_has_attrib(cm, i) && !cm->vattr[i].vbo_valid) {
1571                         glBindBuffer(GL_ARRAY_BUFFER, cm->vattr[i].vbo);
1572                         glBufferData(GL_ARRAY_BUFFER, cm->nverts * cm->vattr[i].nelem * sizeof(float),
1573                                         cm->vattr[i].data, GL_STATIC_DRAW);
1574                         cm->vattr[i].vbo_valid = 1;
1575                 }
1576         }
1577         glBindBuffer(GL_ARRAY_BUFFER, 0);
1578
1579         if(cm->idata_valid && !cm->ibo_valid) {
1580                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cm->ibo);
1581                 glBufferData(GL_ELEMENT_ARRAY_BUFFER, cm->nfaces * 3 * sizeof(unsigned int),
1582                                 cm->idata, GL_STATIC_DRAW);
1583                 cm->ibo_valid = 1;
1584                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1585         }
1586
1587 #elif defined(USE_DLIST)
1588         static int updating;
1589
1590         if(!cm->dlist && !updating) {
1591                 int dlist = gaw_compile_begin();
1592                 updating = 1;
1593                 cmesh_draw(cm);
1594                 updating = 0;
1595                 gaw_compile_end();
1596                 cm->dlist = dlist;
1597         }
1598 #endif
1599 }
1600
1601 #ifdef USE_VBO
1602 static void update_wire_ibo(struct cmesh *cm)
1603 {
1604         int i, num_faces;
1605         unsigned int *wire_idxarr, *dest;
1606
1607         update_buffers(cm);
1608
1609         if(cm->wire_ibo_valid) return;
1610
1611         if(!cm->wire_ibo) {
1612                 glGenBuffers(1, &cm->wire_ibo);
1613         }
1614         num_faces = cmesh_poly_count(cm);
1615
1616         if(!(wire_idxarr = malloc(num_faces * 6 * sizeof *wire_idxarr))) {
1617                 return;
1618         }
1619         dest = wire_idxarr;
1620
1621         if(cm->ibo_valid) {
1622                 /* we're dealing with an indexed mesh */
1623                 const unsigned int *idxarr = cmesh_index_ro(cm);
1624
1625                 for(i=0; i<num_faces; i++) {
1626                         *dest++ = idxarr[0];
1627                         *dest++ = idxarr[1];
1628                         *dest++ = idxarr[1];
1629                         *dest++ = idxarr[2];
1630                         *dest++ = idxarr[2];
1631                         *dest++ = idxarr[0];
1632                         idxarr += 3;
1633                 }
1634         } else {
1635                 /* not an indexed mesh */
1636                 for(i=0; i<num_faces; i++) {
1637                         int vidx = i * 3;
1638                         *dest++ = vidx;
1639                         *dest++ = vidx + 1;
1640                         *dest++ = vidx + 1;
1641                         *dest++ = vidx + 2;
1642                         *dest++ = vidx + 2;
1643                         *dest++ = vidx;
1644                 }
1645         }
1646
1647         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cm->wire_ibo);
1648         glBufferData(GL_ELEMENT_ARRAY_BUFFER, num_faces * 6 * sizeof(unsigned int),
1649                         wire_idxarr, GL_STATIC_DRAW);
1650         free(wire_idxarr);
1651         cm->wire_ibo_valid = 1;
1652         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1653 }
1654 #endif  /* USE_VBO */
1655
1656 static void calc_aabb(struct cmesh *cm)
1657 {
1658         int i, j;
1659
1660         if(!cmesh_attrib_ro(cm, CMESH_ATTR_VERTEX)) {
1661                 return;
1662         }
1663
1664         cgm_vcons(&cm->aabb_min, FLT_MAX, FLT_MAX, FLT_MAX);
1665         cgm_vcons(&cm->aabb_max, -FLT_MAX, -FLT_MAX, -FLT_MAX);
1666
1667         for(i=0; i<cm->nverts; i++) {
1668                 const float *v = cmesh_attrib_at_ro(cm, CMESH_ATTR_VERTEX, i);
1669                 for(j=0; j<3; j++) {
1670                         if(v[j] < (&cm->aabb_min.x)[j]) {
1671                                 (&cm->aabb_min.x)[j] = v[j];
1672                         }
1673                         if(v[j] > (&cm->aabb_max.x)[j]) {
1674                                 (&cm->aabb_max.x)[j] = v[j];
1675                         }
1676                 }
1677         }
1678         cm->aabb_valid = 1;
1679 }
1680
1681 void cmesh_aabbox(const struct cmesh *cm, cgm_vec3 *vmin, cgm_vec3 *vmax)
1682 {
1683         if(!cm->aabb_valid) {
1684                 calc_aabb((struct cmesh*)cm);
1685         }
1686         *vmin = cm->aabb_min;
1687         *vmax = cm->aabb_max;
1688 }
1689
1690 static void calc_bsph(struct cmesh *cm)
1691 {
1692         int i;
1693         float s, dist_sq;
1694
1695         if(!cmesh_attrib_ro(cm, CMESH_ATTR_VERTEX)) {
1696                 return;
1697         }
1698
1699         cgm_vcons(&cm->bsph_center, 0, 0, 0);
1700
1701         /* first find the center */
1702         for(i=0; i<cm->nverts; i++) {
1703                 const float *v = cmesh_attrib_at_ro(cm, CMESH_ATTR_VERTEX, i);
1704                 cm->bsph_center.x += v[0];
1705                 cm->bsph_center.y += v[1];
1706                 cm->bsph_center.z += v[2];
1707         }
1708         s = 1.0f / (float)cm->nverts;
1709         cm->bsph_center.x *= s;
1710         cm->bsph_center.y *= s;
1711         cm->bsph_center.z *= s;
1712
1713         cm->bsph_radius = 0.0f;
1714         for(i=0; i<cm->nverts; i++) {
1715                 const cgm_vec3 *v = (const cgm_vec3*)cmesh_attrib_at_ro(cm, CMESH_ATTR_VERTEX, i);
1716                 if((dist_sq = cgm_vdist_sq(v, &cm->bsph_center)) > cm->bsph_radius) {
1717                         cm->bsph_radius = dist_sq;
1718                 }
1719         }
1720         cm->bsph_radius = sqrt(cm->bsph_radius);
1721         cm->bsph_valid = 1;
1722 }
1723
1724 float cmesh_bsphere(const struct cmesh *cm, cgm_vec3 *center, float *rad)
1725 {
1726         if(!cm->bsph_valid) {
1727                 calc_bsph((struct cmesh*)cm);
1728         }
1729         if(center) *center = cm->bsph_center;
1730         if(rad) *rad = cm->bsph_radius;
1731         return cm->bsph_radius;
1732 }
1733
1734 /* TODO */
1735 void cmesh_texcoord_apply_xform(struct cmesh *cm, float *xform);
1736 void cmesh_texcoord_gen_plane(struct cmesh *cm, cgm_vec3 *norm, cgm_vec3 *tang);
1737 void cmesh_texcoord_gen_box(struct cmesh *cm);
1738 void cmesh_texcoord_gen_cylinder(struct cmesh *cm);
1739
1740 int cmesh_dump(const struct cmesh *cm, const char *fname)
1741 {
1742         FILE *fp = fopen(fname, "wb");
1743         if(fp) {
1744                 int res = cmesh_dump_file(cm, fp);
1745                 fclose(fp);
1746                 return res;
1747         }
1748         return -1;
1749 }
1750
1751 int cmesh_dump_file(const struct cmesh *cm, FILE *fp)
1752 {
1753         static const char *label[] = { "pos", "nor", "tan", "tex", "col", "bw", "bid", "tex2" };
1754         static const char *elemfmt[] = { 0, " %s(%g)", " %s(%g, %g)", " %s(%g, %g, %g)", " %s(%g, %g, %g, %g)", 0 };
1755         int i, j;
1756
1757         if(!cmesh_has_attrib(cm, CMESH_ATTR_VERTEX)) {
1758                 return -1;
1759         }
1760
1761         fprintf(fp, "VERTEX ATTRIBUTES\n");
1762
1763         for(i=0; i<cm->nverts; i++) {
1764                 fprintf(fp, "%5u:", i);
1765                 for(j=0; j<CMESH_NUM_ATTR; j++) {
1766                         if(cmesh_has_attrib(cm, j)) {
1767                                 const float *v = cmesh_attrib_at_ro(cm, j, i);
1768                                 int nelem = cm->vattr[j].nelem;
1769                                 fprintf(fp, elemfmt[nelem], label[j], v[0], nelem > 1 ? v[1] : 0.0f,
1770                                                 nelem > 2 ? v[2] : 0.0f, nelem > 3 ? v[3] : 0.0f);
1771                         }
1772                 }
1773                 fputc('\n', fp);
1774         }
1775
1776         if(cmesh_indexed(cm)) {
1777                 const unsigned int *idx = cmesh_index_ro(cm);
1778                 int numidx = cmesh_index_count(cm);
1779                 int numtri = numidx / 3;
1780                 assert(numidx % 3 == 0);
1781
1782                 fprintf(fp, "FACES\n");
1783
1784                 for(i=0; i<numtri; i++) {
1785                         fprintf(fp, "%5d: %d %d %d\n", i, idx[0], idx[1], idx[2]);
1786                         idx += 3;
1787                 }
1788         }
1789         return 0;
1790 }
1791
1792 int cmesh_dump_obj(const struct cmesh *cm, const char *fname)
1793 {
1794         FILE *fp = fopen(fname, "wb");
1795         if(fp) {
1796                 int res = cmesh_dump_obj_file(cm, fp, 0);
1797                 fclose(fp);
1798                 return res;
1799         }
1800         return -1;
1801 }
1802
1803 #define HAS_VN  1
1804 #define HAS_VT  2
1805
1806 int cmesh_dump_obj_file(const struct cmesh *cm, FILE *fp, int voffs)
1807 {
1808         static const char *fmtstr[] = {" %u", " %u//%u", " %u/%u", " %u/%u/%u"};
1809         int i, j, num, nelem;
1810         unsigned int aflags = 0;
1811
1812         if(!cmesh_has_attrib(cm, CMESH_ATTR_VERTEX)) {
1813                 return -1;
1814         }
1815
1816
1817         nelem = cm->vattr[CMESH_ATTR_VERTEX].nelem;
1818         if((num = cm->vattr[CMESH_ATTR_VERTEX].count) != cm->nverts * nelem) {
1819                 fprintf(stderr, "vertex array size (%d) != nverts (%d)\n", num, cm->nverts);
1820         }
1821         for(i=0; i<cm->nverts; i++) {
1822                 const float *v = cmesh_attrib_at_ro(cm, CMESH_ATTR_VERTEX, i);
1823                 fprintf(fp, "v %f %f %f\n", v[0], nelem > 1 ? v[1] : 0.0f, nelem > 2 ? v[2] : 0.0f);
1824         }
1825
1826         if(cmesh_has_attrib(cm, CMESH_ATTR_NORMAL)) {
1827                 aflags |= HAS_VN;
1828                 nelem = cm->vattr[CMESH_ATTR_NORMAL].nelem;
1829                 if((num = cm->vattr[CMESH_ATTR_NORMAL].count) != cm->nverts * nelem) {
1830                         fprintf(stderr, "normal array size (%d) != nverts (%d)\n", num, cm->nverts);
1831                 }
1832                 for(i=0; i<cm->nverts; i++) {
1833                         const float *v = cmesh_attrib_at_ro(cm, CMESH_ATTR_NORMAL, i);
1834                         fprintf(fp, "vn %f %f %f\n", v[0], nelem > 1 ? v[1] : 0.0f, nelem > 2 ? v[2] : 0.0f);
1835                 }
1836         }
1837
1838         if(cmesh_has_attrib(cm, CMESH_ATTR_TEXCOORD)) {
1839                 aflags |= HAS_VT;
1840                 nelem = cm->vattr[CMESH_ATTR_TEXCOORD].nelem;
1841                 if((num = cm->vattr[CMESH_ATTR_TEXCOORD].count) != cm->nverts * nelem) {
1842                         fprintf(stderr, "texcoord array size (%d) != nverts (%d)\n", num, cm->nverts);
1843                 }
1844                 for(i=0; i<cm->nverts; i++) {
1845                         const float *v = cmesh_attrib_at_ro(cm, CMESH_ATTR_TEXCOORD, i);
1846                         fprintf(fp, "vt %f %f\n", v[0], nelem > 1 ? v[1] : 0.0f);
1847                 }
1848         }
1849
1850         if(cmesh_indexed(cm)) {
1851                 const unsigned int *idxptr = cmesh_index_ro(cm);
1852                 int numidx = cmesh_index_count(cm);
1853                 int numtri = numidx / 3;
1854                 assert(numidx % 3 == 0);
1855
1856                 for(i=0; i<numtri; i++) {
1857                         fputc('f', fp);
1858                         for(j=0; j<3; j++) {
1859                                 unsigned int idx = *idxptr++ + 1 + voffs;
1860                                 fprintf(fp, fmtstr[aflags], idx, idx, idx);
1861                         }
1862                         fputc('\n', fp);
1863                 }
1864         } else {
1865                 int numtri = cm->nverts / 3;
1866                 unsigned int idx = 1 + voffs;
1867                 for(i=0; i<numtri; i++) {
1868                         fputc('f', fp);
1869                         for(j=0; j<3; j++) {
1870                                 fprintf(fp, fmtstr[aflags], idx, idx, idx);
1871                                 ++idx;
1872                         }
1873                         fputc('\n', fp);
1874                 }
1875         }
1876         return 0;
1877 }