3 #include <msp/core/application.h>
4 #include <msp/core/getopt.h>
5 #include <msp/gl/animatedobject.h>
6 #include <msp/gl/bloom.h>
7 #include <msp/gl/box.h>
8 #include <msp/gl/cylinder.h>
9 #include <msp/gl/environmentmap.h>
10 #include <msp/gl/framebuffer.h>
11 #include <msp/gl/grid.h>
12 #include <msp/gl/instancescene.h>
13 #include <msp/gl/light.h>
14 #include <msp/gl/lighting.h>
15 #include <msp/gl/material.h>
16 #include <msp/gl/mesh.h>
17 #include <msp/gl/meshbuilder.h>
18 #include <msp/gl/object.h>
19 #include <msp/gl/pipeline.h>
20 #include <msp/gl/program.h>
21 #include <msp/gl/programbuilder.h>
22 #include <msp/gl/renderer.h>
23 #include <msp/gl/shader.h>
24 #include <msp/gl/shadowmap.h>
25 #include <msp/gl/simplescene.h>
26 #include <msp/gl/technique.h>
27 #include <msp/gl/tests.h>
28 #include <msp/gl/texturecube.h>
29 #include <msp/graphics/simplewindow.h>
30 #include <msp/input/keyboard.h>
31 #include <msp/input/keys.h>
32 #include <msp/time/timestamp.h>
33 #include <msp/time/units.h>
34 #include <msp/time/utils.h>
40 This application demonstrates a variety of features of the mspgl library,
42 - Creating meshes from multiple parts
43 - Creating a mesh and then modifying it
45 - Environment mapped reflections
46 - Skybox using a cube map texture
47 - Nested scenes and pipelines
48 - Complex multitexturing
49 - Shader-based deformations
50 - Creating a normalmapped texture through rendering
52 To run the program in fullscreen mode, specify --fullscreen on the command
55 During execution the following keys are available:
57 space stop camera movement
61 class DesertPillars: public RegisteredApplication<DesertPillars>
66 Graphics::WindowOptions window_opts;
68 Options(const Graphics::Display &, int, char **);
80 class Cube: public GL::AnimatedObject
83 GL::ProgramData shdata;
86 Cube(const GL::Object &);
88 void set_spherify(float);
90 virtual void setup_render(GL::Renderer &, const GL::Tag &) const;
93 Msp::Graphics::Display display;
95 Msp::Graphics::Window window;
96 Msp::Graphics::GLContext gl_context;
97 Msp::Input::Keyboard keyboard;
99 GL::Program skybox_shprog;
100 GL::Technique skybox_tech;
101 GL::TextureCube skybox_tex;
102 ObjectData skybox_data;
104 GL::Program shadow_shprog;
106 GL::Program ground_shprog;
107 GL::ProgramData ground_shdata;
108 GL::Texture2D tiles_texture;
109 GL::Texture2D tiles_normalmap;
110 GL::Texture2D sand_texture;
111 GL::Texture2D sand_normalmap;
112 GL::Technique ground_tech;
113 ObjectData ground_data;
115 GL::Program pillar_shprog;
116 GL::Material pillar_material;
117 GL::Technique pillar_tech;
118 std::vector<ObjectData> pillar_data;
119 std::vector<GL::AnimatedObject *> pillars;
121 GL::Program cube_shprog;
122 GL::Program cube_shadow_shprog;
123 GL::Material cube_material;
124 GL::Technique cube_tech;
125 ObjectData cube_data;
127 GL::EnvironmentMap *env_cube;
129 GL::Pipeline pipeline;
131 GL::SimpleScene sky_scene;
132 GL::InstanceScene scene;
133 GL::Lighting lighting;
135 GL::ShadowMap shadow_scene;
138 GL::Pipeline env_pipeline;
140 Time::TimeStamp last_tick;
149 static const char texture_vertex_src[];
150 static const char texture_fragment_src[];
151 static const char skybox_vertex_src[];
152 static const char skybox_fragment_src[];
153 static const char ground_variables[];
154 static const char cube_variables[];
155 static const float cube_shapes[];
158 DesertPillars(int, char **);
162 void create_pipeline();
163 void create_skybox();
164 static void create_skybox_face(GL::TextureCube &, GL::TextureCubeFace);
165 void create_tiles_texture();
166 void create_sand_texture();
167 static void gaussian_blur(unsigned char *, unsigned, unsigned);
168 static void create_normalmap(const unsigned char *, unsigned char *, unsigned, unsigned, float);
169 void create_ground();
170 static float ground_height(float, float);
171 void create_pillars();
173 static void create_cube_face(GL::MeshBuilder &, const GL::Vector3 &, const GL::Vector3 &, const GL::Vector3 &, unsigned);
180 void key_press(unsigned);
183 const char DesertPillars::texture_vertex_src[] =
188 "out vec3 v_normal;\n"
189 "out vec3 v_color;\n"
192 " gl_Position = vec4(vertex.xy*2.0-1.0, -vertex.z*2.0, 1.0);\n"
193 " v_normal = normal;\n"
194 " v_color = color.rgb;\n"
197 const char DesertPillars::texture_fragment_src[] =
199 "in vec3 v_normal;\n"
201 "out vec4 frag_color;\n"
202 "out vec4 frag_normal;\n"
205 " frag_color = vec4(v_color, 1.0);\n"
206 " frag_normal = vec4(v_normal*0.5+0.5, 1.0);\n"
209 const char DesertPillars::skybox_vertex_src[] =
211 "uniform mat4 projection_matrix;\n"
212 "uniform mat4 eye_obj_matrix;\n"
214 "out vec3 v_texcoord;\n"
217 " gl_Position = projection_matrix*vec4(mat3(eye_obj_matrix)*vertex, 1.0);\n"
218 " v_texcoord = vertex;\n"
221 const char DesertPillars::skybox_fragment_src[] =
223 "uniform samplerCube sky;\n"
224 "in vec3 v_texcoord;\n"
225 "out vec4 frag_color;\n"
228 " frag_color = textureCube(sky, v_texcoord);\n"
231 const char DesertPillars::ground_variables[] =
232 "uniform sampler2D texture1;\n"
233 "uniform sampler2D normalmap1;\n"
234 "uniform sampler2D texture2;\n"
235 "uniform sampler2D normalmap2;\n"
236 "attribute float ground_type;\n"
237 "fragment vec4 diffuse_sample = mix(texture2D(texture1, texture_coord*3.0), texture2D(texture2, texture_coord), ground_type);\n"
238 "fragment vec3 normal_sample = mix(texture2D(normalmap1, texture_coord*3.0).rgb, texture2D(normalmap2, texture_coord).rgb, ground_type);\n";
240 const char DesertPillars::cube_variables[] =
241 "uniform float spherify;\n"
242 "attribute vec3 sphere_coord;\n"
243 "vertex vec3 eye_normal = eye_obj_normal_matrix*normalize(mix(normal, normalize(sphere_coord), spherify));\n"
244 "vertex vec4 eye_vertex = eye_obj_matrix*vec4(mix(vertex.xyz, sphere_coord, spherify), 1.0);\n";
246 const float DesertPillars::cube_shapes[] = { -0.4, 0.5, 1.0, 0.3 };
249 DesertPillars::Options::Options(const Graphics::Display &dpy, int argc, char **argv)
252 getopt.add_option('f', "fullscreen", window_opts.fullscreen, GetOpt::NO_ARG).set_help("Run in fullscreen mode");
255 if(window_opts.fullscreen)
257 const Graphics::VideoMode &mode = dpy.get_desktop_mode();
258 window_opts.width = mode.width;
259 window_opts.height = mode.height;
263 window_opts.width = 800;
264 window_opts.height = 600;
269 DesertPillars::DesertPillars(int argc, char **argv):
270 options(display, argc, argv),
271 window(display, options.window_opts),
274 shadow_shprog(GL::ProgramBuilder::StandardFeatures()),
275 pipeline(window.get_width(), window.get_height()),
276 shadow_scene(2048, scene, light),
277 bloom(window.get_width(), window.get_height()),
278 env_pipeline(512, 512),
280 camera_stopped(false),
287 window.set_title("Desert Pillars");
288 window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &DesertPillars::exit), 0));
289 if(options.window_opts.fullscreen)
290 window.show_cursor(false);
291 keyboard.signal_button_press.connect(sigc::bind_return(sigc::mem_fun(this, &DesertPillars::key_press), false));
300 DesertPillars::~DesertPillars()
304 for(vector<GL::AnimatedObject *>::iterator i=pillars.begin(); i!=pillars.end(); ++i)
308 void DesertPillars::create_pipeline()
310 pipeline.set_multisample(8);
312 camera.set_aspect(float(window.get_width())/window.get_height());
313 camera.set_up_direction(GL::Vector3(0, 0, 1));
314 camera.set_depth_clip(1, 50);
315 pipeline.set_camera(&camera);
317 /* The shadow map is focused on the part of the scene that contains the
318 pillars and the cube. Making the ground cast shadows as well would result
319 either in a very low spatial resolution of the shadow map, or ugly artifacts
320 as the ground crosses the shadow map boundary. */
321 shadow_scene.set_target(GL::Vector3(0, 0, 0), 10);
322 sky_scene.add(shadow_scene);
323 pipeline.add_renderable(sky_scene);
325 // Put the sun pretty high in the sky
326 light.set_position(GL::Vector4(0.5, -2, 3, 0));
327 lighting.attach(0, light);
328 lighting.set_ambient(GL::Color(0.5));
330 // The skybox is rendered first
331 pipeline.add_pass("sky");
333 GL::Pipeline::Pass *pass = &pipeline.add_pass(0);
334 pass->set_lighting(&lighting);
335 pass->set_depth_test(&GL::DepthTest::lequal());
337 /* A bloom filter enhances the realism of bright surfaces, even if there
338 isn't anything really glowy in the scene. */
339 bloom.set_strength(0.3);
340 pipeline.add_postprocessor(bloom);
342 /* Initialize a second pipeline to render the environment map. It has the
343 same renderables and passes, but no postprocessors or camera. */
344 env_pipeline.add_renderable(sky_scene);
345 env_pipeline.add_pass("sky");
346 pass = &env_pipeline.add_pass(0);
347 pass->set_lighting(&lighting);
348 pass->set_depth_test(&GL::DepthTest::lequal());
351 void DesertPillars::create_skybox()
353 skybox_tex.storage(GL::RGB, 128);
354 skybox_tex.set_min_filter(GL::LINEAR);
355 skybox_tex.set_wrap(GL::CLAMP_TO_EDGE);
356 for(unsigned i=0; i<6; ++i)
357 create_skybox_face(skybox_tex, skybox_tex.enumerate_faces(i));
359 skybox_shprog.attach_shader_owned(new GL::VertexShader(skybox_vertex_src));
360 skybox_shprog.attach_shader_owned(new GL::FragmentShader(skybox_fragment_src));
361 skybox_shprog.bind_attribute(GL::VERTEX3, "vertex");
362 skybox_shprog.bind_fragment_data(0, "frag_color");
363 skybox_shprog.link();
365 GL::RenderPass &pass = skybox_tech.add_pass("sky");
366 pass.set_shader_program(&skybox_shprog, 0);
367 pass.set_texture(0, &skybox_tex);
369 // The shader will use the vertex coordinates to initialize texture coordinates as well
370 skybox_data.mesh = new GL::Mesh(GL::VERTEX3);
371 GL::BoxBuilder(10, 10, 10).build(*skybox_data.mesh);
372 skybox_data.object = new GL::Object(skybox_data.mesh, &skybox_tech);
374 sky_scene.add(*skybox_data.object);
377 void DesertPillars::create_skybox_face(GL::TextureCube &texture, GL::TextureCubeFace face)
379 unsigned char *pixels = new unsigned char[128*128*3];
380 for(int y=0; y<128; ++y)
381 for(int x=0; x<128; ++x)
383 unsigned i = (x+y*128)*3;
384 GL::Vector3 v = texture.get_texel_direction(face, x, y);
387 float l = sqrt(v.x*v.x+v.y*v.y+v.z*v.z);
388 // Render a sky-like gradient, with deeper blue at the zenith
389 pixels[i] = 96-48*v.z/l;
390 pixels[i+1] = 168-84*v.z/l;
395 // Fill with a desert-y color below horizon
401 texture.image(face, 0, GL::RGB, GL::UNSIGNED_BYTE, pixels);
405 void DesertPillars::create_tiles_texture()
407 unsigned width = 256;
408 unsigned height = 256;
409 tiles_texture.storage(GL::RGB, width, height);
410 tiles_texture.set_min_filter(GL::LINEAR);
411 tiles_normalmap.storage(GL::RGB, width, height);
412 tiles_normalmap.set_min_filter(GL::LINEAR);
414 GL::Mesh tiles((GL::VERTEX3, GL::NORMAL3, GL::COLOR4_UBYTE));
416 // Prepare some lookup tables for rendering the tiles
417 float split = 1.0f/3;
418 float spacing = split*0.02f;
419 float bevel = split*0.1f;
420 float coords[] = { 0.0f, spacing, spacing+bevel, split-bevel, split, split+spacing, split+spacing+bevel, 1.0f-bevel, 1.0f };
421 unsigned order[] = { 4, 1, 3, 2, 1, 1, 2, 2,
422 1, 4, 2, 3, 4, 4, 3, 3,
423 1, 1, 2, 2, 1, 4, 2, 3,
424 4, 4, 3, 3, 4, 1, 3, 2,
425 2, 3, 2, 2, 3, 3, 3, 2 };
427 GL::MeshBuilder bld(tiles);
429 // Create a dark background
430 bld.color(0.2f, 0.2f, 0.2f);
431 bld.normal(0.0f, 0.0f, 1.0f);
432 bld.begin(GL::TRIANGLE_STRIP);
433 bld.vertex(0.0f, 1.0f);
434 bld.vertex(0.0f, 0.0f);
435 bld.vertex(1.0f, 1.0f);
436 bld.vertex(1.0f, 0.0f);
439 // Create the four tiles
440 bld.color(0.95f, 0.8f, 0.65f);
441 for(unsigned i=0; i<2; ++i)
442 for(unsigned j=0; j<2; ++j)
444 for(unsigned k=0; k<4; ++k)
446 bld.begin(GL::TRIANGLE_STRIP);
447 float facing = (k%2)*2-1.0f;
449 bld.normal(0.0f, 0.7071f*facing, 0.7071f);
451 bld.normal(0.7071f*facing, 0.0f, 0.7071f);
452 for(unsigned l=0; l<4; ++l)
453 bld.vertex(coords[i*4+order[k*8+l*2]], coords[j*4+order[k*8+l*2+1]], (l%2 ? bevel : 0.0f));
457 bld.begin(GL::TRIANGLE_STRIP);
458 bld.normal(0.0f, 0.0f, 1.0f);
459 for(unsigned l=0; l<4; ++l)
460 bld.vertex(coords[i*4+order[32+l*2]], coords[j*4+order[32+l*2+1]], bevel);
464 GL::Program shprog(texture_vertex_src, texture_fragment_src);
465 shprog.bind_attribute(GL::VERTEX3, "vertex");
466 shprog.bind_attribute(GL::NORMAL3, "normal");
467 shprog.bind_attribute(GL::COLOR4_UBYTE, "color");
468 shprog.bind_fragment_data(0, "frag_color");
469 shprog.bind_fragment_data(1, "frag_normal");
472 // Use an FBO to turn the geometry into a normalmapped texture
474 fbo.attach(GL::COLOR_ATTACHMENT0, tiles_texture);
475 fbo.attach(GL::COLOR_ATTACHMENT1, tiles_normalmap);
476 GL::Bind bind_fbo(fbo);
477 GL::Renderer renderer(0);
478 renderer.set_shader_program(&shprog, 0);
479 tiles.draw(renderer);
482 void DesertPillars::create_sand_texture()
484 unsigned width = 512;
485 unsigned height = 512;
487 sand_texture.storage(GL::RGB, width/16, height/16);
488 sand_texture.set_min_filter(GL::LINEAR_MIPMAP_LINEAR);
489 sand_texture.set_max_anisotropy(4);
490 sand_texture.set_generate_mipmap(true);
491 sand_normalmap.storage(GL::RGB, width, height);
492 sand_normalmap.set_min_filter(GL::LINEAR_MIPMAP_LINEAR);
493 sand_normalmap.set_max_anisotropy(4);
494 sand_normalmap.set_generate_mipmap(true);
496 unsigned char *pixels = new unsigned char[width*height*3];
497 unsigned char *bump = new unsigned char[width*height];
498 for(unsigned y=0; y<height; ++y)
499 for(unsigned x=0; x<width; ++x)
501 unsigned i = (x+y*width)*3;
502 unsigned c = rand()%16;
506 bump[x+y*width] = rand();
508 sand_texture.image(0, GL::RGB, GL::UNSIGNED_BYTE, pixels);
509 gaussian_blur(bump, width, height);
510 create_normalmap(bump, pixels, width, height, 4);
511 sand_normalmap.image(0, GL::RGB, GL::UNSIGNED_BYTE, pixels);
516 void DesertPillars::gaussian_blur(unsigned char *data, unsigned width, unsigned height)
518 /* Create a gaussian blur kernel for σ=2. Gaussian blur is separable, so a
519 1-dimensional kernel is enough. */
522 for(int i=-4; i<=4; ++i)
523 sum += (kernel[4+i] = exp(-i*i*0.5));
524 for(unsigned i=0; i<9; ++i)
527 unsigned char *line = new unsigned char[max(width, height)];
528 // Perform the blur in the X direction
529 for(unsigned y=0; y<height; ++y)
531 for(unsigned x=0; x<width; ++x)
534 for(int i=-4; i<=4; ++i)
535 value += data[(x+width+i)%width+y*width]*kernel[4+i];
538 copy(line, line+width, data+y*width);
540 // And then in the Y direction
541 for(unsigned x=0; x<width; ++x)
543 for(unsigned y=0; y<height; ++y)
546 for(int i=-4; i<=4; ++i)
547 value += data[x+((y+height+i)%height)*width]*kernel[4+i];
550 for(unsigned y=0; y<height; ++y)
551 data[x+y*width] = line[y];
556 void DesertPillars::create_normalmap(const unsigned char *bump, unsigned char *normals, unsigned width, unsigned height, float depth)
558 for(unsigned y=0; y<height; ++y)
559 for(unsigned x=0; x<width; ++x)
561 float dz_x = (bump[(x+1)%width+y*width]-bump[(x+width-1)%width+y*width])*depth/510;
562 float dz_y = (bump[x+((y+1)%height)*width]-bump[x+((y+height-1)%height)*width])*depth/510;
563 float l = sqrt(dz_x*dz_x+dz_y*dz_y+1);
564 unsigned i = (x+y*width)*3;
565 normals[i] = (0.5-0.5*dz_x/l)*255;
566 normals[i+1] = (0.5-0.5*dz_y/l)*255;
567 normals[i+2] = (0.5+0.5/l)*255;
571 void DesertPillars::create_ground()
573 create_tiles_texture();
574 create_sand_texture();
576 GL::ProgramBuilder::StandardFeatures features;
577 features.lighting = true;
578 features.shadow = true;
579 features.texture = true;
580 features.normal_map = true;
581 features.custom = ground_variables;
582 GL::ProgramBuilder(features).add_shaders(ground_shprog);
583 ground_shprog.bind_attribute(7, "ground_type");
584 ground_shprog.link();
586 ground_shdata.uniform("texture1", 0);
587 ground_shdata.uniform("normalmap1", 1);
588 ground_shdata.uniform("texture2", 2);
589 ground_shdata.uniform("normalmap2", 3);
591 GL::RenderPass *pass = &ground_tech.add_pass(0);
592 pass->set_shader_program(&ground_shprog, &ground_shdata);
593 pass->set_texture(0, &tiles_texture);
594 pass->set_texture(1, &tiles_normalmap);
595 pass->set_texture(2, &sand_texture);
596 pass->set_texture(3, &sand_normalmap);
598 /* No shadow pass here; the ground only receives shadows, but doesn't cast
601 GL::VertexFormat vfmt = (GL::VERTEX3, GL::NORMAL3, GL::TANGENT3, GL::BINORMAL3, GL::TEXCOORD2, GL::ATTRIB1,7);
602 ground_data.mesh = new GL::Mesh(vfmt);
604 // Create a base grid
605 GL::GridBuilder(80, 80, 200, 200).tbn().build(*ground_data.mesh);
607 // And modify it with a heightmap
608 unsigned n_vertices = ground_data.mesh->get_n_vertices();
609 unsigned pos = vfmt.offset(GL::VERTEX3);
610 unsigned nor = vfmt.offset(GL::NORMAL3);
611 unsigned tan = vfmt.offset(GL::TANGENT3);
612 unsigned bin = vfmt.offset(GL::BINORMAL3);
613 unsigned tex = vfmt.offset(GL::TEXCOORD2);
614 unsigned gt = vfmt.offset(GL::make_indexed_component(GL::ATTRIB1, 7));
615 for(unsigned i=0; i<n_vertices; ++i)
617 float *v = ground_data.mesh->modify_vertex(i);
618 v[pos+2] = ground_height(v[pos], v[pos+1]);
620 float dz_x = (ground_height(v[pos]+0.01, v[pos+1])-ground_height(v[pos]-0.01, v[pos+1]))/0.02;
621 float dz_y = (ground_height(v[pos], v[pos+1]+0.01)-ground_height(v[pos], v[pos+1]-0.01))/0.02;
622 float l = sqrt(dz_x*dz_x+dz_y*dz_y+1);
627 l = sqrt(dz_x*dz_x+1);
631 l = sqrt(dz_y*dz_y+1);
635 v[gt] = min(v[pos+2]*100, 1.0f);
640 ground_data.object = new GL::Object(ground_data.mesh, &ground_tech);
642 scene.add(*ground_data.object);
645 float DesertPillars::ground_height(float x, float y)
647 // Leave a flat area in the middle
648 float d = sqrt(x*x+y*y);
652 // This results in concentric rings of low hills
653 int i = (d-6)/(M_PI*2);
654 float a = atan2(y, x);
657 float h = (i%2) ? 0.5 : 0.3;
658 return (d-6)*0.001f+(1-cos(d-6))*(1-cos(a))*h;
661 void DesertPillars::create_pillars()
663 // The pillars are a matt off-white
664 pillar_material.set_diffuse(GL::Color(0.9, 0.88, 0.8));
665 pillar_material.set_ambient(GL::Color(0.9, 0.88, 0.8));
667 GL::ProgramBuilder::StandardFeatures features;
668 features.lighting = true;
669 features.material = true;
670 features.shadow = true;
671 GL::ProgramBuilder(features).add_shaders(pillar_shprog);
672 pillar_shprog.link();
674 GL::RenderPass *pass = &pillar_tech.add_pass(0);
675 pass->set_material(&pillar_material);
676 pass->set_shader_program(&pillar_shprog, 0);
678 pass = &pillar_tech.add_pass("shadow");
679 pass->set_shader_program(&shadow_shprog, 0);
681 pillar_data.reserve(7);
682 for(unsigned i=3; i<=20; ++i)
685 for(unsigned j=2; j<i; ++j)
689 if(pillar_data.size()<=height)
690 pillar_data.resize(height+1);
692 ObjectData &pd = pillar_data[height];
695 pd.mesh = new GL::Mesh((GL::VERTEX3, GL::NORMAL3));
696 GL::MeshBuilder bld(*pd.mesh);
698 // Produce a fluted cylinder
699 unsigned n_flutes = 12;
700 float r_bottom = cos(M_PI/n_flutes)*0.4;
701 float flute_depth = (0.4-r_bottom)*2;
702 float half_w = sin(M_PI/n_flutes)*0.4;
703 for(unsigned j=0; j<n_flutes; ++j)
705 float a = j*M_PI*2/n_flutes;
706 bld.begin(GL::TRIANGLE_STRIP);
707 for(int k=-3; k<=3; k+=2)
710 float x = cos(a)*(r_bottom-(1-t*t)*flute_depth)-sin(a)*t*half_w;
711 float y = sin(a)*(r_bottom-(1-t*t)*flute_depth)+cos(a)*t*half_w;
712 float d = -t*2*flute_depth;
713 float l = sqrt(d*d+1);
714 bld.normal(cos(a)/l-sin(a)*d/l, sin(a)/l+cos(a)*d/l, 0);
715 bld.vertex(x, y, 0.6+height);
716 bld.vertex(x, y, 0.6);
721 // Create a square plinth and capitel
722 bld.matrix() = GL::Matrix::translation(0, 0, 0.3);
723 GL::BoxBuilder(1.0, 1.0, 0.6).build(bld);
724 bld.matrix() = GL::Matrix::translation(0, 0, height+0.8);
725 GL::BoxBuilder(1.0, 1.0, 0.4).build(bld);
727 pd.object = new GL::Object(pd.mesh, &pillar_tech);
730 GL::AnimatedObject *pillar = new GL::AnimatedObject(*pd.object);
732 float a = (i-3)*2*M_PI/18;
733 matrix.translate(cos(a)*5, sin(a)*5, 0);
734 matrix.rotate(a, 0, 0, 1);
735 pillar->set_matrix(matrix);
737 pillars.push_back(pillar);
742 void DesertPillars::create_cube()
744 /* The cube is bluish-gray, with a hard specular reflection to produce a
746 cube_material.set_diffuse(GL::Color(0.5, 0.5, 0.55));
747 cube_material.set_ambient(GL::Color(0.5, 0.5, 0.55));
748 cube_material.set_specular(GL::Color(1.0));
749 cube_material.set_shininess(120);
750 cube_material.set_reflectivity(0.5);
752 // First create a simplified shader for rendering the shadow map
753 GL::ProgramBuilder::StandardFeatures features;
754 features.custom = cube_variables;
755 GL::ProgramBuilder(features).add_shaders(cube_shadow_shprog);
756 cube_shadow_shprog.bind_attribute(7, "sphere_coord");
757 cube_shadow_shprog.link();
759 // Then add the rest of the features for normal rendering
760 features.lighting = true;
761 features.specular = true;
762 features.material = true;
763 features.shadow = true;
764 features.reflection = true;
765 GL::ProgramBuilder(features).add_shaders(cube_shprog);
766 cube_shprog.bind_attribute(7, "sphere_coord");
769 GL::RenderPass *pass = &cube_tech.add_pass(0);
770 pass->set_material(&cube_material);
771 pass->set_shader_program(&cube_shprog, 0);
773 pass = &cube_tech.add_pass("shadow");
774 pass->set_shader_program(&cube_shadow_shprog, 0);
776 cube_data.mesh = new GL::Mesh((GL::VERTEX3, GL::NORMAL3, GL::ATTRIB3,7));
777 GL::MeshBuilder bld(*cube_data.mesh);
778 create_cube_face(bld, GL::Vector3(-1, -1, -1), GL::Vector3(2, 0, 0), GL::Vector3(0, 2, 0), 16);
779 bld.offset(cube_data.mesh->get_n_vertices());
780 create_cube_face(bld, GL::Vector3(-1, 1, -1), GL::Vector3(2, 0, 0), GL::Vector3(0, 0, 2), 16);
781 bld.offset(cube_data.mesh->get_n_vertices());
782 create_cube_face(bld, GL::Vector3(-1, 1, 1), GL::Vector3(2, 0, 0), GL::Vector3(0, -2, 0), 16);
783 bld.offset(cube_data.mesh->get_n_vertices());
784 create_cube_face(bld, GL::Vector3(1, -1, -1), GL::Vector3(-2, 0, 0), GL::Vector3(0, 0, 2), 16);
785 bld.offset(cube_data.mesh->get_n_vertices());
786 create_cube_face(bld, GL::Vector3(-1, -1, 1), GL::Vector3(0, 0, -2), GL::Vector3(0, 2, 0), 16);
787 bld.offset(cube_data.mesh->get_n_vertices());
788 create_cube_face(bld, GL::Vector3(1, -1, -1), GL::Vector3(0, 0, 2), GL::Vector3(0, 2, 0), 16);
789 cube_data.object = new GL::Object(cube_data.mesh, &cube_tech);
791 cube = new Cube(*cube_data.object);
792 env_cube = new GL::EnvironmentMap(512, *cube, env_pipeline);
793 scene.add(*env_cube);
796 void DesertPillars::create_cube_face(GL::MeshBuilder &bld, const GL::Vector3 &base, const GL::Vector3 &side1, const GL::Vector3 &side2, unsigned div)
798 /* The sides follow the cube map convention where the cross product points
799 inwards. Since the normal has to point outwards, reverse the order. */
801 n.x = side2.y*side1.z-side2.z*side1.y;
802 n.y = side2.z*side1.x-side2.x*side1.z;
803 n.z = side2.x*side1.y-side2.y*side1.x;
804 float l = sqrt(n.x*n.x+n.y*n.y+n.z*n.z);
805 bld.normal(n.x/l, n.y/l, n.z/l);
807 // Create vertices, with precomputed spherified coordinates
808 for(unsigned i=0; i<=div; ++i)
809 for(unsigned j=0; j<=div; ++j)
812 v.x = base.x+side1.x*i/div+side2.x*j/div;
813 v.y = base.y+side1.y*i/div+side2.y*j/div;
814 v.z = base.z+side1.z*i/div+side2.z*j/div;
816 l = sqrt(v.x*v.x+v.y*v.y+v.z*v.z);
818 bld.attrib(7, v.x/l, v.y/l, v.z/l);
823 for(unsigned i=0; i<div; ++i)
825 bld.begin(GL::TRIANGLE_STRIP);
826 for(unsigned j=0; j<=div; ++j)
828 bld.element(i*(div+1)+j);
829 bld.element((i+1)*(div+1)+j);
835 int DesertPillars::main()
838 return Application::main();
841 void DesertPillars::tick()
843 Time::TimeStamp t = Time::now();
851 camera_angle += (dt/Time::sec)*M_PI*2/30;
852 if(camera_angle>M_PI*4)
853 camera_angle -= M_PI*4;
854 float h = 3+(1-cos(camera_angle*1.5))*3;
855 float r = sqrt(225-h*h);
856 camera.set_position(GL::Vector3(cos(camera_angle)*r, sin(camera_angle)*r, 1.5+h));
857 camera.look_at(GL::Vector3(0, 0, 2));
862 cube_angle += (dt/Time::sec)*M_PI*2/20;
863 GL::Matrix cube_matrix;
864 cube_matrix.translate(0, 0, 2.5);
865 cube_matrix.rotate(cube_angle, 0, 0, 1);
866 cube_matrix.rotate(cube_angle*0.5, 1, 0, 0);
867 cube->set_matrix(cube_matrix);
872 cube_phase += (dt/Time::sec)/5;
882 float x = cube_phase*5;
884 cube->set_spherify((1-x)*cube_shapes[(cube_shape+3)%4]+x*cube_shapes[cube_shape]);
887 cube->set_spherify(cube_shapes[cube_shape]);
891 GL::Framebuffer::system().clear(GL::COLOR_BUFFER_BIT|GL::DEPTH_BUFFER_BIT);
893 gl_context.swap_buffers();
896 void DesertPillars::key_press(unsigned key)
898 if(key==Input::KEY_ESC)
900 else if(key==Input::KEY_SPACE)
901 camera_stopped = !camera_stopped;
902 else if(key==Input::KEY_F)
903 cube_frozen = !cube_frozen;
904 else if(key==Input::KEY_S)
905 cube_stopped = !cube_stopped;
909 DesertPillars::ObjectData::ObjectData():
914 DesertPillars::ObjectData::~ObjectData()
921 DesertPillars::Cube::Cube(const GL::Object &obj):
922 GL::AnimatedObject(obj)
925 void DesertPillars::Cube::set_spherify(float s)
927 shdata.uniform("spherify", s);
930 void DesertPillars::Cube::setup_render(GL::Renderer &renderer, const GL::Tag &tag) const
932 AnimatedObject::setup_render(renderer, tag);
933 renderer.add_shader_data(shdata);