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/renderer.h>
23 #include <msp/gl/resources.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;
100 GL::Resources resources;
102 GL::Program skybox_shprog;
103 GL::Technique skybox_tech;
104 GL::TextureCube skybox_tex;
105 GL::Sampler linear_clamped_sampler;
106 ObjectData skybox_data;
108 const GL::Program &shadow_shprog;
110 GL::Program ground_shprog;
111 GL::ProgramData ground_shdata;
112 GL::Texture2D tiles_texture;
113 GL::Texture2D tiles_normalmap;
114 GL::Texture2D sand_texture;
115 GL::Texture2D sand_normalmap;
116 GL::Sampler linear_sampler;
117 GL::Sampler mipmap_sampler;
118 GL::Technique ground_tech;
119 ObjectData ground_data;
121 GL::BasicMaterial pillar_material;
122 GL::Technique pillar_tech;
123 std::vector<ObjectData> pillar_data;
124 std::vector<GL::AnimatedObject *> pillars;
126 GL::Program cube_shprog;
127 GL::Program cube_shadow_shprog;
128 GL::BasicMaterial cube_material;
129 GL::Technique cube_tech;
130 ObjectData cube_data;
132 GL::EnvironmentMap *env_cube;
135 GL::Pipeline pipeline;
137 GL::SimpleScene sky_scene;
138 GL::InstanceScene scene;
139 GL::Lighting lighting;
141 GL::ShadowMap shadow_scene;
143 GL::ColorCurve colorcurve;
145 GL::Pipeline env_pipeline;
147 Time::TimeStamp last_tick;
156 static const char texture_src[];
157 static const char skybox_src[];
158 static const char ground_src[];
159 static const char cube_src[];
160 static const char cube_shadow_src_tail[];
161 static const float cube_shapes[];
164 DesertPillars(int, char **);
169 void create_pipeline();
170 void create_skybox();
171 static void create_skybox_face(GL::TextureCube &, GL::TextureCubeFace);
172 void create_tiles_texture();
173 void create_sand_texture();
174 static void gaussian_blur(unsigned char *, unsigned, unsigned);
175 static void create_normalmap(const unsigned char *, unsigned char *, unsigned, unsigned, float);
176 void create_ground();
177 static float ground_height(float, float);
178 void create_pillars();
180 static void create_cube_face(GL::MeshBuilder &, const GL::Vector3 &, const GL::Vector3 &, const GL::Vector3 &, unsigned);
187 void key_press(unsigned);
190 const char DesertPillars::texture_src[] =
191 "import msp_interface;\n"
192 "#pragma MSP stage(vertex)\n"
195 " gl_Position = vec4(vertex.xy*2.0-1.0, -vertex.z*2.0, 1.0);\n"
198 "#pragma MSP stage(fragment)\n"
199 "layout(location=1) out vec4 frag_normal;\n"
202 " frag_color = color;\n"
203 " frag_normal = vec4(normal*0.5+0.5, 1.0);\n"
206 const char DesertPillars::skybox_src[] =
207 "import msp_interface;\n"
208 "uniform samplerCube sky;\n"
209 "#pragma MSP stage(vertex)\n"
212 " gl_Position = projection_matrix*vec4(mat3(eye_obj_matrix)*vertex.xyz, 1.0);\n"
215 "#pragma MSP stage(fragment)\n"
218 " frag_color = texture(sky, vertex.xyz);\n"
221 const char DesertPillars::ground_src[] =
223 "uniform sampler2D texture1;\n"
224 "uniform sampler2D normalmap1;\n"
225 "uniform sampler2D texture2;\n"
226 "uniform sampler2D normalmap2;\n"
227 "const bool use_normal_map = true;\n"
228 "const bool use_shadow_map = true;\n"
229 "#pragma MSP stage(vertex)\n"
230 "layout(location=7) in float ground_type;\n"
231 "#pragma MSP stage(fragment)\n"
232 "vec4 get_diffuse_color()\n"
234 " return mix(texture(texture1, texcoord.xy*3.0), texture(texture2, texcoord.xy), ground_type);\n"
236 "vec4 get_fragment_normal()\n"
238 " return mix(texture(normalmap1, texcoord.xy*3.0).rgb, texture(normalmap2, texcoord.xy).rgb, ground_type);\n"
241 const char DesertPillars::cube_src[] =
243 "const bool use_specular = true;\n"
244 "const bool use_reflectivity = true;\n"
245 "uniform float spherify;\n"
246 "#pragma MSP stage(vertex)\n"
247 "layout(location=7) in vec3 sphere_coord;\n"
248 "vec4 transform_position(vec4 pos)\n"
250 " return eye_obj_matrix*vec4(mix(vertex.xyz, sphere_coord, spherify), 1.0);\n"
252 "vec3 transform_normal(vec3 pos)\n"
254 " return eye_obj_normal_matrix*normalize(mix(normal, normalize(sphere_coord), spherify));\n"
257 const char DesertPillars::cube_shadow_src_tail[] =
258 "#pragma MSP stage(fragment)\n"
261 " frag_color = vec4(1.0);\n"
264 const float DesertPillars::cube_shapes[] = { -0.4, 0.5, 1.0, 0.3 };
267 DesertPillars::Options::Options(const Graphics::Display &dpy, int argc, char **argv)
270 getopt.add_option('f', "fullscreen", window_opts.fullscreen, GetOpt::NO_ARG).set_help("Run in fullscreen mode");
273 if(window_opts.fullscreen)
275 const Graphics::VideoMode &mode = dpy.get_desktop_mode();
276 window_opts.width = mode.width;
277 window_opts.height = mode.height;
281 window_opts.width = 800;
282 window_opts.height = 600;
287 DesertPillars::DesertPillars(int argc, char **argv):
288 options(display, argc, argv),
289 window(display, options.window_opts),
292 skybox_shprog(skybox_src),
293 shadow_shprog(resources.get<GL::Program>("_occluder.glsl.shader")),
294 ground_shprog(ground_src),
295 cube_shprog(cube_src),
296 cube_shadow_shprog(string(cube_src)+cube_shadow_src_tail),
297 view(window, gl_context),
299 shadow_scene(resources, 2048, scene, light),
300 bloom(resources, window.get_width(), window.get_height()),
301 colorcurve(resources),
302 env_pipeline(512, 512),
304 camera_stopped(false),
311 window.set_title("Desert Pillars");
312 window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &DesertPillars::exit), 0));
313 if(options.window_opts.fullscreen)
314 window.show_cursor(false);
315 keyboard.signal_button_press.connect(sigc::bind_return(sigc::mem_fun(this, &DesertPillars::key_press), false));
325 DesertPillars::~DesertPillars()
329 for(vector<GL::AnimatedObject *>::iterator i=pillars.begin(); i!=pillars.end(); ++i)
333 void DesertPillars::setup_view()
335 camera.set_aspect_ratio(float(window.get_width())/window.get_height());
336 camera.set_up_direction(GL::Vector3(0, 0, 1));
337 camera.set_depth_clip(1, 50);
338 view.set_camera(&camera);
339 view.set_content(&pipeline);
342 void DesertPillars::create_pipeline()
344 pipeline.set_multisample(8);
345 pipeline.set_hdr(true);
347 /* The shadow map is focused on the part of the scene that contains the
348 pillars and the cube. Making the ground cast shadows as well would result
349 either in a very low spatial resolution of the shadow map, or ugly artifacts
350 as the ground crosses the shadow map boundary. */
351 shadow_scene.set_target(GL::Vector3(0, 0, 0), 10);
352 shadow_scene.set_darkness(1);
354 // Put the sun pretty high in the sky
355 light.set_diffuse(GL::Color(2.0));
356 light.set_position(GL::Vector4(0.5, -2, 3, 0));
357 lighting.attach(0, light);
358 lighting.set_ambient(GL::Color(0.2));
360 // The skybox is rendered first
361 pipeline.add_pass(0, sky_scene);
363 GL::Pipeline::Pass *pass = &pipeline.add_pass(0, shadow_scene);
364 pass->set_lighting(&lighting);
365 pass->set_depth_test(&GL::DepthTest::lequal());
367 /* A bloom filter enhances the realism of bright surfaces, even if there
368 isn't anything really glowy in the scene. */
369 bloom.set_strength(0.3);
370 pipeline.add_postprocessor(bloom);
372 /* Lighting calculations are best done in linear color space, so the final
373 image must be converted to srgb for display. */
374 colorcurve.set_srgb();
375 pipeline.add_postprocessor(colorcurve);
377 /* Initialize a second pipeline to render the environment map. It has the
378 same renderables and passes, but no postprocessors or camera. */
379 env_pipeline.add_pass(0, sky_scene);
380 pass = &env_pipeline.add_pass(0, shadow_scene);
381 pass->set_lighting(&lighting);
382 pass->set_depth_test(&GL::DepthTest::lequal());
385 void DesertPillars::create_skybox()
387 skybox_tex.storage(GL::SRGB8, 128, 1);
388 linear_clamped_sampler.set_min_filter(GL::LINEAR);
389 linear_clamped_sampler.set_wrap(GL::CLAMP_TO_EDGE);
390 for(unsigned i=0; i<6; ++i)
391 create_skybox_face(skybox_tex, skybox_tex.enumerate_faces(i));
393 GL::RenderPass &pass = skybox_tech.add_pass(0);
394 pass.set_shader_program(&skybox_shprog, 0);
395 pass.set_texture(0, &skybox_tex, &linear_clamped_sampler);
397 // The shader will use the vertex coordinates to initialize texture coordinates as well
398 skybox_data.mesh = new GL::Mesh(GL::VERTEX3);
399 GL::BoxBuilder(10, 10, 10).build(*skybox_data.mesh);
400 skybox_data.object = new GL::Object(skybox_data.mesh, &skybox_tech);
402 sky_scene.add(*skybox_data.object);
405 void DesertPillars::create_skybox_face(GL::TextureCube &texture, GL::TextureCubeFace face)
407 unsigned char *pixels = new unsigned char[128*128*3];
408 for(int y=0; y<128; ++y)
409 for(int x=0; x<128; ++x)
411 unsigned i = (x+y*128)*3;
412 GL::Vector3 v = texture.get_texel_direction(face, x, y);
415 float l = sqrt(v.x*v.x+v.y*v.y+v.z*v.z);
416 // Render a sky-like gradient, with deeper blue at the zenith
417 pixels[i] = 96-48*v.z/l;
418 pixels[i+1] = 168-84*v.z/l;
423 // Fill with a desert-y color below horizon
429 texture.image(face, 0, pixels);
433 void DesertPillars::create_tiles_texture()
435 unsigned width = 256;
436 unsigned height = 256;
437 tiles_texture.storage(GL::RGB8, width, height, 1);
438 tiles_normalmap.storage(GL::RGB8, width, height, 1);
439 linear_sampler.set_min_filter(GL::LINEAR);
441 GL::Mesh tiles((GL::VERTEX3, GL::NORMAL3, GL::COLOR4_UBYTE));
443 // Prepare some lookup tables for rendering the tiles
444 float split = 1.0f/3;
445 float spacing = split*0.02f;
446 float bevel = split*0.1f;
447 float coords[] = { 0.0f, spacing, spacing+bevel, split-bevel, split, split+spacing, split+spacing+bevel, 1.0f-bevel, 1.0f };
448 unsigned order[] = { 4, 1, 3, 2, 1, 1, 2, 2,
449 1, 4, 2, 3, 4, 4, 3, 3,
450 1, 1, 2, 2, 1, 4, 2, 3,
451 4, 4, 3, 3, 4, 1, 3, 2,
452 2, 3, 2, 2, 3, 3, 3, 2 };
455 GL::MeshBuilder bld(tiles);
457 // Create a dark background
458 bld.color(0.2f, 0.2f, 0.2f);
459 bld.normal(0.0f, 0.0f, 1.0f);
460 bld.begin(GL::TRIANGLE_STRIP);
461 bld.vertex(0.0f, 1.0f);
462 bld.vertex(0.0f, 0.0f);
463 bld.vertex(1.0f, 1.0f);
464 bld.vertex(1.0f, 0.0f);
467 // Create the four tiles
468 bld.color(GL::Color(0.95f, 0.8f, 0.65f).to_linear());
469 for(unsigned i=0; i<2; ++i)
470 for(unsigned j=0; j<2; ++j)
472 for(unsigned k=0; k<4; ++k)
474 bld.begin(GL::TRIANGLE_STRIP);
475 float facing = (k%2)*2-1.0f;
477 bld.normal(0.0f, 0.7071f*facing, 0.7071f);
479 bld.normal(0.7071f*facing, 0.0f, 0.7071f);
480 for(unsigned l=0; l<4; ++l)
481 bld.vertex(coords[i*4+order[k*8+l*2]], coords[j*4+order[k*8+l*2+1]], (l%2 ? bevel : 0.0f));
485 bld.begin(GL::TRIANGLE_STRIP);
486 bld.normal(0.0f, 0.0f, 1.0f);
487 for(unsigned l=0; l<4; ++l)
488 bld.vertex(coords[i*4+order[32+l*2]], coords[j*4+order[32+l*2+1]], bevel);
493 GL::Program shprog(texture_src);
495 // Use an FBO to turn the geometry into a normalmapped texture
497 fbo.attach(GL::COLOR_ATTACHMENT0, tiles_texture);
498 fbo.attach(GL::COLOR_ATTACHMENT1, tiles_normalmap);
499 GL::Bind bind_fbo(fbo);
500 GL::Renderer renderer;
501 renderer.set_shader_program(&shprog, 0);
502 tiles.draw(renderer);
505 void DesertPillars::create_sand_texture()
507 unsigned width = 512;
508 unsigned height = 512;
510 sand_texture.storage(GL::SRGB8, width/16, height/16);
511 sand_texture.set_auto_generate_mipmap(true);
512 sand_normalmap.storage(GL::RGB8, width, height);
513 sand_normalmap.set_auto_generate_mipmap(true);
514 mipmap_sampler.set_min_filter(GL::LINEAR_MIPMAP_LINEAR);
515 mipmap_sampler.set_max_anisotropy(4);
517 unsigned char *pixels = new unsigned char[width*height*3];
518 unsigned char *bump = new unsigned char[width*height];
519 for(unsigned y=0; y<height; ++y)
520 for(unsigned x=0; x<width; ++x)
522 unsigned i = (x+y*width)*3;
523 unsigned c = rand()%16;
527 bump[x+y*width] = rand();
529 sand_texture.image(0, pixels);
530 gaussian_blur(bump, width, height);
531 create_normalmap(bump, pixels, width, height, 4);
532 sand_normalmap.image(0, pixels);
537 void DesertPillars::gaussian_blur(unsigned char *data, unsigned width, unsigned height)
539 /* Create a gaussian blur kernel for σ=2. Gaussian blur is separable, so a
540 1-dimensional kernel is enough. */
543 for(int i=-4; i<=4; ++i)
544 sum += (kernel[4+i] = exp(-i*i*0.5));
545 for(unsigned i=0; i<9; ++i)
548 unsigned char *line = new unsigned char[max(width, height)];
549 // Perform the blur in the X direction
550 for(unsigned y=0; y<height; ++y)
552 for(unsigned x=0; x<width; ++x)
555 for(int i=-4; i<=4; ++i)
556 value += data[(x+width+i)%width+y*width]*kernel[4+i];
559 copy(line, line+width, data+y*width);
561 // And then in the Y direction
562 for(unsigned x=0; x<width; ++x)
564 for(unsigned y=0; y<height; ++y)
567 for(int i=-4; i<=4; ++i)
568 value += data[x+((y+height+i)%height)*width]*kernel[4+i];
571 for(unsigned y=0; y<height; ++y)
572 data[x+y*width] = line[y];
577 void DesertPillars::create_normalmap(const unsigned char *bump, unsigned char *normals, unsigned width, unsigned height, float depth)
579 for(unsigned y=0; y<height; ++y)
580 for(unsigned x=0; x<width; ++x)
582 float dz_x = (bump[(x+1)%width+y*width]-bump[(x+width-1)%width+y*width])*depth/510;
583 float dz_y = (bump[x+((y+1)%height)*width]-bump[x+((y+height-1)%height)*width])*depth/510;
584 float l = sqrt(dz_x*dz_x+dz_y*dz_y+1);
585 unsigned i = (x+y*width)*3;
586 normals[i] = (0.5-0.5*dz_x/l)*255;
587 normals[i+1] = (0.5-0.5*dz_y/l)*255;
588 normals[i+2] = (0.5+0.5/l)*255;
592 void DesertPillars::create_ground()
594 create_tiles_texture();
595 create_sand_texture();
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, &linear_sampler);
605 pass->set_texture(1, &tiles_normalmap, &linear_sampler);
606 pass->set_texture(2, &sand_texture, &mipmap_sampler);
607 pass->set_texture(3, &sand_normalmap, &mipmap_sampler);
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.89, 0.85).to_linear());
676 pillar_material.set_receive_shadows(true);
678 GL::RenderPass *pass = &pillar_tech.add_pass(0);
679 pass->set_material(&pillar_material);
681 pass = &pillar_tech.add_pass("shadow");
682 pass->set_shader_program(&shadow_shprog, 0);
684 pillar_data.reserve(7);
685 for(unsigned i=3; i<=20; ++i)
688 for(unsigned j=2; j<i; ++j)
692 if(pillar_data.size()<=height)
693 pillar_data.resize(height+1);
695 ObjectData &pd = pillar_data[height];
698 pd.mesh = new GL::Mesh((GL::VERTEX3, GL::NORMAL3));
699 GL::MeshBuilder bld(*pd.mesh);
701 // Produce a fluted cylinder
702 unsigned n_flutes = 12;
703 float r_bottom = cos(M_PI/n_flutes)*0.4;
704 float flute_depth = (0.4-r_bottom)*2;
705 float half_w = sin(M_PI/n_flutes)*0.4;
706 for(unsigned j=0; j<n_flutes; ++j)
708 float a = j*M_PI*2/n_flutes;
709 bld.begin(GL::TRIANGLE_STRIP);
710 for(int k=-3; k<=3; k+=2)
713 float x = cos(a)*(r_bottom-(1-t*t)*flute_depth)-sin(a)*t*half_w;
714 float y = sin(a)*(r_bottom-(1-t*t)*flute_depth)+cos(a)*t*half_w;
715 float d = -t*2*flute_depth;
716 float l = sqrt(d*d+1);
717 bld.normal(cos(a)/l-sin(a)*d/l, sin(a)/l+cos(a)*d/l, 0);
718 bld.vertex(x, y, 0.6+height);
719 bld.vertex(x, y, 0.6);
724 // Create a square plinth and capitel
725 bld.set_matrix(GL::Matrix::translation(0, 0, 0.3));
726 GL::BoxBuilder(1.0, 1.0, 0.6).build(bld);
727 bld.set_matrix(GL::Matrix::translation(0, 0, height+0.8));
728 GL::BoxBuilder(1.0, 1.0, 0.4).build(bld);
730 pd.object = new GL::Object(pd.mesh, &pillar_tech);
733 GL::AnimatedObject *pillar = new GL::AnimatedObject(*pd.object);
735 float a = (i-3)*2*M_PI/18;
736 matrix.translate(cos(a)*5, sin(a)*5, 0);
737 matrix.rotate(a, 0, 0, 1);
738 pillar->set_matrix(matrix);
740 pillars.push_back(pillar);
745 void DesertPillars::create_cube()
747 /* The cube is bluish-gray, with a hard specular reflection to produce a
749 cube_material.set_diffuse(GL::Color(0.5, 0.5, 0.55).to_linear());
750 cube_material.set_specular(GL::Color(1.0));
751 cube_material.set_shininess(120);
752 cube_material.set_reflectivity(0.5);
754 GL::RenderPass *pass = &cube_tech.add_pass(0);
755 pass->set_material(&cube_material);
756 pass->set_shader_program(&cube_shprog, 0);
758 pass = &cube_tech.add_pass("shadow");
759 pass->set_shader_program(&cube_shadow_shprog, 0);
761 cube_data.mesh = new GL::Mesh((GL::VERTEX3, GL::NORMAL3, GL::ATTRIB3,7));
762 GL::MeshBuilder bld(*cube_data.mesh);
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(2, 0, 0), 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(-2, 0, 0), GL::Vector3(0, 0, 2), 16);
770 bld.offset(cube_data.mesh->get_n_vertices());
771 create_cube_face(bld, GL::Vector3(-1, -1, 1), GL::Vector3(0, 0, -2), GL::Vector3(0, 2, 0), 16);
772 bld.offset(cube_data.mesh->get_n_vertices());
773 create_cube_face(bld, GL::Vector3(1, -1, -1), GL::Vector3(0, 0, 2), GL::Vector3(0, 2, 0), 16);
774 cube_data.object = new GL::Object(cube_data.mesh, &cube_tech);
776 cube = new Cube(*cube_data.object);
777 env_cube = new GL::EnvironmentMap(resources, 512, *cube, env_pipeline);
778 scene.add(*env_cube);
781 void DesertPillars::create_cube_face(GL::MeshBuilder &bld, const GL::Vector3 &base, const GL::Vector3 &side1, const GL::Vector3 &side2, unsigned div)
783 /* The sides follow the cube map convention where the cross product points
784 inwards. Since the normal has to point outwards, reverse the order. */
786 n.x = side2.y*side1.z-side2.z*side1.y;
787 n.y = side2.z*side1.x-side2.x*side1.z;
788 n.z = side2.x*side1.y-side2.y*side1.x;
789 float l = sqrt(n.x*n.x+n.y*n.y+n.z*n.z);
790 bld.normal(n.x/l, n.y/l, n.z/l);
792 // Create vertices, with precomputed spherified coordinates
793 for(unsigned i=0; i<=div; ++i)
794 for(unsigned j=0; j<=div; ++j)
797 v.x = base.x+side1.x*i/div+side2.x*j/div;
798 v.y = base.y+side1.y*i/div+side2.y*j/div;
799 v.z = base.z+side1.z*i/div+side2.z*j/div;
801 l = sqrt(v.x*v.x+v.y*v.y+v.z*v.z);
803 bld.attrib(7, v.x/l, v.y/l, v.z/l);
808 for(unsigned i=0; i<div; ++i)
810 bld.begin(GL::TRIANGLE_STRIP);
811 for(unsigned j=0; j<=div; ++j)
813 bld.element(i*(div+1)+j);
814 bld.element((i+1)*(div+1)+j);
820 int DesertPillars::main()
823 return Application::main();
826 void DesertPillars::tick()
828 Time::TimeStamp t = Time::now();
836 camera_angle += (dt/Time::sec)*M_PI*2/30;
837 if(camera_angle>M_PI*4)
838 camera_angle -= M_PI*4;
839 float h = 3+(1-cos(camera_angle*1.5))*3;
840 float r = sqrt(225-h*h);
841 camera.set_position(GL::Vector3(cos(camera_angle)*r, sin(camera_angle)*r, 1.5+h));
842 camera.look_at(GL::Vector3(0, 0, 2));
847 cube_angle += (dt/Time::sec)*M_PI*2/20;
848 GL::Matrix cube_matrix;
849 cube_matrix.translate(0, 0, 2.5);
850 cube_matrix.rotate(cube_angle, 0, 0, 1);
851 cube_matrix.rotate(cube_angle*0.5, 1, 0, 0);
852 cube->set_matrix(cube_matrix);
857 cube_phase += (dt/Time::sec)/5;
867 float x = cube_phase*5;
869 cube->set_spherify((1-x)*cube_shapes[(cube_shape+3)%4]+x*cube_shapes[cube_shape]);
872 cube->set_spherify(cube_shapes[cube_shape]);
879 void DesertPillars::key_press(unsigned key)
881 if(key==Input::KEY_ESC)
883 else if(key==Input::KEY_SPACE)
884 camera_stopped = !camera_stopped;
885 else if(key==Input::KEY_F)
886 cube_frozen = !cube_frozen;
887 else if(key==Input::KEY_S)
888 cube_stopped = !cube_stopped;
892 DesertPillars::ObjectData::ObjectData():
897 DesertPillars::ObjectData::~ObjectData()
904 DesertPillars::Cube::Cube(const GL::Object &obj):
905 GL::AnimatedObject(obj)
908 void DesertPillars::Cube::set_spherify(float s)
910 shdata.uniform("spherify", s);
913 void DesertPillars::Cube::setup_render(GL::Renderer &renderer, const GL::Tag &tag) const
915 AnimatedObject::setup_render(renderer, tag);
916 renderer.add_shader_data(shdata);