added 3dengfx into the repo, probably not the correct version for this
[summerhack] / src / 3dengfx / src / 3dengfx / object.cpp
1 /*
2 This file is part of the 3dengfx, realtime visualization system.
3
4 Copyright (c) 2004, 2005 John Tsiombikas <nuclear@siggraph.org>
5
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.
10
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.
15
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
19 */
20
21 /* higher level 3d object abstraction
22  *
23  * Author: John Tsiombikas 2004
24  * Modified: John Tsiombikas 2005, 2006
25  */
26
27 #include "3dengfx_config.h"
28
29 #include "opengl.h"
30 #include "object.hpp"
31 #include "3denginefx.hpp"
32 #include "camera.hpp"
33 #include "gfxprog.hpp"
34 #include "texman.hpp"
35 #include "ggen.hpp"
36 #include "common/err_msg.h"
37
38 RenderParams::RenderParams() {
39         billboarded = false;
40         zwrite = true;
41         blending = false;
42         src_blend = BLEND_SRC_ALPHA;
43         dest_blend = BLEND_ONE_MINUS_SRC_ALPHA;
44         handle_blending = true;
45         gfxprog = 0;
46         auto_cube_maps = false;
47         hidden = false;
48         show_normals = false;
49         show_normals_scale = 0.5;
50         highlight = false;
51         highlight_color = Color(1.0, 1.0, 1.0, 1.0);
52         highlight_line_width = 1.0;
53         use_vertex_color = false;
54         taddr = TEXADDR_WRAP;
55         auto_normalize = false;
56         cast_shadows = true;
57 }
58
59
60 unsigned long master_render_mode = RMODE_ALL;
61         
62
63 Object::Object() {
64         bvol_valid = false;
65         bvol = 0;
66         set_dynamic(false);
67 }
68
69 Object::Object(const TriMesh &mesh) {
70         bvol = 0;
71         set_mesh(mesh);
72         set_dynamic(false);
73 }
74
75 Object::~Object() {
76         if(bvol) delete bvol;
77 }
78
79 void Object::set_mesh(const TriMesh &mesh) {
80         this->mesh = mesh;
81         update_bounding_volume();
82 }
83
84 TriMesh *Object::get_mesh_ptr() {
85         bvol_valid = false;
86         return &mesh;
87 }
88
89 TriMesh &Object::get_mesh() {
90         return mesh;
91 }
92
93 const TriMesh &Object::get_mesh() const {
94         return mesh;
95 }
96
97 unsigned long Object::get_vertex_count() const {
98         return mesh.get_vertex_array()->get_count();
99 }
100
101 const Vertex *Object::get_vertex_data() const {
102         return mesh.get_vertex_array()->get_data();
103 }
104
105 Vertex *Object::get_mod_vertex_data() {
106         return mesh.get_mod_vertex_array()->get_mod_data();
107 }
108
109 unsigned long Object::get_triangle_count() const {
110         return mesh.get_triangle_array()->get_count();
111 }
112
113 const Triangle *Object::get_triangle_data() const {
114         return mesh.get_triangle_array()->get_data();
115 }
116
117 Triangle *Object::get_mod_triangle_data() {
118         return mesh.get_mod_triangle_array()->get_mod_data();
119 }
120
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);
125 }
126
127 bool Object::get_dynamic() const {
128         return mesh.get_vertex_array()->get_dynamic();
129 }
130
131 void Object::set_material(const Material &mat) {
132         this->mat = mat;
133 }
134
135 Material *Object::get_material_ptr() {
136         return &mat;
137 }
138
139 Material Object::get_material() const {
140         return mat;
141 }
142
143 void Object::set_render_params(const RenderParams &rp) {
144         render_params = rp;
145 }
146
147 RenderParams Object::get_render_params() const {
148         return render_params;
149 }
150
151 void Object::set_shading(ShadeMode shading_mode) {
152         mat.shading = shading_mode;
153 }
154
155 void Object::set_billboarding(bool enable) {
156         render_params.billboarded = enable;
157 }
158
159 void Object::set_zwrite(bool enable) {
160         render_params.zwrite = enable;
161 }
162
163 void Object::set_blending(bool enable) {
164         render_params.blending = enable;
165 }
166
167 void Object::set_blending_mode(BlendingFactor sblend, BlendingFactor dblend) {
168         render_params.src_blend = sblend;
169         render_params.dest_blend = dblend;
170 }
171
172 void Object::set_handle_blending(bool enable) {
173         render_params.handle_blending = enable;
174 }
175
176 void Object::set_wireframe(bool enable) {
177         mat.wireframe = enable;
178 }
179
180 void Object::set_gfx_program(GfxProg *prog) {
181         render_params.gfxprog = prog;
182 }
183
184 void Object::set_auto_cube_maps(bool enable) {
185         render_params.auto_cube_maps = enable;
186 }
187
188 void Object::set_hidden(bool enable) {
189         render_params.hidden = enable;
190 }
191
192 void Object::set_show_normals(bool enable) {
193         render_params.show_normals = enable;
194 }
195
196 void Object::set_show_normals_scale(scalar_t scale) {
197         render_params.show_normals_scale = scale;
198 }
199
200 void Object::set_highlight(bool enable)
201 {
202         render_params.highlight = enable;
203 }
204
205 void Object::set_highlight_color(const Color &color)
206 {
207         render_params.highlight_color = color;
208 }
209
210 void Object::set_highlight_line_width(scalar_t width)
211 {
212         render_params.highlight_line_width = width;
213 }
214
215 void Object::set_auto_global(bool enable) {
216         mat.auto_refl = enable;
217 }
218
219 void Object::set_use_vertex_color(bool enable) {
220         render_params.use_vertex_color = enable;
221 }
222
223 void Object::set_texture_addressing(TextureAddressing taddr) {
224         render_params.taddr = taddr;
225 }
226
227 void Object::set_auto_normalize(bool enable) {
228         render_params.auto_normalize = enable;
229 }
230
231 void Object::set_shadow_casting(bool enable)
232 {
233         render_params.cast_shadows = enable;
234 }
235
236 void Object::apply_xform(unsigned long time) {
237         world_mat = get_prs(time).get_xform_matrix();
238         mesh.apply_xform(world_mat);
239         reset_xform(time);
240 }
241
242 void Object::calculate_normals() {
243         mesh.calculate_normals();
244 }
245
246 void Object::normalize_normals() {
247         mesh.normalize_normals();
248 }
249
250 bool Object::render(unsigned long time) {
251         world_mat = get_prs(time).get_xform_matrix();
252
253         if(!bvol_valid) update_bounding_volume();
254
255         // set the active world-space transformation for the bounding volume ...
256         bvol->set_transform(world_mat);
257         
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.
261          */
262         if(engfx_state::view_mat_camera) {
263                 if(!bvol->visible(engfx_state::view_mat_camera->get_frustum())) return false;
264         } else {
265                 Matrix4x4 view_proj = engfx_state::proj_matrix * engfx_state::view_matrix;
266                 
267                 FrustumPlane frustum[6];
268                 for(int i=0; i<6; i++) {
269                         frustum[i] = FrustumPlane(view_proj, i);
270                 }
271
272                 if(!bvol->visible(frustum)) return false;
273         }
274         
275         
276         set_matrix(XFORM_WORLD, world_mat);
277         mat.set_glmaterial();
278
279         ::set_auto_normalize(render_params.auto_normalize);
280         
281         //render8tex_units();
282         render_hack(time);
283
284         if(render_params.auto_normalize) ::set_auto_normalize(false);
285
286         return true;
287 }
288
289 void Object::render_hack(unsigned long time) {
290         //::set_material(mat);
291         int tex_unit = 0;
292
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]
296
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);
302                         tex_unit++;
303                 
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);
311                         tex_unit++;
312                 }
313         
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);
324                         tex_unit++;
325                 }
326
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);
336                         tex_unit++;
337                 }
338         
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);
345
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);
353
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();
357
358                                 set_matrix(XFORM_TEXTURE, inv_view, tex_unit);
359
360                                 ::set_texture_addressing(tex_unit, TEXADDR_CLAMP, TEXADDR_CLAMP);
361                         } else {
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);
367
368                                 // TODO: fix this to produce the correct orientation
369                                 /*glMatrixMode(GL_TEXTURE);
370                                 glLoadIdentity();
371                                 glRotatef(180.0, 0.0, 1.0, 0.0);*/
372                         }
373                         //tex_id = mat.tex[TEXTYPE_ENVMAP]->tex_id;
374                         tex_unit++;
375                 }
376         }
377
378         ::set_zwrite(render_params.zwrite);
379         set_shading_mode(mat.shading);
380
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);
386                         }
387                 } else {
388                         set_alpha_blending(render_params.blending);
389                         set_blend_func(render_params.src_blend, render_params.dest_blend);
390                 }
391         }
392
393         if(mat.wireframe) ::set_wireframe(true);
394
395         if(render_params.gfxprog && (master_render_mode & RMODE_SHADERS)) {
396                 ::set_gfx_program(render_params.gfxprog);
397         }
398         // XXX: cont. here
399
400         if(mat.two_sided) set_backface_culling(false);
401         if(render_params.use_vertex_color) ::use_vertex_colors(true);
402         
403         draw(*mesh.get_vertex_array(), *mesh.get_index_array());
404
405         if(render_params.use_vertex_color) ::use_vertex_colors(false);
406         if(mat.two_sided) set_backface_culling(true);
407
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);
413         }
414         if(!render_params.zwrite) ::set_zwrite(true);
415         if(mat.shading == SHADING_FLAT) set_shading_mode(SHADING_GOURAUD);
416
417
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);
425                         glLoadIdentity();
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);
430                 }
431         }
432
433         if(render_params.show_normals) {
434                 draw_normals();
435         }
436
437         if (render_params.highlight)
438         {
439                 draw_highlight();
440         }
441         
442         if((master_render_mode & RMODE_SHADERS) && render_params.gfxprog) {
443                 ::set_gfx_program(0);
444         }
445 }
446
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();
451
452         set_lighting(false);
453         
454         glBegin(GL_LINES);
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;
459                 
460                 glVertex3f(pos.x, pos.y, pos.z);
461                 glVertex3f(pos.x + normal.x, pos.y + normal.y, pos.z + normal.z);
462                 vptr++;
463         }
464         glEnd();
465
466         set_lighting(true);
467 }
468
469 void Object::draw_highlight()
470 {
471         const Vertex *vptr = mesh.get_vertex_array()->get_data();
472
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);
480         
481         set_lighting(false);
482         ::set_gfx_program(0);
483
484         glEnable(GL_LINE_SMOOTH);
485         glLineWidth(render_params.highlight_line_width);
486         
487         glEnable(GL_BLEND);
488         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
489         
490         Color clr = render_params.highlight_color;
491         glBegin(GL_LINES);
492         glColor4f(clr.r, clr.g, clr.b, clr.a);
493         for (unsigned int i=0; i<edges->size(); i++) 
494         {
495                 Vector3 p1, p2;
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);
500         }
501         glEnd();
502
503         glLineWidth(1);
504         
505         glDisable(GL_BLEND);
506         
507         set_lighting(true);
508
509 }
510
511 void Object::setup_bump_light(unsigned long time) {
512         Vector3 lpos = engfx_state::bump_light->get_prs(time).position;
513
514         Matrix4x4 inv_world = world_mat.inverse();
515         lpos.transform(inv_world);
516
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();
522
523         
524         Vector3 *utan = new Vector3[vcount];
525         memset(utan, 0, vcount * sizeof(Vector3));
526
527         Vector3 *vtan = new Vector3[vcount];
528         memset(vtan, 0, vcount * sizeof(Vector3));
529
530         for(int i=0; i<tcount; i++) {
531                 Vertex *v1 = &varray[tptr->vertices[0]];
532                 Vertex *v2 = &varray[tptr->vertices[1]];
533                 Vertex *v3 = &varray[tptr->vertices[2]];
534
535                 Vector3 vec1 = v2->pos - v1->pos;
536                 Vector3 vec2 = v3->pos - v1->pos;
537
538                 TexCoord tc1(v2->tex[0].u - v1->tex[0].u, v2->tex[0].v - v1->tex[0].v);
539                 TexCoord tc2(v3->tex[0].u - v1->tex[0].u, v3->tex[0].v - v1->tex[0].v);
540         
541                 scalar_t r = 1.0 / (tc1.u * tc2.v - tc2.u * tc1.v);
542                 Vector3 udir(   (tc2.v * vec1.x - tc1.v * vec2.x) * r,
543                                                 (tc2.v * vec1.y - tc1.v * vec2.y) * r,
544                                                 (tc2.v * vec1.z - tc1.v * vec2.z) * r);
545
546                 Vector3 vdir(   (tc1.u * vec2.x - tc2.u * vec1.x) * r,
547                                                 (tc1.u * vec2.y - tc2.u * vec1.y) * r,
548                                                 (tc1.u * vec2.z - tc2.u * vec1.z) * r);
549
550                 utan[tptr->vertices[0]] += udir;
551                 utan[tptr->vertices[1]] += udir;
552                 utan[tptr->vertices[2]] += udir;
553                 
554                 vtan[tptr->vertices[0]] += vdir;
555                 vtan[tptr->vertices[1]] += vdir;
556                 vtan[tptr->vertices[2]] += vdir;
557                 
558                 tptr++;         
559         }
560
561         Vertex *vptr = varray;
562         for(int i=0; i<vcount; i++) {
563                 Vector3 lvec = lpos - vptr->pos;
564
565                 Vector3 normal = -vptr->normal;
566                 Vector3 tan = utan[i];
567                 tan = (tan - normal * dot_product(normal, tan)).normalized();
568                 Vector3 bitan = cross_product(normal, tan);
569
570                 Basis tbn(tan, bitan, normal);
571                 lvec.transform(tbn.create_rotation_matrix());
572                 //lvec.normalize();
573                 
574                 vptr->tex[1].u = -lvec.z;
575                 vptr->tex[1].v = -lvec.y;
576                 vptr->tex[1].w = lvec.x;
577                 vptr++;
578         }
579         
580         delete [] utan;
581         delete [] vtan;
582 }
583
584
585 void Object::update_bounding_volume() {
586         VertexStatistics vstat = mesh.get_vertex_stats();
587
588         if(!bvol) {
589                 bvol = new BoundingSphere(vstat.centroid, vstat.max_dist);
590                 bvol_valid = true;
591         } else {
592                 BoundingSphere *bsph;
593                 
594                 if((bsph = dynamic_cast<BoundingSphere*>(bvol))) {
595                         bsph->set_position(vstat.centroid);
596                         bsph->set_radius(vstat.max_dist);
597                         bvol_valid = true;
598                 } else {
599                         static int dbg;
600                         if(!dbg++) error("obj \"%s\": only bounding spheres are supported at this point", name.c_str());
601                 }
602         }
603 }
604
605
606 // Convenience classes to deal with generated geometry
607
608 ObjCube::ObjCube(scalar_t sz, int subdiv) {
609         create_cube(get_mesh_ptr(), sz, subdiv);
610 }
611
612 ObjPlane::ObjPlane(const Vector3 &normal, const Vector2 &size, int subdiv) {
613         create_plane(get_mesh_ptr(), normal, size, subdiv);
614 }
615
616 ObjCylinder::ObjCylinder(scalar_t rad, scalar_t len, bool caps, int udiv, int vdiv) {
617         create_cylinder(get_mesh_ptr(), rad, len, caps, udiv, vdiv);
618 }
619
620 ObjSphere::ObjSphere(scalar_t radius, int subdiv) {
621         create_sphere(get_mesh_ptr(), radius, subdiv);
622 }
623
624 ObjTorus::ObjTorus(scalar_t circle_rad, scalar_t revolv_rad, int subdiv) {
625         create_torus(get_mesh_ptr(), circle_rad, revolv_rad, subdiv);
626 }
627
628 ObjTeapot::ObjTeapot(scalar_t size, int subdiv) {
629         create_teapot(get_mesh_ptr(), size, subdiv);
630 }