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/colorcurve.h>
9 #include <msp/gl/cylinder.h>
10 #include <msp/gl/environmentmap.h>
11 #include <msp/gl/framebuffer.h>
12 #include <msp/gl/grid.h>
13 #include <msp/gl/instancescene.h>
14 #include <msp/gl/light.h>
15 #include <msp/gl/lighting.h>
16 #include <msp/gl/basicmaterial.h>
17 #include <msp/gl/mesh.h>
18 #include <msp/gl/meshbuilder.h>
19 #include <msp/gl/object.h>
20 #include <msp/gl/pipeline.h>
21 #include <msp/gl/program.h>
22 #include <msp/gl/programbuilder.h>
23 #include <msp/gl/renderer.h>
24 #include <msp/gl/shader.h>
25 #include <msp/gl/shadowmap.h>
26 #include <msp/gl/simplescene.h>
27 #include <msp/gl/technique.h>
28 #include <msp/gl/tests.h>
29 #include <msp/gl/texturecube.h>
30 #include <msp/gl/windowview.h>
31 #include <msp/graphics/simplewindow.h>
32 #include <msp/input/keyboard.h>
33 #include <msp/input/keys.h>
34 #include <msp/time/timestamp.h>
35 #include <msp/time/timedelta.h>
36 #include <msp/time/utils.h>
42 This application demonstrates a variety of features of the mspgl library,
44 - Creating meshes from multiple parts
45 - Creating a mesh and then modifying it
47 - Environment mapped reflections
48 - Skybox using a cube map texture
49 - Effects with nested pipelines
50 - Complex multitexturing
51 - Shader-based deformations
52 - Creating a normalmapped texture through rendering
54 To run the program in fullscreen mode, specify --fullscreen on the command
57 During execution the following keys are available:
59 space stop camera movement
63 class DesertPillars: public RegisteredApplication<DesertPillars>
68 Graphics::WindowOptions window_opts;
70 Options(const Graphics::Display &, int, char **);
82 class Cube: public GL::AnimatedObject
85 GL::ProgramData shdata;
88 Cube(const GL::Object &);
90 void set_spherify(float);
92 virtual void setup_render(GL::Renderer &, const GL::Tag &) const;
95 Msp::Graphics::Display display;
97 Msp::Graphics::Window window;
98 Msp::Graphics::GLContext gl_context;
99 Msp::Input::Keyboard keyboard;
101 GL::Program skybox_shprog;
102 GL::Technique skybox_tech;
103 GL::TextureCube skybox_tex;
104 ObjectData skybox_data;
106 GL::Program shadow_shprog;
108 GL::Program ground_shprog;
109 GL::ProgramData ground_shdata;
110 GL::Texture2D tiles_texture;
111 GL::Texture2D tiles_normalmap;
112 GL::Texture2D sand_texture;
113 GL::Texture2D sand_normalmap;
114 GL::Technique ground_tech;
115 ObjectData ground_data;
117 GL::BasicMaterial pillar_material;
118 GL::Technique pillar_tech;
119 std::vector<ObjectData> pillar_data;
120 std::vector<GL::AnimatedObject *> pillars;
122 GL::Program cube_shprog;
123 GL::Program cube_shadow_shprog;
124 GL::BasicMaterial cube_material;
125 GL::Technique cube_tech;
126 ObjectData cube_data;
128 GL::EnvironmentMap *env_cube;
131 GL::Pipeline pipeline;
133 GL::SimpleScene sky_scene;
134 GL::InstanceScene scene;
135 GL::Lighting lighting;
137 GL::ShadowMap shadow_scene;
139 GL::ColorCurve colorcurve;
141 GL::Pipeline env_pipeline;
143 Time::TimeStamp last_tick;
152 static const char texture_src[];
153 static const char skybox_src[];
154 static const char ground_src[];
155 static const char cube_src[];
156 static const char cube_shadow_src_tail[];
157 static const float cube_shapes[];
160 DesertPillars(int, char **);
165 void create_pipeline();
166 void create_skybox();
167 static void create_skybox_face(GL::TextureCube &, GL::TextureCubeFace);
168 void create_tiles_texture();
169 void create_sand_texture();
170 static void gaussian_blur(unsigned char *, unsigned, unsigned);
171 static void create_normalmap(const unsigned char *, unsigned char *, unsigned, unsigned, float);
172 void create_ground();
173 static float ground_height(float, float);
174 void create_pillars();
176 static void create_cube_face(GL::MeshBuilder &, const GL::Vector3 &, const GL::Vector3 &, const GL::Vector3 &, unsigned);
183 void key_press(unsigned);
186 const char DesertPillars::texture_src[] =
187 "import msp_interface;\n"
188 "#pragma MSP stage(vertex)\n"
191 " gl_Position = vec4(vertex.xy*2.0-1.0, -vertex.z*2.0, 1.0);\n"
194 "#pragma MSP stage(fragment)\n"
195 "layout(location=1) out vec4 frag_normal;\n"
198 " frag_color = color;\n"
199 " frag_normal = vec4(normal*0.5+0.5, 1.0);\n"
202 const char DesertPillars::skybox_src[] =
203 "import msp_interface;\n"
204 "uniform samplerCube sky;\n"
205 "#pragma MSP stage(vertex)\n"
208 " gl_Position = projection_matrix*vec4(mat3(eye_obj_matrix)*vertex.xyz, 1.0);\n"
211 "#pragma MSP stage(fragment)\n"
214 " frag_color = texture(sky, vertex.xyz);\n"
217 const char DesertPillars::ground_src[] =
219 "uniform sampler2D texture1;\n"
220 "uniform sampler2D normalmap1;\n"
221 "uniform sampler2D texture2;\n"
222 "uniform sampler2D normalmap2;\n"
223 "const bool use_normal_map = true;\n"
224 "const bool use_shadow_map = true;\n"
225 "#pragma MSP stage(vertex)\n"
226 "layout(location=7) in float ground_type;\n"
227 "#pragma MSP stage(fragment)\n"
228 "vec4 get_diffuse_color()\n"
230 " return mix(texture(texture1, texcoord.xy*3.0), texture(texture2, texcoord.xy), ground_type);\n"
232 "vec4 get_normal_sample()\n"
234 " return mix(texture(normalmap1, texcoord.xy*3.0).rgb, texture(normalmap2, texcoord.xy).rgb, ground_type);\n"
237 const char DesertPillars::cube_src[] =
239 "const bool use_specular = true;\n"
240 "const bool use_reflectivity = true;\n"
241 "uniform float spherify;\n"
242 "#pragma MSP stage(vertex)\n"
243 "layout(location=7) in vec3 sphere_coord;\n"
244 "vec4 transform_position(vec4 pos)\n"
246 " return eye_obj_matrix*vec4(mix(vertex.xyz, sphere_coord, spherify), 1.0);\n"
248 "vec3 transform_normal(vec3 pos)\n"
250 " return eye_obj_normal_matrix*normalize(mix(normal, normalize(sphere_coord), spherify));\n"
253 const char DesertPillars::cube_shadow_src_tail[] =
254 "#pragma MSP stage(fragment)\n"
257 " frag_color = vec4(1.0);\n"
260 const float DesertPillars::cube_shapes[] = { -0.4, 0.5, 1.0, 0.3 };
263 DesertPillars::Options::Options(const Graphics::Display &dpy, int argc, char **argv)
266 getopt.add_option('f', "fullscreen", window_opts.fullscreen, GetOpt::NO_ARG).set_help("Run in fullscreen mode");
269 if(window_opts.fullscreen)
271 const Graphics::VideoMode &mode = dpy.get_desktop_mode();
272 window_opts.width = mode.width;
273 window_opts.height = mode.height;
277 window_opts.width = 800;
278 window_opts.height = 600;
283 DesertPillars::DesertPillars(int argc, char **argv):
284 options(display, argc, argv),
285 window(display, options.window_opts),
288 skybox_shprog(skybox_src),
289 shadow_shprog("occluder.glsl"),
290 ground_shprog(ground_src),
291 cube_shprog(cube_src),
292 cube_shadow_shprog(string(cube_src)+cube_shadow_src_tail),
293 view(window, gl_context),
295 shadow_scene(2048, scene, light),
296 bloom(window.get_width(), window.get_height()),
297 env_pipeline(512, 512),
299 camera_stopped(false),
306 window.set_title("Desert Pillars");
307 window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &DesertPillars::exit), 0));
308 if(options.window_opts.fullscreen)
309 window.show_cursor(false);
310 keyboard.signal_button_press.connect(sigc::bind_return(sigc::mem_fun(this, &DesertPillars::key_press), false));
320 DesertPillars::~DesertPillars()
324 for(vector<GL::AnimatedObject *>::iterator i=pillars.begin(); i!=pillars.end(); ++i)
328 void DesertPillars::setup_view()
330 camera.set_aspect_ratio(float(window.get_width())/window.get_height());
331 camera.set_up_direction(GL::Vector3(0, 0, 1));
332 camera.set_depth_clip(1, 50);
333 view.set_camera(&camera);
334 view.set_content(&pipeline);
337 void DesertPillars::create_pipeline()
339 pipeline.set_multisample(8);
340 pipeline.set_hdr(true);
342 /* The shadow map is focused on the part of the scene that contains the
343 pillars and the cube. Making the ground cast shadows as well would result
344 either in a very low spatial resolution of the shadow map, or ugly artifacts
345 as the ground crosses the shadow map boundary. */
346 shadow_scene.set_target(GL::Vector3(0, 0, 0), 10);
347 shadow_scene.set_darkness(1);
349 // Put the sun pretty high in the sky
350 light.set_diffuse(GL::Color(2.0));
351 light.set_position(GL::Vector4(0.5, -2, 3, 0));
352 lighting.attach(0, light);
353 lighting.set_ambient(GL::Color(0.2));
355 // The skybox is rendered first
356 pipeline.add_pass(0, sky_scene);
358 GL::Pipeline::Pass *pass = &pipeline.add_pass(0, shadow_scene);
359 pass->set_lighting(&lighting);
360 pass->set_depth_test(&GL::DepthTest::lequal());
362 /* A bloom filter enhances the realism of bright surfaces, even if there
363 isn't anything really glowy in the scene. */
364 bloom.set_strength(0.3);
365 pipeline.add_postprocessor(bloom);
367 /* Lighting calculations are best done in linear color space, so the final
368 image must be converted to srgb for display. */
369 colorcurve.set_srgb();
370 pipeline.add_postprocessor(colorcurve);
372 /* Initialize a second pipeline to render the environment map. It has the
373 same renderables and passes, but no postprocessors or camera. */
374 env_pipeline.add_pass(0, sky_scene);
375 pass = &env_pipeline.add_pass(0, shadow_scene);
376 pass->set_lighting(&lighting);
377 pass->set_depth_test(&GL::DepthTest::lequal());
380 void DesertPillars::create_skybox()
382 skybox_tex.storage(GL::SRGB, 128);
383 skybox_tex.set_min_filter(GL::LINEAR);
384 skybox_tex.set_wrap(GL::CLAMP_TO_EDGE);
385 for(unsigned i=0; i<6; ++i)
386 create_skybox_face(skybox_tex, skybox_tex.enumerate_faces(i));
388 GL::RenderPass &pass = skybox_tech.add_pass(0);
389 pass.set_shader_program(&skybox_shprog, 0);
390 pass.set_texture(0, &skybox_tex);
392 // The shader will use the vertex coordinates to initialize texture coordinates as well
393 skybox_data.mesh = new GL::Mesh(GL::VERTEX3);
394 GL::BoxBuilder(10, 10, 10).build(*skybox_data.mesh);
395 skybox_data.object = new GL::Object(skybox_data.mesh, &skybox_tech);
397 sky_scene.add(*skybox_data.object);
400 void DesertPillars::create_skybox_face(GL::TextureCube &texture, GL::TextureCubeFace face)
402 unsigned char *pixels = new unsigned char[128*128*3];
403 for(int y=0; y<128; ++y)
404 for(int x=0; x<128; ++x)
406 unsigned i = (x+y*128)*3;
407 GL::Vector3 v = texture.get_texel_direction(face, x, y);
410 float l = sqrt(v.x*v.x+v.y*v.y+v.z*v.z);
411 // Render a sky-like gradient, with deeper blue at the zenith
412 pixels[i] = 96-48*v.z/l;
413 pixels[i+1] = 168-84*v.z/l;
418 // Fill with a desert-y color below horizon
424 texture.image(face, 0, GL::RGB, GL::UNSIGNED_BYTE, pixels);
428 void DesertPillars::create_tiles_texture()
430 unsigned width = 256;
431 unsigned height = 256;
432 tiles_texture.storage(GL::RGB, width, height);
433 tiles_texture.set_min_filter(GL::LINEAR);
434 tiles_normalmap.storage(GL::RGB, width, height);
435 tiles_normalmap.set_min_filter(GL::LINEAR);
437 GL::Mesh tiles((GL::VERTEX3, GL::NORMAL3, GL::COLOR4_UBYTE));
439 // Prepare some lookup tables for rendering the tiles
440 float split = 1.0f/3;
441 float spacing = split*0.02f;
442 float bevel = split*0.1f;
443 float coords[] = { 0.0f, spacing, spacing+bevel, split-bevel, split, split+spacing, split+spacing+bevel, 1.0f-bevel, 1.0f };
444 unsigned order[] = { 4, 1, 3, 2, 1, 1, 2, 2,
445 1, 4, 2, 3, 4, 4, 3, 3,
446 1, 1, 2, 2, 1, 4, 2, 3,
447 4, 4, 3, 3, 4, 1, 3, 2,
448 2, 3, 2, 2, 3, 3, 3, 2 };
450 GL::MeshBuilder bld(tiles);
452 // Create a dark background
453 bld.color(0.2f, 0.2f, 0.2f);
454 bld.normal(0.0f, 0.0f, 1.0f);
455 bld.begin(GL::TRIANGLE_STRIP);
456 bld.vertex(0.0f, 1.0f);
457 bld.vertex(0.0f, 0.0f);
458 bld.vertex(1.0f, 1.0f);
459 bld.vertex(1.0f, 0.0f);
462 // Create the four tiles
463 bld.color(GL::Color(0.95f, 0.8f, 0.65f).to_linear());
464 for(unsigned i=0; i<2; ++i)
465 for(unsigned j=0; j<2; ++j)
467 for(unsigned k=0; k<4; ++k)
469 bld.begin(GL::TRIANGLE_STRIP);
470 float facing = (k%2)*2-1.0f;
472 bld.normal(0.0f, 0.7071f*facing, 0.7071f);
474 bld.normal(0.7071f*facing, 0.0f, 0.7071f);
475 for(unsigned l=0; l<4; ++l)
476 bld.vertex(coords[i*4+order[k*8+l*2]], coords[j*4+order[k*8+l*2+1]], (l%2 ? bevel : 0.0f));
480 bld.begin(GL::TRIANGLE_STRIP);
481 bld.normal(0.0f, 0.0f, 1.0f);
482 for(unsigned l=0; l<4; ++l)
483 bld.vertex(coords[i*4+order[32+l*2]], coords[j*4+order[32+l*2+1]], bevel);
487 GL::Program shprog(texture_src);
489 // Use an FBO to turn the geometry into a normalmapped texture
491 fbo.attach(GL::COLOR_ATTACHMENT0, tiles_texture);
492 fbo.attach(GL::COLOR_ATTACHMENT1, tiles_normalmap);
493 GL::Bind bind_fbo(fbo);
494 GL::Renderer renderer;
495 renderer.set_shader_program(&shprog, 0);
496 tiles.draw(renderer);
499 void DesertPillars::create_sand_texture()
501 unsigned width = 512;
502 unsigned height = 512;
504 sand_texture.storage(GL::SRGB, width/16, height/16);
505 sand_texture.set_min_filter(GL::LINEAR_MIPMAP_LINEAR);
506 sand_texture.set_max_anisotropy(4);
507 sand_texture.set_auto_generate_mipmap(true);
508 sand_normalmap.storage(GL::RGB, width, height);
509 sand_normalmap.set_min_filter(GL::LINEAR_MIPMAP_LINEAR);
510 sand_normalmap.set_max_anisotropy(4);
511 sand_normalmap.set_auto_generate_mipmap(true);
513 unsigned char *pixels = new unsigned char[width*height*3];
514 unsigned char *bump = new unsigned char[width*height];
515 for(unsigned y=0; y<height; ++y)
516 for(unsigned x=0; x<width; ++x)
518 unsigned i = (x+y*width)*3;
519 unsigned c = rand()%16;
523 bump[x+y*width] = rand();
525 sand_texture.image(0, GL::RGB, GL::UNSIGNED_BYTE, pixels);
526 gaussian_blur(bump, width, height);
527 create_normalmap(bump, pixels, width, height, 4);
528 sand_normalmap.image(0, GL::RGB, GL::UNSIGNED_BYTE, pixels);
533 void DesertPillars::gaussian_blur(unsigned char *data, unsigned width, unsigned height)
535 /* Create a gaussian blur kernel for σ=2. Gaussian blur is separable, so a
536 1-dimensional kernel is enough. */
539 for(int i=-4; i<=4; ++i)
540 sum += (kernel[4+i] = exp(-i*i*0.5));
541 for(unsigned i=0; i<9; ++i)
544 unsigned char *line = new unsigned char[max(width, height)];
545 // Perform the blur in the X direction
546 for(unsigned y=0; y<height; ++y)
548 for(unsigned x=0; x<width; ++x)
551 for(int i=-4; i<=4; ++i)
552 value += data[(x+width+i)%width+y*width]*kernel[4+i];
555 copy(line, line+width, data+y*width);
557 // And then in the Y direction
558 for(unsigned x=0; x<width; ++x)
560 for(unsigned y=0; y<height; ++y)
563 for(int i=-4; i<=4; ++i)
564 value += data[x+((y+height+i)%height)*width]*kernel[4+i];
567 for(unsigned y=0; y<height; ++y)
568 data[x+y*width] = line[y];
573 void DesertPillars::create_normalmap(const unsigned char *bump, unsigned char *normals, unsigned width, unsigned height, float depth)
575 for(unsigned y=0; y<height; ++y)
576 for(unsigned x=0; x<width; ++x)
578 float dz_x = (bump[(x+1)%width+y*width]-bump[(x+width-1)%width+y*width])*depth/510;
579 float dz_y = (bump[x+((y+1)%height)*width]-bump[x+((y+height-1)%height)*width])*depth/510;
580 float l = sqrt(dz_x*dz_x+dz_y*dz_y+1);
581 unsigned i = (x+y*width)*3;
582 normals[i] = (0.5-0.5*dz_x/l)*255;
583 normals[i+1] = (0.5-0.5*dz_y/l)*255;
584 normals[i+2] = (0.5+0.5/l)*255;
588 void DesertPillars::create_ground()
590 create_tiles_texture();
591 create_sand_texture();
593 ground_shdata.uniform("texture1", 0);
594 ground_shdata.uniform("normalmap1", 1);
595 ground_shdata.uniform("texture2", 2);
596 ground_shdata.uniform("normalmap2", 3);
598 GL::RenderPass *pass = &ground_tech.add_pass(0);
599 pass->set_shader_program(&ground_shprog, &ground_shdata);
600 pass->set_texture(0, &tiles_texture);
601 pass->set_texture(1, &tiles_normalmap);
602 pass->set_texture(2, &sand_texture);
603 pass->set_texture(3, &sand_normalmap);
605 /* No shadow pass here; the ground only receives shadows, but doesn't cast
608 GL::VertexFormat vfmt = (GL::VERTEX3, GL::NORMAL3, GL::TANGENT3, GL::BINORMAL3, GL::TEXCOORD2, GL::ATTRIB1,7);
609 ground_data.mesh = new GL::Mesh(vfmt);
611 // Create a base grid
612 GL::GridBuilder(80, 80, 200, 200).tbn().build(*ground_data.mesh);
614 // And modify it with a heightmap
615 unsigned n_vertices = ground_data.mesh->get_n_vertices();
616 unsigned pos = vfmt.offset(GL::VERTEX3);
617 unsigned nor = vfmt.offset(GL::NORMAL3);
618 unsigned tan = vfmt.offset(GL::TANGENT3);
619 unsigned bin = vfmt.offset(GL::BINORMAL3);
620 unsigned tex = vfmt.offset(GL::TEXCOORD2);
621 unsigned gt = vfmt.offset(GL::make_indexed_component(GL::ATTRIB1, 7));
622 for(unsigned i=0; i<n_vertices; ++i)
624 float *v = ground_data.mesh->modify_vertex(i);
625 v[pos+2] = ground_height(v[pos], v[pos+1]);
627 float dz_x = (ground_height(v[pos]+0.01, v[pos+1])-ground_height(v[pos]-0.01, v[pos+1]))/0.02;
628 float dz_y = (ground_height(v[pos], v[pos+1]+0.01)-ground_height(v[pos], v[pos+1]-0.01))/0.02;
629 float l = sqrt(dz_x*dz_x+dz_y*dz_y+1);
634 l = sqrt(dz_x*dz_x+1);
638 l = sqrt(dz_y*dz_y+1);
642 v[gt] = min(v[pos+2]*100, 1.0f);
647 ground_data.object = new GL::Object(ground_data.mesh, &ground_tech);
649 scene.add(*ground_data.object);
652 float DesertPillars::ground_height(float x, float y)
654 // Leave a flat area in the middle
655 float d = sqrt(x*x+y*y);
659 // This results in concentric rings of low hills
660 int i = (d-6)/(M_PI*2);
661 float a = atan2(y, x);
664 float h = (i%2) ? 0.5 : 0.3;
665 return (d-6)*0.001f+(1-cos(d-6))*(1-cos(a))*h;
668 void DesertPillars::create_pillars()
670 // The pillars are a matt off-white
671 pillar_material.set_diffuse(GL::Color(0.9, 0.89, 0.85).to_linear());
672 pillar_material.set_receive_shadows(true);
674 GL::RenderPass *pass = &pillar_tech.add_pass(0);
675 pass->set_material(&pillar_material);
677 pass = &pillar_tech.add_pass("shadow");
678 pass->set_shader_program(&shadow_shprog, 0);
680 pillar_data.reserve(7);
681 for(unsigned i=3; i<=20; ++i)
684 for(unsigned j=2; j<i; ++j)
688 if(pillar_data.size()<=height)
689 pillar_data.resize(height+1);
691 ObjectData &pd = pillar_data[height];
694 pd.mesh = new GL::Mesh((GL::VERTEX3, GL::NORMAL3));
695 GL::MeshBuilder bld(*pd.mesh);
697 // Produce a fluted cylinder
698 unsigned n_flutes = 12;
699 float r_bottom = cos(M_PI/n_flutes)*0.4;
700 float flute_depth = (0.4-r_bottom)*2;
701 float half_w = sin(M_PI/n_flutes)*0.4;
702 for(unsigned j=0; j<n_flutes; ++j)
704 float a = j*M_PI*2/n_flutes;
705 bld.begin(GL::TRIANGLE_STRIP);
706 for(int k=-3; k<=3; k+=2)
709 float x = cos(a)*(r_bottom-(1-t*t)*flute_depth)-sin(a)*t*half_w;
710 float y = sin(a)*(r_bottom-(1-t*t)*flute_depth)+cos(a)*t*half_w;
711 float d = -t*2*flute_depth;
712 float l = sqrt(d*d+1);
713 bld.normal(cos(a)/l-sin(a)*d/l, sin(a)/l+cos(a)*d/l, 0);
714 bld.vertex(x, y, 0.6+height);
715 bld.vertex(x, y, 0.6);
720 // Create a square plinth and capitel
721 bld.set_matrix(GL::Matrix::translation(0, 0, 0.3));
722 GL::BoxBuilder(1.0, 1.0, 0.6).build(bld);
723 bld.set_matrix(GL::Matrix::translation(0, 0, height+0.8));
724 GL::BoxBuilder(1.0, 1.0, 0.4).build(bld);
726 pd.object = new GL::Object(pd.mesh, &pillar_tech);
729 GL::AnimatedObject *pillar = new GL::AnimatedObject(*pd.object);
731 float a = (i-3)*2*M_PI/18;
732 matrix.translate(cos(a)*5, sin(a)*5, 0);
733 matrix.rotate(a, 0, 0, 1);
734 pillar->set_matrix(matrix);
736 pillars.push_back(pillar);
741 void DesertPillars::create_cube()
743 /* The cube is bluish-gray, with a hard specular reflection to produce a
745 cube_material.set_diffuse(GL::Color(0.5, 0.5, 0.55).to_linear());
746 cube_material.set_specular(GL::Color(1.0));
747 cube_material.set_shininess(120);
748 cube_material.set_reflectivity(0.5);
750 GL::RenderPass *pass = &cube_tech.add_pass(0);
751 pass->set_material(&cube_material);
752 pass->set_shader_program(&cube_shprog, 0);
754 pass = &cube_tech.add_pass("shadow");
755 pass->set_shader_program(&cube_shadow_shprog, 0);
757 cube_data.mesh = new GL::Mesh((GL::VERTEX3, GL::NORMAL3, GL::ATTRIB3,7));
758 GL::MeshBuilder bld(*cube_data.mesh);
759 create_cube_face(bld, GL::Vector3(-1, -1, -1), GL::Vector3(2, 0, 0), GL::Vector3(0, 2, 0), 16);
760 bld.offset(cube_data.mesh->get_n_vertices());
761 create_cube_face(bld, GL::Vector3(-1, 1, -1), GL::Vector3(2, 0, 0), GL::Vector3(0, 0, 2), 16);
762 bld.offset(cube_data.mesh->get_n_vertices());
763 create_cube_face(bld, GL::Vector3(-1, 1, 1), GL::Vector3(2, 0, 0), GL::Vector3(0, -2, 0), 16);
764 bld.offset(cube_data.mesh->get_n_vertices());
765 create_cube_face(bld, GL::Vector3(1, -1, -1), GL::Vector3(-2, 0, 0), GL::Vector3(0, 0, 2), 16);
766 bld.offset(cube_data.mesh->get_n_vertices());
767 create_cube_face(bld, GL::Vector3(-1, -1, 1), GL::Vector3(0, 0, -2), GL::Vector3(0, 2, 0), 16);
768 bld.offset(cube_data.mesh->get_n_vertices());
769 create_cube_face(bld, GL::Vector3(1, -1, -1), GL::Vector3(0, 0, 2), GL::Vector3(0, 2, 0), 16);
770 cube_data.object = new GL::Object(cube_data.mesh, &cube_tech);
772 cube = new Cube(*cube_data.object);
773 env_cube = new GL::EnvironmentMap(512, *cube, env_pipeline);
774 scene.add(*env_cube);
777 void DesertPillars::create_cube_face(GL::MeshBuilder &bld, const GL::Vector3 &base, const GL::Vector3 &side1, const GL::Vector3 &side2, unsigned div)
779 /* The sides follow the cube map convention where the cross product points
780 inwards. Since the normal has to point outwards, reverse the order. */
782 n.x = side2.y*side1.z-side2.z*side1.y;
783 n.y = side2.z*side1.x-side2.x*side1.z;
784 n.z = side2.x*side1.y-side2.y*side1.x;
785 float l = sqrt(n.x*n.x+n.y*n.y+n.z*n.z);
786 bld.normal(n.x/l, n.y/l, n.z/l);
788 // Create vertices, with precomputed spherified coordinates
789 for(unsigned i=0; i<=div; ++i)
790 for(unsigned j=0; j<=div; ++j)
793 v.x = base.x+side1.x*i/div+side2.x*j/div;
794 v.y = base.y+side1.y*i/div+side2.y*j/div;
795 v.z = base.z+side1.z*i/div+side2.z*j/div;
797 l = sqrt(v.x*v.x+v.y*v.y+v.z*v.z);
799 bld.attrib(7, v.x/l, v.y/l, v.z/l);
804 for(unsigned i=0; i<div; ++i)
806 bld.begin(GL::TRIANGLE_STRIP);
807 for(unsigned j=0; j<=div; ++j)
809 bld.element(i*(div+1)+j);
810 bld.element((i+1)*(div+1)+j);
816 int DesertPillars::main()
819 return Application::main();
822 void DesertPillars::tick()
824 Time::TimeStamp t = Time::now();
832 camera_angle += (dt/Time::sec)*M_PI*2/30;
833 if(camera_angle>M_PI*4)
834 camera_angle -= M_PI*4;
835 float h = 3+(1-cos(camera_angle*1.5))*3;
836 float r = sqrt(225-h*h);
837 camera.set_position(GL::Vector3(cos(camera_angle)*r, sin(camera_angle)*r, 1.5+h));
838 camera.look_at(GL::Vector3(0, 0, 2));
843 cube_angle += (dt/Time::sec)*M_PI*2/20;
844 GL::Matrix cube_matrix;
845 cube_matrix.translate(0, 0, 2.5);
846 cube_matrix.rotate(cube_angle, 0, 0, 1);
847 cube_matrix.rotate(cube_angle*0.5, 1, 0, 0);
848 cube->set_matrix(cube_matrix);
853 cube_phase += (dt/Time::sec)/5;
863 float x = cube_phase*5;
865 cube->set_spherify((1-x)*cube_shapes[(cube_shape+3)%4]+x*cube_shapes[cube_shape]);
868 cube->set_spherify(cube_shapes[cube_shape]);
875 void DesertPillars::key_press(unsigned key)
877 if(key==Input::KEY_ESC)
879 else if(key==Input::KEY_SPACE)
880 camera_stopped = !camera_stopped;
881 else if(key==Input::KEY_F)
882 cube_frozen = !cube_frozen;
883 else if(key==Input::KEY_S)
884 cube_stopped = !cube_stopped;
888 DesertPillars::ObjectData::ObjectData():
893 DesertPillars::ObjectData::~ObjectData()
900 DesertPillars::Cube::Cube(const GL::Object &obj):
901 GL::AnimatedObject(obj)
904 void DesertPillars::Cube::set_spherify(float s)
906 shdata.uniform("spherify", s);
909 void DesertPillars::Cube::setup_render(GL::Renderer &renderer, const GL::Tag &tag) const
911 AnimatedObject::setup_render(renderer, tag);
912 renderer.add_shader_data(shdata);