first attempt to build on the octane, nosound for now
[summerhack] / src / 3dengfx / src / 3dengfx / 3dscene.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 #include "3dengfx_config.h"
22
23 #include <algorithm>
24 #include <string>
25 #include <limits.h>
26 #include "3dscene.hpp"
27 #include "texman.hpp"
28 #include "3denginefx.hpp"
29 #include "common/err_msg.h"
30 #include "dsys/fx.hpp"
31
32 #ifndef PATH_MAX
33 #define PATH_MAX        1024
34 #endif
35
36 using std::string;
37
38 Scene::Scene() {
39         active_camera = 0;
40         shadows = false;
41         light_halos = false;
42         halo_size = 10.0f;
43         use_fog = false;
44
45         lights = new Light*[engfx_state::sys_caps.max_lights];
46         memset(lights, 0, engfx_state::sys_caps.max_lights * sizeof(Light*));
47         lcount = 0;
48
49         ambient_light = Color(0.0f, 0.0f, 0.0f);
50         manage_data = true;
51
52         auto_clear = true;
53         bg_color = 0;
54         scene_poly_count = 0;
55         poly_count = 0;
56
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);
63         }
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));
66
67         first_render = true;
68         frame_count = 0;
69 }
70
71 Scene::~Scene() {
72         for(int i=0; i>6; i++) {
73                 delete cubic_cam[i];
74         }
75
76         if(manage_data) {
77                 std::list<Object*>::iterator obj = objects.begin();
78                 while(obj != objects.end()) {
79                         delete *obj++;
80                 }
81
82                 std::list<Camera*>::iterator cam = cameras.begin();
83                 while(cam != cameras.end()) {
84                         delete *cam++;
85                 }
86
87                 for(int i=0; i<engfx_state::sys_caps.max_lights; i++) {
88                         delete lights[i];
89                 }
90
91                 std::list<Curve*>::iterator citer = curves.begin();
92                 while(citer != curves.end()) {
93                         delete *citer++;
94                 }
95
96                 std::list<ParticleSystem*>::iterator piter = psys.begin();
97                 while(piter != psys.end()) {
98                         delete *piter++;
99                 }
100         }
101
102         delete [] lights;
103 }
104
105 void Scene::set_poly_count(unsigned long pcount) {
106         scene_poly_count = pcount;
107 }
108
109 unsigned long Scene::get_poly_count() const {
110         return scene_poly_count;
111 }
112
113 unsigned long Scene::get_frame_poly_count() const {
114         return poly_count;
115 }
116
117 void Scene::add_camera(Camera *cam) {
118         cameras.push_back(cam);
119         if(!active_camera) active_camera = cam;
120 }
121
122 void Scene::add_light(Light *light) {
123         if(lcount >= engfx_state::sys_caps.max_lights) return;
124         lights[lcount++] = light;
125 }
126
127 void Scene::add_object(Object *obj) {
128         if(obj->get_material_ptr()->alpha < 1.0f - small_number) {
129         objects.push_back(obj);
130         } else {
131                 objects.push_front(obj);
132         }
133 }
134
135 void Scene::add_curve(Curve *curve) {
136         curves.push_back(curve);
137 }
138
139 void Scene::add_particle_sys(ParticleSystem *p) {
140         psys.push_back(p);
141 }
142
143 /* adds a cubemapped skycube, by creating a cube with the correct
144  * texture coordinates to map into the cubemap texture.
145  */
146 void Scene::add_skycube(scalar_t size, Texture *cubemap) {
147         Object *obj = new ObjCube(size, 4);
148         obj->get_mesh_ptr()->invert_winding();
149
150         // generate appropriate texture coordinates
151         unsigned int vcount = obj->get_vertex_count();
152         Vertex *vptr = obj->get_mod_vertex_data();
153
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);
157         }
158
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);
164
165         obj->set_texture_addressing(TEXADDR_CLAMP);
166
167         add_object(obj);
168 }
169
170 bool Scene::remove_light(const Light *light) {
171         int idx;
172         for(idx = 0; idx < lcount; idx++) {
173                 if(light == lights[idx]) {
174                         break;
175                 }
176         }
177
178         if(idx < lcount) {
179                 lights[idx] = 0;
180                 for(int i=idx; i<lcount-1; i++) {
181                         lights[i] = lights[i + 1];
182                 }
183                 return true;
184         }
185         
186         return false;
187 }
188
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()) {
192                 objects.erase(iter);
193                 return true;
194         }
195         return false;
196 }
197
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()) {
201                 psys.erase(iter);
202                 return true;
203         }
204         return false;
205 }
206
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;
211                 iter++;
212         }
213         return 0;
214 }
215
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];
219         }
220         return 0;
221 }
222
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;
227                 iter++;
228         }
229         return 0;
230 }
231
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;
236                 iter++;
237         }
238         return 0;
239 }
240
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;
245                 iter++;
246         }
247         return 0;
248 }
249
250 XFormNode *Scene::get_node(const char *name) {
251         XFormNode *node;
252
253         if((node = get_object(name))) return node;
254         if((node = get_light(name))) return node;
255         if((node = get_camera(name))) return node;
256         
257         return 0;
258 }
259
260 std::list<Object*> *Scene::get_object_list() {
261         return &objects;
262 }
263
264 std::list<Camera*> *Scene::get_camera_list() {
265         return &cameras;
266 }
267
268 void Scene::set_active_camera(const Camera *cam) {
269         active_camera = cam;
270 }
271
272 Camera *Scene::get_active_camera() const {
273         return const_cast<Camera*>(active_camera);
274 }
275
276 void Scene::set_halo_drawing(bool enable) {
277         light_halos = enable;
278 }
279
280 void Scene::set_halo_size(float size) {
281         halo_size = size;
282 }
283
284 void Scene::set_ambient_light(Color ambient) {
285         ambient_light = ambient;
286 }
287
288 Color Scene::get_ambient_light() const {
289         return ambient_light;
290 }
291
292 void Scene::set_fog(bool enable, Color fog_color, float near_fog, float far_fog) {
293         use_fog = enable;
294         if(enable) {
295                 this->fog_color = fog_color;
296                 near_fog_range = near_fog;
297                 far_fog_range = far_fog;
298         }
299 }
300
301 void Scene::set_auto_clear(bool enable) {
302         auto_clear = enable;
303 }
304
305 void Scene::set_background(const Color &bg) {
306         bg_color = bg;
307 }
308
309 void Scene::setup_lights(unsigned long msec) const {
310         int light_index = 0;
311         for(int i=0; i<lcount; i++) {
312                 if(lights[i]) {
313                         lights[i]->set_gl_light(light_index++, msec);
314                 }
315         }
316         glDisable(GL_LIGHT0 + light_index);
317 }
318
319 void Scene::set_shadows(bool enable) {
320         shadows = enable;
321 }
322
323 void Scene::render(unsigned long msec) const {
324         static int call_depth = -1;
325         call_depth++;
326         
327         bool fb_dirty = false;
328         if(!call_depth) {
329                 // ---- this part is guaranteed to be executed once for each frame ----
330                 poly_count = 0;         // reset the polygon counter
331                 
332                 ::set_ambient_light(ambient_light);
333                 
334                 // XXX: render_all_cube_maps() will call Scene::render() recursively as necessary.
335                 fb_dirty = render_all_cube_maps(msec);
336                 
337                 
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.
341
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()) {
346                         (*iter++)->update();
347                 }
348                 // TODO: also update other simulations here (see sim framework).
349         }
350
351         if(auto_clear || fb_dirty) {
352                 clear(bg_color);
353                 clear_zbuffer_stencil(1.0, 0);
354         }
355         
356         // set camera
357         if(!active_camera) {
358                 call_depth--;
359                 return;
360         }
361         active_camera->activate(msec);
362
363         // set lights
364         setup_lights(msec);
365
366         // render stuff
367         if(shadows) {
368                 bool at_least_one = false;
369                 for(int i=0; i<lcount; i++) {
370                         if(!lights[i]->casts_shadows()) continue;
371                         at_least_one = true;
372
373                         glDisable(GL_LIGHT0 + i);
374                         render_objects(msec);   // scene minus this shadow casting light.
375
376                         set_zwrite(false);
377                         set_lighting(false);
378                         set_color_write(false, false, false, false);
379                         set_stencil_buffering(true);
380                         set_stencil_func(CMP_ALWAYS);
381
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);
386                         
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);
391                         
392                         // restore states
393                         set_stencil_op(SOP_KEEP, SOP_KEEP, SOP_KEEP);
394                         set_front_face(ORDER_CW);
395                                                 
396                         set_color_write(true, true, true, true);
397                         set_lighting(true);
398                         set_zwrite(true);
399
400                         clear_zbuffer(1.0);
401
402                         set_stencil_buffering(true);
403                         set_stencil_func(CMP_EQUAL);
404                         set_stencil_reference(0);
405
406                         glEnable(GL_LIGHT0 + i);
407
408                         render_objects(msec);
409                 }
410                 set_stencil_buffering(false);
411                 if(!at_least_one) render_objects(msec);
412         } else {
413                 render_objects(msec);
414         }
415         
416         render_particles(msec);
417
418         call_depth--;
419 }
420
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++;
425
426                 RenderParams rp = obj->get_render_params();
427
428                 if(!rp.hidden) {
429                         if(obj->render(msec)) {
430                                 poly_count += obj->get_mesh_ptr()->get_triangle_array()->get_count();
431                         }
432                 }
433         }
434 }
435
436 void Scene::render_particles(unsigned long msec) const {
437         std::list<ParticleSystem*>::const_iterator piter = psys.begin();
438         while(piter != psys.end()) {
439                 (*piter++)->draw();
440         }
441 }
442
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();
449
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();
453
454                         Vector3 lt;
455                         bool is_dir = false;
456                         if(dynamic_cast<DirLight*>(lights[lidx])) {
457                                 lt = ((DirLight*)lights[lidx])->get_direction();
458                                 lt.transform(Matrix3x3(inv_xform));
459                                 is_dir = true;
460                         } else {
461                                 lt = lights[lidx]->get_position(msec);
462                                 lt.transform(inv_xform);
463                         }
464                         
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());
468                         delete vol;
469                 }
470         }
471 }
472
473 void Scene::render_cube_map(Object *obj, unsigned long msec) const {
474         Scene *non_const_this = const_cast<Scene*>(this);
475
476         Material *mat = obj->get_material_ptr();
477         Texture *tex = mat->get_texture(TEXTYPE_ENVMAP);
478
479         if(!tex || (tex && tex->get_type() != TEX_CUBE)) {
480                 warning("tried to render_cube_map() on a non-cubemapped object");
481                 return;
482         }
483
484         RenderParams render_params = obj->get_render_params();
485         if(render_params.hidden) return;
486
487         Vector3 obj_pos = obj->get_prs(msec).position;
488
489         non_const_this->place_cube_camera(obj_pos + obj->get_pivot());
490
491         const Camera *active_cam = get_active_camera();
492
493         obj->set_hidden(true);
494
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]);
499                 clear(bg_color);
500                 clear_zbuffer_stencil(1.0, 0);
501                 render(msec);
502
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);
508                 
509                 set_render_target(0);
510         }
511
512         non_const_this->set_active_camera(active_cam);
513         setup_lights(msec);
514
515         obj->set_hidden(false);
516 }
517
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
523         };
524
525         for(int i=0; i<6; i++) {
526                 cubic_cam[i]->set_position(pos);
527                 cubic_cam[i]->set_target(targets[i] + pos);
528         }
529 }
530
531
532 bool Scene::render_all_cube_maps(unsigned long msec) const {
533         bool did_some = false;
534         
535         std::list<Object *>::const_iterator iter = objects.begin();
536         while(iter != objects.end()) {
537                 Object *obj = *iter++;
538
539                 Material *mat = obj->get_material_ptr();
540                 Texture *env = mat->get_texture(TEXTYPE_ENVMAP);
541                 if(!env || env->get_type() != TEX_CUBE) continue;
542
543                 RenderParams rp = obj->get_render_params();
544                 if(rp.hidden) continue;
545
546                 // if it is marked as a non-automatically updated reflection map, skip it.
547                 if(mat->auto_refl) {
548                         if(mat->auto_refl_upd > 1 && frame_count % mat->auto_refl_upd) continue;
549                 } else {
550                         if(!first_render) continue;
551                 }
552
553                 // ... otherwise, update the reflection in the cubemap.
554                 did_some = true;
555                 render_cube_map(obj, msec);
556         }
557
558         return did_some;
559 }
560
561 #include "fxwt/text.hpp"
562 #if defined(unix) || defined(__unix__)
563 #include <unistd.h>
564 #include <sys/stat.h>
565 #endif  // __unix__
566
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);
572
573         struct stat sbuf;
574         if(stat(out_dir, &sbuf) == -1) {
575                 mkdir(out_dir, 0770);
576         }       
577         
578         chdir(out_dir);
579 #endif  // __unix__
580
581         warning("Sequence rendering is experimental; this may make the program unresponsive while it renders, be patient.");
582
583         // render frames until we reach the end time
584         unsigned long time = start;
585         unsigned long dt = 1000 / fps;
586
587         while(time < end) {
588                 render(time);
589                 screen_capture();
590
591                 // draw progress bar
592                 scalar_t t = (scalar_t)time / (scalar_t)(end - start);
593                 set_zbuffering(false);
594                 set_lighting(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);
600                 set_lighting(true);
601                 set_zbuffering(true);
602                 
603                 flip();
604                 time += dt;
605         }
606
607 #if defined(unix) || defined(__unix__)
608         chdir(curr_dir);
609 #endif  // __unix__
610 }