cubemapper

view src/mesh.cc @ 4:2bfafdced01a

added README, COPYING, and copyright headers
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 30 Jul 2017 16:11:19 +0300
parents 8fc9e1d3aad2
children
line source
1 /*
2 Cubemapper - a program for converting panoramic images into cubemaps
3 Copyright (C) 2017 John Tsiombikas <nuclear@member.fsf.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <float.h>
21 #include <assert.h>
22 #include "opengl.h"
23 #include "mesh.h"
24 //#include "xform_node.h"
26 #define USE_OLDGL
28 bool Mesh::use_custom_sdr_attr = true;
29 int Mesh::global_sdr_loc[NUM_MESH_ATTR] = { 0, 1, 2, 3, 4, 5, 6 };
30 /*
31 (int)SDR_ATTR_VERTEX,
32 (int)SDR_ATTR_NORMAL,
33 (int)SDR_ATTR_TANGENT,
34 (int)SDR_ATTR_TEXCOORD,
35 (int)SDR_ATTR_COLOR,
36 -1, -1};
37 */
38 unsigned int Mesh::intersect_mode = ISECT_DEFAULT;
39 float Mesh::vertex_sel_dist = 0.01;
40 float Mesh::vis_vecsize = 1.0;
42 Mesh::Mesh()
43 {
44 clear();
46 glGenBuffers(NUM_MESH_ATTR + 1, buffer_objects);
48 for(int i=0; i<NUM_MESH_ATTR; i++) {
49 vattr[i].vbo = buffer_objects[i];
50 }
51 ibo = buffer_objects[NUM_MESH_ATTR];
52 wire_ibo = 0;
53 }
55 Mesh::~Mesh()
56 {
57 glDeleteBuffers(NUM_MESH_ATTR + 1, buffer_objects);
59 if(wire_ibo) {
60 glDeleteBuffers(1, &wire_ibo);
61 }
62 }
64 Mesh::Mesh(const Mesh &rhs)
65 {
66 clear();
68 glGenBuffers(NUM_MESH_ATTR + 1, buffer_objects);
70 for(int i=0; i<NUM_MESH_ATTR; i++) {
71 vattr[i].vbo = buffer_objects[i];
72 }
73 ibo = buffer_objects[NUM_MESH_ATTR];
74 wire_ibo = 0;
76 clone(rhs);
77 }
79 Mesh &Mesh::operator =(const Mesh &rhs)
80 {
81 if(&rhs != this) {
82 clone(rhs);
83 }
84 return *this;
85 }
87 bool Mesh::clone(const Mesh &m)
88 {
89 clear();
91 for(int i=0; i<NUM_MESH_ATTR; i++) {
92 if(m.has_attrib(i)) {
93 m.get_attrib_data(i); // force validation of the actual data on the source mesh
95 vattr[i].nelem = m.vattr[i].nelem;
96 vattr[i].data = m.vattr[i].data; // copy the actual data
97 vattr[i].data_valid = true;
98 }
99 }
101 if(m.is_indexed()) {
102 m.get_index_data(); // again, force validation
104 // copy the index data
105 idata = m.idata;
106 idata_valid = true;
107 }
109 name = m.name;
110 nverts = m.nverts;
111 nfaces = m.nfaces;
113 //bones = m.bones;
115 memcpy(cur_val, m.cur_val, sizeof cur_val);
117 aabb = m.aabb;
118 aabb_valid = m.aabb_valid;
119 bsph = m.bsph;
120 bsph_valid = m.bsph_valid;
122 hitface = m.hitface;
123 hitvert = m.hitvert;
125 intersect_mode = m.intersect_mode;
126 vertex_sel_dist = m.vertex_sel_dist;
127 vis_vecsize = m.vis_vecsize;
129 return true;
130 }
132 void Mesh::set_name(const char *name)
133 {
134 this->name = name;
135 }
137 const char *Mesh::get_name() const
138 {
139 return name.c_str();
140 }
142 bool Mesh::has_attrib(int attr) const
143 {
144 if(attr < 0 || attr >= NUM_MESH_ATTR) {
145 return false;
146 }
148 // if neither of these is valid, then nobody has set this attribute
149 return vattr[attr].vbo_valid || vattr[attr].data_valid;
150 }
152 bool Mesh::is_indexed() const
153 {
154 return ibo_valid || idata_valid;
155 }
157 void Mesh::clear()
158 {
159 //bones.clear();
161 for(int i=0; i<NUM_MESH_ATTR; i++) {
162 vattr[i].nelem = 0;
163 vattr[i].vbo_valid = false;
164 vattr[i].data_valid = false;
165 //vattr[i].sdr_loc = -1;
166 vattr[i].data.clear();
167 }
168 ibo_valid = idata_valid = false;
169 idata.clear();
171 wire_ibo_valid = false;
173 nverts = nfaces = 0;
175 bsph_valid = false;
176 aabb_valid = false;
177 }
179 float *Mesh::set_attrib_data(int attrib, int nelem, unsigned int num, const float *data)
180 {
181 if(attrib < 0 || attrib >= NUM_MESH_ATTR) {
182 fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib);
183 return 0;
184 }
186 if(nverts && num != nverts) {
187 fprintf(stderr, "%s: attribute count missmatch (%d instead of %d)\n", __FUNCTION__, num, nverts);
188 return 0;
189 }
190 nverts = num;
192 vattr[attrib].data.clear();
193 vattr[attrib].nelem = nelem;
194 vattr[attrib].data.resize(num * nelem);
196 if(data) {
197 memcpy(&vattr[attrib].data[0], data, num * nelem * sizeof *data);
198 }
200 vattr[attrib].data_valid = true;
201 vattr[attrib].vbo_valid = false;
202 return &vattr[attrib].data[0];
203 }
205 float *Mesh::get_attrib_data(int attrib)
206 {
207 if(attrib < 0 || attrib >= NUM_MESH_ATTR) {
208 fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib);
209 return 0;
210 }
212 vattr[attrib].vbo_valid = false;
213 return (float*)((const Mesh*)this)->get_attrib_data(attrib);
214 }
216 const float *Mesh::get_attrib_data(int attrib) const
217 {
218 if(attrib < 0 || attrib >= NUM_MESH_ATTR) {
219 fprintf(stderr, "%s: invalid attrib: %d\n", __FUNCTION__, attrib);
220 return 0;
221 }
223 if(!vattr[attrib].data_valid) {
224 #if GL_ES_VERSION_2_0
225 fprintf(stderr, "%s: can't read back attrib data on CrippledGL ES\n", __FUNCTION__);
226 return 0;
227 #else
228 if(!vattr[attrib].vbo_valid) {
229 fprintf(stderr, "%s: unavailable attrib: %d\n", __FUNCTION__, attrib);
230 return 0;
231 }
233 // local data copy is unavailable, grab the data from the vbo
234 Mesh *m = (Mesh*)this;
235 m->vattr[attrib].data.resize(nverts * vattr[attrib].nelem);
237 glBindBuffer(GL_ARRAY_BUFFER, vattr[attrib].vbo);
238 void *data = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
239 memcpy(&m->vattr[attrib].data[0], data, nverts * vattr[attrib].nelem * sizeof(float));
240 glUnmapBuffer(GL_ARRAY_BUFFER);
242 vattr[attrib].data_valid = true;
243 #endif
244 }
246 return &vattr[attrib].data[0];
247 }
249 void Mesh::set_attrib(int attrib, int idx, const Vec4 &v)
250 {
251 float *data = get_attrib_data(attrib);
252 if(data) {
253 data += idx * vattr[attrib].nelem;
254 for(int i=0; i<vattr[attrib].nelem; i++) {
255 data[i] = v[i];
256 }
257 }
258 }
260 Vec4 Mesh::get_attrib(int attrib, int idx) const
261 {
262 Vec4 v(0.0, 0.0, 0.0, 1.0);
263 const float *data = get_attrib_data(attrib);
264 if(data) {
265 data += idx * vattr[attrib].nelem;
266 for(int i=0; i<vattr[attrib].nelem; i++) {
267 v[i] = data[i];
268 }
269 }
270 return v;
271 }
273 int Mesh::get_attrib_count(int attrib) const
274 {
275 return has_attrib(attrib) ? nverts : 0;
276 }
279 unsigned int *Mesh::set_index_data(int num, const unsigned int *indices)
280 {
281 int nidx = nfaces * 3;
282 if(nidx && num != nidx) {
283 fprintf(stderr, "%s: index count missmatch (%d instead of %d)\n", __FUNCTION__, num, nidx);
284 return 0;
285 }
286 nfaces = num / 3;
288 idata.clear();
289 idata.resize(num);
291 if(indices) {
292 memcpy(&idata[0], indices, num * sizeof *indices);
293 }
295 idata_valid = true;
296 ibo_valid = false;
298 return &idata[0];
299 }
301 unsigned int *Mesh::get_index_data()
302 {
303 ibo_valid = false;
304 return (unsigned int*)((const Mesh*)this)->get_index_data();
305 }
307 const unsigned int *Mesh::get_index_data() const
308 {
309 if(!idata_valid) {
310 #if GL_ES_VERSION_2_0
311 fprintf(stderr, "%s: can't read back index data in CrippledGL ES\n", __FUNCTION__);
312 return 0;
313 #else
314 if(!ibo_valid) {
315 fprintf(stderr, "%s: indices unavailable\n", __FUNCTION__);
316 return 0;
317 }
319 // local data copy is unavailable, gram the data from the ibo
320 Mesh *m = (Mesh*)this;
321 int nidx = nfaces * 3;
322 m->idata.resize(nidx);
324 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
325 void *data = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY);
326 memcpy(&m->idata[0], data, nidx * sizeof(unsigned int));
327 glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
329 idata_valid = true;
330 #endif
331 }
333 return &idata[0];
334 }
336 int Mesh::get_index_count() const
337 {
338 return nfaces * 3;
339 }
341 void Mesh::append(const Mesh &mesh)
342 {
343 unsigned int idxoffs = nverts;
345 if(!nverts) {
346 clone(mesh);
347 return;
348 }
350 nverts += mesh.nverts;
351 nfaces += mesh.nfaces;
353 for(int i=0; i<NUM_MESH_ATTR; i++) {
354 if(has_attrib(i) && mesh.has_attrib(i)) {
355 // force validating the data arrays
356 get_attrib_data(i);
357 mesh.get_attrib_data(i);
359 // append the mesh data
360 vattr[i].data.insert(vattr[i].data.end(), mesh.vattr[i].data.begin(), mesh.vattr[i].data.end());
361 }
362 }
364 if(ibo_valid || idata_valid) {
365 // make index arrays valid
366 get_index_data();
367 mesh.get_index_data();
369 size_t orig_sz = idata.size();
371 idata.insert(idata.end(), mesh.idata.begin(), mesh.idata.end());
373 // fixup all the new indices
374 for(size_t i=orig_sz; i<idata.size(); i++) {
375 idata[i] += idxoffs;
376 }
377 }
379 // fuck everything
380 wire_ibo_valid = false;
381 aabb_valid = false;
382 bsph_valid = false;
383 }
385 // assemble a complete vertex by adding all the useful attributes
386 void Mesh::vertex(float x, float y, float z)
387 {
388 cur_val[MESH_ATTR_VERTEX] = Vec4(x, y, z, 1.0f);
389 vattr[MESH_ATTR_VERTEX].data_valid = true;
390 vattr[MESH_ATTR_VERTEX].nelem = 3;
392 for(int i=0; i<NUM_MESH_ATTR; i++) {
393 if(vattr[i].data_valid) {
394 for(int j=0; j<vattr[MESH_ATTR_VERTEX].nelem; j++) {
395 vattr[i].data.push_back(cur_val[i][j]);
396 }
397 }
398 vattr[i].vbo_valid = false;
399 }
401 if(idata_valid) {
402 idata.clear();
403 }
404 ibo_valid = idata_valid = false;
405 }
407 void Mesh::normal(float nx, float ny, float nz)
408 {
409 cur_val[MESH_ATTR_NORMAL] = Vec4(nx, ny, nz, 1.0f);
410 vattr[MESH_ATTR_NORMAL].data_valid = true;
411 vattr[MESH_ATTR_NORMAL].nelem = 3;
412 }
414 void Mesh::tangent(float tx, float ty, float tz)
415 {
416 cur_val[MESH_ATTR_TANGENT] = Vec4(tx, ty, tz, 1.0f);
417 vattr[MESH_ATTR_TANGENT].data_valid = true;
418 vattr[MESH_ATTR_TANGENT].nelem = 3;
419 }
421 void Mesh::texcoord(float u, float v, float w)
422 {
423 cur_val[MESH_ATTR_TEXCOORD] = Vec4(u, v, w, 1.0f);
424 vattr[MESH_ATTR_TEXCOORD].data_valid = true;
425 vattr[MESH_ATTR_TEXCOORD].nelem = 3;
426 }
428 void Mesh::boneweights(float w1, float w2, float w3, float w4)
429 {
430 cur_val[MESH_ATTR_BONEWEIGHTS] = Vec4(w1, w2, w3, w4);
431 vattr[MESH_ATTR_BONEWEIGHTS].data_valid = true;
432 vattr[MESH_ATTR_BONEWEIGHTS].nelem = 4;
433 }
435 void Mesh::boneidx(int idx1, int idx2, int idx3, int idx4)
436 {
437 cur_val[MESH_ATTR_BONEIDX] = Vec4(idx1, idx2, idx3, idx4);
438 vattr[MESH_ATTR_BONEIDX].data_valid = true;
439 vattr[MESH_ATTR_BONEIDX].nelem = 4;
440 }
442 int Mesh::get_poly_count() const
443 {
444 if(nfaces) {
445 return nfaces;
446 }
447 if(nverts) {
448 return nverts / 3;
449 }
450 return 0;
451 }
453 /// static function
454 void Mesh::set_attrib_location(int attr, int loc)
455 {
456 if(attr < 0 || attr >= NUM_MESH_ATTR) {
457 return;
458 }
459 Mesh::global_sdr_loc[attr] = loc;
460 }
462 /// static function
463 int Mesh::get_attrib_location(int attr)
464 {
465 if(attr < 0 || attr >= NUM_MESH_ATTR) {
466 return -1;
467 }
468 return Mesh::global_sdr_loc[attr];
469 }
471 /// static function
472 void Mesh::clear_attrib_locations()
473 {
474 for(int i=0; i<NUM_MESH_ATTR; i++) {
475 Mesh::global_sdr_loc[i] = -1;
476 }
477 }
479 /// static function
480 void Mesh::set_vis_vecsize(float sz)
481 {
482 Mesh::vis_vecsize = sz;
483 }
485 float Mesh::get_vis_vecsize()
486 {
487 return Mesh::vis_vecsize;
488 }
490 void Mesh::apply_xform(const Mat4 &xform)
491 {
492 Mat4 dir_xform = xform.upper3x3();
493 apply_xform(xform, dir_xform);
494 }
496 void Mesh::apply_xform(const Mat4 &xform, const Mat4 &dir_xform)
497 {
498 for(unsigned int i=0; i<nverts; i++) {
499 Vec4 v = get_attrib(MESH_ATTR_VERTEX, i);
500 set_attrib(MESH_ATTR_VERTEX, i, xform * v);
502 if(has_attrib(MESH_ATTR_NORMAL)) {
503 Vec3 n = get_attrib(MESH_ATTR_NORMAL, i).xyz();
504 set_attrib(MESH_ATTR_NORMAL, i, Vec4(dir_xform * n));
505 }
506 if(has_attrib(MESH_ATTR_TANGENT)) {
507 Vec3 t = get_attrib(MESH_ATTR_TANGENT, i).xyz();
508 set_attrib(MESH_ATTR_TANGENT, i, Vec4(dir_xform * t));
509 }
510 }
511 }
513 void Mesh::flip()
514 {
515 flip_faces();
516 flip_normals();
517 }
519 void Mesh::flip_faces()
520 {
521 if(is_indexed()) {
522 unsigned int *indices = get_index_data();
523 if(!indices) return;
525 int idxnum = get_index_count();
526 for(int i=0; i<idxnum; i+=3) {
527 unsigned int tmp = indices[i + 2];
528 indices[i + 2] = indices[i + 1];
529 indices[i + 1] = tmp;
530 }
532 } else {
533 Vec3 *verts = (Vec3*)get_attrib_data(MESH_ATTR_VERTEX);
534 if(!verts) return;
536 int vnum = get_attrib_count(MESH_ATTR_VERTEX);
537 for(int i=0; i<vnum; i+=3) {
538 Vec3 tmp = verts[i + 2];
539 verts[i + 2] = verts[i + 1];
540 verts[i + 1] = tmp;
541 }
542 }
543 }
545 void Mesh::flip_normals()
546 {
547 Vec3 *normals = (Vec3*)get_attrib_data(MESH_ATTR_NORMAL);
548 if(!normals) return;
550 int num = get_attrib_count(MESH_ATTR_NORMAL);
551 for(int i=0; i<num; i++) {
552 normals[i] = -normals[i];
553 }
554 }
556 /*
557 int Mesh::add_bone(XFormNode *bone)
558 {
559 int idx = bones.size();
560 bones.push_back(bone);
561 return idx;
562 }
564 const XFormNode *Mesh::get_bone(int idx) const
565 {
566 if(idx < 0 || idx >= (int)bones.size()) {
567 return 0;
568 }
569 return bones[idx];
570 }
572 int Mesh::get_bones_count() const
573 {
574 return (int)bones.size();
575 }
576 */
578 bool Mesh::pre_draw() const
579 {
580 cur_sdr = 0;
581 glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr);
583 ((Mesh*)this)->update_buffers();
585 if(!vattr[MESH_ATTR_VERTEX].vbo_valid) {
586 fprintf(stderr, "%s: invalid vertex buffer\n", __FUNCTION__);
587 return false;
588 }
590 if(cur_sdr && use_custom_sdr_attr) {
591 // rendering with shaders
592 if(global_sdr_loc[MESH_ATTR_VERTEX] == -1) {
593 fprintf(stderr, "%s: shader attribute location for vertices unset\n", __FUNCTION__);
594 return false;
595 }
597 for(int i=0; i<NUM_MESH_ATTR; i++) {
598 int loc = global_sdr_loc[i];
599 if(loc >= 0 && vattr[i].vbo_valid) {
600 glBindBuffer(GL_ARRAY_BUFFER, vattr[i].vbo);
601 glVertexAttribPointer(loc, vattr[i].nelem, GL_FLOAT, GL_FALSE, 0, 0);
602 glEnableVertexAttribArray(loc);
603 }
604 }
605 } else {
606 #ifndef GL_ES_VERSION_2_0
607 // rendering with fixed-function (not available in GLES2)
608 glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_VERTEX].vbo);
609 glVertexPointer(vattr[MESH_ATTR_VERTEX].nelem, GL_FLOAT, 0, 0);
610 glEnableClientState(GL_VERTEX_ARRAY);
612 if(vattr[MESH_ATTR_NORMAL].vbo_valid) {
613 glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_NORMAL].vbo);
614 glNormalPointer(GL_FLOAT, 0, 0);
615 glEnableClientState(GL_NORMAL_ARRAY);
616 }
617 if(vattr[MESH_ATTR_TEXCOORD].vbo_valid) {
618 glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_TEXCOORD].vbo);
619 glTexCoordPointer(vattr[MESH_ATTR_TEXCOORD].nelem, GL_FLOAT, 0, 0);
620 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
621 }
622 if(vattr[MESH_ATTR_COLOR].vbo_valid) {
623 glBindBuffer(GL_ARRAY_BUFFER, vattr[MESH_ATTR_COLOR].vbo);
624 glColorPointer(vattr[MESH_ATTR_COLOR].nelem, GL_FLOAT, 0, 0);
625 glEnableClientState(GL_COLOR_ARRAY);
626 }
627 #endif
628 }
629 glBindBuffer(GL_ARRAY_BUFFER, 0);
631 return true;
632 }
634 void Mesh::draw() const
635 {
636 if(!pre_draw()) return;
638 if(ibo_valid) {
639 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
640 glDrawElements(GL_TRIANGLES, nfaces * 3, GL_UNSIGNED_INT, 0);
641 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
642 } else {
643 glDrawArrays(GL_TRIANGLES, 0, nverts);
644 }
646 post_draw();
647 }
649 void Mesh::post_draw() const
650 {
651 if(cur_sdr && use_custom_sdr_attr) {
652 // rendered with shaders
653 for(int i=0; i<NUM_MESH_ATTR; i++) {
654 int loc = global_sdr_loc[i];
655 if(loc >= 0 && vattr[i].vbo_valid) {
656 glDisableVertexAttribArray(loc);
657 }
658 }
659 } else {
660 #ifndef GL_ES_VERSION_2_0
661 // rendered with fixed-function
662 glDisableClientState(GL_VERTEX_ARRAY);
663 if(vattr[MESH_ATTR_NORMAL].vbo_valid) {
664 glDisableClientState(GL_NORMAL_ARRAY);
665 }
666 if(vattr[MESH_ATTR_TEXCOORD].vbo_valid) {
667 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
668 }
669 if(vattr[MESH_ATTR_COLOR].vbo_valid) {
670 glDisableClientState(GL_COLOR_ARRAY);
671 }
672 #endif
673 }
674 }
676 void Mesh::draw_wire() const
677 {
678 if(!pre_draw()) return;
680 ((Mesh*)this)->update_wire_ibo();
682 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, wire_ibo);
683 glDrawElements(GL_LINES, nfaces * 6, GL_UNSIGNED_INT, 0);
684 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
686 post_draw();
687 }
689 void Mesh::draw_vertices() const
690 {
691 if(!pre_draw()) return;
693 glDrawArrays(GL_POINTS, 0, nverts);
695 post_draw();
696 }
698 void Mesh::draw_normals() const
699 {
700 #ifdef USE_OLDGL
701 int cur_sdr = 0;
702 glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr);
704 Vec3 *varr = (Vec3*)get_attrib_data(MESH_ATTR_VERTEX);
705 Vec3 *norm = (Vec3*)get_attrib_data(MESH_ATTR_NORMAL);
706 if(!varr || !norm) {
707 return;
708 }
710 glBegin(GL_LINES);
711 if(cur_sdr && use_custom_sdr_attr) {
712 int vert_loc = global_sdr_loc[MESH_ATTR_VERTEX];
713 if(vert_loc < 0) {
714 glEnd();
715 return;
716 }
718 for(size_t i=0; i<nverts; i++) {
719 glVertexAttrib3f(vert_loc, varr[i].x, varr[i].y, varr[i].z);
720 Vec3 end = varr[i] + norm[i] * vis_vecsize;
721 glVertexAttrib3f(vert_loc, end.x, end.y, end.z);
722 }
723 } else {
724 for(size_t i=0; i<nverts; i++) {
725 glVertex3f(varr[i].x, varr[i].y, varr[i].z);
726 Vec3 end = varr[i] + norm[i] * vis_vecsize;
727 glVertex3f(end.x, end.y, end.z);
728 }
729 }
730 glEnd();
731 #endif // USE_OLDGL
732 }
734 void Mesh::draw_tangents() const
735 {
736 #ifdef USE_OLDGL
737 int cur_sdr = 0;
738 glGetIntegerv(GL_CURRENT_PROGRAM, &cur_sdr);
740 Vec3 *varr = (Vec3*)get_attrib_data(MESH_ATTR_VERTEX);
741 Vec3 *tang = (Vec3*)get_attrib_data(MESH_ATTR_TANGENT);
742 if(!varr || !tang) {
743 return;
744 }
746 glBegin(GL_LINES);
747 if(cur_sdr && use_custom_sdr_attr) {
748 int vert_loc = global_sdr_loc[MESH_ATTR_VERTEX];
749 if(vert_loc < 0) {
750 glEnd();
751 return;
752 }
754 for(size_t i=0; i<nverts; i++) {
755 glVertexAttrib3f(vert_loc, varr[i].x, varr[i].y, varr[i].z);
756 Vec3 end = varr[i] + tang[i] * vis_vecsize;
757 glVertexAttrib3f(vert_loc, end.x, end.y, end.z);
758 }
759 } else {
760 for(size_t i=0; i<nverts; i++) {
761 glVertex3f(varr[i].x, varr[i].y, varr[i].z);
762 Vec3 end = varr[i] + tang[i] * vis_vecsize;
763 glVertex3f(end.x, end.y, end.z);
764 }
765 }
766 glEnd();
767 #endif // USE_OLDGL
768 }
770 void Mesh::get_aabbox(Vec3 *vmin, Vec3 *vmax) const
771 {
772 if(!aabb_valid) {
773 ((Mesh*)this)->calc_aabb();
774 }
775 *vmin = aabb.min;
776 *vmax = aabb.max;
777 }
779 const AABox &Mesh::get_aabbox() const
780 {
781 if(!aabb_valid) {
782 ((Mesh*)this)->calc_aabb();
783 }
784 return aabb;
785 }
787 float Mesh::get_bsphere(Vec3 *center, float *rad) const
788 {
789 if(!bsph_valid) {
790 ((Mesh*)this)->calc_bsph();
791 }
792 *center = bsph.center;
793 *rad = bsph.radius;
794 return bsph.radius;
795 }
797 const Sphere &Mesh::get_bsphere() const
798 {
799 if(!bsph_valid) {
800 ((Mesh*)this)->calc_bsph();
801 }
802 return bsph;
803 }
805 /// static function
806 void Mesh::set_intersect_mode(unsigned int mode)
807 {
808 Mesh::intersect_mode = mode;
809 }
811 /// static function
812 unsigned int Mesh::get_intersect_mode()
813 {
814 return Mesh::intersect_mode;
815 }
817 /// static function
818 void Mesh::set_vertex_select_distance(float dist)
819 {
820 Mesh::vertex_sel_dist = dist;
821 }
823 /// static function
824 float Mesh::get_vertex_select_distance()
825 {
826 return Mesh::vertex_sel_dist;
827 }
829 bool Mesh::intersect(const Ray &ray, HitPoint *hit) const
830 {
831 assert((Mesh::intersect_mode & (ISECT_VERTICES | ISECT_FACE)) != (ISECT_VERTICES | ISECT_FACE));
833 const Vec3 *varr = (Vec3*)get_attrib_data(MESH_ATTR_VERTEX);
834 const Vec3 *narr = (Vec3*)get_attrib_data(MESH_ATTR_NORMAL);
835 if(!varr) {
836 return false;
837 }
838 const unsigned int *idxarr = get_index_data();
840 // first test with the bounding box
841 AABox box;
842 get_aabbox(&box.min, &box.max);
843 if(!box.intersect(ray)) {
844 return false;
845 }
847 HitPoint nearest_hit;
848 nearest_hit.dist = FLT_MAX;
849 nearest_hit.obj = 0;
851 if(Mesh::intersect_mode & ISECT_VERTICES) {
852 // we asked for "intersections" with the vertices of the mesh
853 long nearest_vidx = -1;
854 float thres_sq = Mesh::vertex_sel_dist * Mesh::vertex_sel_dist;
856 for(unsigned int i=0; i<nverts; i++) {
858 if((Mesh::intersect_mode & ISECT_FRONT) && dot(narr[i], ray.dir) > 0) {
859 continue;
860 }
862 // project the vertex onto the ray line
863 float t = dot(varr[i] - ray.origin, ray.dir);
864 Vec3 vproj = ray.origin + ray.dir * t;
866 float dist_sq = length_sq(vproj - varr[i]);
867 if(dist_sq < thres_sq) {
868 if(!hit) {
869 return true;
870 }
871 if(t < nearest_hit.dist) {
872 nearest_hit.dist = t;
873 nearest_vidx = i;
874 }
875 }
876 }
878 if(nearest_vidx != -1) {
879 hitvert = varr[nearest_vidx];
880 nearest_hit.obj = &hitvert;
881 }
883 } else {
884 // regular intersection test with polygons
886 for(unsigned int i=0; i<nfaces; i++) {
887 Triangle face(i, varr, idxarr);
889 // ignore back-facing polygons if the mode flags include ISECT_FRONT
890 if((Mesh::intersect_mode & ISECT_FRONT) && dot(face.get_normal(), ray.dir) > 0) {
891 continue;
892 }
894 HitPoint fhit;
895 if(face.intersect(ray, hit ? &fhit : 0)) {
896 if(!hit) {
897 return true;
898 }
899 if(fhit.dist < nearest_hit.dist) {
900 nearest_hit = fhit;
901 hitface = face;
902 }
903 }
904 }
905 }
907 if(nearest_hit.obj) {
908 if(hit) {
909 *hit = nearest_hit;
911 // if we are interested in the mesh and not the faces set obj to this
912 if(Mesh::intersect_mode & ISECT_FACE) {
913 hit->obj = &hitface;
914 } else if(Mesh::intersect_mode & ISECT_VERTICES) {
915 hit->obj = &hitvert;
916 } else {
917 hit->obj = this;
918 }
919 }
920 return true;
921 }
922 return false;
923 }
926 // texture coordinate manipulation
927 void Mesh::texcoord_apply_xform(const Mat4 &xform)
928 {
929 if(!has_attrib(MESH_ATTR_TEXCOORD)) {
930 return;
931 }
933 for(unsigned int i=0; i<nverts; i++) {
934 Vec4 tc = get_attrib(MESH_ATTR_TEXCOORD, i);
935 set_attrib(MESH_ATTR_TEXCOORD, i, xform * tc);
936 }
937 }
939 void Mesh::texcoord_gen_plane(const Vec3 &norm, const Vec3 &tang)
940 {
941 if(!nverts) return;
943 if(!has_attrib(MESH_ATTR_TEXCOORD)) {
944 // allocate texture coordinate attribute array
945 set_attrib_data(MESH_ATTR_TEXCOORD, 2, nverts);
946 }
948 Vec3 n = normalize(norm);
949 Vec3 b = normalize(cross(n, tang));
950 Vec3 t = cross(b, n);
952 for(unsigned int i=0; i<nverts; i++) {
953 Vec3 pos = get_attrib(MESH_ATTR_VERTEX, i).xyz();
955 // distance along the tangent direction
956 float u = dot(pos, t);
957 // distance along the bitangent direction
958 float v = dot(pos, b);
960 set_attrib(MESH_ATTR_TEXCOORD, i, Vec4(u, v, 0, 1));
961 }
962 }
964 void Mesh::texcoord_gen_box()
965 {
966 if(!nverts || !has_attrib(MESH_ATTR_NORMAL)) return;
968 if(!has_attrib(MESH_ATTR_TEXCOORD)) {
969 // allocate texture coordinate attribute array
970 set_attrib_data(MESH_ATTR_TEXCOORD, 2, nverts);
971 }
973 for(unsigned int i=0; i<nverts; i++) {
974 Vec3 pos = Vec3(get_attrib(MESH_ATTR_VERTEX, i)) * 0.5 + Vec3(0.5, 0.5, 0.5);
975 Vec3 norm = get_attrib(MESH_ATTR_NORMAL, i).xyz();
977 float abs_nx = fabs(norm.x);
978 float abs_ny = fabs(norm.y);
979 float abs_nz = fabs(norm.z);
980 int dom = abs_nx > abs_ny && abs_nx > abs_nz ? 0 : (abs_ny > abs_nz ? 1 : 2);
982 float uv[2], *uvptr = uv;
983 for(int j=0; j<3; j++) {
984 if(j == dom) continue; // skip dominant axis
986 *uvptr++ = pos[j];
987 }
988 set_attrib(MESH_ATTR_TEXCOORD, i, Vec4(uv[0], uv[1], 0, 1));
989 }
990 }
992 void Mesh::texcoord_gen_cylinder()
993 {
994 if(!nverts) return;
996 if(!has_attrib(MESH_ATTR_TEXCOORD)) {
997 // allocate texture coordinate attribute array
998 set_attrib_data(MESH_ATTR_TEXCOORD, 2, nverts);
999 }
1001 for(unsigned int i=0; i<nverts; i++) {
1002 Vec3 pos = get_attrib(MESH_ATTR_VERTEX, i).xyz();
1004 float rho = sqrt(pos.x * pos.x + pos.z * pos.z);
1005 float theta = rho == 0.0 ? 0.0 : atan2(pos.z / rho, pos.x / rho);
1007 float u = theta / (2.0 * M_PI) + 0.5;
1008 float v = pos.y;
1010 set_attrib(MESH_ATTR_TEXCOORD, i, Vec4(u, v, 0, 1));
1015 bool Mesh::dump(const char *fname) const
1017 FILE *fp = fopen(fname, "wb");
1018 if(fp) {
1019 bool res = dump(fp);
1020 fclose(fp);
1021 return res;
1023 return false;
1026 bool Mesh::dump(FILE *fp) const
1028 if(!has_attrib(MESH_ATTR_VERTEX)) {
1029 return false;
1032 fprintf(fp, "VERTEX ATTRIBUTES\n");
1033 static const char *label[] = { "pos", "nor", "tan", "tex", "col", "bw", "bid" };
1034 static const char *elemfmt[] = { 0, " %s(%g)", " %s(%g, %g)", " %s(%g, %g, %g)", " %s(%g, %g, %g, %g)", 0 };
1036 for(int i=0; i<(int)nverts; i++) {
1037 fprintf(fp, "%5u:", i);
1038 for(int j=0; j<NUM_MESH_ATTR; j++) {
1039 if(has_attrib(j)) {
1040 Vec4 v = get_attrib(j, i);
1041 int nelem = vattr[j].nelem;
1042 fprintf(fp, elemfmt[nelem], label[j], v.x, v.y, v.z, v.w);
1045 fputc('\n', fp);
1048 if(is_indexed()) {
1049 const unsigned int *idx = get_index_data();
1050 int numidx = get_index_count();
1051 int numtri = numidx / 3;
1052 assert(numidx % 3 == 0);
1054 fprintf(fp, "FACES\n");
1056 for(int i=0; i<numtri; i++) {
1057 fprintf(fp, "%5d: %d %d %d\n", i, idx[0], idx[1], idx[2]);
1058 idx += 3;
1061 return true;
1064 bool Mesh::dump_obj(const char *fname) const
1066 FILE *fp = fopen(fname, "wb");
1067 if(fp) {
1068 bool res = dump_obj(fp);
1069 fclose(fp);
1070 return res;
1072 return false;
1075 bool Mesh::dump_obj(FILE *fp) const
1077 if(!has_attrib(MESH_ATTR_VERTEX)) {
1078 return false;
1081 for(int i=0; i<(int)nverts; i++) {
1082 Vec4 v = get_attrib(MESH_ATTR_VERTEX, i);
1083 fprintf(fp, "v %g %g %g\n", v.x, v.y, v.z);
1086 if(has_attrib(MESH_ATTR_NORMAL)) {
1087 for(int i=0; i<(int)nverts; i++) {
1088 Vec4 v = get_attrib(MESH_ATTR_NORMAL, i);
1089 fprintf(fp, "vn %g %g %g\n", v.x, v.y, v.z);
1093 if(has_attrib(MESH_ATTR_TEXCOORD)) {
1094 for(int i=0; i<(int)nverts; i++) {
1095 Vec4 v = get_attrib(MESH_ATTR_TEXCOORD, i);
1096 fprintf(fp, "vt %g %g\n", v.x, v.y);
1100 if(is_indexed()) {
1101 const unsigned int *idxptr = get_index_data();
1102 int numidx = get_index_count();
1103 int numtri = numidx / 3;
1104 assert(numidx % 3 == 0);
1106 for(int i=0; i<numtri; i++) {
1107 fputc('f', fp);
1108 for(int j=0; j<3; j++) {
1109 unsigned int idx = *idxptr++ + 1;
1110 fprintf(fp, " %u/%u/%u", idx, idx, idx);
1112 fputc('\n', fp);
1114 } else {
1115 int numtri = nverts / 3;
1116 unsigned int idx = 1;
1117 for(int i=0; i<numtri; i++) {
1118 fputc('f', fp);
1119 for(int j=0; j<3; j++) {
1120 fprintf(fp, " %u/%u/%u", idx, idx, idx);
1121 ++idx;
1123 fputc('\n', fp);
1126 return true;
1129 // ------ private member functions ------
1131 void Mesh::calc_aabb()
1133 // the cast is to force calling the const version which doesn't invalidate
1134 if(!((const Mesh*)this)->get_attrib_data(MESH_ATTR_VERTEX)) {
1135 return;
1138 aabb.min = Vec3(FLT_MAX, FLT_MAX, FLT_MAX);
1139 aabb.max = -aabb.min;
1141 for(unsigned int i=0; i<nverts; i++) {
1142 Vec4 v = get_attrib(MESH_ATTR_VERTEX, i);
1143 for(int j=0; j<3; j++) {
1144 if(v[j] < aabb.min[j]) {
1145 aabb.min[j] = v[j];
1147 if(v[j] > aabb.max[j]) {
1148 aabb.max[j] = v[j];
1152 aabb_valid = true;
1155 void Mesh::calc_bsph()
1157 // the cast is to force calling the const version which doesn't invalidate
1158 if(!((const Mesh*)this)->get_attrib_data(MESH_ATTR_VERTEX)) {
1159 return;
1162 Vec3 v;
1163 bsph.center = Vec3(0, 0, 0);
1165 // first find the center
1166 for(unsigned int i=0; i<nverts; i++) {
1167 v = get_attrib(MESH_ATTR_VERTEX, i).xyz();
1168 bsph.center += v;
1170 bsph.center /= (float)nverts;
1172 bsph.radius = 0.0f;
1173 for(unsigned int i=0; i<nverts; i++) {
1174 v = get_attrib(MESH_ATTR_VERTEX, i).xyz();
1175 float dist_sq = length_sq(v - bsph.center);
1176 if(dist_sq > bsph.radius) {
1177 bsph.radius = dist_sq;
1180 bsph.radius = sqrt(bsph.radius);
1182 bsph_valid = true;
1185 void Mesh::update_buffers()
1187 for(int i=0; i<NUM_MESH_ATTR; i++) {
1188 if(has_attrib(i) && !vattr[i].vbo_valid) {
1189 glBindBuffer(GL_ARRAY_BUFFER, vattr[i].vbo);
1190 glBufferData(GL_ARRAY_BUFFER, nverts * vattr[i].nelem * sizeof(float), &vattr[i].data[0], GL_STATIC_DRAW);
1191 vattr[i].vbo_valid = true;
1194 glBindBuffer(GL_ARRAY_BUFFER, 0);
1196 if(idata_valid && !ibo_valid) {
1197 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
1198 glBufferData(GL_ELEMENT_ARRAY_BUFFER, nfaces * 3 * sizeof(unsigned int), &idata[0], GL_STATIC_DRAW);
1199 ibo_valid = true;
1201 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1204 void Mesh::update_wire_ibo()
1206 update_buffers();
1208 if(wire_ibo_valid) {
1209 return;
1212 if(!wire_ibo) {
1213 glGenBuffers(1, &wire_ibo);
1216 unsigned int *wire_idxarr = new unsigned int[nfaces * 6];
1217 unsigned int *dest = wire_idxarr;
1219 if(ibo_valid) {
1220 // we're dealing with an indexed mesh
1221 const unsigned int *idxarr = ((const Mesh*)this)->get_index_data();
1223 for(unsigned int i=0; i<nfaces; i++) {
1224 *dest++ = idxarr[0];
1225 *dest++ = idxarr[1];
1226 *dest++ = idxarr[1];
1227 *dest++ = idxarr[2];
1228 *dest++ = idxarr[2];
1229 *dest++ = idxarr[0];
1230 idxarr += 3;
1232 } else {
1233 // not an indexed mesh ...
1234 for(unsigned int i=0; i<nfaces; i++) {
1235 int vidx = i * 3;
1236 *dest++ = vidx;
1237 *dest++ = vidx + 1;
1238 *dest++ = vidx + 1;
1239 *dest++ = vidx + 2;
1240 *dest++ = vidx + 2;
1241 *dest++ = vidx;
1245 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, wire_ibo);
1246 glBufferData(GL_ELEMENT_ARRAY_BUFFER, nfaces * 6 * sizeof(unsigned int), wire_idxarr, GL_STATIC_DRAW);
1247 delete [] wire_idxarr;
1248 wire_ibo_valid = true;
1249 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
1253 // ------ class Triangle ------
1254 Triangle::Triangle()
1256 normal_valid = false;
1257 id = -1;
1260 Triangle::Triangle(const Vec3 &v0, const Vec3 &v1, const Vec3 &v2)
1262 v[0] = v0;
1263 v[1] = v1;
1264 v[2] = v2;
1265 normal_valid = false;
1266 id = -1;
1269 Triangle::Triangle(int n, const Vec3 *varr, const unsigned int *idxarr)
1271 if(idxarr) {
1272 v[0] = varr[idxarr[n * 3]];
1273 v[1] = varr[idxarr[n * 3 + 1]];
1274 v[2] = varr[idxarr[n * 3 + 2]];
1275 } else {
1276 v[0] = varr[n * 3];
1277 v[1] = varr[n * 3 + 1];
1278 v[2] = varr[n * 3 + 2];
1280 normal_valid = false;
1281 id = n;
1284 void Triangle::calc_normal()
1286 normal = normalize(cross(v[1] - v[0], v[2] - v[0]));
1287 normal_valid = true;
1290 const Vec3 &Triangle::get_normal() const
1292 if(!normal_valid) {
1293 ((Triangle*)this)->calc_normal();
1295 return normal;
1298 void Triangle::transform(const Mat4 &xform)
1300 v[0] = xform * v[0];
1301 v[1] = xform * v[1];
1302 v[2] = xform * v[2];
1303 normal_valid = false;
1306 void Triangle::draw() const
1308 Vec3 n[3];
1309 n[0] = n[1] = n[2] = get_normal();
1311 int vloc = Mesh::get_attrib_location(MESH_ATTR_VERTEX);
1312 int nloc = Mesh::get_attrib_location(MESH_ATTR_NORMAL);
1314 glEnableVertexAttribArray(vloc);
1315 glVertexAttribPointer(vloc, 3, GL_FLOAT, GL_FALSE, 0, &v[0].x);
1316 glVertexAttribPointer(nloc, 3, GL_FLOAT, GL_FALSE, 0, &n[0].x);
1318 glDrawArrays(GL_TRIANGLES, 0, 3);
1320 glDisableVertexAttribArray(vloc);
1321 glDisableVertexAttribArray(nloc);
1324 void Triangle::draw_wire() const
1326 static const int idxarr[] = {0, 1, 1, 2, 2, 0};
1327 int vloc = Mesh::get_attrib_location(MESH_ATTR_VERTEX);
1329 glEnableVertexAttribArray(vloc);
1330 glVertexAttribPointer(vloc, 3, GL_FLOAT, GL_FALSE, 0, &v[0].x);
1332 glDrawElements(GL_LINES, 6, GL_UNSIGNED_INT, idxarr);
1334 glDisableVertexAttribArray(vloc);
1337 Vec3 Triangle::calc_barycentric(const Vec3 &pos) const
1339 Vec3 norm = get_normal();
1341 float area_sq = fabs(dot(cross(v[1] - v[0], v[2] - v[0]), norm));
1342 if(area_sq < 1e-5) {
1343 return Vec3(0, 0, 0);
1346 float asq0 = fabs(dot(cross(v[1] - pos, v[2] - pos), norm));
1347 float asq1 = fabs(dot(cross(v[2] - pos, v[0] - pos), norm));
1348 float asq2 = fabs(dot(cross(v[0] - pos, v[1] - pos), norm));
1350 return Vec3(asq0 / area_sq, asq1 / area_sq, asq2 / area_sq);
1353 bool Triangle::intersect(const Ray &ray, HitPoint *hit) const
1355 Vec3 normal = get_normal();
1357 float ndotdir = dot(ray.dir, normal);
1358 if(fabs(ndotdir) < 1e-4) {
1359 return false;
1362 Vec3 vertdir = v[0] - ray.origin;
1363 float t = dot(normal, vertdir) / ndotdir;
1365 Vec3 pos = ray.origin + ray.dir * t;
1366 Vec3 bary = calc_barycentric(pos);
1368 if(bary.x + bary.y + bary.z > 1.00001) {
1369 return false;
1372 if(hit) {
1373 hit->dist = t;
1374 hit->pos = ray.origin + ray.dir * t;
1375 hit->normal = normal;
1376 hit->obj = this;
1378 return true;