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