added 3dengfx into the repo, probably not the correct version for this
[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 using std::string;
33
34 Scene::Scene() {
35         active_camera = 0;
36         shadows = false;
37         light_halos = false;
38         halo_size = 10.0f;
39         use_fog = false;
40
41         lights = new Light*[engfx_state::sys_caps.max_lights];
42         memset(lights, 0, engfx_state::sys_caps.max_lights * sizeof(Light*));
43         lcount = 0;
44
45         ambient_light = Color(0.0f, 0.0f, 0.0f);
46         manage_data = true;
47
48         auto_clear = true;
49         bg_color = 0;
50         scene_poly_count = 0;
51         poly_count = 0;
52
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);
59         }
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));
62
63         first_render = true;
64         frame_count = 0;
65 }
66
67 Scene::~Scene() {
68         for(int i=0; i>6; i++) {
69                 delete cubic_cam[i];
70         }
71
72         if(manage_data) {
73                 std::list<Object*>::iterator obj = objects.begin();
74                 while(obj != objects.end()) {
75                         delete *obj++;
76                 }
77
78                 std::list<Camera*>::iterator cam = cameras.begin();
79                 while(cam != cameras.end()) {
80                         delete *cam++;
81                 }
82
83                 for(int i=0; i<engfx_state::sys_caps.max_lights; i++) {
84                         delete lights[i];
85                 }
86
87                 std::list<Curve*>::iterator citer = curves.begin();
88                 while(citer != curves.end()) {
89                         delete *citer++;
90                 }
91
92                 std::list<ParticleSystem*>::iterator piter = psys.begin();
93                 while(piter != psys.end()) {
94                         delete *piter++;
95                 }
96         }
97
98         delete [] lights;
99 }
100
101 void Scene::set_poly_count(unsigned long pcount) {
102         scene_poly_count = pcount;
103 }
104
105 unsigned long Scene::get_poly_count() const {
106         return scene_poly_count;
107 }
108
109 unsigned long Scene::get_frame_poly_count() const {
110         return poly_count;
111 }
112
113 void Scene::add_camera(Camera *cam) {
114         cameras.push_back(cam);
115         if(!active_camera) active_camera = cam;
116 }
117
118 void Scene::add_light(Light *light) {
119         if(lcount >= engfx_state::sys_caps.max_lights) return;
120         lights[lcount++] = light;
121 }
122
123 void Scene::add_object(Object *obj) {
124         if(obj->get_material_ptr()->alpha < 1.0f - small_number) {
125         objects.push_back(obj);
126         } else {
127                 objects.push_front(obj);
128         }
129 }
130
131 void Scene::add_curve(Curve *curve) {
132         curves.push_back(curve);
133 }
134
135 void Scene::add_particle_sys(ParticleSystem *p) {
136         psys.push_back(p);
137 }
138
139 /* adds a cubemapped skycube, by creating a cube with the correct
140  * texture coordinates to map into the cubemap texture.
141  */
142 void Scene::add_skycube(scalar_t size, Texture *cubemap) {
143         Object *obj = new ObjCube(size, 4);
144         obj->get_mesh_ptr()->invert_winding();
145
146         // generate appropriate texture coordinates
147         unsigned int vcount = obj->get_vertex_count();
148         Vertex *vptr = obj->get_mod_vertex_data();
149
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);
153         }
154
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);
160
161         obj->set_texture_addressing(TEXADDR_CLAMP);
162
163         add_object(obj);
164 }
165
166 bool Scene::remove_light(const Light *light) {
167         int idx;
168         for(idx = 0; idx < lcount; idx++) {
169                 if(light == lights[idx]) {
170                         break;
171                 }
172         }
173
174         if(idx < lcount) {
175                 lights[idx] = 0;
176                 for(int i=idx; i<lcount-1; i++) {
177                         lights[i] = lights[i + 1];
178                 }
179                 return true;
180         }
181         
182         return false;
183 }
184
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()) {
188                 objects.erase(iter);
189                 return true;
190         }
191         return false;
192 }
193
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()) {
197                 psys.erase(iter);
198                 return true;
199         }
200         return false;
201 }
202
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;
207                 iter++;
208         }
209         return 0;
210 }
211
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];
215         }
216         return 0;
217 }
218
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;
223                 iter++;
224         }
225         return 0;
226 }
227
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;
232                 iter++;
233         }
234         return 0;
235 }
236
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;
241                 iter++;
242         }
243         return 0;
244 }
245
246 XFormNode *Scene::get_node(const char *name) {
247         XFormNode *node;
248
249         if((node = get_object(name))) return node;
250         if((node = get_light(name))) return node;
251         if((node = get_camera(name))) return node;
252         
253         return 0;
254 }
255
256 std::list<Object*> *Scene::get_object_list() {
257         return &objects;
258 }
259
260 std::list<Camera*> *Scene::get_camera_list() {
261         return &cameras;
262 }
263
264 void Scene::set_active_camera(const Camera *cam) {
265         active_camera = cam;
266 }
267
268 Camera *Scene::get_active_camera() const {
269         return const_cast<Camera*>(active_camera);
270 }
271
272 void Scene::set_halo_drawing(bool enable) {
273         light_halos = enable;
274 }
275
276 void Scene::set_halo_size(float size) {
277         halo_size = size;
278 }
279
280 void Scene::set_ambient_light(Color ambient) {
281         ambient_light = ambient;
282 }
283
284 Color Scene::get_ambient_light() const {
285         return ambient_light;
286 }
287
288 void Scene::set_fog(bool enable, Color fog_color, float near_fog, float far_fog) {
289         use_fog = enable;
290         if(enable) {
291                 this->fog_color = fog_color;
292                 near_fog_range = near_fog;
293                 far_fog_range = far_fog;
294         }
295 }
296
297 void Scene::set_auto_clear(bool enable) {
298         auto_clear = enable;
299 }
300
301 void Scene::set_background(const Color &bg) {
302         bg_color = bg;
303 }
304
305 void Scene::setup_lights(unsigned long msec) const {
306         int light_index = 0;
307         for(int i=0; i<lcount; i++) {
308                 if(lights[i]) {
309                         lights[i]->set_gl_light(light_index++, msec);
310                 }
311         }
312         glDisable(GL_LIGHT0 + light_index);
313 }
314
315 void Scene::set_shadows(bool enable) {
316         shadows = enable;
317 }
318
319 void Scene::render(unsigned long msec) const {
320         static int call_depth = -1;
321         call_depth++;
322         
323         bool fb_dirty = false;
324         if(!call_depth) {
325                 // ---- this part is guaranteed to be executed once for each frame ----
326                 poly_count = 0;         // reset the polygon counter
327                 
328                 ::set_ambient_light(ambient_light);
329                 
330                 // XXX: render_all_cube_maps() will call Scene::render() recursively as necessary.
331                 fb_dirty = render_all_cube_maps(msec);
332                 
333                 
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.
337
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()) {
342                         (*iter++)->update();
343                 }
344                 // TODO: also update other simulations here (see sim framework).
345         }
346
347         if(auto_clear || fb_dirty) {
348                 clear(bg_color);
349                 clear_zbuffer_stencil(1.0, 0);
350         }
351         
352         // set camera
353         if(!active_camera) {
354                 call_depth--;
355                 return;
356         }
357         active_camera->activate(msec);
358
359         // set lights
360         setup_lights(msec);
361
362         // render stuff
363         if(shadows) {
364                 bool at_least_one = false;
365                 for(int i=0; i<lcount; i++) {
366                         if(!lights[i]->casts_shadows()) continue;
367                         at_least_one = true;
368
369                         glDisable(GL_LIGHT0 + i);
370                         render_objects(msec);   // scene minus this shadow casting light.
371
372                         set_zwrite(false);
373                         set_lighting(false);
374                         set_color_write(false, false, false, false);
375                         set_stencil_buffering(true);
376                         set_stencil_func(CMP_ALWAYS);
377
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);
382                         
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);
387                         
388                         // restore states
389                         set_stencil_op(SOP_KEEP, SOP_KEEP, SOP_KEEP);
390                         set_front_face(ORDER_CW);
391                                                 
392                         set_color_write(true, true, true, true);
393                         set_lighting(true);
394                         set_zwrite(true);
395
396                         clear_zbuffer(1.0);
397
398                         set_stencil_buffering(true);
399                         set_stencil_func(CMP_EQUAL);
400                         set_stencil_reference(0);
401
402                         glEnable(GL_LIGHT0 + i);
403
404                         render_objects(msec);
405                 }
406                 set_stencil_buffering(false);
407                 if(!at_least_one) render_objects(msec);
408         } else {
409                 render_objects(msec);
410         }
411         
412         render_particles(msec);
413
414         call_depth--;
415 }
416
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++;
421
422                 RenderParams rp = obj->get_render_params();
423
424                 if(!rp.hidden) {
425                         if(obj->render(msec)) {
426                                 poly_count += obj->get_mesh_ptr()->get_triangle_array()->get_count();
427                         }
428                 }
429         }
430 }
431
432 void Scene::render_particles(unsigned long msec) const {
433         std::list<ParticleSystem*>::const_iterator piter = psys.begin();
434         while(piter != psys.end()) {
435                 (*piter++)->draw();
436         }
437 }
438
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();
445
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();
449
450                         Vector3 lt;
451                         bool is_dir = false;
452                         if(dynamic_cast<DirLight*>(lights[lidx])) {
453                                 lt = ((DirLight*)lights[lidx])->get_direction();
454                                 lt.transform(Matrix3x3(inv_xform));
455                                 is_dir = true;
456                         } else {
457                                 lt = lights[lidx]->get_position(msec);
458                                 lt.transform(inv_xform);
459                         }
460                         
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());
464                         delete vol;
465                 }
466         }
467 }
468
469 void Scene::render_cube_map(Object *obj, unsigned long msec) const {
470         Scene *non_const_this = const_cast<Scene*>(this);
471
472         Material *mat = obj->get_material_ptr();
473         Texture *tex = mat->get_texture(TEXTYPE_ENVMAP);
474
475         if(!tex || (tex && tex->get_type() != TEX_CUBE)) {
476                 warning("tried to render_cube_map() on a non-cubemapped object");
477                 return;
478         }
479
480         RenderParams render_params = obj->get_render_params();
481         if(render_params.hidden) return;
482
483         Vector3 obj_pos = obj->get_prs(msec).position;
484
485         non_const_this->place_cube_camera(obj_pos + obj->get_pivot());
486
487         const Camera *active_cam = get_active_camera();
488
489         obj->set_hidden(true);
490
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]);
495                 clear(bg_color);
496                 clear_zbuffer_stencil(1.0, 0);
497                 render(msec);
498
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);
504                 
505                 set_render_target(0);
506         }
507
508         non_const_this->set_active_camera(active_cam);
509         setup_lights(msec);
510
511         obj->set_hidden(false);
512 }
513
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
519         };
520
521         for(int i=0; i<6; i++) {
522                 cubic_cam[i]->set_position(pos);
523                 cubic_cam[i]->set_target(targets[i] + pos);
524         }
525 }
526
527
528 bool Scene::render_all_cube_maps(unsigned long msec) const {
529         bool did_some = false;
530         
531         std::list<Object *>::const_iterator iter = objects.begin();
532         while(iter != objects.end()) {
533                 Object *obj = *iter++;
534
535                 Texture *env;
536                 Material *mat = obj->get_material_ptr();
537                 RenderParams rp = obj->get_render_params();
538                 if(rp.hidden) continue;
539
540                 // if it is marked as a non-automatically updated reflection map, skip it.
541                 if(!mat->auto_refl) {
542                         continue;
543                 }
544
545                 // if auto-reflect is set for updating every nth frame,
546                 // and this is not one of them, skip it.
547                 if(mat->auto_refl_upd > 1 && frame_count % mat->auto_refl_upd) {
548                         continue;
549                 }
550                 
551                 // if auto-reflect is set to update only during the first frame,
552                 // and this is not the first frame, skip it.
553                 if(mat->auto_refl_upd == 0 && !first_render) {
554                         continue;
555                 }
556                 
557                 // ... otherwise, update the reflection in the cubemap.
558                 if((env = mat->get_texture(TEXTYPE_ENVMAP))) {
559                         if(env->get_type() == TEX_CUBE) {
560                                 did_some = true;
561                                 render_cube_map(obj, msec);
562                         }
563                 }
564         }
565
566         return did_some;
567 }
568
569 #include "fxwt/text.hpp"
570 #if defined(unix) || defined(__unix__)
571 #include <unistd.h>
572 #include <sys/stat.h>
573 #endif  // __unix__
574
575 void Scene::render_sequence(unsigned long start, unsigned long end, int fps, const char *out_dir) {
576 #if defined(unix) || defined(__unix__)
577         // change to the specified directory
578         char curr_dir[PATH_MAX];
579         getcwd(curr_dir, PATH_MAX);
580
581         struct stat sbuf;
582         if(stat(out_dir, &sbuf) == -1) {
583                 mkdir(out_dir, 0770);
584         }       
585         
586         chdir(out_dir);
587 #endif  // __unix__
588
589         warning("Sequence rendering is experimental; this may make the program unresponsive while it renders, be patient.");
590
591         // render frames until we reach the end time
592         unsigned long time = start;
593         unsigned long dt = 1000 / fps;
594
595         while(time < end) {
596                 render(time);
597                 screen_capture();
598
599                 // draw progress bar
600                 scalar_t t = (scalar_t)time / (scalar_t)(end - start);
601                 set_zbuffering(false);
602                 set_lighting(false);
603                 set_alpha_blending(true);
604                 set_blend_func(BLEND_ONE_MINUS_DST_COLOR, BLEND_ZERO);
605                 draw_scr_quad(Vector3(0.0, 0.49), Vector3(t, 0.51), Color(1, 1, 1));
606                 set_blend_func(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA);
607                 set_alpha_blending(false);
608                 set_lighting(true);
609                 set_zbuffering(true);
610                 
611                 flip();
612                 time += dt;
613         }
614
615 #if defined(unix) || defined(__unix__)
616         chdir(curr_dir);
617 #endif  // __unix__
618 }