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 #include "3dengfx_config.h"
26 #include "3dscene.hpp"
28 #include "3denginefx.hpp"
29 #include "common/err_msg.h"
30 #include "dsys/fx.hpp"
41 lights = new Light*[engfx_state::sys_caps.max_lights];
42 memset(lights, 0, engfx_state::sys_caps.max_lights * sizeof(Light*));
45 ambient_light = Color(0.0f, 0.0f, 0.0f);
53 // setup the cube-map cameras
54 for(int i=0; i<6; i++) {
55 cubic_cam[i] = new TargetCamera;
56 cubic_cam[i]->set_fov(half_pi);
57 cubic_cam[i]->set_aspect(1.0);
58 cubic_cam[i]->flip(false, true, false);
60 cubic_cam[CUBE_MAP_INDEX_PY]->set_up_vector(Vector3(0, 0, -1));
61 cubic_cam[CUBE_MAP_INDEX_NY]->set_up_vector(Vector3(0, 0, 1));
68 for(int i=0; i>6; i++) {
73 std::list<Object*>::iterator obj = objects.begin();
74 while(obj != objects.end()) {
78 std::list<Camera*>::iterator cam = cameras.begin();
79 while(cam != cameras.end()) {
83 for(int i=0; i<engfx_state::sys_caps.max_lights; i++) {
87 std::list<Curve*>::iterator citer = curves.begin();
88 while(citer != curves.end()) {
92 std::list<ParticleSystem*>::iterator piter = psys.begin();
93 while(piter != psys.end()) {
101 void Scene::set_poly_count(unsigned long pcount) {
102 scene_poly_count = pcount;
105 unsigned long Scene::get_poly_count() const {
106 return scene_poly_count;
109 unsigned long Scene::get_frame_poly_count() const {
113 void Scene::add_camera(Camera *cam) {
114 cameras.push_back(cam);
115 if(!active_camera) active_camera = cam;
118 void Scene::add_light(Light *light) {
119 if(lcount >= engfx_state::sys_caps.max_lights) return;
120 lights[lcount++] = light;
123 void Scene::add_object(Object *obj) {
124 if(obj->get_material_ptr()->alpha < 1.0f - small_number) {
125 objects.push_back(obj);
127 objects.push_front(obj);
131 void Scene::add_curve(Curve *curve) {
132 curves.push_back(curve);
135 void Scene::add_particle_sys(ParticleSystem *p) {
139 /* adds a cubemapped skycube, by creating a cube with the correct
140 * texture coordinates to map into the cubemap texture.
142 void Scene::add_skycube(scalar_t size, Texture *cubemap) {
143 Object *obj = new ObjCube(size, 4);
144 obj->get_mesh_ptr()->invert_winding();
146 // generate appropriate texture coordinates
147 unsigned int vcount = obj->get_vertex_count();
148 Vertex *vptr = obj->get_mod_vertex_data();
150 for(unsigned int i=0; i<vcount; i++) {
151 Vector3 vpos = vptr->pos.normalized();
152 (vptr++)->tex[0] = TexCoord(vpos.x, -vpos.y, vpos.z);
155 // setup material parameters
156 Material *mat = obj->get_material_ptr();
157 mat->ambient_color = mat->diffuse_color = mat->specular_color = 0.0;
158 mat->emissive_color = 1.0;
159 mat->set_texture(cubemap, TEXTYPE_DIFFUSE);
161 obj->set_texture_addressing(TEXADDR_CLAMP);
166 bool Scene::remove_light(const Light *light) {
168 for(idx = 0; idx < lcount; idx++) {
169 if(light == lights[idx]) {
176 for(int i=idx; i<lcount-1; i++) {
177 lights[i] = lights[i + 1];
185 bool Scene::remove_object(const Object *obj) {
186 std::list<Object*>::iterator iter = find(objects.begin(), objects.end(), obj);
187 if(iter != objects.end()) {
194 bool Scene::remove_particle_sys(const ParticleSystem *p) {
195 std::list<ParticleSystem*>::iterator iter = find(psys.begin(), psys.end(), p);
196 if(iter != psys.end()) {
203 Camera *Scene::get_camera(const char *name) {
204 std::list<Camera *>::iterator iter = cameras.begin();
205 while(iter != cameras.end()) {
206 if(!strcmp((*iter)->name.c_str(), name)) return *iter;
212 Light *Scene::get_light(const char *name) {
213 for(int i=0; i<lcount; i++) {
214 if(lights[i] && !strcmp(lights[i]->name.c_str(), name)) return lights[i];
219 Object *Scene::get_object(const char *name) {
220 std::list<Object *>::iterator iter = objects.begin();
221 while(iter != objects.end()) {
222 if(!strcmp((*iter)->name.c_str(), name)) return *iter;
228 Curve *Scene::get_curve(const char *name) {
229 std::list<Curve *>::iterator iter = curves.begin();
230 while(iter != curves.end()) {
231 if(!strcmp((*iter)->name.c_str(), name)) return *iter;
237 ParticleSystem *Scene::get_particle_sys(const char *name) {
238 std::list<ParticleSystem*>::iterator iter = psys.begin();
239 while(iter != psys.end()) {
240 if(!strcmp((*iter)->name.c_str(), name)) return *iter;
246 XFormNode *Scene::get_node(const char *name) {
249 if((node = get_object(name))) return node;
250 if((node = get_light(name))) return node;
251 if((node = get_camera(name))) return node;
256 std::list<Object*> *Scene::get_object_list() {
260 std::list<Camera*> *Scene::get_camera_list() {
264 void Scene::set_active_camera(const Camera *cam) {
268 Camera *Scene::get_active_camera() const {
269 return const_cast<Camera*>(active_camera);
272 void Scene::set_halo_drawing(bool enable) {
273 light_halos = enable;
276 void Scene::set_halo_size(float size) {
280 void Scene::set_ambient_light(Color ambient) {
281 ambient_light = ambient;
284 Color Scene::get_ambient_light() const {
285 return ambient_light;
288 void Scene::set_fog(bool enable, Color fog_color, float near_fog, float far_fog) {
291 this->fog_color = fog_color;
292 near_fog_range = near_fog;
293 far_fog_range = far_fog;
297 void Scene::set_auto_clear(bool enable) {
301 void Scene::set_background(const Color &bg) {
305 void Scene::setup_lights(unsigned long msec) const {
307 for(int i=0; i<lcount; i++) {
309 lights[i]->set_gl_light(light_index++, msec);
312 glDisable(GL_LIGHT0 + light_index);
315 void Scene::set_shadows(bool enable) {
319 void Scene::render(unsigned long msec) const {
320 static int call_depth = -1;
323 bool fb_dirty = false;
325 // ---- this part is guaranteed to be executed once for each frame ----
326 poly_count = 0; // reset the polygon counter
328 ::set_ambient_light(ambient_light);
330 // XXX: render_all_cube_maps() will call Scene::render() recursively as necessary.
331 fb_dirty = render_all_cube_maps(msec);
334 first_render = false; // this is needed by the cubemap calculation routine
335 // to support 1st frame only cubemap calculation.
336 frame_count++; // for statistics.
338 // --- update particle systems (not render) ---
339 psys::set_global_time(msec);
340 std::list<ParticleSystem*>::const_iterator iter = psys.begin();
341 while(iter != psys.end()) {
344 // TODO: also update other simulations here (see sim framework).
347 if(auto_clear || fb_dirty) {
349 clear_zbuffer_stencil(1.0, 0);
357 active_camera->activate(msec);
364 bool at_least_one = false;
365 for(int i=0; i<lcount; i++) {
366 if(!lights[i]->casts_shadows()) continue;
369 glDisable(GL_LIGHT0 + i);
370 render_objects(msec); // scene minus this shadow casting light.
374 set_color_write(false, false, false, false);
375 set_stencil_buffering(true);
376 set_stencil_func(CMP_ALWAYS);
378 // render volume front faces
379 set_stencil_op(SOP_KEEP, SOP_KEEP, SOP_INC);
380 set_front_face(ORDER_CW);
381 render_svol(i, msec);
383 // render volume back faces
384 set_stencil_op(SOP_KEEP, SOP_KEEP, SOP_DEC);
385 set_front_face(ORDER_CCW);
386 render_svol(i, msec);
389 set_stencil_op(SOP_KEEP, SOP_KEEP, SOP_KEEP);
390 set_front_face(ORDER_CW);
392 set_color_write(true, true, true, true);
398 set_stencil_buffering(true);
399 set_stencil_func(CMP_EQUAL);
400 set_stencil_reference(0);
402 glEnable(GL_LIGHT0 + i);
404 render_objects(msec);
406 set_stencil_buffering(false);
407 if(!at_least_one) render_objects(msec);
409 render_objects(msec);
412 render_particles(msec);
417 void Scene::render_objects(unsigned long msec) const {
418 std::list<Object *>::const_iterator iter = objects.begin();
419 while(iter != objects.end()) {
420 Object *obj = *iter++;
422 RenderParams rp = obj->get_render_params();
425 if(obj->render(msec)) {
426 poly_count += obj->get_mesh_ptr()->get_triangle_array()->get_count();
432 void Scene::render_particles(unsigned long msec) const {
433 std::list<ParticleSystem*>::const_iterator piter = psys.begin();
434 while(piter != psys.end()) {
439 // TODO: optimize this...
440 void Scene::render_svol(int lidx, unsigned long msec) const {
441 std::list<Object *>::const_iterator iter = objects.begin();
442 while(iter != objects.end()) {
443 Object *obj = *iter++;
444 RenderParams rp = obj->get_render_params();
446 if(!rp.hidden && rp.cast_shadows && obj->get_material_ptr()->alpha > 0.995) {
447 Matrix4x4 xform = obj->get_prs(msec).get_xform_matrix();
448 Matrix4x4 inv_xform = xform.inverse();
452 if(dynamic_cast<DirLight*>(lights[lidx])) {
453 lt = ((DirLight*)lights[lidx])->get_direction();
454 lt.transform(Matrix3x3(inv_xform));
457 lt = lights[lidx]->get_position(msec);
458 lt.transform(inv_xform);
461 TriMesh *vol = obj->get_mesh_ptr()->get_shadow_volume(lt, is_dir);
462 set_matrix(XFORM_WORLD, xform);
463 draw(*vol->get_vertex_array(), *vol->get_index_array());
469 void Scene::render_cube_map(Object *obj, unsigned long msec) const {
470 Scene *non_const_this = const_cast<Scene*>(this);
472 Material *mat = obj->get_material_ptr();
473 Texture *tex = mat->get_texture(TEXTYPE_ENVMAP);
475 if(!tex || (tex && tex->get_type() != TEX_CUBE)) {
476 warning("tried to render_cube_map() on a non-cubemapped object");
480 RenderParams render_params = obj->get_render_params();
481 if(render_params.hidden) return;
483 Vector3 obj_pos = obj->get_prs(msec).position;
485 non_const_this->place_cube_camera(obj_pos + obj->get_pivot());
487 const Camera *active_cam = get_active_camera();
489 obj->set_hidden(true);
491 for(int i=0; i<6; i++) {
492 static CubeMapFace cube_face[] = {CUBE_MAP_PX, CUBE_MAP_NX, CUBE_MAP_PY, CUBE_MAP_NY, CUBE_MAP_PZ, CUBE_MAP_NZ};
493 set_render_target(tex, cube_face[i]);
494 non_const_this->set_active_camera(cubic_cam[i]);
496 clear_zbuffer_stencil(1.0, 0);
499 Color overlay_color = mat->specular_color * mat->env_intensity;
500 set_alpha_blending(true);
501 set_blend_func(BLEND_ZERO, BLEND_SRC_COLOR);
502 dsys::overlay(0, Vector2(0,0), Vector2(1,1), overlay_color, 0, false);
503 set_alpha_blending(false);
505 set_render_target(0);
508 non_const_this->set_active_camera(active_cam);
511 obj->set_hidden(false);
514 void Scene::place_cube_camera(const Vector3 &pos) {
515 static const Vector3 targets[] = {
516 Vector3(1, 0, 0), Vector3(-1, 0, 0), // +/- X
517 Vector3(0, 1, 0), Vector3(0, -1, 0), // +/- Y
518 Vector3(0, 0, 1), Vector3(0, 0, -1) // +/- Z
521 for(int i=0; i<6; i++) {
522 cubic_cam[i]->set_position(pos);
523 cubic_cam[i]->set_target(targets[i] + pos);
528 bool Scene::render_all_cube_maps(unsigned long msec) const {
529 bool did_some = false;
531 std::list<Object *>::const_iterator iter = objects.begin();
532 while(iter != objects.end()) {
533 Object *obj = *iter++;
535 Material *mat = obj->get_material_ptr();
536 Texture *env = mat->get_texture(TEXTYPE_ENVMAP);
537 if(!env || env->get_type() != TEX_CUBE) continue;
539 RenderParams rp = obj->get_render_params();
540 if(rp.hidden) continue;
542 // if it is marked as a non-automatically updated reflection map, skip it.
544 if(mat->auto_refl_upd > 1 && frame_count % mat->auto_refl_upd) continue;
546 if(!first_render) continue;
549 // ... otherwise, update the reflection in the cubemap.
551 render_cube_map(obj, msec);
557 #include "fxwt/text.hpp"
558 #if defined(unix) || defined(__unix__)
560 #include <sys/stat.h>
563 void Scene::render_sequence(unsigned long start, unsigned long end, int fps, const char *out_dir) {
564 #if defined(unix) || defined(__unix__)
565 // change to the specified directory
566 char curr_dir[PATH_MAX];
567 getcwd(curr_dir, PATH_MAX);
570 if(stat(out_dir, &sbuf) == -1) {
571 mkdir(out_dir, 0770);
577 warning("Sequence rendering is experimental; this may make the program unresponsive while it renders, be patient.");
579 // render frames until we reach the end time
580 unsigned long time = start;
581 unsigned long dt = 1000 / fps;
588 scalar_t t = (scalar_t)time / (scalar_t)(end - start);
589 set_zbuffering(false);
591 set_alpha_blending(true);
592 set_blend_func(BLEND_ONE_MINUS_DST_COLOR, BLEND_ZERO);
593 draw_scr_quad(Vector3(0.0, 0.49), Vector3(t, 0.51), Color(1, 1, 1));
594 set_blend_func(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA);
595 set_alpha_blending(false);
597 set_zbuffering(true);
603 #if defined(unix) || defined(__unix__)