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[] =
206 "varying vec3 v_texcoord;\n"
209 " gl_Position = gl_ProjectionMatrix*vec4(mat3(gl_ModelViewMatrix)*gl_Vertex.xyz, 1.0);\n"
210 " v_texcoord = gl_Vertex.xyz;\n"
213 const char DesertPillars::skybox_fragment_src[] =
214 "uniform samplerCube sky;\n"
215 "varying vec3 v_texcoord;\n"
218 " gl_FragColor = textureCube(sky, v_texcoord);\n"
221 // This exists only to transfer the ground type to fragment shader
222 const char DesertPillars::ground_transform_src[] =
223 "attribute float ground_type;\n"
224 "varying float v_ground_type;\n"
225 "vec4 transform_vertex(vec4 vertex)\n"
227 " v_ground_type = ground_type;\n"
228 " return gl_ModelViewMatrix*vertex;\n"
230 "vec3 transform_normal(vec3 normal)\n"
232 " return gl_NormalMatrix*normal;\n"
235 const char DesertPillars::ground_colorify_src[] =
236 "uniform sampler2D texture1;\n"
237 "uniform sampler2D normalmap1;\n"
238 "uniform sampler2D texture2;\n"
239 "uniform sampler2D normalmap2;\n"
240 "varying float v_ground_type;\n"
241 "vec4 sample_texture(vec2 coord)\n"
243 " return mix(texture2D(texture1, coord*3.0), texture2D(texture2, coord), v_ground_type);\n"
245 "vec3 sample_normalmap(vec2 coord)\n"
247 " return mix(texture2D(normalmap1, coord*3.0).rgb, texture2D(normalmap2, coord).rgb, v_ground_type);\n"
250 const char DesertPillars::cube_transform_src[] =
251 "uniform float spherify;\n"
252 "attribute vec3 sphere_coord;\n"
253 "vec4 transform_vertex(vec4 vertex)\n"
255 " return gl_ModelViewMatrix*vec4(mix(vertex.xyz, sphere_coord, spherify), 1.0);\n"
257 "vec3 transform_normal(vec3 normal)\n"
259 " return gl_NormalMatrix*normalize(mix(normal, normalize(sphere_coord), spherify));\n"
262 const float DesertPillars::cube_shapes[] = { -0.4, 0.5, 1.0, 0.3 };
265 DesertPillars::Options::Options(const Graphics::Display &dpy, int argc, char **argv)
268 getopt.add_option('f', "fullscreen", window_opts.fullscreen, GetOpt::NO_ARG).set_help("Run in fullscreen mode");
271 if(window_opts.fullscreen)
273 const Graphics::VideoMode &mode = dpy.get_desktop_mode();
274 window_opts.width = mode.width;
275 window_opts.height = mode.height;
279 window_opts.width = 800;
280 window_opts.height = 600;
285 DesertPillars::DesertPillars(int argc, char **argv):
286 options(display, argc, argv),
287 window(display, options.window_opts),
290 shadow_shprog(GL::ProgramBuilder::StandardFeatures()),
291 pipeline(window.get_width(), window.get_height()),
292 shadow_scene(2048, scene, light),
293 bloom(window.get_width(), window.get_height()),
294 env_pipeline(512, 512),
296 camera_stopped(false),
303 window.set_title("Desert Pillars");
304 window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &DesertPillars::exit), 0));
305 if(options.window_opts.fullscreen)
306 window.show_cursor(false);
307 keyboard.signal_button_press.connect(sigc::bind_return(sigc::mem_fun(this, &DesertPillars::key_press), false));
316 DesertPillars::~DesertPillars()
320 for(vector<GL::AnimatedObject *>::iterator i=pillars.begin(); i!=pillars.end(); ++i)
324 void DesertPillars::create_pipeline()
326 pipeline.set_multisample(8);
328 camera.set_aspect(float(window.get_width())/window.get_height());
329 camera.set_up_direction(GL::Vector3(0, 0, 1));
330 camera.set_depth_clip(1, 50);
331 pipeline.set_camera(&camera);
333 /* The shadow map is focused on the part of the scene that contains the
334 pillars and the cube. Making the ground cast shadows as well would result
335 either in a very low spatial resolution of the shadow map, or ugly artifacts
336 as the ground crosses the shadow map boundary. */
337 shadow_scene.set_target(GL::Vector3(0, 0, 0), 10);
338 shadow_scene.set_texture_unit(5);
339 sky_scene.add(shadow_scene);
340 pipeline.add_renderable(sky_scene);
342 // Put the sun pretty high in the sky
343 light.set_position(GL::Vector4(0.5, -2, 3, 0));
344 lighting.attach(0, light);
345 lighting.set_ambient(GL::Color(0.5));
347 // The skybox is rendered first
348 pipeline.add_pass("sky");
350 GL::Pipeline::Pass *pass = &pipeline.add_pass(0);
351 pass->set_lighting(&lighting);
352 pass->set_depth_test(&GL::DepthTest::lequal());
354 /* A bloom filter enhances the realism of bright surfaces, even if there
355 isn't anything really glowy in the scene. */
356 bloom.set_strength(0.3);
357 pipeline.add_postprocessor(bloom);
359 /* Initialize a second pipeline to render the environment map. It has the
360 same renderables and passes, but no postprocessors or camera. */
361 env_pipeline.add_renderable(sky_scene);
362 env_pipeline.add_pass("sky");
363 pass = &env_pipeline.add_pass(0);
364 pass->set_lighting(&lighting);
365 pass->set_depth_test(&GL::DepthTest::lequal());
368 void DesertPillars::create_skybox()
370 skybox_tex.storage(GL::RGB, 128);
371 skybox_tex.set_min_filter(GL::LINEAR);
372 skybox_tex.set_wrap(GL::CLAMP_TO_EDGE);
373 for(unsigned i=0; i<6; ++i)
374 create_skybox_face(skybox_tex, skybox_tex.enumerate_faces(i));
376 skybox_shprog.attach_shader_owned(new GL::VertexShader(skybox_vertex_src));
377 skybox_shprog.attach_shader_owned(new GL::FragmentShader(skybox_fragment_src));
378 skybox_shprog.link();
380 GL::RenderPass &pass = skybox_tech.add_pass("sky");
381 pass.set_shader_program(&skybox_shprog, 0);
382 pass.set_texture(0, &skybox_tex);
384 // The shader will use the vertex coordinates to initialize texture coordinates as well
385 skybox_data.mesh = new GL::Mesh(GL::VERTEX3);
386 GL::BoxBuilder(10, 10, 10).build(*skybox_data.mesh);
387 skybox_data.object = new GL::Object(skybox_data.mesh, &skybox_tech);
389 sky_scene.add(*skybox_data.object);
392 void DesertPillars::create_skybox_face(GL::TextureCube &texture, GL::TextureCubeFace face)
394 unsigned char *pixels = new unsigned char[128*128*3];
395 for(int y=0; y<128; ++y)
396 for(int x=0; x<128; ++x)
398 unsigned i = (x+y*128)*3;
399 GL::Vector3 v = texture.get_texel_direction(face, x, y);
402 float l = sqrt(v.x*v.x+v.y*v.y+v.z*v.z);
403 // Render a sky-like gradient, with deeper blue at the zenith
404 pixels[i] = 96-48*v.z/l;
405 pixels[i+1] = 168-84*v.z/l;
410 // Fill with a desert-y color below horizon
416 texture.image(face, 0, GL::RGB, GL::UNSIGNED_BYTE, pixels);
420 void DesertPillars::create_tiles_texture()
422 unsigned width = 256;
423 unsigned height = 256;
424 tiles_texture.storage(GL::RGB, width, height);
425 tiles_texture.set_min_filter(GL::LINEAR);
426 tiles_normalmap.storage(GL::RGB, width, height);
427 tiles_normalmap.set_min_filter(GL::LINEAR);
429 GL::Mesh tiles((GL::VERTEX3, GL::NORMAL3, GL::COLOR4_UBYTE));
431 // Prepare some lookup tables for rendering the tiles
432 float split = 1.0f/3;
433 float spacing = split*0.02f;
434 float bevel = split*0.1f;
435 float coords[] = { 0.0f, spacing, spacing+bevel, split-bevel, split, split+spacing, split+spacing+bevel, 1.0f-bevel, 1.0f };
436 unsigned order[] = { 4, 1, 3, 2, 1, 1, 2, 2,
437 1, 4, 2, 3, 4, 4, 3, 3,
438 1, 1, 2, 2, 1, 4, 2, 3,
439 4, 4, 3, 3, 4, 1, 3, 2,
440 2, 3, 2, 2, 3, 3, 3, 2 };
442 GL::MeshBuilder bld(tiles);
444 // Create a dark background
445 bld.color(0.2f, 0.2f, 0.2f);
446 bld.normal(0.0f, 0.0f, 1.0f);
447 bld.begin(GL::TRIANGLE_STRIP);
448 bld.vertex(0.0f, 1.0f);
449 bld.vertex(0.0f, 0.0f);
450 bld.vertex(1.0f, 1.0f);
451 bld.vertex(1.0f, 0.0f);
454 // Create the four tiles
455 bld.color(0.95f, 0.8f, 0.65f);
456 for(unsigned i=0; i<2; ++i)
457 for(unsigned j=0; j<2; ++j)
459 for(unsigned k=0; k<4; ++k)
461 bld.begin(GL::TRIANGLE_STRIP);
462 float facing = (k%2)*2-1.0f;
464 bld.normal(0.0f, 0.7071f*facing, 0.7071f);
466 bld.normal(0.7071f*facing, 0.0f, 0.7071f);
467 for(unsigned l=0; l<4; ++l)
468 bld.vertex(coords[i*4+order[k*8+l*2]], coords[j*4+order[k*8+l*2+1]], (l%2 ? bevel : 0.0f));
472 bld.begin(GL::TRIANGLE_STRIP);
473 bld.normal(0.0f, 0.0f, 1.0f);
474 for(unsigned l=0; l<4; ++l)
475 bld.vertex(coords[i*4+order[32+l*2]], coords[j*4+order[32+l*2+1]], bevel);
479 GL::Program shprog(texture_vertex_src, texture_fragment_src);
481 // Use an FBO to turn the geometry into a normalmapped texture
483 fbo.attach(GL::COLOR_ATTACHMENT0, tiles_texture);
484 fbo.attach(GL::COLOR_ATTACHMENT1, tiles_normalmap);
485 GL::Bind bind_fbo(fbo);
486 GL::Renderer renderer(0);
487 renderer.set_shader_program(&shprog, 0);
488 tiles.draw(renderer);
491 void DesertPillars::create_sand_texture()
493 unsigned width = 512;
494 unsigned height = 512;
496 sand_texture.storage(GL::RGB, width/16, height/16);
497 sand_texture.set_min_filter(GL::LINEAR_MIPMAP_LINEAR);
498 sand_texture.set_max_anisotropy(4);
499 sand_texture.set_generate_mipmap(true);
500 sand_normalmap.storage(GL::RGB, width, height);
501 sand_normalmap.set_min_filter(GL::LINEAR_MIPMAP_LINEAR);
502 sand_normalmap.set_max_anisotropy(4);
503 sand_normalmap.set_generate_mipmap(true);
505 unsigned char *pixels = new unsigned char[width*height*3];
506 unsigned char *bump = new unsigned char[width*height];
507 for(unsigned y=0; y<height; ++y)
508 for(unsigned x=0; x<width; ++x)
510 unsigned i = (x+y*width)*3;
511 unsigned c = rand()%16;
515 bump[x+y*width] = rand();
517 sand_texture.image(0, GL::RGB, GL::UNSIGNED_BYTE, pixels);
518 gaussian_blur(bump, width, height);
519 create_normalmap(bump, pixels, width, height, 4);
520 sand_normalmap.image(0, GL::RGB, GL::UNSIGNED_BYTE, pixels);
525 void DesertPillars::gaussian_blur(unsigned char *data, unsigned width, unsigned height)
527 /* Create a gaussian blur kernel for σ=2. Gaussian blur is separable, so a
528 1-dimensional kernel is enough. */
531 for(int i=-4; i<=4; ++i)
532 sum += (kernel[4+i] = exp(-i*i*0.5));
533 for(unsigned i=0; i<9; ++i)
536 unsigned char *line = new unsigned char[max(width, height)];
537 // Perform the blur in the X direction
538 for(unsigned y=0; y<height; ++y)
540 for(unsigned x=0; x<width; ++x)
543 for(int i=-4; i<=4; ++i)
544 value += data[(x+width+i)%width+y*width]*kernel[4+i];
547 copy(line, line+width, data+y*width);
549 // And then in the Y direction
550 for(unsigned x=0; x<width; ++x)
552 for(unsigned y=0; y<height; ++y)
555 for(int i=-4; i<=4; ++i)
556 value += data[x+((y+height+i)%height)*width]*kernel[4+i];
559 for(unsigned y=0; y<height; ++y)
560 data[x+y*width] = line[y];
565 void DesertPillars::create_normalmap(const unsigned char *bump, unsigned char *normals, unsigned width, unsigned height, float depth)
567 for(unsigned y=0; y<height; ++y)
568 for(unsigned x=0; x<width; ++x)
570 float dz_x = (bump[(x+1)%width+y*width]-bump[(x+width-1)%width+y*width])*depth/510;
571 float dz_y = (bump[x+((y+1)%height)*width]-bump[x+((y+height-1)%height)*width])*depth/510;
572 float l = sqrt(dz_x*dz_x+dz_y*dz_y+1);
573 unsigned i = (x+y*width)*3;
574 normals[i] = (0.5-0.5*dz_x/l)*255;
575 normals[i+1] = (0.5-0.5*dz_y/l)*255;
576 normals[i+2] = (0.5+0.5/l)*255;
580 void DesertPillars::create_ground()
582 create_tiles_texture();
583 create_sand_texture();
585 GL::ProgramBuilder::StandardFeatures features;
586 features.lighting = true;
587 features.shadow = true;
588 features.texture = true;
589 features.normalmap = true;
590 features.transform = true;
591 features.colorify = true;
592 GL::ProgramBuilder(features).add_shaders(ground_shprog);
593 ground_shprog.attach_shader_owned(new GL::VertexShader(ground_transform_src));
594 ground_shprog.attach_shader_owned(new GL::FragmentShader(ground_colorify_src));
595 ground_shprog.bind_attribute(7, "ground_type");
596 ground_shprog.link();
598 ground_shdata.uniform("texture1", 0);
599 ground_shdata.uniform("normalmap1", 1);
600 ground_shdata.uniform("texture2", 2);
601 ground_shdata.uniform("normalmap2", 3);
603 GL::RenderPass *pass = &ground_tech.add_pass(0);
604 pass->set_shader_program(&ground_shprog, &ground_shdata);
605 pass->set_texture(0, &tiles_texture);
606 pass->set_texture(1, &tiles_normalmap);
607 pass->set_texture(2, &sand_texture);
608 pass->set_texture(3, &sand_normalmap);
610 /* No shadow pass here; the ground only receives shadows, but doesn't cast
613 GL::VertexFormat vfmt = (GL::VERTEX3, GL::NORMAL3, GL::TANGENT3, GL::BINORMAL3, GL::TEXCOORD2, GL::ATTRIB1,7);
614 ground_data.mesh = new GL::Mesh(vfmt);
616 // Create a base grid
617 GL::GridBuilder(80, 80, 200, 200).tbn().build(*ground_data.mesh);
619 // And modify it with a heightmap
620 unsigned n_vertices = ground_data.mesh->get_n_vertices();
621 unsigned pos = vfmt.offset(GL::VERTEX3);
622 unsigned nor = vfmt.offset(GL::NORMAL3);
623 unsigned tan = vfmt.offset(GL::TANGENT3);
624 unsigned bin = vfmt.offset(GL::BINORMAL3);
625 unsigned tex = vfmt.offset(GL::TEXCOORD2);
626 unsigned gt = vfmt.offset(GL::make_indexed_component(GL::ATTRIB1, 7));
627 for(unsigned i=0; i<n_vertices; ++i)
629 float *v = ground_data.mesh->modify_vertex(i);
630 v[pos+2] = ground_height(v[pos], v[pos+1]);
632 float dz_x = (ground_height(v[pos]+0.01, v[pos+1])-ground_height(v[pos]-0.01, v[pos+1]))/0.02;
633 float dz_y = (ground_height(v[pos], v[pos+1]+0.01)-ground_height(v[pos], v[pos+1]-0.01))/0.02;
634 float l = sqrt(dz_x*dz_x+dz_y*dz_y+1);
639 l = sqrt(dz_x*dz_x+1);
643 l = sqrt(dz_y*dz_y+1);
647 v[gt] = min(v[pos+2]*100, 1.0f);
652 ground_data.object = new GL::Object(ground_data.mesh, &ground_tech);
654 scene.add(*ground_data.object);
657 float DesertPillars::ground_height(float x, float y)
659 // Leave a flat area in the middle
660 float d = sqrt(x*x+y*y);
664 // This results in concentric rings of low hills
665 int i = (d-6)/(M_PI*2);
666 float a = atan2(y, x);
669 float h = (i%2) ? 0.5 : 0.3;
670 return (d-6)*0.001f+(1-cos(d-6))*(1-cos(a))*h;
673 void DesertPillars::create_pillars()
675 // The pillars are a matt off-white
676 pillar_material.set_diffuse(GL::Color(0.9, 0.88, 0.8));
677 pillar_material.set_ambient(GL::Color(0.9, 0.88, 0.8));
679 GL::ProgramBuilder::StandardFeatures features;
680 features.lighting = true;
681 features.material = true;
682 features.shadow = true;
683 GL::ProgramBuilder(features).add_shaders(pillar_shprog);
684 pillar_shprog.link();
686 GL::RenderPass *pass = &pillar_tech.add_pass(0);
687 pass->set_material(&pillar_material);
688 pass->set_shader_program(&pillar_shprog, 0);
690 pass = &pillar_tech.add_pass("shadow");
691 pass->set_shader_program(&shadow_shprog, 0);
693 pillar_data.reserve(7);
694 for(unsigned i=3; i<=20; ++i)
697 for(unsigned j=2; j<i; ++j)
701 if(pillar_data.size()<=height)
702 pillar_data.resize(height+1);
704 ObjectData &pd = pillar_data[height];
707 pd.mesh = new GL::Mesh((GL::VERTEX3, GL::NORMAL3));
708 GL::MeshBuilder bld(*pd.mesh);
710 // Produce a fluted cylinder
711 unsigned n_flutes = 12;
712 float r_bottom = cos(M_PI/n_flutes)*0.4;
713 float flute_depth = (0.4-r_bottom)*2;
714 float half_w = sin(M_PI/n_flutes)*0.4;
715 for(unsigned j=0; j<n_flutes; ++j)
717 float a = j*M_PI*2/n_flutes;
718 bld.begin(GL::TRIANGLE_STRIP);
719 for(int k=-3; k<=3; k+=2)
722 float x = cos(a)*(r_bottom-(1-t*t)*flute_depth)-sin(a)*t*half_w;
723 float y = sin(a)*(r_bottom-(1-t*t)*flute_depth)+cos(a)*t*half_w;
724 float d = -t*2*flute_depth;
725 float l = sqrt(d*d+1);
726 bld.normal(cos(a)/l-sin(a)*d/l, sin(a)/l+cos(a)*d/l, 0);
727 bld.vertex(x, y, 0.6+height);
728 bld.vertex(x, y, 0.6);
733 // Create a square plinth and capitel
734 bld.matrix() = GL::Matrix::translation(0, 0, 0.3);
735 GL::BoxBuilder(1.0, 1.0, 0.6).build(bld);
736 bld.matrix() = GL::Matrix::translation(0, 0, height+0.8);
737 GL::BoxBuilder(1.0, 1.0, 0.4).build(bld);
739 pd.object = new GL::Object(pd.mesh, &pillar_tech);
742 GL::AnimatedObject *pillar = new GL::AnimatedObject(*pd.object);
744 float a = (i-3)*2*M_PI/18;
745 matrix.translate(cos(a)*5, sin(a)*5, 0);
746 matrix.rotate(a, 0, 0, 1);
747 pillar->set_matrix(matrix);
749 pillars.push_back(pillar);
754 void DesertPillars::create_cube()
756 /* The cube is bluish-gray, with a hard specular reflection to produce a
758 cube_material.set_diffuse(GL::Color(0.5, 0.5, 0.55));
759 cube_material.set_ambient(GL::Color(0.5, 0.5, 0.55));
760 cube_material.set_specular(GL::Color(1.0));
761 cube_material.set_shininess(150);
763 cube_transform.source(cube_transform_src);
764 cube_transform.compile();
766 // First create a simplified shader for rendering the shadow map
767 GL::ProgramBuilder::StandardFeatures features;
768 features.transform = true;
769 GL::ProgramBuilder(features).add_shaders(cube_shadow_shprog);
770 cube_shadow_shprog.attach_shader(cube_transform);
771 cube_shadow_shprog.bind_attribute(7, "sphere_coord");
772 cube_shadow_shprog.link();
774 // Then add the rest of the features for normal rendering
775 features.lighting = true;
776 features.specular = true;
777 features.material = true;
778 features.shadow = true;
779 features.reflection = true;
780 GL::ProgramBuilder(features).add_shaders(cube_shprog);
781 cube_shprog.attach_shader(cube_transform);
782 cube_shprog.bind_attribute(7, "sphere_coord");
785 GL::RenderPass *pass = &cube_tech.add_pass(0);
786 pass->set_material(&cube_material);
787 pass->set_shader_program(&cube_shprog, 0);
789 pass = &cube_tech.add_pass("shadow");
790 pass->set_shader_program(&cube_shadow_shprog, 0);
792 cube_data.mesh = new GL::Mesh((GL::VERTEX3, GL::NORMAL3, GL::ATTRIB3,7));
793 GL::MeshBuilder bld(*cube_data.mesh);
794 create_cube_face(bld, GL::Vector3(-1, -1, -1), GL::Vector3(2, 0, 0), GL::Vector3(0, 2, 0), 16);
795 bld.offset(cube_data.mesh->get_n_vertices());
796 create_cube_face(bld, GL::Vector3(-1, 1, -1), GL::Vector3(2, 0, 0), GL::Vector3(0, 0, 2), 16);
797 bld.offset(cube_data.mesh->get_n_vertices());
798 create_cube_face(bld, GL::Vector3(-1, 1, 1), GL::Vector3(2, 0, 0), GL::Vector3(0, -2, 0), 16);
799 bld.offset(cube_data.mesh->get_n_vertices());
800 create_cube_face(bld, GL::Vector3(1, -1, -1), GL::Vector3(-2, 0, 0), GL::Vector3(0, 0, 2), 16);
801 bld.offset(cube_data.mesh->get_n_vertices());
802 create_cube_face(bld, GL::Vector3(-1, -1, 1), GL::Vector3(0, 0, -2), GL::Vector3(0, 2, 0), 16);
803 bld.offset(cube_data.mesh->get_n_vertices());
804 create_cube_face(bld, GL::Vector3(1, -1, -1), GL::Vector3(0, 0, 2), GL::Vector3(0, 2, 0), 16);
805 cube_data.object = new GL::Object(cube_data.mesh, &cube_tech);
807 cube = new Cube(*cube_data.object);
808 env_cube = new GL::EnvironmentMap(512, *cube, env_pipeline);
809 scene.add(*env_cube);
812 void DesertPillars::create_cube_face(GL::MeshBuilder &bld, const GL::Vector3 &base, const GL::Vector3 &side1, const GL::Vector3 &side2, unsigned div)
814 /* The sides follow the cube map convention where the cross product points
815 inwards. Since the normal has to point outwards, reverse the order. */
817 n.x = side2.y*side1.z-side2.z*side1.y;
818 n.y = side2.z*side1.x-side2.x*side1.z;
819 n.z = side2.x*side1.y-side2.y*side1.x;
820 float l = sqrt(n.x*n.x+n.y*n.y+n.z*n.z);
821 bld.normal(n.x/l, n.y/l, n.z/l);
823 // Create vertices, with precomputed spherified coordinates
824 for(unsigned i=0; i<=div; ++i)
825 for(unsigned j=0; j<=div; ++j)
828 v.x = base.x+side1.x*i/div+side2.x*j/div;
829 v.y = base.y+side1.y*i/div+side2.y*j/div;
830 v.z = base.z+side1.z*i/div+side2.z*j/div;
832 l = sqrt(v.x*v.x+v.y*v.y+v.z*v.z);
834 bld.attrib(7, v.x/l, v.y/l, v.z/l);
839 for(unsigned i=0; i<div; ++i)
841 bld.begin(GL::TRIANGLE_STRIP);
842 for(unsigned j=0; j<=div; ++j)
844 bld.element(i*(div+1)+j);
845 bld.element((i+1)*(div+1)+j);
851 int DesertPillars::main()
854 return Application::main();
857 void DesertPillars::tick()
859 Time::TimeStamp t = Time::now();
867 camera_angle += (dt/Time::sec)*M_PI*2/30;
868 if(camera_angle>M_PI*4)
869 camera_angle -= M_PI*4;
870 float h = 3+(1-cos(camera_angle*1.5))*3;
871 float r = sqrt(225-h*h);
872 camera.set_position(GL::Vector3(cos(camera_angle)*r, sin(camera_angle)*r, 1.5+h));
873 camera.look_at(GL::Vector3(0, 0, 2));
878 cube_angle += (dt/Time::sec)*M_PI*2/20;
879 GL::Matrix cube_matrix;
880 cube_matrix.translate(0, 0, 2.5);
881 cube_matrix.rotate(cube_angle, 0, 0, 1);
882 cube_matrix.rotate(cube_angle*0.5, 1, 0, 0);
883 cube->set_matrix(cube_matrix);
888 cube_phase += (dt/Time::sec)/5;
898 float x = cube_phase*5;
900 cube->set_spherify((1-x)*cube_shapes[(cube_shape+3)%4]+x*cube_shapes[cube_shape]);
903 cube->set_spherify(cube_shapes[cube_shape]);
907 GL::Framebuffer::system().clear(GL::COLOR_BUFFER_BIT|GL::DEPTH_BUFFER_BIT);
909 gl_context.swap_buffers();
912 void DesertPillars::key_press(unsigned key)
914 if(key==Input::KEY_ESC)
916 else if(key==Input::KEY_SPACE)
917 camera_stopped = !camera_stopped;
918 else if(key==Input::KEY_F)
919 cube_frozen = !cube_frozen;
920 else if(key==Input::KEY_S)
921 cube_stopped = !cube_stopped;
925 DesertPillars::ObjectData::ObjectData():
930 DesertPillars::ObjectData::~ObjectData()
937 DesertPillars::Cube::Cube(const GL::Object &obj):
938 GL::AnimatedObject(obj)
940 shdata.uniform("reflectivity", 0.5f);
943 void DesertPillars::Cube::set_spherify(float s)
945 shdata.uniform("spherify", s);
948 void DesertPillars::Cube::setup_render(GL::Renderer &renderer, const GL::Tag &tag) const
950 AnimatedObject::setup_render(renderer, tag);
951 renderer.add_shader_data(shdata);