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
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::VertexShader cube_transform;
122 GL::Program cube_shprog;
123 GL::Program cube_shadow_shprog;
124 GL::Material cube_material;
125 GL::Technique cube_tech;
126 ObjectData cube_data;
128 GL::EnvironmentMap *env_cube;
130 GL::Pipeline pipeline;
132 GL::SimpleScene sky_scene;
133 GL::InstanceScene scene;
134 GL::Lighting lighting;
136 GL::ShadowMap shadow_scene;
139 GL::Pipeline env_pipeline;
141 Time::TimeStamp last_tick;
150 static const char texture_vertex_src[];
151 static const char texture_fragment_src[];
152 static const char skybox_vertex_src[];
153 static const char skybox_fragment_src[];
154 static const char ground_transform_src[];
155 static const char ground_colorify_src[];
156 static const char cube_transform_src[];
157 static const float cube_shapes[];
160 DesertPillars(int, char **);
164 void create_pipeline();
165 void create_skybox();
166 static void create_skybox_face(GL::TextureCube &, GL::TextureCubeFace);
167 void create_tiles_texture();
168 void create_sand_texture();
169 static void gaussian_blur(unsigned char *, unsigned, unsigned);
170 static void create_normalmap(const unsigned char *, unsigned char *, unsigned, unsigned, float);
171 void create_ground();
172 static float ground_height(float, float);
173 void create_pillars();
175 static void create_cube_face(GL::MeshBuilder &, const GL::Vector3 &, const GL::Vector3 &, const GL::Vector3 &, unsigned);
182 void key_press(unsigned);
185 const char DesertPillars::texture_vertex_src[] =
186 "varying vec3 v_normal;\n"
187 "varying vec3 v_color;\n"
190 " gl_Position = vec4(gl_Vertex.xy*2.0-1.0, -gl_Vertex.z*2.0, 1.0);\n"
191 " v_normal = gl_Normal;\n"
192 " v_color = gl_Color.rgb;\n"
195 const char DesertPillars::texture_fragment_src[] =
196 "varying vec3 v_normal;\n"
197 "varying vec3 v_color;\n"
200 " gl_FragData[0] = vec4(v_color, 1.0);\n"
201 " gl_FragData[1] = vec4(v_normal*0.5+0.5, 1.0);\n"
204 const char DesertPillars::skybox_vertex_src[] =
205 "varying vec3 v_texcoord;\n"
208 " gl_Position = gl_ProjectionMatrix*vec4(mat3(gl_ModelViewMatrix)*gl_Vertex.xyz, 1.0);\n"
209 " v_texcoord = gl_Vertex;\n"
212 const char DesertPillars::skybox_fragment_src[] =
213 "uniform samplerCube sky;\n"
214 "varying vec3 v_texcoord;\n"
217 " gl_FragColor = textureCube(sky, v_texcoord);\n"
220 // This exists only to transfer the ground type to fragment shader
221 const char DesertPillars::ground_transform_src[] =
222 "attribute float ground_type;\n"
223 "varying float v_ground_type;\n"
224 "vec4 transform_vertex(vec4 vertex)\n"
226 " v_ground_type = ground_type;\n"
227 " return gl_ModelViewMatrix*vertex;\n"
229 "vec3 transform_normal(vec3 normal)\n"
231 " return gl_NormalMatrix*normal;\n"
234 const char DesertPillars::ground_colorify_src[] =
235 "uniform sampler2D texture1;\n"
236 "uniform sampler2D normalmap1;\n"
237 "uniform sampler2D texture2;\n"
238 "uniform sampler2D normalmap2;\n"
239 "varying float v_ground_type;\n"
240 "vec4 sample_texture(vec2 coord)\n"
242 " return mix(texture2D(texture1, coord*3), texture2D(texture2, coord), v_ground_type);\n"
244 "vec3 sample_normalmap(vec2 coord)\n"
246 " return mix(texture2D(normalmap1, coord*3), texture2D(normalmap2, coord), v_ground_type);\n"
249 const char DesertPillars::cube_transform_src[] =
250 "uniform float spherify;\n"
251 "attribute vec3 sphere_coord;\n"
252 "vec4 transform_vertex(vec4 vertex)\n"
254 " return gl_ModelViewMatrix*vec4(mix(vertex, sphere_coord, spherify), 1.0);\n"
256 "vec3 transform_normal(vec3 normal)\n"
258 " return gl_NormalMatrix*normalize(mix(normal, normalize(sphere_coord), spherify));\n"
261 const float DesertPillars::cube_shapes[] = { -0.4, 0.5, 1.0, 0.3 };
264 DesertPillars::Options::Options(const Graphics::Display &display, int argc, char **argv)
267 getopt.add_option('f', "fullscreen", window_opts.fullscreen, GetOpt::NO_ARG).set_help("Run in fullscreen mode");
270 if(window_opts.fullscreen)
272 const Graphics::VideoMode &mode = display.get_desktop_mode();
273 window_opts.width = mode.width;
274 window_opts.height = mode.height;
278 window_opts.width = 800;
279 window_opts.height = 600;
284 DesertPillars::DesertPillars(int argc, char **argv):
285 options(display, argc, argv),
286 window(display, options.window_opts),
289 shadow_shprog(GL::ProgramBuilder::StandardFeatures()),
290 pipeline(window.get_width(), window.get_height()),
291 shadow_scene(2048, scene, light),
292 bloom(window.get_width(), window.get_height()),
293 env_pipeline(512, 512),
295 camera_stopped(false),
302 window.set_title("Desert Pillars");
303 window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &DesertPillars::exit), 0));
304 if(options.window_opts.fullscreen)
305 window.show_cursor(false);
306 keyboard.signal_button_press.connect(sigc::mem_fun(this, &DesertPillars::key_press));
315 DesertPillars::~DesertPillars()
319 for(vector<GL::AnimatedObject *>::iterator i=pillars.begin(); i!=pillars.end(); ++i)
323 void DesertPillars::create_pipeline()
325 pipeline.set_multisample(8);
327 camera.set_aspect(float(window.get_width())/window.get_height());
328 camera.set_up_direction(GL::Vector3(0, 0, 1));
329 camera.set_depth_clip(1, 50);
330 pipeline.set_camera(&camera);
332 /* The shadow map is focused on the part of the scene that contains the
333 pillars and the cube. Making the ground cast shadows as well would result
334 either in a very low spatial resolution of the shadow map, or ugly artifacts
335 as the ground crosses the shadow map boundary. */
336 shadow_scene.set_target(GL::Vector3(0, 0, 0), 10);
337 shadow_scene.set_texture_unit(5);
338 sky_scene.add(shadow_scene);
339 pipeline.add_renderable(sky_scene);
341 // Put the sun pretty high in the sky
342 light.set_position(GL::Vector4(0.5, -2, 3, 0));
343 lighting.attach(0, light);
344 lighting.set_ambient(GL::Color(0.5));
346 // The skybox is rendered first
347 pipeline.add_pass("sky");
349 GL::Pipeline::Pass *pass = &pipeline.add_pass(0);
350 pass->set_lighting(&lighting);
351 pass->set_depth_test(&GL::DepthTest::lequal());
353 /* A bloom filter enhances the realism of bright surfaces, even if there
354 isn't anything really glowy in the scene. */
355 bloom.set_strength(0.3);
356 pipeline.add_postprocessor(bloom);
358 /* Initialize a second pipeline to render the environment map. It has the
359 same renderables and passes, but no postprocessors or camera. */
360 env_pipeline.add_renderable(sky_scene);
361 env_pipeline.add_pass("sky");
362 pass = &env_pipeline.add_pass(0);
363 pass->set_lighting(&lighting);
364 pass->set_depth_test(&GL::DepthTest::lequal());
367 void DesertPillars::create_skybox()
369 skybox_tex.storage(GL::RGB, 128);
370 skybox_tex.set_min_filter(GL::LINEAR);
371 skybox_tex.set_wrap(GL::CLAMP_TO_EDGE);
372 for(unsigned i=0; i<6; ++i)
373 create_skybox_face(skybox_tex, skybox_tex.enumerate_faces(i));
375 skybox_shprog.attach_shader_owned(new GL::VertexShader(skybox_vertex_src));
376 skybox_shprog.attach_shader_owned(new GL::FragmentShader(skybox_fragment_src));
377 skybox_shprog.link();
379 GL::RenderPass &pass = skybox_tech.add_pass("sky");
380 pass.set_shader_program(&skybox_shprog, 0);
381 pass.set_texture(0, &skybox_tex);
383 // The shader will use the vertex coordinates to initialize texture coordinates as well
384 skybox_data.mesh = new GL::Mesh(GL::VERTEX3);
385 GL::BoxBuilder(10, 10, 10).build(*skybox_data.mesh);
386 skybox_data.object = new GL::Object(skybox_data.mesh, &skybox_tech);
388 sky_scene.add(*skybox_data.object);
391 void DesertPillars::create_skybox_face(GL::TextureCube &texture, GL::TextureCubeFace face)
393 unsigned char *pixels = new unsigned char[128*128*3];
394 for(int y=0; y<128; ++y)
395 for(int x=0; x<128; ++x)
397 unsigned i = (x+y*128)*3;
398 GL::Vector3 v = texture.get_texel_direction(face, x, y);
401 float l = sqrt(v.x*v.x+v.y*v.y+v.z*v.z);
402 // Render a sky-like gradient, with deeper blue at the zenith
403 pixels[i] = 96-48*v.z/l;
404 pixels[i+1] = 168-84*v.z/l;
409 // Fill with a desert-y color below horizon
415 texture.image(face, 0, GL::RGB, GL::UNSIGNED_BYTE, pixels);
419 void DesertPillars::create_tiles_texture()
421 unsigned width = 256;
422 unsigned height = 256;
423 tiles_texture.storage(GL::RGB, width, height);
424 tiles_texture.set_min_filter(GL::LINEAR);
425 tiles_normalmap.storage(GL::RGB, width, height);
426 tiles_normalmap.set_min_filter(GL::LINEAR);
428 GL::Mesh tiles((GL::VERTEX3, GL::NORMAL3, GL::COLOR4_UBYTE));
430 // Prepare some lookup tables for rendering the tiles
431 float split = 1.0f/3;
432 float spacing = split*0.02f;
433 float bevel = split*0.1f;
434 float coords[] = { 0.0f, spacing, spacing+bevel, split-bevel, split, split+spacing, split+spacing+bevel, 1.0f-bevel, 1.0f };
435 unsigned order[] = { 4, 1, 3, 2, 1, 1, 2, 2,
436 1, 4, 2, 3, 4, 4, 3, 3,
437 1, 1, 2, 2, 1, 4, 2, 3,
438 4, 4, 3, 3, 4, 1, 3, 2,
439 2, 3, 2, 2, 3, 3, 3, 2 };
441 GL::MeshBuilder bld(tiles);
443 // Create a dark background
444 bld.color(0.2f, 0.2f, 0.2f);
445 bld.normal(0.0f, 0.0f, 1.0f);
446 bld.begin(GL::TRIANGLE_STRIP);
447 bld.vertex(0.0f, 1.0f);
448 bld.vertex(0.0f, 0.0f);
449 bld.vertex(1.0f, 1.0f);
450 bld.vertex(1.0f, 0.0f);
453 // Create the four tiles
454 bld.color(0.95f, 0.8f, 0.65f);
455 for(unsigned i=0; i<2; ++i)
456 for(unsigned j=0; j<2; ++j)
458 for(unsigned k=0; k<4; ++k)
460 bld.begin(GL::TRIANGLE_STRIP);
461 float facing = (k%2)*2-1.0f;
463 bld.normal(0.0f, 0.7071f*facing, 0.7071f);
465 bld.normal(0.7071f*facing, 0.0f, 0.7071f);
466 for(unsigned l=0; l<4; ++l)
467 bld.vertex(coords[i*4+order[k*8+l*2]], coords[j*4+order[k*8+l*2+1]], (l%2 ? bevel : 0.0f));
471 bld.begin(GL::TRIANGLE_STRIP);
472 bld.normal(0.0f, 0.0f, 1.0f);
473 for(unsigned l=0; l<4; ++l)
474 bld.vertex(coords[i*4+order[32+l*2]], coords[j*4+order[32+l*2+1]], bevel);
478 GL::Program shprog(texture_vertex_src, texture_fragment_src);
480 // Use an FBO to turn the geometry into a normalmapped texture
482 fbo.attach(GL::COLOR_ATTACHMENT0, tiles_texture);
483 fbo.attach(GL::COLOR_ATTACHMENT1, tiles_normalmap);
484 GL::Bind bind_fbo(fbo);
485 GL::Renderer renderer(0);
486 renderer.set_shader_program(&shprog, 0);
487 tiles.draw(renderer);
490 void DesertPillars::create_sand_texture()
492 unsigned width = 512;
493 unsigned height = 512;
495 sand_texture.storage(GL::RGB, width/16, height/16);
496 sand_texture.set_min_filter(GL::LINEAR_MIPMAP_LINEAR);
497 sand_texture.set_max_anisotropy(4);
498 sand_texture.set_generate_mipmap(true);
499 sand_normalmap.storage(GL::RGB, width, height);
500 sand_normalmap.set_min_filter(GL::LINEAR_MIPMAP_LINEAR);
501 sand_normalmap.set_max_anisotropy(4);
502 sand_normalmap.set_generate_mipmap(true);
504 unsigned char *pixels = new unsigned char[width*height*3];
505 unsigned char *bump = new unsigned char[width*height];
506 for(unsigned y=0; y<height; ++y)
507 for(unsigned x=0; x<width; ++x)
509 unsigned i = (x+y*width)*3;
510 unsigned c = rand()%16;
514 bump[x+y*width] = rand();
516 sand_texture.image(0, GL::RGB, GL::UNSIGNED_BYTE, pixels);
517 gaussian_blur(bump, width, height);
518 create_normalmap(bump, pixels, width, height, 4);
519 sand_normalmap.image(0, GL::RGB, GL::UNSIGNED_BYTE, pixels);
524 void DesertPillars::gaussian_blur(unsigned char *data, unsigned width, unsigned height)
526 /* Create a gaussian blur kernel for σ=2. Gaussian blur is separable, so a
527 1-dimensional kernel is enough. */
530 for(int i=-4; i<=4; ++i)
531 sum += (kernel[4+i] = exp(-i*i*0.5));
532 for(unsigned i=0; i<9; ++i)
535 unsigned char *line = new unsigned char[max(width, height)];
536 // Perform the blur in the X direction
537 for(unsigned y=0; y<height; ++y)
539 for(unsigned x=0; x<width; ++x)
542 for(int i=-4; i<=4; ++i)
543 value += data[(x+width+i)%width+y*width]*kernel[4+i];
546 copy(line, line+width, data+y*width);
548 // And then in the Y direction
549 for(unsigned x=0; x<width; ++x)
551 for(unsigned y=0; y<height; ++y)
554 for(int i=-4; i<=4; ++i)
555 value += data[x+((y+height+i)%height)*width]*kernel[4+i];
558 for(unsigned y=0; y<height; ++y)
559 data[x+y*width] = line[y];
564 void DesertPillars::create_normalmap(const unsigned char *bump, unsigned char *normals, unsigned width, unsigned height, float depth)
566 for(unsigned y=0; y<height; ++y)
567 for(unsigned x=0; x<width; ++x)
569 float dz_x = (bump[(x+1)%width+y*width]-bump[(x+width-1)%width+y*width])*depth/510;
570 float dz_y = (bump[x+((y+1)%height)*width]-bump[x+((y+height-1)%height)*width])*depth/510;
571 float l = sqrt(dz_x*dz_x+dz_y*dz_y+1);
572 unsigned i = (x+y*width)*3;
573 normals[i] = (0.5-0.5*dz_x/l)*255;
574 normals[i+1] = (0.5-0.5*dz_y/l)*255;
575 normals[i+2] = (0.5+0.5/l)*255;
579 void DesertPillars::create_ground()
581 create_tiles_texture();
582 create_sand_texture();
584 GL::ProgramBuilder::StandardFeatures features;
585 features.lighting = true;
586 features.shadow = true;
587 features.texture = true;
588 features.normalmap = true;
589 features.transform = true;
590 features.colorify = true;
591 GL::ProgramBuilder(features).add_shaders(ground_shprog);
592 ground_shprog.attach_shader_owned(new GL::VertexShader(ground_transform_src));
593 ground_shprog.attach_shader_owned(new GL::FragmentShader(ground_colorify_src));
594 ground_shprog.bind_attribute(7, "ground_type");
595 ground_shprog.link();
597 ground_shdata.uniform("texture1", 0);
598 ground_shdata.uniform("normalmap1", 1);
599 ground_shdata.uniform("texture2", 2);
600 ground_shdata.uniform("normalmap2", 3);
602 GL::RenderPass *pass = &ground_tech.add_pass(0);
603 pass->set_shader_program(&ground_shprog, &ground_shdata);
604 pass->set_texture(0, &tiles_texture);
605 pass->set_texture(1, &tiles_normalmap);
606 pass->set_texture(2, &sand_texture);
607 pass->set_texture(3, &sand_normalmap);
609 /* No shadow pass here; the ground only receives shadows, but doesn't cast
612 GL::VertexFormat vfmt = (GL::VERTEX3, GL::NORMAL3, GL::TANGENT3, GL::BINORMAL3, GL::TEXCOORD2, GL::ATTRIB1,7);
613 ground_data.mesh = new GL::Mesh(vfmt);
615 // Create a base grid
616 GL::GridBuilder(80, 80, 200, 200).tbn().build(*ground_data.mesh);
618 // And modify it with a heightmap
619 unsigned n_vertices = ground_data.mesh->get_n_vertices();
620 unsigned pos = vfmt.offset(GL::VERTEX3);
621 unsigned nor = vfmt.offset(GL::NORMAL3);
622 unsigned tan = vfmt.offset(GL::TANGENT3);
623 unsigned bin = vfmt.offset(GL::BINORMAL3);
624 unsigned tex = vfmt.offset(GL::TEXCOORD2);
625 unsigned gt = vfmt.offset(GL::make_indexed_component(GL::ATTRIB1, 7));
626 for(unsigned i=0; i<n_vertices; ++i)
628 float *v = ground_data.mesh->modify_vertex(i);
629 v[pos+2] = ground_height(v[pos], v[pos+1]);
631 float dz_x = (ground_height(v[pos]+0.01, v[pos+1])-ground_height(v[pos]-0.01, v[pos+1]))/0.02;
632 float dz_y = (ground_height(v[pos], v[pos+1]+0.01)-ground_height(v[pos], v[pos+1]-0.01))/0.02;
633 float l = sqrt(dz_x*dz_x+dz_y*dz_y+1);
638 l = sqrt(dz_x*dz_x+1);
642 l = sqrt(dz_y*dz_y+1);
646 v[gt] = min(v[pos+2]*100, 1.0f);
651 ground_data.object = new GL::Object(ground_data.mesh, &ground_tech);
653 scene.add(*ground_data.object);
656 float DesertPillars::ground_height(float x, float y)
658 // Leave a flat area in the middle
659 float d = sqrt(x*x+y*y);
663 // This results in concentric rings of low hills
664 int i = (d-6)/(M_PI*2);
665 float a = atan2(y, x);
668 float h = (i%2) ? 0.5 : 0.3;
669 return (d-6)*0.001f+(1-cos(d-6))*(1-cos(a))*h;
672 void DesertPillars::create_pillars()
674 // The pillars are a matt off-white
675 pillar_material.set_diffuse(GL::Color(0.9, 0.88, 0.8));
676 pillar_material.set_ambient(GL::Color(0.9, 0.88, 0.8));
678 GL::ProgramBuilder::StandardFeatures features;
679 features.lighting = true;
680 features.material = true;
681 features.shadow = true;
682 GL::ProgramBuilder(features).add_shaders(pillar_shprog);
683 pillar_shprog.link();
685 GL::RenderPass *pass = &pillar_tech.add_pass(0);
686 pass->set_material(&pillar_material);
687 pass->set_shader_program(&pillar_shprog, 0);
689 pass = &pillar_tech.add_pass("shadow");
690 pass->set_shader_program(&shadow_shprog, 0);
692 pillar_data.reserve(7);
693 for(unsigned i=3; i<=20; ++i)
696 for(unsigned j=2; j<i; ++j)
700 if(pillar_data.size()<=height)
701 pillar_data.resize(height+1);
703 ObjectData &pd = pillar_data[height];
706 pd.mesh = new GL::Mesh((GL::VERTEX3, GL::NORMAL3));
707 GL::MeshBuilder bld(*pd.mesh);
709 // Produce a fluted cylinder
710 unsigned n_flutes = 12;
711 float r_bottom = cos(M_PI/n_flutes)*0.4;
712 float flute_depth = (0.4-r_bottom)*2;
713 float half_w = sin(M_PI/n_flutes)*0.4;
714 for(unsigned j=0; j<n_flutes; ++j)
716 float a = j*M_PI*2/n_flutes;
717 bld.begin(GL::TRIANGLE_STRIP);
718 for(int k=-3; k<=3; k+=2)
721 float x = cos(a)*(r_bottom-(1-t*t)*flute_depth)-sin(a)*t*half_w;
722 float y = sin(a)*(r_bottom-(1-t*t)*flute_depth)+cos(a)*t*half_w;
723 float d = -t*2*flute_depth;
724 float l = sqrt(d*d+1);
725 bld.normal(cos(a)/l-sin(a)*d/l, sin(a)/l+cos(a)*d/l, 0);
726 bld.vertex(x, y, 0.6+height);
727 bld.vertex(x, y, 0.6);
732 // Create a square plinth and capitel
733 bld.matrix() = GL::Matrix::translation(0, 0, 0.3);
734 GL::BoxBuilder(1.0, 1.0, 0.6).build(bld);
735 bld.matrix() = GL::Matrix::translation(0, 0, height+0.8);
736 GL::BoxBuilder(1.0, 1.0, 0.4).build(bld);
738 pd.object = new GL::Object(pd.mesh, &pillar_tech);
741 GL::AnimatedObject *pillar = new GL::AnimatedObject(*pd.object);
743 float a = (i-3)*2*M_PI/18;
744 matrix.translate(cos(a)*5, sin(a)*5, 0);
745 matrix.rotate(a, 0, 0, 1);
746 pillar->set_matrix(matrix);
748 pillars.push_back(pillar);
753 void DesertPillars::create_cube()
755 /* The cube is bluish-gray, with a hard specular reflection to produce a
757 cube_material.set_diffuse(GL::Color(0.5, 0.5, 0.55));
758 cube_material.set_ambient(GL::Color(0.5, 0.5, 0.55));
759 cube_material.set_specular(GL::Color(1.0));
760 cube_material.set_shininess(150);
762 cube_transform.source(cube_transform_src);
763 cube_transform.compile();
765 // First create a simplified shader for rendering the shadow map
766 GL::ProgramBuilder::StandardFeatures features;
767 features.transform = true;
768 GL::ProgramBuilder(features).add_shaders(cube_shadow_shprog);
769 cube_shadow_shprog.attach_shader(cube_transform);
770 cube_shadow_shprog.bind_attribute(7, "sphere_coord");
771 cube_shadow_shprog.link();
773 // Then add the rest of the features for normal rendering
774 features.lighting = true;
775 features.specular = true;
776 features.material = true;
777 features.shadow = true;
778 features.reflection = true;
779 GL::ProgramBuilder(features).add_shaders(cube_shprog);
780 cube_shprog.attach_shader(cube_transform);
781 cube_shprog.bind_attribute(7, "sphere_coord");
784 GL::RenderPass *pass = &cube_tech.add_pass(0);
785 pass->set_material(&cube_material);
786 pass->set_shader_program(&cube_shprog, 0);
788 pass = &cube_tech.add_pass("shadow");
789 pass->set_shader_program(&cube_shadow_shprog, 0);
791 cube_data.mesh = new GL::Mesh((GL::VERTEX3, GL::NORMAL3, GL::ATTRIB3,7));
792 GL::MeshBuilder bld(*cube_data.mesh);
793 create_cube_face(bld, GL::Vector3(-1, -1, -1), GL::Vector3(2, 0, 0), GL::Vector3(0, 2, 0), 16);
794 bld.offset(cube_data.mesh->get_n_vertices());
795 create_cube_face(bld, GL::Vector3(-1, 1, -1), GL::Vector3(2, 0, 0), GL::Vector3(0, 0, 2), 16);
796 bld.offset(cube_data.mesh->get_n_vertices());
797 create_cube_face(bld, GL::Vector3(-1, 1, 1), GL::Vector3(2, 0, 0), GL::Vector3(0, -2, 0), 16);
798 bld.offset(cube_data.mesh->get_n_vertices());
799 create_cube_face(bld, GL::Vector3(1, -1, -1), GL::Vector3(-2, 0, 0), GL::Vector3(0, 0, 2), 16);
800 bld.offset(cube_data.mesh->get_n_vertices());
801 create_cube_face(bld, GL::Vector3(-1, -1, 1), GL::Vector3(0, 0, -2), GL::Vector3(0, 2, 0), 16);
802 bld.offset(cube_data.mesh->get_n_vertices());
803 create_cube_face(bld, GL::Vector3(1, -1, -1), GL::Vector3(0, 0, 2), GL::Vector3(0, 2, 0), 16);
804 cube_data.object = new GL::Object(cube_data.mesh, &cube_tech);
806 cube = new Cube(*cube_data.object);
807 env_cube = new GL::EnvironmentMap(512, *cube, env_pipeline);
808 scene.add(*env_cube);
811 void DesertPillars::create_cube_face(GL::MeshBuilder &bld, const GL::Vector3 &base, const GL::Vector3 &side1, const GL::Vector3 &side2, unsigned div)
813 /* The sides follow the cube map convention where the cross product points
814 inwards. Since the normal has to point outwards, reverse the order. */
816 n.x = side2.y*side1.z-side2.z*side1.y;
817 n.y = side2.z*side1.x-side2.x*side1.z;
818 n.z = side2.x*side1.y-side2.y*side1.x;
819 float l = sqrt(n.x*n.x+n.y*n.y+n.z*n.z);
820 bld.normal(n.x/l, n.y/l, n.z/l);
822 // Create vertices, with precomputed spherified coordinates
823 for(unsigned i=0; i<=div; ++i)
824 for(unsigned j=0; j<=div; ++j)
827 v.x = base.x+side1.x*i/div+side2.x*j/div;
828 v.y = base.y+side1.y*i/div+side2.y*j/div;
829 v.z = base.z+side1.z*i/div+side2.z*j/div;
831 l = sqrt(v.x*v.x+v.y*v.y+v.z*v.z);
833 bld.attrib(7, v.x/l, v.y/l, v.z/l);
838 for(unsigned i=0; i<div; ++i)
840 bld.begin(GL::TRIANGLE_STRIP);
841 for(unsigned j=0; j<=div; ++j)
843 bld.element(i*(div+1)+j);
844 bld.element((i+1)*(div+1)+j);
850 int DesertPillars::main()
853 return Application::main();
856 void DesertPillars::tick()
858 Time::TimeStamp t = Time::now();
866 camera_angle += (dt/Time::sec)*M_PI*2/30;
867 if(camera_angle>M_PI*4)
868 camera_angle -= M_PI*4;
869 float h = 3+(1-cos(camera_angle*1.5))*3;
870 float r = sqrt(225-h*h);
871 camera.set_position(GL::Vector3(cos(camera_angle)*r, sin(camera_angle)*r, 1.5+h));
872 camera.look_at(GL::Vector3(0, 0, 2));
877 cube_angle += (dt/Time::sec)*M_PI*2/20;
878 GL::Matrix cube_matrix;
879 cube_matrix.translate(0, 0, 2.5);
880 cube_matrix.rotate(cube_angle, 0, 0, 1);
881 cube_matrix.rotate(cube_angle*0.5, 1, 0, 0);
882 cube->set_matrix(cube_matrix);
887 cube_phase += (dt/Time::sec)/5;
897 float x = cube_phase*5;
899 cube->set_spherify((1-x)*cube_shapes[(cube_shape+3)%4]+x*cube_shapes[cube_shape]);
902 cube->set_spherify(cube_shapes[cube_shape]);
906 GL::Framebuffer::system().clear(GL::COLOR_BUFFER_BIT|GL::DEPTH_BUFFER_BIT);
908 gl_context.swap_buffers();
911 void DesertPillars::key_press(unsigned key)
913 if(key==Input::KEY_ESC)
915 else if(key==Input::KEY_SPACE)
916 camera_stopped = !camera_stopped;
917 else if(key==Input::KEY_F)
918 cube_frozen = !cube_frozen;
919 else if(key==Input::KEY_S)
920 cube_stopped = !cube_stopped;
924 DesertPillars::ObjectData::ObjectData():
929 DesertPillars::ObjectData::~ObjectData()
936 DesertPillars::Cube::Cube(const GL::Object &obj):
937 GL::AnimatedObject(obj)
939 shdata.uniform("reflectivity", 0.5f);
942 void DesertPillars::Cube::set_spherify(float s)
944 shdata.uniform("spherify", s);
947 void DesertPillars::Cube::setup_render(GL::Renderer &renderer, const GL::Tag &tag) const
949 AnimatedObject::setup_render(renderer, tag);
950 renderer.add_shader_data(shdata);