tavli

view src/mesh.cc @ 7:f1ecc2439802

hinges
author John Tsiombikas <nuclear@member.fsf.org>
date Fri, 26 Jun 2015 05:23:46 +0300
parents a0d30f6f20d4
children a8e26f163f99
line source
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"
9 int Mesh::global_sdr_loc[NUM_MESH_ATTR] = { 0, 1, 2, 3, 4, 5, 6 };
10 /*
11 (int)SDR_ATTR_VERTEX,
12 (int)SDR_ATTR_NORMAL,
13 (int)SDR_ATTR_TANGENT,
14 (int)SDR_ATTR_TEXCOORD,
15 (int)SDR_ATTR_COLOR,
16 -1, -1};
17 */
18 unsigned int Mesh::intersect_mode = ISECT_DEFAULT;
19 float Mesh::vertex_sel_dist = 0.01;
20 float Mesh::vis_vecsize = 1.0;
22 Mesh::Mesh()
23 {
24 clear();
26 glGenBuffers(NUM_MESH_ATTR + 1, buffer_objects);
28 for(int i=0; i<NUM_MESH_ATTR; i++) {
29 vattr[i].vbo = buffer_objects[i];
30 }
31 ibo = buffer_objects[NUM_MESH_ATTR];
32 wire_ibo = 0;
33 }
35 Mesh::~Mesh()
36 {
37 glDeleteBuffers(NUM_MESH_ATTR + 1, buffer_objects);
39 if(wire_ibo) {
40 glDeleteBuffers(1, &wire_ibo);
41 }
42 }
44 Mesh::Mesh(const Mesh &rhs)
45 {
46 clear();
48 glGenBuffers(NUM_MESH_ATTR + 1, buffer_objects);
50 for(int i=0; i<NUM_MESH_ATTR; i++) {
51 vattr[i].vbo = buffer_objects[i];
52 }
53 ibo = buffer_objects[NUM_MESH_ATTR];
54 wire_ibo = 0;
56 clone(rhs);
57 }
59 Mesh &Mesh::operator =(const Mesh &rhs)
60 {
61 if(&rhs != this) {
62 clone(rhs);
63 }
64 return *this;
65 }
67 bool Mesh::clone(const Mesh &m)
68 {
69 clear();
71 for(int i=0; i<NUM_MESH_ATTR; i++) {
72 if(m.has_attrib(i)) {
73 m.get_attrib_data(i); // force validation of the actual data on the source mesh
75 vattr[i].nelem = m.vattr[i].nelem;
76 vattr[i].data = m.vattr[i].data; // copy the actual data
77 vattr[i].data_valid = true;
78 }
79 }
81 if(m.is_indexed()) {
82 m.get_index_data(); // again, force validation
84 // copy the index data
85 idata = m.idata;
86 idata_valid = true;
87 }
89 name = m.name;
90 nverts = m.nverts;
91 nfaces = m.nfaces;
93 //bones = m.bones;
95 memcpy(cur_val, m.cur_val, sizeof cur_val);
97 aabb = m.aabb;
98 aabb_valid = m.aabb_valid;
99 bsph = m.bsph;
100 bsph_valid = m.bsph_valid;
102 hitface = m.hitface;
103 hitvert = m.hitvert;
105 intersect_mode = m.intersect_mode;
106 vertex_sel_dist = m.vertex_sel_dist;
107 vis_vecsize = m.vis_vecsize;
109 return true;
110 }
112 void Mesh::set_name(const char *name)
113 {
114 this->name = name;
115 }
117 const char *Mesh::get_name() const
118 {
119 return name.c_str();
120 }
122 bool Mesh::has_attrib(int attr) const
123 {
124 if(attr < 0 || attr >= NUM_MESH_ATTR) {
125 return false;
126 }
128 // if neither of these is valid, then nobody has set this attribute
129 return vattr[attr].vbo_valid || vattr[attr].data_valid;
130 }
132 bool Mesh::is_indexed() const
133 {
134 return ibo_valid || idata_valid;
135 }
137 void Mesh::clear()
138 {
139 //bones.clear();
141 for(int i=0; i<NUM_MESH_ATTR; i++) {
142 vattr[i].nelem = 0;
143 vattr[i].vbo_valid = false;
144 vattr[i].data_valid = false;
145 //vattr[i].sdr_loc = -1;
146 vattr[i].data.clear();
147 }
148 ibo_valid = idata_valid = false;
149 idata.clear();
151 wire_ibo_valid = false;
153 nverts = nfaces = 0;
155 bsph_valid = false;
156 aabb_valid = false;
157 }
159 float *Mesh::set_attrib_data(int attrib, int nelem, unsigned int num, const float *data)
160 {
161 if(attrib < 0 || attrib >= NUM_MESH_ATTR) {
162 fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib);
163 return 0;
164 }
166 if(nverts && num != nverts) {
167 fprintf(stderr, "%s: attribute count missmatch (%d instead of %d)\n", __FUNCTION__, num, nverts);
168 return 0;
169 }
170 nverts = num;
172 vattr[attrib].data.clear();
173 vattr[attrib].nelem = nelem;
174 vattr[attrib].data.resize(num * nelem);
176 if(data) {
177 memcpy(&vattr[attrib].data[0], data, num * nelem * sizeof *data);
178 }
180 vattr[attrib].data_valid = true;
181 vattr[attrib].vbo_valid = false;
182 return &vattr[attrib].data[0];
183 }
185 float *Mesh::get_attrib_data(int attrib)
186 {
187 if(attrib < 0 || attrib >= NUM_MESH_ATTR) {
188 fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib);
189 return 0;
190 }
192 vattr[attrib].vbo_valid = false;
193 return (float*)((const Mesh*)this)->get_attrib_data(attrib);
194 }
196 const float *Mesh::get_attrib_data(int attrib) const
197 {
198 if(attrib < 0 || attrib >= NUM_MESH_ATTR) {
199 fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib);
200 return 0;
201 }
203 if(!vattr[attrib].data_valid) {
204 #if GL_ES_VERSION_2_0
205 fprintf(stderr, "%s: can't read back attrib data on CrippledGL ES\n", __FUNCTION__);
206 return 0;
207 #else
208 if(!vattr[attrib].vbo_valid) {
209 fprintf(stderr, "%s: unavailable attrib: %d\n", __FUNCTION__, attrib);
210 return 0;
211 }
213 // local data copy is unavailable, grab the data from the vbo
214 Mesh *m = (Mesh*)this;
215 m->vattr[attrib].data.resize(nverts * vattr[attrib].nelem);
217 glBindBuffer(GL_ARRAY_BUFFER, vattr[attrib].vbo);
218 void *data = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
219 memcpy(&m->vattr[attrib].data[0], data, nverts * vattr[attrib].nelem * sizeof(float));
220 glUnmapBuffer(GL_ARRAY_BUFFER);
222 vattr[attrib].data_valid = true;
223 #endif
224 }
226 return &vattr[attrib].data[0];
227 }
229 void Mesh::set_attrib(int attrib, int idx, const Vector4 &v)
230 {
231 float *data = get_attrib_data(attrib);
232 if(data) {
233 data += idx * vattr[attrib].nelem;
234 for(int i=0; i<vattr[attrib].nelem; i++) {
235 data[i] = v[i];
236 }
237 }
238 }
240 Vector4 Mesh::get_attrib(int attrib, int idx) const
241 {
242 Vector4 v(0.0, 0.0, 0.0, 1.0);
243 const float *data = get_attrib_data(attrib);
244 if(data) {
245 data += idx * vattr[attrib].nelem;
246 for(int i=0; i<vattr[attrib].nelem; i++) {
247 v[i] = data[i];
248 }
249 }
250 return v;
251 }
253 int Mesh::get_attrib_count(int attrib) const
254 {
255 return has_attrib(attrib) ? nverts : 0;
256 }
259 unsigned int *Mesh::set_index_data(int num, const unsigned int *indices)
260 {
261 int nidx = nfaces * 3;
262 if(nidx && num != nidx) {
263 fprintf(stderr, "%s: index count missmatch (%d instead of %d)\n", __FUNCTION__, num, nidx);
264 return 0;
265 }
266 nfaces = num / 3;
268 idata.clear();
269 idata.resize(num);
271 if(indices) {
272 memcpy(&idata[0], indices, num * sizeof *indices);
273 }
275 idata_valid = true;
276 ibo_valid = false;
278 return &idata[0];
279 }
281 unsigned int *Mesh::get_index_data()
282 {
283 ibo_valid = false;
284 return (unsigned int*)((const Mesh*)this)->get_index_data();
285 }
287 const unsigned int *Mesh::get_index_data() const
288 {
289 if(!idata_valid) {
290 #if GL_ES_VERSION_2_0
291 fprintf(stderr, "%s: can't read back index data in CrippledGL ES\n", __FUNCTION__);
292 return 0;
293 #else
294 if(!ibo_valid) {
295 fprintf(stderr, "%s: indices unavailable\n", __FUNCTION__);
296 return 0;
297 }
299 // local data copy is unavailable, gram the data from the ibo
300 Mesh *m = (Mesh*)this;
301 int nidx = nfaces * 3;
302 m->idata.resize(nidx);
304 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
305 void *data = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY);
306 memcpy(&m->idata[0], data, nidx * sizeof(unsigned int));
307 glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
309 idata_valid = true;
310 #endif
311 }
313 return &idata[0];
314 }
316 int Mesh::get_index_count() const
317 {
318 return nfaces * 3;
319 }
321 void Mesh::append(const Mesh &mesh)
322 {
323 unsigned int idxoffs = nverts;
325 if(!nverts) {
326 clone(mesh);
327 return;
328 }
330 nverts += mesh.nverts;
331 nfaces += mesh.nfaces;
333 for(int i=0; i<NUM_MESH_ATTR; i++) {
334 if(has_attrib(i) && mesh.has_attrib(i)) {
335 // force validating the data arrays
336 get_attrib_data(i);
337 mesh.get_attrib_data(i);
339 // append the mesh data
340 vattr[i].data.insert(vattr[i].data.end(), mesh.vattr[i].data.begin(), mesh.vattr[i].data.end());
341 }
342 }
344 if(ibo_valid || idata_valid) {
345 // make index arrays valid
346 get_index_data();
347 mesh.get_index_data();
349 size_t orig_sz = idata.size();
351 idata.insert(idata.end(), mesh.idata.begin(), mesh.idata.end());
353 // fixup all the new indices
354 for(size_t i=orig_sz; i<idata.size(); i++) {
355 idata[i] += idxoffs;
356 }
357 }
359 // fuck everything
360 wire_ibo_valid = false;
361 aabb_valid = false;
362 bsph_valid = false;
363 }
365 // assemble a complete vertex by adding all the useful attributes
366 void Mesh::vertex(float x, float y, float z)
367 {
368 cur_val[MESH_ATTR_VERTEX] = Vector4(x, y, z, 1.0f);
369 vattr[MESH_ATTR_VERTEX].data_valid = true;
370 vattr[MESH_ATTR_VERTEX].nelem = 3;
372 for(int i=0; i<NUM_MESH_ATTR; i++) {
373 if(vattr[i].data_valid) {
374 for(int j=0; j<vattr[MESH_ATTR_VERTEX].nelem; j++) {
375 vattr[i].data.push_back(cur_val[i][j]);
376 }
377 }
378 vattr[i].vbo_valid = false;
379 }
381 if(idata_valid) {
382 idata.clear();
383 }
384 ibo_valid = idata_valid = false;
385 }
387 void Mesh::normal(float nx, float ny, float nz)
388 {
389 cur_val[MESH_ATTR_NORMAL] = Vector4(nx, ny, nz, 1.0f);
390 vattr[MESH_ATTR_NORMAL].data_valid = true;
391 vattr[MESH_ATTR_NORMAL].nelem = 3;
392 }
394 void Mesh::tangent(float tx, float ty, float tz)
395 {
396 cur_val[MESH_ATTR_TANGENT] = Vector4(tx, ty, tz, 1.0f);
397 vattr[MESH_ATTR_TANGENT].data_valid = true;
398 vattr[MESH_ATTR_TANGENT].nelem = 3;
399 }
401 void Mesh::texcoord(float u, float v, float w)
402 {
403 cur_val[MESH_ATTR_TEXCOORD] = Vector4(u, v, w, 1.0f);
404 vattr[MESH_ATTR_TEXCOORD].data_valid = true;
405 vattr[MESH_ATTR_TEXCOORD].nelem = 3;
406 }
408 void Mesh::boneweights(float w1, float w2, float w3, float w4)
409 {
410 cur_val[MESH_ATTR_BONEWEIGHTS] = Vector4(w1, w2, w3, w4);
411 vattr[MESH_ATTR_BONEWEIGHTS].data_valid = true;
412 vattr[MESH_ATTR_BONEWEIGHTS].nelem = 4;
413 }
415 void Mesh::boneidx(int idx1, int idx2, int idx3, int idx4)
416 {
417 cur_val[MESH_ATTR_BONEIDX] = Vector4(idx1, idx2, idx3, idx4);
418 vattr[MESH_ATTR_BONEIDX].data_valid = true;
419 vattr[MESH_ATTR_BONEIDX].nelem = 4;
420 }
422 int Mesh::get_poly_count() const
423 {
424 if(nfaces) {
425 return nfaces;
426 }
427 if(nverts) {
428 return nverts / 3;
429 }
430 return 0;
431 }
433 /// static function
434 void Mesh::set_attrib_location(int attr, int loc)
435 {
436 if(attr < 0 || attr >= NUM_MESH_ATTR) {
437 return;
438 }
439 Mesh::global_sdr_loc[attr] = loc;
440 }
442 /// static function
443 int Mesh::get_attrib_location(int attr)
444 {
445 if(attr < 0 || attr >= NUM_MESH_ATTR) {
446 return -1;
447 }
448 return Mesh::global_sdr_loc[attr];
449 }
451 /// static function
452 void Mesh::clear_attrib_locations()
453 {
454 for(int i=0; i<NUM_MESH_ATTR; i++) {
455 Mesh::global_sdr_loc[i] = -1;
456 }
457 }
459 /// static function
460 void Mesh::set_vis_vecsize(float sz)
461 {
462 Mesh::vis_vecsize = sz;
463 }
465 float Mesh::get_vis_vecsize()
466 {
467 return Mesh::vis_vecsize;
468 }
470 void Mesh::apply_xform(const Matrix4x4 &xform)
471 {
472 Matrix4x4 dir_xform = xform;
473 dir_xform[0][3] = dir_xform[1][3] = dir_xform[2][3] = 0.0f;
474 dir_xform[3][0] = dir_xform[3][1] = dir_xform[3][2] = 0.0f;
475 dir_xform[3][3] = 1.0f;
477 apply_xform(xform, dir_xform);
478 }
480 void Mesh::apply_xform(const Matrix4x4 &xform, const Matrix4x4 &dir_xform)
481 {
482 for(unsigned int i=0; i<nverts; i++) {
483 Vector4 v = get_attrib(MESH_ATTR_VERTEX, i);
484 set_attrib(MESH_ATTR_VERTEX, i, v.transformed(xform));
486 if(has_attrib(MESH_ATTR_NORMAL)) {
487 Vector3 n = get_attrib(MESH_ATTR_NORMAL, i);
488 set_attrib(MESH_ATTR_NORMAL, i, n.transformed(dir_xform));
489 }
490 if(has_attrib(MESH_ATTR_TANGENT)) {
491 Vector3 t = get_attrib(MESH_ATTR_TANGENT, i);
492 set_attrib(MESH_ATTR_TANGENT, i, t.transformed(dir_xform));
493 }
494 }
495 }
497 void Mesh::flip()
498 {
499 flip_faces();
500 flip_normals();
501 }
503 void Mesh::flip_faces()
504 {
505 if(is_indexed()) {
506 unsigned int *indices = get_index_data();
507 if(!indices) return;
509 int idxnum = get_index_count();
510 for(int i=0; i<idxnum; i+=3) {
511 unsigned int tmp = indices[i + 2];
512 indices[i + 2] = indices[i + 1];
513 indices[i + 1] = tmp;
514 }
516 } else {
517 Vector3 *verts = (Vector3*)get_attrib_data(MESH_ATTR_VERTEX);
518 if(!verts) return;
520 int vnum = get_attrib_count(MESH_ATTR_VERTEX);
521 for(int i=0; i<vnum; i+=3) {
522 Vector3 tmp = verts[i + 2];
523 verts[i + 2] = verts[i + 1];
524 verts[i + 1] = tmp;
525 }
526 }
527 }
529 void Mesh::flip_normals()
530 {
531 Vector3 *normals = (Vector3*)get_attrib_data(MESH_ATTR_NORMAL);
532 if(!normals) return;
534 int num = get_attrib_count(MESH_ATTR_NORMAL);
535 for(int i=0; i<num; i++) {
536 normals[i] = -normals[i];
537 }
538 }
540 /*
541 int Mesh::add_bone(XFormNode *bone)
542 {
543 int idx = bones.size();
544 bones.push_back(bone);
545 return idx;
546 }
548 const XFormNode *Mesh::get_bone(int idx) const
549 {
550 if(idx < 0 || idx >= (int)bones.size()) {
551 return 0;
552 }
553 return bones[idx];
554 }
556 int Mesh::get_bones_count() const
557 {
558 return (int)bones.size();
559 }
560 */
562 void Mesh::draw() const
563 {
564 int cur_sdr;
565 glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr);
568 ((Mesh*)this)->update_buffers();
570 if(!vattr[MESH_ATTR_VERTEX].vbo_valid) {
571 fprintf(stderr, "%s: invalid vertex buffer\n", __FUNCTION__);
572 return;
573 }
575 if(cur_sdr) {
576 // rendering with shaders
577 if(global_sdr_loc[MESH_ATTR_VERTEX] == -1) {
578 fprintf(stderr, "%s: shader attribute location for vertices unset\n", __FUNCTION__);
579 return;
580 }
582 for(int i=0; i<NUM_MESH_ATTR; i++) {
583 int loc = global_sdr_loc[i];
584 if(loc >= 0 && vattr[i].vbo_valid) {
585 glBindBuffer(GL_ARRAY_BUFFER, vattr[i].vbo);
586 glVertexAttribPointer(loc, vattr[i].nelem, GL_FLOAT, GL_FALSE, 0, 0);
587 glEnableVertexAttribArray(loc);
588 }
589 }
590 } else {
591 #ifndef GL_ES_VERSION_2_0
592 // rendering with fixed-function (not available in GLES2)
593 glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_VERTEX].vbo);
594 glVertexPointer(vattr[MESH_ATTR_VERTEX].nelem, GL_FLOAT, 0, 0);
595 glEnableClientState(GL_VERTEX_ARRAY);
597 if(vattr[MESH_ATTR_NORMAL].vbo_valid) {
598 glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_NORMAL].vbo);
599 glNormalPointer(GL_FLOAT, 0, 0);
600 glEnableClientState(GL_NORMAL_ARRAY);
601 }
602 if(vattr[MESH_ATTR_TEXCOORD].vbo_valid) {
603 glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_TEXCOORD].vbo);
604 glTexCoordPointer(vattr[MESH_ATTR_TEXCOORD].nelem, GL_FLOAT, 0, 0);
605 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
606 }
607 if(vattr[MESH_ATTR_COLOR].vbo_valid) {
608 glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_COLOR].vbo);
609 glColorPointer(vattr[MESH_ATTR_COLOR].nelem, GL_FLOAT, 0, 0);
610 glEnableClientState(GL_COLOR_ARRAY);
611 }
612 #endif
613 }
614 glBindBuffer(GL_ARRAY_BUFFER, 0);
616 if(ibo_valid) {
617 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
618 glDrawElements(GL_TRIANGLES, nfaces * 3, GL_UNSIGNED_INT, 0);
619 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
620 } else {
621 glDrawArrays(GL_TRIANGLES, 0, nverts);
622 }
624 if(cur_sdr) {
625 // rendered with shaders
626 for(int i=0; i<NUM_MESH_ATTR; i++) {
627 int loc = global_sdr_loc[i];
628 if(loc >= 0 && vattr[i].vbo_valid) {
629 glDisableVertexAttribArray(loc);
630 }
631 }
632 } else {
633 #ifndef GL_ES_VERSION_2_0
634 // rendered with fixed-function
635 glDisableClientState(GL_VERTEX_ARRAY);
636 if(vattr[MESH_ATTR_NORMAL].vbo_valid) {
637 glDisableClientState(GL_NORMAL_ARRAY);
638 }
639 if(vattr[MESH_ATTR_TEXCOORD].vbo_valid) {
640 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
641 }
642 if(vattr[MESH_ATTR_COLOR].vbo_valid) {
643 glDisableClientState(GL_COLOR_ARRAY);
644 }
645 #endif
646 }
647 }
649 void Mesh::draw_wire() const
650 {
651 ((Mesh*)this)->update_wire_ibo();
653 if(!vattr[MESH_ATTR_VERTEX].vbo_valid || !wire_ibo_valid) {
654 fprintf(stderr, "%s: invalid vertex buffer\n", __FUNCTION__);
655 return;
656 }
657 if(global_sdr_loc[MESH_ATTR_VERTEX] == -1) {
658 fprintf(stderr, "%s: shader attribute location for vertices unset\n", __FUNCTION__);
659 return;
660 }
662 for(int i=0; i<NUM_MESH_ATTR; i++) {
663 int loc = global_sdr_loc[i];
664 if(loc >= 0 && vattr[i].vbo_valid) {
665 glBindBuffer(GL_ARRAY_BUFFER, vattr[i].vbo);
666 glVertexAttribPointer(loc, vattr[i].nelem, GL_FLOAT, GL_FALSE, 0, 0);
667 glEnableVertexAttribArray(loc);
668 }
669 }
670 glBindBuffer(GL_ARRAY_BUFFER, 0);
672 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, wire_ibo);
673 glDrawElements(GL_LINES, nfaces * 6, GL_UNSIGNED_INT, 0);
674 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
676 for(int i=0; i<NUM_MESH_ATTR; i++) {
677 int loc = global_sdr_loc[i];
678 if(loc >= 0 && vattr[i].vbo_valid) {
679 glDisableVertexAttribArray(loc);
680 }
681 }
682 }
684 void Mesh::draw_vertices() const
685 {
686 ((Mesh*)this)->update_buffers();
688 if(!vattr[MESH_ATTR_VERTEX].vbo_valid) {
689 fprintf(stderr, "%s: invalid vertex buffer\n", __FUNCTION__);
690 return;
691 }
692 if(global_sdr_loc[MESH_ATTR_VERTEX] == -1) {
693 fprintf(stderr, "%s: shader attribute location for vertices unset\n", __FUNCTION__);
694 return;
695 }
697 for(int i=0; i<NUM_MESH_ATTR; i++) {
698 int loc = global_sdr_loc[i];
699 if(loc >= 0 && vattr[i].vbo_valid) {
700 glBindBuffer(GL_ARRAY_BUFFER, vattr[i].vbo);
701 glVertexAttribPointer(loc, vattr[i].nelem, GL_FLOAT, GL_FALSE, 0, 0);
702 glEnableVertexAttribArray(loc);
703 }
704 }
705 glBindBuffer(GL_ARRAY_BUFFER, 0);
707 glDrawArrays(GL_POINTS, 0, nverts);
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 }
717 void Mesh::draw_normals() const
718 {
719 #ifdef USE_OLDGL
720 Vector3 *varr = (Vector3*)get_attrib_data(MESH_ATTR_VERTEX);
721 Vector3 *norm = (Vector3*)get_attrib_data(MESH_ATTR_NORMAL);
722 if(!varr || !norm) {
723 return;
724 }
726 glBegin(GL_LINES);
727 if(get_current_shader()) {
728 int vert_loc = global_sdr_loc[MESH_ATTR_VERTEX];
729 if(vert_loc < 0) {
730 glEnd();
731 return;
732 }
734 for(size_t i=0; i<nverts; i++) {
735 glVertexAttrib3f(vert_loc, varr[i].x, varr[i].y, varr[i].z);
736 Vector3 end = varr[i] + norm[i] * vis_vecsize;
737 glVertexAttrib3f(vert_loc, end.x, end.y, end.z);
738 }
739 } else {
740 for(size_t i=0; i<nverts; i++) {
741 glVertex3f(varr[i].x, varr[i].y, varr[i].z);
742 Vector3 end = varr[i] + norm[i] * vis_vecsize;
743 glVertex3f(end.x, end.y, end.z);
744 }
745 }
746 glEnd();
747 #endif // USE_OLDGL
748 }
750 void Mesh::draw_tangents() const
751 {
752 #ifdef USE_OLDGL
753 Vector3 *varr = (Vector3*)get_attrib_data(MESH_ATTR_VERTEX);
754 Vector3 *tang = (Vector3*)get_attrib_data(MESH_ATTR_TANGENT);
755 if(!varr || !tang) {
756 return;
757 }
759 glBegin(GL_LINES);
760 if(get_current_shader()) {
761 int vert_loc = global_sdr_loc[MESH_ATTR_VERTEX];
762 if(vert_loc < 0) {
763 glEnd();
764 return;
765 }
767 for(size_t i=0; i<nverts; i++) {
768 glVertexAttrib3f(vert_loc, varr[i].x, varr[i].y, varr[i].z);
769 Vector3 end = varr[i] + tang[i] * vis_vecsize;
770 glVertexAttrib3f(vert_loc, end.x, end.y, end.z);
771 }
772 } else {
773 for(size_t i=0; i<nverts; i++) {
774 glVertex3f(varr[i].x, varr[i].y, varr[i].z);
775 Vector3 end = varr[i] + tang[i] * vis_vecsize;
776 glVertex3f(end.x, end.y, end.z);
777 }
778 }
779 glEnd();
780 #endif // USE_OLDGL
781 }
783 void Mesh::get_aabbox(Vector3 *vmin, Vector3 *vmax) const
784 {
785 if(!aabb_valid) {
786 ((Mesh*)this)->calc_aabb();
787 }
788 *vmin = aabb.min;
789 *vmax = aabb.max;
790 }
792 const AABox &Mesh::get_aabbox() const
793 {
794 if(!aabb_valid) {
795 ((Mesh*)this)->calc_aabb();
796 }
797 return aabb;
798 }
800 float Mesh::get_bsphere(Vector3 *center, float *rad) const
801 {
802 if(!bsph_valid) {
803 ((Mesh*)this)->calc_bsph();
804 }
805 *center = bsph.center;
806 *rad = bsph.radius;
807 return bsph.radius;
808 }
810 const Sphere &Mesh::get_bsphere() const
811 {
812 if(!bsph_valid) {
813 ((Mesh*)this)->calc_bsph();
814 }
815 return bsph;
816 }
818 /// static function
819 void Mesh::set_intersect_mode(unsigned int mode)
820 {
821 Mesh::intersect_mode = mode;
822 }
824 /// static function
825 unsigned int Mesh::get_intersect_mode()
826 {
827 return Mesh::intersect_mode;
828 }
830 /// static function
831 void Mesh::set_vertex_select_distance(float dist)
832 {
833 Mesh::vertex_sel_dist = dist;
834 }
836 /// static function
837 float Mesh::get_vertex_select_distance()
838 {
839 return Mesh::vertex_sel_dist;
840 }
842 bool Mesh::intersect(const Ray &ray, HitPoint *hit) const
843 {
844 assert((Mesh::intersect_mode & (ISECT_VERTICES | ISECT_FACE)) != (ISECT_VERTICES | ISECT_FACE));
846 const Vector3 *varr = (Vector3*)get_attrib_data(MESH_ATTR_VERTEX);
847 const Vector3 *narr = (Vector3*)get_attrib_data(MESH_ATTR_NORMAL);
848 if(!varr) {
849 return false;
850 }
851 const unsigned int *idxarr = get_index_data();
853 // first test with the bounding box
854 AABox box;
855 get_aabbox(&box.min, &box.max);
856 if(!box.intersect(ray)) {
857 return false;
858 }
860 HitPoint nearest_hit;
861 nearest_hit.dist = FLT_MAX;
862 nearest_hit.obj = 0;
864 if(Mesh::intersect_mode & ISECT_VERTICES) {
865 // we asked for "intersections" with the vertices of the mesh
866 long nearest_vidx = -1;
867 float thres_sq = Mesh::vertex_sel_dist * Mesh::vertex_sel_dist;
869 for(unsigned int i=0; i<nverts; i++) {
871 if((Mesh::intersect_mode & ISECT_FRONT) && dot_product(narr[i], ray.dir) > 0) {
872 continue;
873 }
875 // project the vertex onto the ray line
876 float t = dot_product(varr[i] - ray.origin, ray.dir);
877 Vector3 vproj = ray.origin + ray.dir * t;
879 float dist_sq = (vproj - varr[i]).length_sq();
880 if(dist_sq < thres_sq) {
881 if(!hit) {
882 return true;
883 }
884 if(t < nearest_hit.dist) {
885 nearest_hit.dist = t;
886 nearest_vidx = i;
887 }
888 }
889 }
891 if(nearest_vidx != -1) {
892 hitvert = varr[nearest_vidx];
893 nearest_hit.obj = &hitvert;
894 }
896 } else {
897 // regular intersection test with polygons
899 for(unsigned int i=0; i<nfaces; i++) {
900 Triangle face(i, varr, idxarr);
902 // ignore back-facing polygons if the mode flags include ISECT_FRONT
903 if((Mesh::intersect_mode & ISECT_FRONT) && dot_product(face.get_normal(), ray.dir) > 0) {
904 continue;
905 }
907 HitPoint fhit;
908 if(face.intersect(ray, hit ? &fhit : 0)) {
909 if(!hit) {
910 return true;
911 }
912 if(fhit.dist < nearest_hit.dist) {
913 nearest_hit = fhit;
914 hitface = face;
915 }
916 }
917 }
918 }
920 if(nearest_hit.obj) {
921 if(hit) {
922 *hit = nearest_hit;
924 // if we are interested in the mesh and not the faces set obj to this
925 if(Mesh::intersect_mode & ISECT_FACE) {
926 hit->obj = &hitface;
927 } else if(Mesh::intersect_mode & ISECT_VERTICES) {
928 hit->obj = &hitvert;
929 } else {
930 hit->obj = this;
931 }
932 }
933 return true;
934 }
935 return false;
936 }
939 // texture coordinate manipulation
940 void Mesh::texcoord_apply_xform(const Matrix4x4 &xform)
941 {
942 if(!has_attrib(MESH_ATTR_TEXCOORD)) {
943 return;
944 }
946 for(unsigned int i=0; i<nverts; i++) {
947 Vector4 tc = get_attrib(MESH_ATTR_TEXCOORD, i);
948 set_attrib(MESH_ATTR_TEXCOORD, i, tc.transformed(xform));
949 }
950 }
952 void Mesh::texcoord_gen_plane(const Vector3 &norm, const Vector3 &tang)
953 {
954 if(!nverts) return;
956 if(!has_attrib(MESH_ATTR_TEXCOORD)) {
957 // allocate texture coordinate attribute array
958 set_attrib_data(MESH_ATTR_TEXCOORD, 2, nverts);
959 }
961 Vector3 n = norm.normalized();
962 Vector3 b = cross_product(n, tang).normalized();
963 Vector3 t = cross_product(b, n);
965 for(unsigned int i=0; i<nverts; i++) {
966 Vector3 pos = get_attrib(MESH_ATTR_VERTEX, i);
968 // distance along the tangent direction
969 float u = dot_product(pos, t);
970 // distance along the bitangent direction
971 float v = dot_product(pos, b);
973 set_attrib(MESH_ATTR_TEXCOORD, i, Vector4(u, v, 0, 1));
974 }
975 }
977 void Mesh::texcoord_gen_box()
978 {
979 if(!nverts || !has_attrib(MESH_ATTR_NORMAL)) return;
981 if(!has_attrib(MESH_ATTR_TEXCOORD)) {
982 // allocate texture coordinate attribute array
983 set_attrib_data(MESH_ATTR_TEXCOORD, 2, nverts);
984 }
986 for(unsigned int i=0; i<nverts; i++) {
987 Vector3 pos = Vector3(get_attrib(MESH_ATTR_VERTEX, i)) * 0.5 + Vector3(0.5, 0.5, 0.5);
988 Vector3 norm = get_attrib(MESH_ATTR_NORMAL, i);
990 float abs_nx = fabs(norm.x);
991 float abs_ny = fabs(norm.y);
992 float abs_nz = fabs(norm.z);
993 int dom = abs_nx > abs_ny && abs_nx > abs_nz ? 0 : (abs_ny > abs_nz ? 1 : 2);
995 float uv[2], *uvptr = uv;
996 for(int j=0; j<3; j++) {
997 if(j == dom) continue; // skip dominant axis
999 *uvptr++ = pos[j];
1001 set_attrib(MESH_ATTR_TEXCOORD, i, Vector4(uv[0], uv[1], 0, 1));
1005 // ------ private member functions ------
1007 void Mesh::calc_aabb()
1009 // the cast is to force calling the const version which doesn't invalidate
1010 if(!((const Mesh*)this)->get_attrib_data(MESH_ATTR_VERTEX)) {
1011 return;
1014 aabb.min = Vector3(FLT_MAX, FLT_MAX, FLT_MAX);
1015 aabb.max = -aabb.min;
1017 for(unsigned int i=0; i<nverts; i++) {
1018 Vector4 v = get_attrib(MESH_ATTR_VERTEX, i);
1019 for(int j=0; j<3; j++) {
1020 if(v[j] < aabb.min[j]) {
1021 aabb.min[j] = v[j];
1023 if(v[j] > aabb.max[j]) {
1024 aabb.max[j] = v[j];
1028 aabb_valid = true;
1031 void Mesh::calc_bsph()
1033 // the cast is to force calling the const version which doesn't invalidate
1034 if(!((const Mesh*)this)->get_attrib_data(MESH_ATTR_VERTEX)) {
1035 return;
1038 Vector3 v;
1039 bsph.center = Vector3(0, 0, 0);
1041 // first find the center
1042 for(unsigned int i=0; i<nverts; i++) {
1043 v = get_attrib(MESH_ATTR_VERTEX, i);
1044 bsph.center += v;
1046 bsph.center /= (float)nverts;
1048 bsph.radius = 0.0f;
1049 for(unsigned int i=0; i<nverts; i++) {
1050 v = get_attrib(MESH_ATTR_VERTEX, i);
1051 float dist_sq = (v - bsph.center).length_sq();
1052 if(dist_sq > bsph.radius) {
1053 bsph.radius = dist_sq;
1056 bsph.radius = sqrt(bsph.radius);
1058 bsph_valid = true;
1061 void Mesh::update_buffers()
1063 for(int i=0; i<NUM_MESH_ATTR; i++) {
1064 if(has_attrib(i) && !vattr[i].vbo_valid) {
1065 glBindBuffer(GL_ARRAY_BUFFER, vattr[i].vbo);
1066 glBufferData(GL_ARRAY_BUFFER, nverts * vattr[i].nelem * sizeof(float), &vattr[i].data[0], GL_STATIC_DRAW);
1067 vattr[i].vbo_valid = true;
1070 glBindBuffer(GL_ARRAY_BUFFER, 0);
1072 if(idata_valid && !ibo_valid) {
1073 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
1074 glBufferData(GL_ELEMENT_ARRAY_BUFFER, nfaces * 3 * sizeof(unsigned int), &idata[0], GL_STATIC_DRAW);
1075 ibo_valid = true;
1077 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1080 void Mesh::update_wire_ibo()
1082 update_buffers();
1084 if(wire_ibo_valid) {
1085 return;
1088 if(!wire_ibo) {
1089 glGenBuffers(1, &wire_ibo);
1092 unsigned int *wire_idxarr = new unsigned int[nfaces * 6];
1093 unsigned int *dest = wire_idxarr;
1095 if(ibo_valid) {
1096 // we're dealing with an indexed mesh
1097 const unsigned int *idxarr = ((const Mesh*)this)->get_index_data();
1099 for(unsigned int i=0; i<nfaces; i++) {
1100 *dest++ = idxarr[0];
1101 *dest++ = idxarr[1];
1102 *dest++ = idxarr[1];
1103 *dest++ = idxarr[2];
1104 *dest++ = idxarr[2];
1105 *dest++ = idxarr[0];
1106 idxarr += 3;
1108 } else {
1109 // not an indexed mesh ...
1110 for(unsigned int i=0; i<nfaces; i++) {
1111 int vidx = i * 3;
1112 *dest++ = vidx;
1113 *dest++ = vidx + 1;
1114 *dest++ = vidx + 1;
1115 *dest++ = vidx + 2;
1116 *dest++ = vidx + 2;
1117 *dest++ = vidx;
1121 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, wire_ibo);
1122 glBufferData(GL_ELEMENT_ARRAY_BUFFER, nfaces * 6 * sizeof(unsigned int), wire_idxarr, GL_STATIC_DRAW);
1123 delete [] wire_idxarr;
1124 wire_ibo_valid = true;
1125 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1129 // ------ class Triangle ------
1130 Triangle::Triangle()
1132 normal_valid = false;
1133 id = -1;
1136 Triangle::Triangle(const Vector3 &v0, const Vector3 &v1, const Vector3 &v2)
1138 v[0] = v0;
1139 v[1] = v1;
1140 v[2] = v2;
1141 normal_valid = false;
1142 id = -1;
1145 Triangle::Triangle(int n, const Vector3 *varr, const unsigned int *idxarr)
1147 if(idxarr) {
1148 v[0] = varr[idxarr[n * 3]];
1149 v[1] = varr[idxarr[n * 3 + 1]];
1150 v[2] = varr[idxarr[n * 3 + 2]];
1151 } else {
1152 v[0] = varr[n * 3];
1153 v[1] = varr[n * 3 + 1];
1154 v[2] = varr[n * 3 + 2];
1156 normal_valid = false;
1157 id = n;
1160 void Triangle::calc_normal()
1162 normal = cross_product(v[1] - v[0], v[2] - v[0]).normalized();
1163 normal_valid = true;
1166 const Vector3 &Triangle::get_normal() const
1168 if(!normal_valid) {
1169 ((Triangle*)this)->calc_normal();
1171 return normal;
1174 void Triangle::transform(const Matrix4x4 &xform)
1176 v[0].transform(xform);
1177 v[1].transform(xform);
1178 v[2].transform(xform);
1179 normal_valid = false;
1182 void Triangle::draw() const
1184 Vector3 n[3];
1185 n[0] = get_normal();
1186 n[1] = get_normal();
1187 n[2] = get_normal();
1189 int vloc = Mesh::get_attrib_location(MESH_ATTR_VERTEX);
1190 int nloc = Mesh::get_attrib_location(MESH_ATTR_NORMAL);
1192 glEnableVertexAttribArray(vloc);
1193 glVertexAttribPointer(vloc, 3, GL_FLOAT, GL_FALSE, 0, &v[0].x);
1194 glVertexAttribPointer(nloc, 3, GL_FLOAT, GL_FALSE, 0, &n[0].x);
1196 glDrawArrays(GL_TRIANGLES, 0, 3);
1198 glDisableVertexAttribArray(vloc);
1199 glDisableVertexAttribArray(nloc);
1202 void Triangle::draw_wire() const
1204 static const int idxarr[] = {0, 1, 1, 2, 2, 0};
1205 int vloc = Mesh::get_attrib_location(MESH_ATTR_VERTEX);
1207 glEnableVertexAttribArray(vloc);
1208 glVertexAttribPointer(vloc, 3, GL_FLOAT, GL_FALSE, 0, &v[0].x);
1210 glDrawElements(GL_LINES, 6, GL_UNSIGNED_INT, idxarr);
1212 glDisableVertexAttribArray(vloc);
1215 Vector3 Triangle::calc_barycentric(const Vector3 &pos) const
1217 Vector3 norm = get_normal();
1219 float area_sq = fabs(dot_product(cross_product(v[1] - v[0], v[2] - v[0]), norm));
1220 if(area_sq < 1e-5) {
1221 return Vector3(0, 0, 0);
1224 float asq0 = fabs(dot_product(cross_product(v[1] - pos, v[2] - pos), norm));
1225 float asq1 = fabs(dot_product(cross_product(v[2] - pos, v[0] - pos), norm));
1226 float asq2 = fabs(dot_product(cross_product(v[0] - pos, v[1] - pos), norm));
1228 return Vector3(asq0 / area_sq, asq1 / area_sq, asq2 / area_sq);
1231 bool Triangle::intersect(const Ray &ray, HitPoint *hit) const
1233 Vector3 normal = get_normal();
1235 float ndotdir = dot_product(ray.dir, normal);
1236 if(fabs(ndotdir) < 1e-4) {
1237 return false;
1240 Vector3 vertdir = v[0] - ray.origin;
1241 float t = dot_product(normal, vertdir) / ndotdir;
1243 Vector3 pos = ray.origin + ray.dir * t;
1244 Vector3 bary = calc_barycentric(pos);
1246 if(bary.x + bary.y + bary.z > 1.00001) {
1247 return false;
1250 if(hit) {
1251 hit->dist = t;
1252 hit->pos = ray.origin + ray.dir * t;
1253 hit->normal = normal;
1254 hit->obj = this;
1256 return true;