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"
45 lights = new Light*[engfx_state::sys_caps.max_lights];
46 memset(lights, 0, engfx_state::sys_caps.max_lights * sizeof(Light*));
49 ambient_light = Color(0.0f, 0.0f, 0.0f);
57 // setup the cube-map cameras
58 for(int i=0; i<6; i++) {
59 cubic_cam[i] = new TargetCamera;
60 cubic_cam[i]->set_fov(half_pi);
61 cubic_cam[i]->set_aspect(1.0);
62 cubic_cam[i]->flip(false, true, false);
64 cubic_cam[CUBE_MAP_INDEX_PY]->set_up_vector(Vector3(0, 0, -1));
65 cubic_cam[CUBE_MAP_INDEX_NY]->set_up_vector(Vector3(0, 0, 1));
72 for(int i=0; i>6; i++) {
77 std::list<Object*>::iterator obj = objects.begin();
78 while(obj != objects.end()) {
82 std::list<Camera*>::iterator cam = cameras.begin();
83 while(cam != cameras.end()) {
87 for(int i=0; i<engfx_state::sys_caps.max_lights; i++) {
91 std::list<Curve*>::iterator citer = curves.begin();
92 while(citer != curves.end()) {
96 std::list<ParticleSystem*>::iterator piter = psys.begin();
97 while(piter != psys.end()) {
105 void Scene::set_poly_count(unsigned long pcount) {
106 scene_poly_count = pcount;
109 unsigned long Scene::get_poly_count() const {
110 return scene_poly_count;
113 unsigned long Scene::get_frame_poly_count() const {
117 void Scene::add_camera(Camera *cam) {
118 cameras.push_back(cam);
119 if(!active_camera) active_camera = cam;
122 void Scene::add_light(Light *light) {
123 if(lcount >= engfx_state::sys_caps.max_lights) return;
124 lights[lcount++] = light;
127 void Scene::add_object(Object *obj) {
128 if(obj->get_material_ptr()->alpha < 1.0f - small_number) {
129 objects.push_back(obj);
131 objects.push_front(obj);
135 void Scene::add_curve(Curve *curve) {
136 curves.push_back(curve);
139 void Scene::add_particle_sys(ParticleSystem *p) {
143 /* adds a cubemapped skycube, by creating a cube with the correct
144 * texture coordinates to map into the cubemap texture.
146 void Scene::add_skycube(scalar_t size, Texture *cubemap) {
147 Object *obj = new ObjCube(size, 4);
148 obj->get_mesh_ptr()->invert_winding();
150 // generate appropriate texture coordinates
151 unsigned int vcount = obj->get_vertex_count();
152 Vertex *vptr = obj->get_mod_vertex_data();
154 for(unsigned int i=0; i<vcount; i++) {
155 Vector3 vpos = vptr->pos.normalized();
156 (vptr++)->tex[0] = TexCoord(vpos.x, -vpos.y, vpos.z);
159 // setup material parameters
160 Material *mat = obj->get_material_ptr();
161 mat->ambient_color = mat->diffuse_color = mat->specular_color = 0.0;
162 mat->emissive_color = 1.0;
163 mat->set_texture(cubemap, TEXTYPE_DIFFUSE);
165 obj->set_texture_addressing(TEXADDR_CLAMP);
170 bool Scene::remove_light(const Light *light) {
172 for(idx = 0; idx < lcount; idx++) {
173 if(light == lights[idx]) {
180 for(int i=idx; i<lcount-1; i++) {
181 lights[i] = lights[i + 1];
189 bool Scene::remove_object(const Object *obj) {
190 std::list<Object*>::iterator iter = find(objects.begin(), objects.end(), obj);
191 if(iter != objects.end()) {
198 bool Scene::remove_particle_sys(const ParticleSystem *p) {
199 std::list<ParticleSystem*>::iterator iter = find(psys.begin(), psys.end(), p);
200 if(iter != psys.end()) {
207 Camera *Scene::get_camera(const char *name) {
208 std::list<Camera *>::iterator iter = cameras.begin();
209 while(iter != cameras.end()) {
210 if(!strcmp((*iter)->name.c_str(), name)) return *iter;
216 Light *Scene::get_light(const char *name) {
217 for(int i=0; i<lcount; i++) {
218 if(lights[i] && !strcmp(lights[i]->name.c_str(), name)) return lights[i];
223 Object *Scene::get_object(const char *name) {
224 std::list<Object *>::iterator iter = objects.begin();
225 while(iter != objects.end()) {
226 if(!strcmp((*iter)->name.c_str(), name)) return *iter;
232 Curve *Scene::get_curve(const char *name) {
233 std::list<Curve *>::iterator iter = curves.begin();
234 while(iter != curves.end()) {
235 if(!strcmp((*iter)->name.c_str(), name)) return *iter;
241 ParticleSystem *Scene::get_particle_sys(const char *name) {
242 std::list<ParticleSystem*>::iterator iter = psys.begin();
243 while(iter != psys.end()) {
244 if(!strcmp((*iter)->name.c_str(), name)) return *iter;
250 XFormNode *Scene::get_node(const char *name) {
253 if((node = get_object(name))) return node;
254 if((node = get_light(name))) return node;
255 if((node = get_camera(name))) return node;
260 std::list<Object*> *Scene::get_object_list() {
264 std::list<Camera*> *Scene::get_camera_list() {
268 void Scene::set_active_camera(const Camera *cam) {
272 Camera *Scene::get_active_camera() const {
273 return const_cast<Camera*>(active_camera);
276 void Scene::set_halo_drawing(bool enable) {
277 light_halos = enable;
280 void Scene::set_halo_size(float size) {
284 void Scene::set_ambient_light(Color ambient) {
285 ambient_light = ambient;
288 Color Scene::get_ambient_light() const {
289 return ambient_light;
292 void Scene::set_fog(bool enable, Color fog_color, float near_fog, float far_fog) {
295 this->fog_color = fog_color;
296 near_fog_range = near_fog;
297 far_fog_range = far_fog;
301 void Scene::set_auto_clear(bool enable) {
305 void Scene::set_background(const Color &bg) {
309 void Scene::setup_lights(unsigned long msec) const {
311 for(int i=0; i<lcount; i++) {
313 lights[i]->set_gl_light(light_index++, msec);
316 glDisable(GL_LIGHT0 + light_index);
319 void Scene::set_shadows(bool enable) {
323 void Scene::render(unsigned long msec) const {
324 static int call_depth = -1;
327 bool fb_dirty = false;
329 // ---- this part is guaranteed to be executed once for each frame ----
330 poly_count = 0; // reset the polygon counter
332 ::set_ambient_light(ambient_light);
334 // XXX: render_all_cube_maps() will call Scene::render() recursively as necessary.
335 fb_dirty = render_all_cube_maps(msec);
338 first_render = false; // this is needed by the cubemap calculation routine
339 // to support 1st frame only cubemap calculation.
340 frame_count++; // for statistics.
342 // --- update particle systems (not render) ---
343 psys::set_global_time(msec);
344 std::list<ParticleSystem*>::const_iterator iter = psys.begin();
345 while(iter != psys.end()) {
348 // TODO: also update other simulations here (see sim framework).
351 if(auto_clear || fb_dirty) {
353 clear_zbuffer_stencil(1.0, 0);
361 active_camera->activate(msec);
368 bool at_least_one = false;
369 for(int i=0; i<lcount; i++) {
370 if(!lights[i]->casts_shadows()) continue;
373 glDisable(GL_LIGHT0 + i);
374 render_objects(msec); // scene minus this shadow casting light.
378 set_color_write(false, false, false, false);
379 set_stencil_buffering(true);
380 set_stencil_func(CMP_ALWAYS);
382 // render volume front faces
383 set_stencil_op(SOP_KEEP, SOP_KEEP, SOP_INC);
384 set_front_face(ORDER_CW);
385 render_svol(i, msec);
387 // render volume back faces
388 set_stencil_op(SOP_KEEP, SOP_KEEP, SOP_DEC);
389 set_front_face(ORDER_CCW);
390 render_svol(i, msec);
393 set_stencil_op(SOP_KEEP, SOP_KEEP, SOP_KEEP);
394 set_front_face(ORDER_CW);
396 set_color_write(true, true, true, true);
402 set_stencil_buffering(true);
403 set_stencil_func(CMP_EQUAL);
404 set_stencil_reference(0);
406 glEnable(GL_LIGHT0 + i);
408 render_objects(msec);
410 set_stencil_buffering(false);
411 if(!at_least_one) render_objects(msec);
413 render_objects(msec);
416 render_particles(msec);
421 void Scene::render_objects(unsigned long msec) const {
422 std::list<Object *>::const_iterator iter = objects.begin();
423 while(iter != objects.end()) {
424 Object *obj = *iter++;
426 RenderParams rp = obj->get_render_params();
429 if(obj->render(msec)) {
430 poly_count += obj->get_mesh_ptr()->get_triangle_array()->get_count();
436 void Scene::render_particles(unsigned long msec) const {
437 std::list<ParticleSystem*>::const_iterator piter = psys.begin();
438 while(piter != psys.end()) {
443 // TODO: optimize this...
444 void Scene::render_svol(int lidx, unsigned long msec) const {
445 std::list<Object *>::const_iterator iter = objects.begin();
446 while(iter != objects.end()) {
447 Object *obj = *iter++;
448 RenderParams rp = obj->get_render_params();
450 if(!rp.hidden && rp.cast_shadows && obj->get_material_ptr()->alpha > 0.995) {
451 Matrix4x4 xform = obj->get_prs(msec).get_xform_matrix();
452 Matrix4x4 inv_xform = xform.inverse();
456 if(dynamic_cast<DirLight*>(lights[lidx])) {
457 lt = ((DirLight*)lights[lidx])->get_direction();
458 lt.transform(Matrix3x3(inv_xform));
461 lt = lights[lidx]->get_position(msec);
462 lt.transform(inv_xform);
465 TriMesh *vol = obj->get_mesh_ptr()->get_shadow_volume(lt, is_dir);
466 set_matrix(XFORM_WORLD, xform);
467 draw(*vol->get_vertex_array(), *vol->get_index_array());
473 void Scene::render_cube_map(Object *obj, unsigned long msec) const {
474 Scene *non_const_this = const_cast<Scene*>(this);
476 Material *mat = obj->get_material_ptr();
477 Texture *tex = mat->get_texture(TEXTYPE_ENVMAP);
479 if(!tex || (tex && tex->get_type() != TEX_CUBE)) {
480 warning("tried to render_cube_map() on a non-cubemapped object");
484 RenderParams render_params = obj->get_render_params();
485 if(render_params.hidden) return;
487 Vector3 obj_pos = obj->get_prs(msec).position;
489 non_const_this->place_cube_camera(obj_pos + obj->get_pivot());
491 const Camera *active_cam = get_active_camera();
493 obj->set_hidden(true);
495 for(int i=0; i<6; i++) {
496 static CubeMapFace cube_face[] = {CUBE_MAP_PX, CUBE_MAP_NX, CUBE_MAP_PY, CUBE_MAP_NY, CUBE_MAP_PZ, CUBE_MAP_NZ};
497 set_render_target(tex, cube_face[i]);
498 non_const_this->set_active_camera(cubic_cam[i]);
500 clear_zbuffer_stencil(1.0, 0);
503 Color overlay_color = mat->specular_color * mat->env_intensity;
504 set_alpha_blending(true);
505 set_blend_func(BLEND_ZERO, BLEND_SRC_COLOR);
506 dsys::overlay(0, Vector2(0,0), Vector2(1,1), overlay_color, 0, false);
507 set_alpha_blending(false);
509 set_render_target(0);
512 non_const_this->set_active_camera(active_cam);
515 obj->set_hidden(false);
518 void Scene::place_cube_camera(const Vector3 &pos) {
519 static const Vector3 targets[] = {
520 Vector3(1, 0, 0), Vector3(-1, 0, 0), // +/- X
521 Vector3(0, 1, 0), Vector3(0, -1, 0), // +/- Y
522 Vector3(0, 0, 1), Vector3(0, 0, -1) // +/- Z
525 for(int i=0; i<6; i++) {
526 cubic_cam[i]->set_position(pos);
527 cubic_cam[i]->set_target(targets[i] + pos);
532 bool Scene::render_all_cube_maps(unsigned long msec) const {
533 bool did_some = false;
535 std::list<Object *>::const_iterator iter = objects.begin();
536 while(iter != objects.end()) {
537 Object *obj = *iter++;
539 Material *mat = obj->get_material_ptr();
540 Texture *env = mat->get_texture(TEXTYPE_ENVMAP);
541 if(!env || env->get_type() != TEX_CUBE) continue;
543 RenderParams rp = obj->get_render_params();
544 if(rp.hidden) continue;
546 // if it is marked as a non-automatically updated reflection map, skip it.
548 if(mat->auto_refl_upd > 1 && frame_count % mat->auto_refl_upd) continue;
550 if(!first_render) continue;
553 // ... otherwise, update the reflection in the cubemap.
555 render_cube_map(obj, msec);
561 #include "fxwt/text.hpp"
562 #if defined(unix) || defined(__unix__)
564 #include <sys/stat.h>
567 void Scene::render_sequence(unsigned long start, unsigned long end, int fps, const char *out_dir) {
568 #if defined(unix) || defined(__unix__)
569 // change to the specified directory
570 char curr_dir[PATH_MAX];
571 getcwd(curr_dir, PATH_MAX);
574 if(stat(out_dir, &sbuf) == -1) {
575 mkdir(out_dir, 0770);
581 warning("Sequence rendering is experimental; this may make the program unresponsive while it renders, be patient.");
583 // render frames until we reach the end time
584 unsigned long time = start;
585 unsigned long dt = 1000 / fps;
592 scalar_t t = (scalar_t)time / (scalar_t)(end - start);
593 set_zbuffering(false);
595 set_alpha_blending(true);
596 set_blend_func(BLEND_ONE_MINUS_DST_COLOR, BLEND_ZERO);
597 draw_scr_quad(Vector3(0.0, 0.49), Vector3(t, 0.51), Color(1, 1, 1));
598 set_blend_func(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA);
599 set_alpha_blending(false);
601 set_zbuffering(true);
607 #if defined(unix) || defined(__unix__)