2 This file is part of the 3dengfx, realtime visualization system.
4 Copyright (c) 2004, 2005 John Tsiombikas <nuclear@siggraph.org>
6 3dengfx is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 3dengfx is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with 3dengfx; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 /* higher level 3d object abstraction
23 * Author: John Tsiombikas 2004
24 * Modified: John Tsiombikas 2005, 2006
27 #include "3dengfx_config.h"
31 #include "3denginefx.hpp"
33 #include "gfxprog.hpp"
36 #include "common/err_msg.h"
38 RenderParams::RenderParams() {
42 src_blend = BLEND_SRC_ALPHA;
43 dest_blend = BLEND_ONE_MINUS_SRC_ALPHA;
44 handle_blending = true;
46 auto_cube_maps = false;
49 show_normals_scale = 0.5;
51 highlight_color = Color(1.0, 1.0, 1.0, 1.0);
52 highlight_line_width = 1.0;
53 use_vertex_color = false;
55 auto_normalize = false;
60 unsigned long master_render_mode = RMODE_ALL;
69 Object::Object(const TriMesh &mesh) {
79 void Object::set_mesh(const TriMesh &mesh) {
81 update_bounding_volume();
84 TriMesh *Object::get_mesh_ptr() {
89 TriMesh &Object::get_mesh() {
93 const TriMesh &Object::get_mesh() const {
97 unsigned long Object::get_vertex_count() const {
98 return mesh.get_vertex_array()->get_count();
101 const Vertex *Object::get_vertex_data() const {
102 return mesh.get_vertex_array()->get_data();
105 Vertex *Object::get_mod_vertex_data() {
106 return mesh.get_mod_vertex_array()->get_mod_data();
109 unsigned long Object::get_triangle_count() const {
110 return mesh.get_triangle_array()->get_count();
113 const Triangle *Object::get_triangle_data() const {
114 return mesh.get_triangle_array()->get_data();
117 Triangle *Object::get_mod_triangle_data() {
118 return mesh.get_mod_triangle_array()->get_mod_data();
121 void Object::set_dynamic(bool enable) {
122 const_cast<VertexArray*>(mesh.get_vertex_array())->set_dynamic(enable);
123 const_cast<TriangleArray*>(mesh.get_triangle_array())->set_dynamic(enable);
124 //const_cast<IndexArray*>(mesh.get_index_array())->set_dynamic(enable);
127 bool Object::get_dynamic() const {
128 return mesh.get_vertex_array()->get_dynamic();
131 void Object::set_material(const Material &mat) {
135 Material *Object::get_material_ptr() {
139 Material Object::get_material() const {
143 void Object::set_render_params(const RenderParams &rp) {
147 RenderParams Object::get_render_params() const {
148 return render_params;
151 void Object::set_shading(ShadeMode shading_mode) {
152 mat.shading = shading_mode;
155 void Object::set_billboarding(bool enable) {
156 render_params.billboarded = enable;
159 void Object::set_zwrite(bool enable) {
160 render_params.zwrite = enable;
163 void Object::set_blending(bool enable) {
164 render_params.blending = enable;
167 void Object::set_blending_mode(BlendingFactor sblend, BlendingFactor dblend) {
168 render_params.src_blend = sblend;
169 render_params.dest_blend = dblend;
172 void Object::set_handle_blending(bool enable) {
173 render_params.handle_blending = enable;
176 void Object::set_wireframe(bool enable) {
177 mat.wireframe = enable;
180 void Object::set_gfx_program(GfxProg *prog) {
181 render_params.gfxprog = prog;
184 void Object::set_auto_cube_maps(bool enable) {
185 render_params.auto_cube_maps = enable;
188 void Object::set_hidden(bool enable) {
189 render_params.hidden = enable;
192 void Object::set_show_normals(bool enable) {
193 render_params.show_normals = enable;
196 void Object::set_show_normals_scale(scalar_t scale) {
197 render_params.show_normals_scale = scale;
200 void Object::set_highlight(bool enable)
202 render_params.highlight = enable;
205 void Object::set_highlight_color(const Color &color)
207 render_params.highlight_color = color;
210 void Object::set_highlight_line_width(scalar_t width)
212 render_params.highlight_line_width = width;
215 void Object::set_auto_global(bool enable) {
216 mat.auto_refl = enable;
219 void Object::set_use_vertex_color(bool enable) {
220 render_params.use_vertex_color = enable;
223 void Object::set_texture_addressing(TextureAddressing taddr) {
224 render_params.taddr = taddr;
227 void Object::set_auto_normalize(bool enable) {
228 render_params.auto_normalize = enable;
231 void Object::set_shadow_casting(bool enable)
233 render_params.cast_shadows = enable;
236 void Object::apply_xform(unsigned long time) {
237 world_mat = get_prs(time).get_xform_matrix();
238 mesh.apply_xform(world_mat);
242 void Object::calculate_normals() {
243 mesh.calculate_normals();
246 void Object::normalize_normals() {
247 mesh.normalize_normals();
250 bool Object::render(unsigned long time) {
251 world_mat = get_prs(time).get_xform_matrix();
253 if(!bvol_valid) update_bounding_volume();
255 // set the active world-space transformation for the bounding volume ...
256 bvol->set_transform(world_mat);
258 /* if we have the camera that generated the active view matrix available
259 * chances are it already has the view frustum, so use it directly to test
260 * the object, otherwise generate one.
262 if(engfx_state::view_mat_camera) {
263 if(!bvol->visible(engfx_state::view_mat_camera->get_frustum())) return false;
265 Matrix4x4 view_proj = engfx_state::proj_matrix * engfx_state::view_matrix;
267 FrustumPlane frustum[6];
268 for(int i=0; i<6; i++) {
269 frustum[i] = FrustumPlane(view_proj, i);
272 if(!bvol->visible(frustum)) return false;
276 set_matrix(XFORM_WORLD, world_mat);
277 mat.set_glmaterial();
279 ::set_auto_normalize(render_params.auto_normalize);
281 //render8tex_units();
284 if(render_params.auto_normalize) ::set_auto_normalize(false);
289 void Object::render_hack(unsigned long time) {
290 //::set_material(mat);
293 if(master_render_mode & RMODE_TEXTURES) {
294 if(mat.tex[TEXTYPE_BUMPMAP]) {
295 setup_bump_light(time); // sets the light vector into texcoord[1]
297 set_texture(tex_unit, mat.tex[TEXTYPE_BUMPMAP]);
298 enable_texture_unit(tex_unit);
299 set_texture_coord_index(tex_unit, 0);
300 set_texture_unit_color(tex_unit, TOP_REPLACE, TARG_TEXTURE, TARG_TEXTURE);
301 set_texture_unit_alpha(tex_unit, TOP_REPLACE, TARG_PREV, TARG_PREV);
304 select_texture_unit(tex_unit);
305 set_texture(tex_unit, get_normal_cube());
306 enable_texture_unit(tex_unit);
307 // ::set_texture_filtering(tex_unit, render_params.tfilter);
308 set_texture_coord_index(tex_unit, 1); // tex coord with the light vector (UVW)
309 set_texture_unit_color(tex_unit, TOP_DOT3, TARG_TEXTURE, TARG_PREV);
310 set_texture_unit_alpha(tex_unit, TOP_REPLACE, TARG_PREV, TARG_PREV);
314 if(mat.tex[TEXTYPE_DIFFUSE]) {
315 set_texture(tex_unit, mat.tex[TEXTYPE_DIFFUSE]);
316 enable_texture_unit(tex_unit);
317 set_texture_coord_index(tex_unit, 0);
318 set_texture_unit_color(tex_unit, TOP_MODULATE, TARG_TEXTURE, TARG_PREV);
319 set_texture_unit_alpha(tex_unit, TOP_MODULATE, TARG_TEXTURE, TARG_PREV);
320 //tex_id = mat.tex[TEXTYPE_DIFFUSE]->tex_id;
321 ::set_texture_addressing(tex_unit, render_params.taddr, render_params.taddr);
322 // ::set_texture_filtering(tex_unit, render_params.tfilter);
323 set_matrix(XFORM_TEXTURE, mat.tmat[TEXTYPE_DIFFUSE], 0);
327 if(mat.tex[TEXTYPE_DETAIL]) {
328 set_texture(tex_unit, mat.tex[TEXTYPE_DETAIL]);
329 enable_texture_unit(tex_unit);
330 set_texture_coord_index(tex_unit, 1);
331 set_texture_unit_color(tex_unit, TOP_ADD, TARG_TEXTURE, TARG_PREV);
332 set_texture_unit_color(tex_unit, TOP_MODULATE, TARG_PREV, TARG_TEXTURE);
333 ::set_texture_addressing(tex_unit, render_params.taddr, render_params.taddr);
334 // ::set_texture_filtering(tex_unit, render_params.tfilter);
335 set_matrix(XFORM_TEXTURE, mat.tmat[TEXTYPE_DIFFUSE], 1);
339 if(mat.tex[TEXTYPE_ENVMAP]) {
340 set_texture(tex_unit, mat.tex[TEXTYPE_ENVMAP]);
341 // ::set_texture_filtering(tex_unit, render_params.tfilter);
342 enable_texture_unit(tex_unit);
343 set_texture_unit_color(tex_unit, TOP_ADD, TARG_TEXTURE, TARG_PREV);
344 set_texture_unit_alpha(tex_unit, TOP_REPLACE, TARG_PREV, TARG_TEXTURE);
346 if(mat.tex[TEXTYPE_ENVMAP]->get_type() == TEX_CUBE) {
347 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
348 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
349 glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
350 glEnable(GL_TEXTURE_GEN_S);
351 glEnable(GL_TEXTURE_GEN_T);
352 glEnable(GL_TEXTURE_GEN_R);
354 Matrix4x4 inv_view = engfx_state::view_matrix;
355 inv_view[0][3] = inv_view[1][3] = inv_view[2][3] = 0.0;
356 inv_view.transpose();
358 set_matrix(XFORM_TEXTURE, inv_view, tex_unit);
360 ::set_texture_addressing(tex_unit, TEXADDR_CLAMP, TEXADDR_CLAMP);
362 ::set_texture_addressing(tex_unit, render_params.taddr, render_params.taddr);
363 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
364 glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
365 glEnable(GL_TEXTURE_GEN_S);
366 glEnable(GL_TEXTURE_GEN_T);
368 // TODO: fix this to produce the correct orientation
369 /*glMatrixMode(GL_TEXTURE);
371 glRotatef(180.0, 0.0, 1.0, 0.0);*/
373 //tex_id = mat.tex[TEXTYPE_ENVMAP]->tex_id;
378 ::set_zwrite(render_params.zwrite);
379 set_shading_mode(mat.shading);
381 if(master_render_mode & RMODE_BLENDING) {
382 if(render_params.handle_blending) {
383 if(mat.alpha < 1.0 - small_number) {
384 set_alpha_blending(true);
385 set_blend_func(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA);
388 set_alpha_blending(render_params.blending);
389 set_blend_func(render_params.src_blend, render_params.dest_blend);
393 if(mat.wireframe) ::set_wireframe(true);
395 if(render_params.gfxprog && (master_render_mode & RMODE_SHADERS)) {
396 ::set_gfx_program(render_params.gfxprog);
400 if(mat.two_sided) set_backface_culling(false);
401 if(render_params.use_vertex_color) ::use_vertex_colors(true);
403 draw(*mesh.get_vertex_array(), *mesh.get_index_array());
405 if(render_params.use_vertex_color) ::use_vertex_colors(false);
406 if(mat.two_sided) set_backface_culling(true);
408 if(mat.wireframe) ::set_wireframe(false);
409 if((master_render_mode & RMODE_BLENDING) &&
410 ((render_params.handle_blending && mat.alpha < 1.0 - small_number) ||
411 (!render_params.handle_blending && render_params.blending))) {
412 set_alpha_blending(false);
414 if(!render_params.zwrite) ::set_zwrite(true);
415 if(mat.shading == SHADING_FLAT) set_shading_mode(SHADING_GOURAUD);
418 if(master_render_mode & RMODE_TEXTURES) {
419 for(int i=0; i<tex_unit; i++) {
420 disable_texture_unit(i);
421 glDisable(GL_TEXTURE_GEN_S);
422 glDisable(GL_TEXTURE_GEN_T);
423 glDisable(GL_TEXTURE_GEN_R);
424 glMatrixMode(GL_TEXTURE);
426 glMatrixMode(GL_MODELVIEW);
427 set_matrix(XFORM_TEXTURE, Matrix4x4::identity_matrix, i);
428 ::set_texture_addressing(tex_unit, TEXADDR_WRAP, TEXADDR_WRAP);
429 //::set_texture_filtering(tex_unit, BILINEAR_FILTERING);
433 if(render_params.show_normals) {
437 if (render_params.highlight)
442 if((master_render_mode & RMODE_SHADERS) && render_params.gfxprog) {
443 ::set_gfx_program(0);
447 void Object::draw_normals() {
448 scalar_t normal_scale = mesh.get_vertex_stats().avg_dist * render_params.show_normals_scale;
449 int vcount = mesh.get_vertex_array()->get_count();
450 const Vertex *vptr = mesh.get_vertex_array()->get_data();
455 glColor4f(1.0, 1.0, 1.0, 1.0);
456 for(int i=0; i<vcount; i++) {
457 Vector3 pos = vptr->pos;
458 Vector3 normal = vptr->normal * normal_scale;
460 glVertex3f(pos.x, pos.y, pos.z);
461 glVertex3f(pos.x + normal.x, pos.y + normal.y, pos.z + normal.z);
469 void Object::draw_highlight()
471 const Vertex *vptr = mesh.get_vertex_array()->get_data();
473 // get contour edges relative to viewer
474 Vector3 pov = Vector3(0, 0, 0);
475 Matrix4x4 model = get_matrix(XFORM_WORLD);
476 Matrix4x4 view = get_matrix(XFORM_VIEW);
477 pov.transform(view.inverse());
478 pov.transform(model.inverse());
479 std::vector<Edge> *edges = mesh.get_contour_edges(pov, false);
482 ::set_gfx_program(0);
484 glEnable(GL_LINE_SMOOTH);
485 glLineWidth(render_params.highlight_line_width);
488 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
490 Color clr = render_params.highlight_color;
492 glColor4f(clr.r, clr.g, clr.b, clr.a);
493 for (unsigned int i=0; i<edges->size(); i++)
496 p1 = vptr[(*edges)[i].vertices[0]].pos;
497 p2 = vptr[(*edges)[i].vertices[1]].pos;
498 glVertex3f(p1.x, p1.y, p1.z);
499 glVertex3f(p2.x, p2.y, p2.z);
511 void Object::setup_bump_light(unsigned long time) {
512 Vector3 lpos = engfx_state::bump_light->get_prs(time).position;
514 Matrix4x4 inv_world = world_mat.inverse();
515 lpos.transform(inv_world);
517 VertexArray *va = mesh.get_mod_vertex_array();
518 int vcount = va->get_count();
519 Vertex *varray = va->get_mod_data();
520 int tcount = mesh.get_triangle_array()->get_count();
521 const Triangle *tptr = mesh.get_triangle_array()->get_data();
524 Vector3 *utan = new Vector3[vcount];
525 Vector3 *vtan = new Vector3[vcount];
527 for(int i=0; i<tcount; i++) {
528 Vertex *v1 = &varray[tptr->vertices[0]];
529 Vertex *v2 = &varray[tptr->vertices[1]];
530 Vertex *v3 = &varray[tptr->vertices[2]];
532 Vector3 vec1 = v2->pos - v1->pos;
533 Vector3 vec2 = v3->pos - v1->pos;
535 TexCoord tc1(v2->tex[0].u - v1->tex[0].u, v2->tex[0].v - v1->tex[0].v);
536 TexCoord tc2(v3->tex[0].u - v1->tex[0].u, v3->tex[0].v - v1->tex[0].v);
538 scalar_t r = 1.0 / (tc1.u * tc2.v - tc2.u * tc1.v);
539 Vector3 udir( (tc2.v * vec1.x - tc1.v * vec2.x) * r,
540 (tc2.v * vec1.y - tc1.v * vec2.y) * r,
541 (tc2.v * vec1.z - tc1.v * vec2.z) * r);
543 Vector3 vdir( (tc1.u * vec2.x - tc2.u * vec1.x) * r,
544 (tc1.u * vec2.y - tc2.u * vec1.y) * r,
545 (tc1.u * vec2.z - tc2.u * vec1.z) * r);
547 utan[tptr->vertices[0]] += udir;
548 utan[tptr->vertices[1]] += udir;
549 utan[tptr->vertices[2]] += udir;
551 vtan[tptr->vertices[0]] += vdir;
552 vtan[tptr->vertices[1]] += vdir;
553 vtan[tptr->vertices[2]] += vdir;
558 Vertex *vptr = varray;
559 for(int i=0; i<vcount; i++) {
560 Vector3 lvec = lpos - vptr->pos;
562 Vector3 normal = -vptr->normal;
563 Vector3 tan = utan[i];
564 tan = (tan - normal * dot_product(normal, tan)).normalized();
565 Vector3 bitan = cross_product(normal, tan);
567 Basis tbn(tan, bitan, normal);
568 lvec.transform(tbn.create_rotation_matrix());
571 vptr->tex[1].u = -lvec.z;
572 vptr->tex[1].v = -lvec.y;
573 vptr->tex[1].w = lvec.x;
582 void Object::update_bounding_volume() {
583 VertexStatistics vstat = mesh.get_vertex_stats();
586 bvol = new BoundingSphere(vstat.centroid, vstat.max_dist);
589 BoundingSphere *bsph;
591 if((bsph = dynamic_cast<BoundingSphere*>(bvol))) {
592 bsph->set_position(vstat.centroid);
593 bsph->set_radius(vstat.max_dist);
597 if(!dbg++) error("obj \"%s\": only bounding spheres are supported at this point", name.c_str());
603 // Convenience classes to deal with generated geometry
605 ObjCube::ObjCube(scalar_t sz, int subdiv) {
606 create_cube(get_mesh_ptr(), sz, subdiv);
609 ObjPlane::ObjPlane(const Vector3 &normal, const Vector2 &size, int subdiv) {
610 create_plane(get_mesh_ptr(), normal, size, subdiv);
613 ObjCylinder::ObjCylinder(scalar_t rad, scalar_t len, bool caps, int udiv, int vdiv) {
614 create_cylinder(get_mesh_ptr(), rad, len, caps, udiv, vdiv);
617 ObjSphere::ObjSphere(scalar_t radius, int subdiv) {
618 create_sphere(get_mesh_ptr(), radius, subdiv);
621 ObjTorus::ObjTorus(scalar_t circle_rad, scalar_t revolv_rad, int subdiv) {
622 create_torus(get_mesh_ptr(), circle_rad, revolv_rad, subdiv);
625 ObjTeapot::ObjTeapot(scalar_t size, int subdiv) {
626 create_teapot(get_mesh_ptr(), size, subdiv);