cybergrid
[vrfileman] / src / mesh.cc
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <float.h>
4 #include <assert.h>
5 #include "opengl.h"
6 #include "mesh.h"
7 //#include "xform_node.h"
8
9 #define USE_OLDGL
10
11 bool Mesh::use_custom_sdr_attr = true;
12 int Mesh::global_sdr_loc[NUM_MESH_ATTR] = { 0, 1, 2, 3, 4, 5, 6 };
13 /*
14         (int)SDR_ATTR_VERTEX,
15         (int)SDR_ATTR_NORMAL,
16         (int)SDR_ATTR_TANGENT,
17         (int)SDR_ATTR_TEXCOORD,
18         (int)SDR_ATTR_COLOR,
19         -1, -1};
20 */
21 unsigned int Mesh::intersect_mode = ISECT_DEFAULT;
22 float Mesh::vertex_sel_dist = 0.01;
23 float Mesh::vis_vecsize = 1.0;
24
25 Mesh::Mesh()
26 {
27         clear();
28
29         glGenBuffers(NUM_MESH_ATTR + 1, buffer_objects);
30
31         for(int i=0; i<NUM_MESH_ATTR; i++) {
32                 vattr[i].vbo = buffer_objects[i];
33         }
34         ibo = buffer_objects[NUM_MESH_ATTR];
35         wire_ibo = 0;
36 }
37
38 Mesh::~Mesh()
39 {
40         glDeleteBuffers(NUM_MESH_ATTR + 1, buffer_objects);
41
42         if(wire_ibo) {
43                 glDeleteBuffers(1, &wire_ibo);
44         }
45 }
46
47 Mesh::Mesh(const Mesh &rhs)
48 {
49         clear();
50
51         glGenBuffers(NUM_MESH_ATTR + 1, buffer_objects);
52
53         for(int i=0; i<NUM_MESH_ATTR; i++) {
54                 vattr[i].vbo = buffer_objects[i];
55         }
56         ibo = buffer_objects[NUM_MESH_ATTR];
57         wire_ibo = 0;
58
59         clone(rhs);
60 }
61
62 Mesh &Mesh::operator =(const Mesh &rhs)
63 {
64         if(&rhs != this) {
65                 clone(rhs);
66         }
67         return *this;
68 }
69
70 bool Mesh::clone(const Mesh &m)
71 {
72         clear();
73
74         for(int i=0; i<NUM_MESH_ATTR; i++) {
75                 if(m.has_attrib(i)) {
76                         m.get_attrib_data(i);   // force validation of the actual data on the source mesh
77
78                         vattr[i].nelem = m.vattr[i].nelem;
79                         vattr[i].data = m.vattr[i].data;        // copy the actual data
80                         vattr[i].data_valid = true;
81                 }
82         }
83
84         if(m.is_indexed()) {
85                 m.get_index_data();             // again, force validation
86
87                 // copy the index data
88                 idata = m.idata;
89                 idata_valid = true;
90         }
91
92         name = m.name;
93         nverts = m.nverts;
94         nfaces = m.nfaces;
95
96         //bones = m.bones;
97
98         memcpy(cur_val, m.cur_val, sizeof cur_val);
99
100         aabb = m.aabb;
101         aabb_valid = m.aabb_valid;
102         bsph = m.bsph;
103         bsph_valid = m.bsph_valid;
104
105         hitface = m.hitface;
106         hitvert = m.hitvert;
107
108         intersect_mode = m.intersect_mode;
109         vertex_sel_dist = m.vertex_sel_dist;
110         vis_vecsize = m.vis_vecsize;
111
112         return true;
113 }
114
115 void Mesh::set_name(const char *name)
116 {
117         this->name = name;
118 }
119
120 const char *Mesh::get_name() const
121 {
122         return name.c_str();
123 }
124
125 bool Mesh::has_attrib(int attr) const
126 {
127         if(attr < 0 || attr >= NUM_MESH_ATTR) {
128                 return false;
129         }
130
131         // if neither of these is valid, then nobody has set this attribute
132         return vattr[attr].vbo_valid || vattr[attr].data_valid;
133 }
134
135 bool Mesh::is_indexed() const
136 {
137         return ibo_valid || idata_valid;
138 }
139
140 void Mesh::clear()
141 {
142         //bones.clear();
143
144         for(int i=0; i<NUM_MESH_ATTR; i++) {
145                 vattr[i].nelem = 0;
146                 vattr[i].vbo_valid = false;
147                 vattr[i].data_valid = false;
148                 //vattr[i].sdr_loc = -1;
149                 vattr[i].data.clear();
150         }
151         ibo_valid = idata_valid = false;
152         idata.clear();
153
154         wire_ibo_valid = false;
155
156         nverts = nfaces = 0;
157
158         bsph_valid = false;
159         aabb_valid = false;
160 }
161
162 float *Mesh::set_attrib_data(int attrib, int nelem, unsigned int num, const float *data)
163 {
164         if(attrib < 0 || attrib >= NUM_MESH_ATTR) {
165                 fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib);
166                 return 0;
167         }
168
169         if(nverts && num != nverts) {
170                 fprintf(stderr, "%s: attribute count missmatch (%d instead of %d)\n", __FUNCTION__, num, nverts);
171                 return 0;
172         }
173         nverts = num;
174
175         vattr[attrib].data.clear();
176         vattr[attrib].nelem = nelem;
177         vattr[attrib].data.resize(num * nelem);
178
179         if(data) {
180                 memcpy(&vattr[attrib].data[0], data, num * nelem * sizeof *data);
181         }
182
183         vattr[attrib].data_valid = true;
184         vattr[attrib].vbo_valid = false;
185         return &vattr[attrib].data[0];
186 }
187
188 float *Mesh::get_attrib_data(int attrib)
189 {
190         if(attrib < 0 || attrib >= NUM_MESH_ATTR) {
191                 fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib);
192                 return 0;
193         }
194
195         vattr[attrib].vbo_valid = false;
196         return (float*)((const Mesh*)this)->get_attrib_data(attrib);
197 }
198
199 const float *Mesh::get_attrib_data(int attrib) const
200 {
201         if(attrib < 0 || attrib >= NUM_MESH_ATTR) {
202                 fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib);
203                 return 0;
204         }
205
206         if(!vattr[attrib].data_valid) {
207 #if GL_ES_VERSION_2_0
208                 fprintf(stderr, "%s: can't read back attrib data on CrippledGL ES\n", __FUNCTION__);
209                 return 0;
210 #else
211                 if(!vattr[attrib].vbo_valid) {
212                         fprintf(stderr, "%s: unavailable attrib: %d\n", __FUNCTION__, attrib);
213                         return 0;
214                 }
215
216                 // local data copy is unavailable, grab the data from the vbo
217                 Mesh *m = (Mesh*)this;
218                 m->vattr[attrib].data.resize(nverts * vattr[attrib].nelem);
219
220                 glBindBuffer(GL_ARRAY_BUFFER, vattr[attrib].vbo);
221                 void *data = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
222                 memcpy(&m->vattr[attrib].data[0], data, nverts * vattr[attrib].nelem * sizeof(float));
223                 glUnmapBuffer(GL_ARRAY_BUFFER);
224
225                 vattr[attrib].data_valid = true;
226 #endif
227         }
228
229         return &vattr[attrib].data[0];
230 }
231
232 void Mesh::set_attrib(int attrib, int idx, const Vec4 &v)
233 {
234         float *data = get_attrib_data(attrib);
235         if(data) {
236                 data += idx * vattr[attrib].nelem;
237                 for(int i=0; i<vattr[attrib].nelem; i++) {
238                         data[i] = v[i];
239                 }
240         }
241 }
242
243 Vec4 Mesh::get_attrib(int attrib, int idx) const
244 {
245         Vec4 v(0.0, 0.0, 0.0, 1.0);
246         const float *data = get_attrib_data(attrib);
247         if(data) {
248                 data += idx * vattr[attrib].nelem;
249                 for(int i=0; i<vattr[attrib].nelem; i++) {
250                         v[i] = data[i];
251                 }
252         }
253         return v;
254 }
255
256 int Mesh::get_attrib_count(int attrib) const
257 {
258         return has_attrib(attrib) ? nverts : 0;
259 }
260
261
262 unsigned int *Mesh::set_index_data(int num, const unsigned int *indices)
263 {
264         int nidx = nfaces * 3;
265         if(nidx && num != nidx) {
266                 fprintf(stderr, "%s: index count missmatch (%d instead of %d)\n", __FUNCTION__, num, nidx);
267                 return 0;
268         }
269         nfaces = num / 3;
270
271         idata.clear();
272         idata.resize(num);
273
274         if(indices) {
275                 memcpy(&idata[0], indices, num * sizeof *indices);
276         }
277
278         idata_valid = true;
279         ibo_valid = false;
280
281         return &idata[0];
282 }
283
284 unsigned int *Mesh::get_index_data()
285 {
286         ibo_valid = false;
287         return (unsigned int*)((const Mesh*)this)->get_index_data();
288 }
289
290 const unsigned int *Mesh::get_index_data() const
291 {
292         if(!idata_valid) {
293 #if GL_ES_VERSION_2_0
294                 fprintf(stderr, "%s: can't read back index data in CrippledGL ES\n", __FUNCTION__);
295                 return 0;
296 #else
297                 if(!ibo_valid) {
298                         fprintf(stderr, "%s: indices unavailable\n", __FUNCTION__);
299                         return 0;
300                 }
301
302                 // local data copy is unavailable, gram the data from the ibo
303                 Mesh *m = (Mesh*)this;
304                 int nidx = nfaces * 3;
305                 m->idata.resize(nidx);
306
307                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
308                 void *data = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY);
309                 memcpy(&m->idata[0], data, nidx * sizeof(unsigned int));
310                 glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
311
312                 idata_valid = true;
313 #endif
314         }
315
316         return &idata[0];
317 }
318
319 int Mesh::get_index_count() const
320 {
321         return nfaces * 3;
322 }
323
324 void Mesh::append(const Mesh &mesh)
325 {
326         unsigned int idxoffs = nverts;
327
328         if(!nverts) {
329                 clone(mesh);
330                 return;
331         }
332
333         nverts += mesh.nverts;
334         nfaces += mesh.nfaces;
335
336         for(int i=0; i<NUM_MESH_ATTR; i++) {
337                 if(has_attrib(i) && mesh.has_attrib(i)) {
338                         // force validating the data arrays
339                         get_attrib_data(i);
340                         mesh.get_attrib_data(i);
341
342                         // append the mesh data
343                         vattr[i].data.insert(vattr[i].data.end(), mesh.vattr[i].data.begin(), mesh.vattr[i].data.end());
344                 }
345         }
346
347         if(ibo_valid || idata_valid) {
348                 // make index arrays valid
349                 get_index_data();
350                 mesh.get_index_data();
351
352                 size_t orig_sz = idata.size();
353
354                 idata.insert(idata.end(), mesh.idata.begin(), mesh.idata.end());
355
356                 // fixup all the new indices
357                 for(size_t i=orig_sz; i<idata.size(); i++) {
358                         idata[i] += idxoffs;
359                 }
360         }
361
362         // fuck everything
363         wire_ibo_valid = false;
364         aabb_valid = false;
365         bsph_valid = false;
366 }
367
368 // assemble a complete vertex by adding all the useful attributes
369 void Mesh::vertex(float x, float y, float z)
370 {
371         cur_val[MESH_ATTR_VERTEX] = Vec4(x, y, z, 1.0f);
372         vattr[MESH_ATTR_VERTEX].data_valid = true;
373         vattr[MESH_ATTR_VERTEX].nelem = 3;
374
375         for(int i=0; i<NUM_MESH_ATTR; i++) {
376                 if(vattr[i].data_valid) {
377                         for(int j=0; j<vattr[MESH_ATTR_VERTEX].nelem; j++) {
378                                 vattr[i].data.push_back(cur_val[i][j]);
379                         }
380                 }
381                 vattr[i].vbo_valid = false;
382         }
383
384         if(idata_valid) {
385                 idata.clear();
386         }
387         ibo_valid = idata_valid = false;
388 }
389
390 void Mesh::normal(float nx, float ny, float nz)
391 {
392         cur_val[MESH_ATTR_NORMAL] = Vec4(nx, ny, nz, 1.0f);
393         vattr[MESH_ATTR_NORMAL].data_valid = true;
394         vattr[MESH_ATTR_NORMAL].nelem = 3;
395 }
396
397 void Mesh::tangent(float tx, float ty, float tz)
398 {
399         cur_val[MESH_ATTR_TANGENT] = Vec4(tx, ty, tz, 1.0f);
400         vattr[MESH_ATTR_TANGENT].data_valid = true;
401         vattr[MESH_ATTR_TANGENT].nelem = 3;
402 }
403
404 void Mesh::texcoord(float u, float v, float w)
405 {
406         cur_val[MESH_ATTR_TEXCOORD] = Vec4(u, v, w, 1.0f);
407         vattr[MESH_ATTR_TEXCOORD].data_valid = true;
408         vattr[MESH_ATTR_TEXCOORD].nelem = 3;
409 }
410
411 void Mesh::boneweights(float w1, float w2, float w3, float w4)
412 {
413         cur_val[MESH_ATTR_BONEWEIGHTS] = Vec4(w1, w2, w3, w4);
414         vattr[MESH_ATTR_BONEWEIGHTS].data_valid = true;
415         vattr[MESH_ATTR_BONEWEIGHTS].nelem = 4;
416 }
417
418 void Mesh::boneidx(int idx1, int idx2, int idx3, int idx4)
419 {
420         cur_val[MESH_ATTR_BONEIDX] = Vec4(idx1, idx2, idx3, idx4);
421         vattr[MESH_ATTR_BONEIDX].data_valid = true;
422         vattr[MESH_ATTR_BONEIDX].nelem = 4;
423 }
424
425 int Mesh::get_poly_count() const
426 {
427         if(nfaces) {
428                 return nfaces;
429         }
430         if(nverts) {
431                 return nverts / 3;
432         }
433         return 0;
434 }
435
436 /// static function
437 void Mesh::set_attrib_location(int attr, int loc)
438 {
439         if(attr < 0 || attr >= NUM_MESH_ATTR) {
440                 return;
441         }
442         Mesh::global_sdr_loc[attr] = loc;
443 }
444
445 /// static function
446 int Mesh::get_attrib_location(int attr)
447 {
448         if(attr < 0 || attr >= NUM_MESH_ATTR) {
449                 return -1;
450         }
451         return Mesh::global_sdr_loc[attr];
452 }
453
454 /// static function
455 void Mesh::clear_attrib_locations()
456 {
457         for(int i=0; i<NUM_MESH_ATTR; i++) {
458                 Mesh::global_sdr_loc[i] = -1;
459         }
460 }
461
462 /// static function
463 void Mesh::set_vis_vecsize(float sz)
464 {
465         Mesh::vis_vecsize = sz;
466 }
467
468 float Mesh::get_vis_vecsize()
469 {
470         return Mesh::vis_vecsize;
471 }
472
473 void Mesh::apply_xform(const Mat4 &xform)
474 {
475         Mat4 dir_xform = xform.upper3x3();
476         apply_xform(xform, dir_xform);
477 }
478
479 void Mesh::apply_xform(const Mat4 &xform, const Mat4 &dir_xform)
480 {
481         for(unsigned int i=0; i<nverts; i++) {
482                 Vec4 v = get_attrib(MESH_ATTR_VERTEX, i);
483                 set_attrib(MESH_ATTR_VERTEX, i, xform * v);
484
485                 if(has_attrib(MESH_ATTR_NORMAL)) {
486                         Vec3 n = Vec3(get_attrib(MESH_ATTR_NORMAL, i));
487                         set_attrib(MESH_ATTR_NORMAL, i, Vec4(dir_xform * n));
488                 }
489                 if(has_attrib(MESH_ATTR_TANGENT)) {
490                         Vec3 t = Vec3(get_attrib(MESH_ATTR_TANGENT, i));
491                         set_attrib(MESH_ATTR_TANGENT, i, Vec4(dir_xform * t));
492                 }
493         }
494 }
495
496 void Mesh::flip()
497 {
498         flip_faces();
499         flip_normals();
500 }
501
502 void Mesh::flip_faces()
503 {
504         if(is_indexed()) {
505                 unsigned int *indices = get_index_data();
506                 if(!indices) return;
507
508                 int idxnum = get_index_count();
509                 for(int i=0; i<idxnum; i+=3) {
510                         unsigned int tmp = indices[i + 2];
511                         indices[i + 2] = indices[i + 1];
512                         indices[i + 1] = tmp;
513                 }
514
515         } else {
516                 Vec3 *verts = (Vec3*)get_attrib_data(MESH_ATTR_VERTEX);
517                 if(!verts) return;
518
519                 int vnum = get_attrib_count(MESH_ATTR_VERTEX);
520                 for(int i=0; i<vnum; i+=3) {
521                         Vec3 tmp = verts[i + 2];
522                         verts[i + 2] = verts[i + 1];
523                         verts[i + 1] = tmp;
524                 }
525         }
526 }
527
528 void Mesh::flip_normals()
529 {
530         Vec3 *normals = (Vec3*)get_attrib_data(MESH_ATTR_NORMAL);
531         if(!normals) return;
532
533         int num = get_attrib_count(MESH_ATTR_NORMAL);
534         for(int i=0; i<num; i++) {
535                 normals[i] = -normals[i];
536         }
537 }
538
539 /*
540 int Mesh::add_bone(XFormNode *bone)
541 {
542         int idx = bones.size();
543         bones.push_back(bone);
544         return idx;
545 }
546
547 const XFormNode *Mesh::get_bone(int idx) const
548 {
549         if(idx < 0 || idx >= (int)bones.size()) {
550                 return 0;
551         }
552         return bones[idx];
553 }
554
555 int Mesh::get_bones_count() const
556 {
557         return (int)bones.size();
558 }
559 */
560
561 bool Mesh::pre_draw() const
562 {
563         cur_sdr = 0;
564         glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr);
565
566         ((Mesh*)this)->update_buffers();
567
568         if(!vattr[MESH_ATTR_VERTEX].vbo_valid) {
569                 fprintf(stderr, "%s: invalid vertex buffer\n", __FUNCTION__);
570                 return false;
571         }
572
573         if(cur_sdr && use_custom_sdr_attr) {
574                 // rendering with shaders
575                 if(global_sdr_loc[MESH_ATTR_VERTEX] == -1) {
576                         fprintf(stderr, "%s: shader attribute location for vertices unset\n", __FUNCTION__);
577                         return false;
578                 }
579
580                 for(int i=0; i<NUM_MESH_ATTR; i++) {
581                         int loc = global_sdr_loc[i];
582                         if(loc >= 0 && vattr[i].vbo_valid) {
583                                 glBindBuffer(GL_ARRAY_BUFFER, vattr[i].vbo);
584                                 glVertexAttribPointer(loc, vattr[i].nelem, GL_FLOAT, GL_FALSE, 0, 0);
585                                 glEnableVertexAttribArray(loc);
586                         }
587                 }
588         } else {
589 #ifndef GL_ES_VERSION_2_0
590                 // rendering with fixed-function (not available in GLES2)
591                 glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_VERTEX].vbo);
592                 glVertexPointer(vattr[MESH_ATTR_VERTEX].nelem, GL_FLOAT, 0, 0);
593                 glEnableClientState(GL_VERTEX_ARRAY);
594
595                 if(vattr[MESH_ATTR_NORMAL].vbo_valid) {
596                         glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_NORMAL].vbo);
597                         glNormalPointer(GL_FLOAT, 0, 0);
598                         glEnableClientState(GL_NORMAL_ARRAY);
599                 }
600                 if(vattr[MESH_ATTR_TEXCOORD].vbo_valid) {
601                         glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_TEXCOORD].vbo);
602                         glTexCoordPointer(vattr[MESH_ATTR_TEXCOORD].nelem, GL_FLOAT, 0, 0);
603                         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
604                 }
605                 if(vattr[MESH_ATTR_COLOR].vbo_valid) {
606                         glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_COLOR].vbo);
607                         glColorPointer(vattr[MESH_ATTR_COLOR].nelem, GL_FLOAT, 0, 0);
608                         glEnableClientState(GL_COLOR_ARRAY);
609                 }
610 #endif
611         }
612         glBindBuffer(GL_ARRAY_BUFFER, 0);
613
614         return true;
615 }
616
617 void Mesh::draw() const
618 {
619         if(!pre_draw()) return;
620
621         if(ibo_valid) {
622                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
623                 glDrawElements(GL_TRIANGLES, nfaces * 3, GL_UNSIGNED_INT, 0);
624                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
625         } else {
626                 glDrawArrays(GL_TRIANGLES, 0, nverts);
627         }
628
629         post_draw();
630 }
631
632 void Mesh::post_draw() const
633 {
634         if(cur_sdr && use_custom_sdr_attr) {
635                 // rendered with shaders
636                 for(int i=0; i<NUM_MESH_ATTR; i++) {
637                         int loc = global_sdr_loc[i];
638                         if(loc >= 0 && vattr[i].vbo_valid) {
639                                 glDisableVertexAttribArray(loc);
640                         }
641                 }
642         } else {
643 #ifndef GL_ES_VERSION_2_0
644                 // rendered with fixed-function
645                 glDisableClientState(GL_VERTEX_ARRAY);
646                 if(vattr[MESH_ATTR_NORMAL].vbo_valid) {
647                         glDisableClientState(GL_NORMAL_ARRAY);
648                 }
649                 if(vattr[MESH_ATTR_TEXCOORD].vbo_valid) {
650                         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
651                 }
652                 if(vattr[MESH_ATTR_COLOR].vbo_valid) {
653                         glDisableClientState(GL_COLOR_ARRAY);
654                 }
655 #endif
656         }
657 }
658
659 void Mesh::draw_wire() const
660 {
661         if(!pre_draw()) return;
662
663         ((Mesh*)this)->update_wire_ibo();
664
665         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, wire_ibo);
666         glDrawElements(GL_LINES, nfaces * 6, GL_UNSIGNED_INT, 0);
667         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
668
669         post_draw();
670 }
671
672 void Mesh::draw_vertices() const
673 {
674         if(!pre_draw()) return;
675
676         glDrawArrays(GL_POINTS, 0, nverts);
677
678         post_draw();
679 }
680
681 void Mesh::draw_normals() const
682 {
683 #ifdef USE_OLDGL
684         int cur_sdr = 0;
685         glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr);
686
687         Vec3 *varr = (Vec3*)get_attrib_data(MESH_ATTR_VERTEX);
688         Vec3 *norm = (Vec3*)get_attrib_data(MESH_ATTR_NORMAL);
689         if(!varr || !norm) {
690                 return;
691         }
692
693         glBegin(GL_LINES);
694         if(cur_sdr && use_custom_sdr_attr) {
695                 int vert_loc = global_sdr_loc[MESH_ATTR_VERTEX];
696                 if(vert_loc < 0) {
697                         glEnd();
698                         return;
699                 }
700
701                 for(size_t i=0; i<nverts; i++) {
702                         glVertexAttrib3f(vert_loc, varr[i].x, varr[i].y, varr[i].z);
703                         Vec3 end = varr[i] + norm[i] * vis_vecsize;
704                         glVertexAttrib3f(vert_loc, end.x, end.y, end.z);
705                 }
706         } else {
707                 for(size_t i=0; i<nverts; i++) {
708                         glVertex3f(varr[i].x, varr[i].y, varr[i].z);
709                         Vec3 end = varr[i] + norm[i] * vis_vecsize;
710                         glVertex3f(end.x, end.y, end.z);
711                 }
712         }
713         glEnd();
714 #endif  // USE_OLDGL
715 }
716
717 void Mesh::draw_tangents() const
718 {
719 #ifdef USE_OLDGL
720         int cur_sdr = 0;
721         glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr);
722
723         Vec3 *varr = (Vec3*)get_attrib_data(MESH_ATTR_VERTEX);
724         Vec3 *tang = (Vec3*)get_attrib_data(MESH_ATTR_TANGENT);
725         if(!varr || !tang) {
726                 return;
727         }
728
729         glBegin(GL_LINES);
730         if(cur_sdr && use_custom_sdr_attr) {
731                 int vert_loc = global_sdr_loc[MESH_ATTR_VERTEX];
732                 if(vert_loc < 0) {
733                         glEnd();
734                         return;
735                 }
736
737                 for(size_t i=0; i<nverts; i++) {
738                         glVertexAttrib3f(vert_loc, varr[i].x, varr[i].y, varr[i].z);
739                         Vec3 end = varr[i] + tang[i] * vis_vecsize;
740                         glVertexAttrib3f(vert_loc, end.x, end.y, end.z);
741                 }
742         } else {
743                 for(size_t i=0; i<nverts; i++) {
744                         glVertex3f(varr[i].x, varr[i].y, varr[i].z);
745                         Vec3 end = varr[i] + tang[i] * vis_vecsize;
746                         glVertex3f(end.x, end.y, end.z);
747                 }
748         }
749         glEnd();
750 #endif  // USE_OLDGL
751 }
752
753 void Mesh::get_aabbox(Vec3 *vmin, Vec3 *vmax) const
754 {
755         if(!aabb_valid) {
756                 ((Mesh*)this)->calc_aabb();
757         }
758         *vmin = aabb.min;
759         *vmax = aabb.max;
760 }
761
762 const AABox &Mesh::get_aabbox() const
763 {
764         if(!aabb_valid) {
765                 ((Mesh*)this)->calc_aabb();
766         }
767         return aabb;
768 }
769
770 float Mesh::get_bsphere(Vec3 *center, float *rad) const
771 {
772         if(!bsph_valid) {
773                 ((Mesh*)this)->calc_bsph();
774         }
775         *center = bsph.center;
776         *rad = bsph.radius;
777         return bsph.radius;
778 }
779
780 const Sphere &Mesh::get_bsphere() const
781 {
782         if(!bsph_valid) {
783                 ((Mesh*)this)->calc_bsph();
784         }
785         return bsph;
786 }
787
788 /// static function
789 void Mesh::set_intersect_mode(unsigned int mode)
790 {
791         Mesh::intersect_mode = mode;
792 }
793
794 /// static function
795 unsigned int Mesh::get_intersect_mode()
796 {
797         return Mesh::intersect_mode;
798 }
799
800 /// static function
801 void Mesh::set_vertex_select_distance(float dist)
802 {
803         Mesh::vertex_sel_dist = dist;
804 }
805
806 /// static function
807 float Mesh::get_vertex_select_distance()
808 {
809         return Mesh::vertex_sel_dist;
810 }
811
812 bool Mesh::intersect(const Ray &ray, HitPoint *hit) const
813 {
814         assert((Mesh::intersect_mode & (ISECT_VERTICES | ISECT_FACE)) != (ISECT_VERTICES | ISECT_FACE));
815
816         const Vec3 *varr = (Vec3*)get_attrib_data(MESH_ATTR_VERTEX);
817         const Vec3 *narr = (Vec3*)get_attrib_data(MESH_ATTR_NORMAL);
818         if(!varr) {
819                 return false;
820         }
821         const unsigned int *idxarr = get_index_data();
822
823         // first test with the bounding box
824         AABox box;
825         get_aabbox(&box.min, &box.max);
826         if(!box.intersect(ray)) {
827                 return false;
828         }
829
830         HitPoint nearest_hit;
831         nearest_hit.dist = FLT_MAX;
832         nearest_hit.obj = 0;
833
834         if(Mesh::intersect_mode & ISECT_VERTICES) {
835                 // we asked for "intersections" with the vertices of the mesh
836                 long nearest_vidx = -1;
837                 float thres_sq = Mesh::vertex_sel_dist * Mesh::vertex_sel_dist;
838
839                 for(unsigned int i=0; i<nverts; i++) {
840
841                         if((Mesh::intersect_mode & ISECT_FRONT) && dot(narr[i], ray.dir) > 0) {
842                                 continue;
843                         }
844
845                         // project the vertex onto the ray line
846                         float t = dot(varr[i] - ray.origin, ray.dir);
847                         Vec3 vproj = ray.origin + ray.dir * t;
848
849                         float dist_sq = length_sq(vproj - varr[i]);
850                         if(dist_sq < thres_sq) {
851                                 if(!hit) {
852                                         return true;
853                                 }
854                                 if(t < nearest_hit.dist) {
855                                         nearest_hit.dist = t;
856                                         nearest_vidx = i;
857                                 }
858                         }
859                 }
860
861                 if(nearest_vidx != -1) {
862                         hitvert = varr[nearest_vidx];
863                         nearest_hit.obj = &hitvert;
864                 }
865
866         } else {
867                 // regular intersection test with polygons
868
869                 for(unsigned int i=0; i<nfaces; i++) {
870                         Triangle face(i, varr, idxarr);
871
872                         // ignore back-facing polygons if the mode flags include ISECT_FRONT
873                         if((Mesh::intersect_mode & ISECT_FRONT) && dot(face.get_normal(), ray.dir) > 0) {
874                                 continue;
875                         }
876
877                         HitPoint fhit;
878                         if(face.intersect(ray, hit ? &fhit : 0)) {
879                                 if(!hit) {
880                                         return true;
881                                 }
882                                 if(fhit.dist < nearest_hit.dist) {
883                                         nearest_hit = fhit;
884                                         hitface = face;
885                                 }
886                         }
887                 }
888         }
889
890         if(nearest_hit.obj) {
891                 if(hit) {
892                         *hit = nearest_hit;
893
894                         // if we are interested in the mesh and not the faces set obj to this
895                         if(Mesh::intersect_mode & ISECT_FACE) {
896                                 hit->obj = &hitface;
897                         } else if(Mesh::intersect_mode & ISECT_VERTICES) {
898                                 hit->obj = &hitvert;
899                         } else {
900                                 hit->obj = this;
901                         }
902                 }
903                 return true;
904         }
905         return false;
906 }
907
908
909 // texture coordinate manipulation
910 void Mesh::texcoord_apply_xform(const Mat4 &xform)
911 {
912         if(!has_attrib(MESH_ATTR_TEXCOORD)) {
913                 return;
914         }
915
916         for(unsigned int i=0; i<nverts; i++) {
917                 Vec4 tc = get_attrib(MESH_ATTR_TEXCOORD, i);
918                 set_attrib(MESH_ATTR_TEXCOORD, i, xform * tc);
919         }
920 }
921
922 void Mesh::texcoord_gen_plane(const Vec3 &norm, const Vec3 &tang)
923 {
924         if(!nverts) return;
925
926         if(!has_attrib(MESH_ATTR_TEXCOORD)) {
927                 // allocate texture coordinate attribute array
928                 set_attrib_data(MESH_ATTR_TEXCOORD, 2, nverts);
929         }
930
931         Vec3 n = normalize(norm);
932         Vec3 b = normalize(cross(n, tang));
933         Vec3 t = cross(b, n);
934
935         for(unsigned int i=0; i<nverts; i++) {
936                 Vec3 pos = Vec3(get_attrib(MESH_ATTR_VERTEX, i));
937
938                 // distance along the tangent direction
939                 float u = dot(pos, t);
940                 // distance along the bitangent direction
941                 float v = dot(pos, b);
942
943                 set_attrib(MESH_ATTR_TEXCOORD, i, Vec4(u, v, 0, 1));
944         }
945 }
946
947 void Mesh::texcoord_gen_box()
948 {
949         if(!nverts || !has_attrib(MESH_ATTR_NORMAL)) return;
950
951         if(!has_attrib(MESH_ATTR_TEXCOORD)) {
952                 // allocate texture coordinate attribute array
953                 set_attrib_data(MESH_ATTR_TEXCOORD, 2, nverts);
954         }
955
956         for(unsigned int i=0; i<nverts; i++) {
957                 Vec3 pos = Vec3(get_attrib(MESH_ATTR_VERTEX, i)) * 0.5 + Vec3(0.5, 0.5, 0.5);
958                 Vec3 norm = Vec3(get_attrib(MESH_ATTR_NORMAL, i));
959
960                 float abs_nx = fabs(norm.x);
961                 float abs_ny = fabs(norm.y);
962                 float abs_nz = fabs(norm.z);
963                 int dom = abs_nx > abs_ny && abs_nx > abs_nz ? 0 : (abs_ny > abs_nz ? 1 : 2);
964
965                 float uv[2], *uvptr = uv;
966                 for(int j=0; j<3; j++) {
967                         if(j == dom) continue;  // skip dominant axis
968
969                         *uvptr++ = pos[j];
970                 }
971                 set_attrib(MESH_ATTR_TEXCOORD, i, Vec4(uv[0], uv[1], 0, 1));
972         }
973 }
974
975 void Mesh::texcoord_gen_cylinder()
976 {
977         if(!nverts) return;
978
979         if(!has_attrib(MESH_ATTR_TEXCOORD)) {
980                 // allocate texture coordinate attribute array
981                 set_attrib_data(MESH_ATTR_TEXCOORD, 2, nverts);
982         }
983
984         for(unsigned int i=0; i<nverts; i++) {
985                 Vec3 pos = Vec3(get_attrib(MESH_ATTR_VERTEX, i));
986
987                 float rho = sqrt(pos.x * pos.x + pos.z * pos.z);
988                 float theta = rho == 0.0 ? 0.0 : atan2(pos.z / rho, pos.x / rho);
989
990                 float u = theta / (2.0 * M_PI) + 0.5;
991                 float v = pos.y;
992
993                 set_attrib(MESH_ATTR_TEXCOORD, i, Vec4(u, v, 0, 1));
994         }
995 }
996
997
998 bool Mesh::dump(const char *fname) const
999 {
1000         FILE *fp = fopen(fname, "wb");
1001         if(fp) {
1002                 bool res = dump(fp);
1003                 fclose(fp);
1004                 return res;
1005         }
1006         return false;
1007 }
1008
1009 bool Mesh::dump(FILE *fp) const
1010 {
1011         if(!has_attrib(MESH_ATTR_VERTEX)) {
1012                 return false;
1013         }
1014
1015         fprintf(fp, "VERTEX ATTRIBUTES\n");
1016         static const char *label[] = { "pos", "nor", "tan", "tex", "col", "bw", "bid" };
1017         static const char *elemfmt[] = { 0, " %s(%g)", " %s(%g, %g)", " %s(%g, %g, %g)", " %s(%g, %g, %g, %g)", 0 };
1018
1019         for(int i=0; i<(int)nverts; i++) {
1020                 fprintf(fp, "%5u:", i);
1021                 for(int j=0; j<NUM_MESH_ATTR; j++) {
1022                         if(has_attrib(j)) {
1023                                 Vec4 v = get_attrib(j, i);
1024                                 int nelem = vattr[j].nelem;
1025                                 fprintf(fp, elemfmt[nelem], label[j], v.x, v.y, v.z, v.w);
1026                         }
1027                 }
1028                 fputc('\n', fp);
1029         }
1030
1031         if(is_indexed()) {
1032                 const unsigned int *idx = get_index_data();
1033                 int numidx = get_index_count();
1034                 int numtri = numidx / 3;
1035                 assert(numidx % 3 == 0);
1036
1037                 fprintf(fp, "FACES\n");
1038
1039                 for(int i=0; i<numtri; i++) {
1040                         fprintf(fp, "%5d: %d %d %d\n", i, idx[0], idx[1], idx[2]);
1041                         idx += 3;
1042                 }
1043         }
1044         return true;
1045 }
1046
1047 bool Mesh::dump_obj(const char *fname) const
1048 {
1049         FILE *fp = fopen(fname, "wb");
1050         if(fp) {
1051                 bool res = dump_obj(fp);
1052                 fclose(fp);
1053                 return res;
1054         }
1055         return false;
1056 }
1057
1058 bool Mesh::dump_obj(FILE *fp) const
1059 {
1060         if(!has_attrib(MESH_ATTR_VERTEX)) {
1061                 return false;
1062         }
1063
1064         for(int i=0; i<(int)nverts; i++) {
1065                 Vec4 v = get_attrib(MESH_ATTR_VERTEX, i);
1066                 fprintf(fp, "v %g %g %g\n", v.x, v.y, v.z);
1067         }
1068
1069         if(has_attrib(MESH_ATTR_NORMAL)) {
1070                 for(int i=0; i<(int)nverts; i++) {
1071                         Vec4 v = get_attrib(MESH_ATTR_NORMAL, i);
1072                         fprintf(fp, "vn %g %g %g\n", v.x, v.y, v.z);
1073                 }
1074         }
1075
1076         if(has_attrib(MESH_ATTR_TEXCOORD)) {
1077                 for(int i=0; i<(int)nverts; i++) {
1078                         Vec4 v = get_attrib(MESH_ATTR_TEXCOORD, i);
1079                         fprintf(fp, "vt %g %g\n", v.x, v.y);
1080                 }
1081         }
1082
1083         if(is_indexed()) {
1084                 const unsigned int *idxptr = get_index_data();
1085                 int numidx = get_index_count();
1086                 int numtri = numidx / 3;
1087                 assert(numidx % 3 == 0);
1088
1089                 for(int i=0; i<numtri; i++) {
1090                         fputc('f', fp);
1091                         for(int j=0; j<3; j++) {
1092                                 unsigned int idx = *idxptr++ + 1;
1093                                 fprintf(fp, " %u/%u/%u", idx, idx, idx);
1094                         }
1095                         fputc('\n', fp);
1096                 }
1097         } else {
1098                 int numtri = nverts / 3;
1099                 unsigned int idx = 1;
1100                 for(int i=0; i<numtri; i++) {
1101                         fputc('f', fp);
1102                         for(int j=0; j<3; j++) {
1103                                 fprintf(fp, " %u/%u/%u", idx, idx, idx);
1104                                 ++idx;
1105                         }
1106                         fputc('\n', fp);
1107                 }
1108         }
1109         return true;
1110 }
1111
1112 // ------ private member functions ------
1113
1114 void Mesh::calc_aabb()
1115 {
1116         // the cast is to force calling the const version which doesn't invalidate
1117         if(!((const Mesh*)this)->get_attrib_data(MESH_ATTR_VERTEX)) {
1118                 return;
1119         }
1120
1121         aabb.min = Vec3(FLT_MAX, FLT_MAX, FLT_MAX);
1122         aabb.max = -aabb.min;
1123
1124         for(unsigned int i=0; i<nverts; i++) {
1125                 Vec4 v = get_attrib(MESH_ATTR_VERTEX, i);
1126                 for(int j=0; j<3; j++) {
1127                         if(v[j] < aabb.min[j]) {
1128                                 aabb.min[j] = v[j];
1129                         }
1130                         if(v[j] > aabb.max[j]) {
1131                                 aabb.max[j] = v[j];
1132                         }
1133                 }
1134         }
1135         aabb_valid = true;
1136 }
1137
1138 void Mesh::calc_bsph()
1139 {
1140         // the cast is to force calling the const version which doesn't invalidate
1141         if(!((const Mesh*)this)->get_attrib_data(MESH_ATTR_VERTEX)) {
1142                 return;
1143         }
1144
1145         Vec3 v;
1146         bsph.center = Vec3(0, 0, 0);
1147
1148         // first find the center
1149         for(unsigned int i=0; i<nverts; i++) {
1150                 v = Vec3(get_attrib(MESH_ATTR_VERTEX, i));
1151                 bsph.center += v;
1152         }
1153         bsph.center /= (float)nverts;
1154
1155         bsph.radius = 0.0f;
1156         for(unsigned int i=0; i<nverts; i++) {
1157                 v = Vec3(get_attrib(MESH_ATTR_VERTEX, i));
1158                 float dist_sq = length_sq(v - bsph.center);
1159                 if(dist_sq > bsph.radius) {
1160                         bsph.radius = dist_sq;
1161                 }
1162         }
1163         bsph.radius = sqrt(bsph.radius);
1164
1165         bsph_valid = true;
1166 }
1167
1168 void Mesh::update_buffers()
1169 {
1170         for(int i=0; i<NUM_MESH_ATTR; i++) {
1171                 if(has_attrib(i) && !vattr[i].vbo_valid) {
1172                         glBindBuffer(GL_ARRAY_BUFFER, vattr[i].vbo);
1173                         glBufferData(GL_ARRAY_BUFFER, nverts * vattr[i].nelem * sizeof(float), &vattr[i].data[0], GL_STATIC_DRAW);
1174                         vattr[i].vbo_valid = true;
1175                 }
1176         }
1177         glBindBuffer(GL_ARRAY_BUFFER, 0);
1178
1179         if(idata_valid && !ibo_valid) {
1180                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
1181                 glBufferData(GL_ELEMENT_ARRAY_BUFFER, nfaces * 3 * sizeof(unsigned int), &idata[0], GL_STATIC_DRAW);
1182                 ibo_valid = true;
1183         }
1184         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1185 }
1186
1187 void Mesh::update_wire_ibo()
1188 {
1189         update_buffers();
1190
1191         if(wire_ibo_valid) {
1192                 return;
1193         }
1194
1195         if(!wire_ibo) {
1196                 glGenBuffers(1, &wire_ibo);
1197         }
1198
1199         unsigned int *wire_idxarr = new unsigned int[nfaces * 6];
1200         unsigned int *dest = wire_idxarr;
1201
1202         if(ibo_valid) {
1203                 // we're dealing with an indexed mesh
1204                 const unsigned int *idxarr = ((const Mesh*)this)->get_index_data();
1205
1206                 for(unsigned int i=0; i<nfaces; i++) {
1207                         *dest++ = idxarr[0];
1208                         *dest++ = idxarr[1];
1209                         *dest++ = idxarr[1];
1210                         *dest++ = idxarr[2];
1211                         *dest++ = idxarr[2];
1212                         *dest++ = idxarr[0];
1213                         idxarr += 3;
1214                 }
1215         } else {
1216                 // not an indexed mesh ...
1217                 for(unsigned int i=0; i<nfaces; i++) {
1218                         int vidx = i * 3;
1219                         *dest++ = vidx;
1220                         *dest++ = vidx + 1;
1221                         *dest++ = vidx + 1;
1222                         *dest++ = vidx + 2;
1223                         *dest++ = vidx + 2;
1224                         *dest++ = vidx;
1225                 }
1226         }
1227
1228         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, wire_ibo);
1229         glBufferData(GL_ELEMENT_ARRAY_BUFFER, nfaces * 6 * sizeof(unsigned int), wire_idxarr, GL_STATIC_DRAW);
1230         delete [] wire_idxarr;
1231         wire_ibo_valid = true;
1232         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1233 }
1234
1235
1236 // ------ class Triangle ------
1237 Triangle::Triangle()
1238 {
1239         normal_valid = false;
1240         id = -1;
1241 }
1242
1243 Triangle::Triangle(const Vec3 &v0, const Vec3 &v1, const Vec3 &v2)
1244 {
1245         v[0] = v0;
1246         v[1] = v1;
1247         v[2] = v2;
1248         normal_valid = false;
1249         id = -1;
1250 }
1251
1252 Triangle::Triangle(int n, const Vec3 *varr, const unsigned int *idxarr)
1253 {
1254         if(idxarr) {
1255                 v[0] = varr[idxarr[n * 3]];
1256                 v[1] = varr[idxarr[n * 3 + 1]];
1257                 v[2] = varr[idxarr[n * 3 + 2]];
1258         } else {
1259                 v[0] = varr[n * 3];
1260                 v[1] = varr[n * 3 + 1];
1261                 v[2] = varr[n * 3 + 2];
1262         }
1263         normal_valid = false;
1264         id = n;
1265 }
1266
1267 void Triangle::calc_normal()
1268 {
1269         normal = normalize(cross(v[1] - v[0], v[2] - v[0]));
1270         normal_valid = true;
1271 }
1272
1273 const Vec3 &Triangle::get_normal() const
1274 {
1275         if(!normal_valid) {
1276                 ((Triangle*)this)->calc_normal();
1277         }
1278         return normal;
1279 }
1280
1281 void Triangle::transform(const Mat4 &xform)
1282 {
1283         v[0] = xform * v[0];
1284         v[1] = xform * v[1];
1285         v[2] = xform * v[2];
1286         normal_valid = false;
1287 }
1288
1289 void Triangle::draw() const
1290 {
1291         Vec3 n[3];
1292         n[0] = n[1] = n[2] = get_normal();
1293
1294         int vloc = Mesh::get_attrib_location(MESH_ATTR_VERTEX);
1295         int nloc = Mesh::get_attrib_location(MESH_ATTR_NORMAL);
1296
1297         glEnableVertexAttribArray(vloc);
1298         glVertexAttribPointer(vloc, 3, GL_FLOAT, GL_FALSE, 0, &v[0].x);
1299         glVertexAttribPointer(nloc, 3, GL_FLOAT, GL_FALSE, 0, &n[0].x);
1300
1301         glDrawArrays(GL_TRIANGLES, 0, 3);
1302
1303         glDisableVertexAttribArray(vloc);
1304         glDisableVertexAttribArray(nloc);
1305 }
1306
1307 void Triangle::draw_wire() const
1308 {
1309         static const int idxarr[] = {0, 1, 1, 2, 2, 0};
1310         int vloc = Mesh::get_attrib_location(MESH_ATTR_VERTEX);
1311
1312         glEnableVertexAttribArray(vloc);
1313         glVertexAttribPointer(vloc, 3, GL_FLOAT, GL_FALSE, 0, &v[0].x);
1314
1315         glDrawElements(GL_LINES, 6, GL_UNSIGNED_INT, idxarr);
1316
1317         glDisableVertexAttribArray(vloc);
1318 }
1319
1320 Vec3 Triangle::calc_barycentric(const Vec3 &pos) const
1321 {
1322         Vec3 norm = get_normal();
1323
1324         float area_sq = fabs(dot(cross(v[1] - v[0], v[2] - v[0]), norm));
1325         if(area_sq < 1e-5) {
1326                 return Vec3(0, 0, 0);
1327         }
1328
1329         float asq0 = fabs(dot(cross(v[1] - pos, v[2] - pos), norm));
1330         float asq1 = fabs(dot(cross(v[2] - pos, v[0] - pos), norm));
1331         float asq2 = fabs(dot(cross(v[0] - pos, v[1] - pos), norm));
1332
1333         return Vec3(asq0 / area_sq, asq1 / area_sq, asq2 / area_sq);
1334 }
1335
1336 bool Triangle::intersect(const Ray &ray, HitPoint *hit) const
1337 {
1338         Vec3 normal = get_normal();
1339
1340         float ndotdir = dot(ray.dir, normal);
1341         if(fabs(ndotdir) < 1e-4) {
1342                 return false;
1343         }
1344
1345         Vec3 vertdir = v[0] - ray.origin;
1346         float t = dot(normal, vertdir) / ndotdir;
1347
1348         Vec3 pos = ray.origin + ray.dir * t;
1349         Vec3 bary = calc_barycentric(pos);
1350
1351         if(bary.x + bary.y + bary.z > 1.00001) {
1352                 return false;
1353         }
1354
1355         if(hit) {
1356                 hit->dist = t;
1357                 hit->pos = ray.origin + ray.dir * t;
1358                 hit->normal = normal;
1359                 hit->obj = this;
1360         }
1361         return true;
1362 }