]> git.tdb.fi Git - libs/gl.git/blob - demos/desertpillars.cpp
Modernize the demo and tool programs
[libs/gl.git] / demos / desertpillars.cpp
1 #include <cmath>
2 #include <cstdlib>
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/basicmaterial.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/gl/windowview.h>
30 #include <msp/graphics/simplewindow.h>
31 #include <msp/input/keyboard.h>
32 #include <msp/input/keys.h>
33 #include <msp/time/timestamp.h>
34 #include <msp/time/timedelta.h>
35 #include <msp/time/utils.h>
36
37 using namespace std;
38 using namespace Msp;
39
40 /**
41 This application demonstrates a variety of features of the mspgl library,
42 including:
43 - Creating meshes from multiple parts
44 - Creating a mesh and then modifying it
45 - Shadow mapping
46 - Environment mapped reflections
47 - Skybox using a cube map texture
48 - Effects with nested pipelines
49 - Complex multitexturing
50 - Shader-based deformations
51 - Creating a normalmapped texture through rendering
52
53 To run the program in fullscreen mode, specify --fullscreen on the command
54 line.
55
56 During execution the following keys are available:
57 esc    exit
58 space  stop camera movement
59 s      stop cube rotation
60 f      freeze cube shape
61 */
62 class DesertPillars: public RegisteredApplication<DesertPillars>
63 {
64 private:
65         struct Options
66         {
67                 Graphics::WindowOptions window_opts;
68
69                 Options(const Graphics::Display &, int, char **);
70         };
71
72         struct ObjectData
73         {
74                 GL::Mesh *mesh;
75                 GL::Object *object;
76
77                 ObjectData();
78                 ~ObjectData();
79         };
80
81         class Cube: public GL::AnimatedObject
82         {
83         private:
84                 GL::ProgramData shdata;
85
86         public:
87                 Cube(const GL::Object &);
88
89                 void set_spherify(float);
90
91                 virtual void setup_render(GL::Renderer &, const GL::Tag &) const;
92         };
93
94         Msp::Graphics::Display display;
95         Options options;
96         Msp::Graphics::Window window;
97         Msp::Graphics::GLContext gl_context;
98         Msp::Input::Keyboard keyboard;
99
100         GL::Program skybox_shprog;
101         GL::Technique skybox_tech;
102         GL::TextureCube skybox_tex;
103         ObjectData skybox_data;
104
105         GL::Program shadow_shprog;
106
107         GL::Program ground_shprog;
108         GL::ProgramData ground_shdata;
109         GL::Texture2D tiles_texture;
110         GL::Texture2D tiles_normalmap;
111         GL::Texture2D sand_texture;
112         GL::Texture2D sand_normalmap;
113         GL::Technique ground_tech;
114         ObjectData ground_data;
115
116         GL::BasicMaterial pillar_material;
117         GL::Technique pillar_tech;
118         std::vector<ObjectData> pillar_data;
119         std::vector<GL::AnimatedObject *> pillars;
120
121         GL::Program cube_shprog;
122         GL::Program cube_shadow_shprog;
123         GL::BasicMaterial cube_material;
124         GL::Technique cube_tech;
125         ObjectData cube_data;
126         Cube *cube;
127         GL::EnvironmentMap *env_cube;
128
129         GL::WindowView view;
130         GL::Pipeline pipeline;
131         GL::Camera camera;
132         GL::SimpleScene sky_scene;
133         GL::InstanceScene scene;
134         GL::Lighting lighting;
135         GL::Light light;
136         GL::ShadowMap shadow_scene;
137         GL::Bloom bloom;
138
139         GL::Pipeline env_pipeline;
140
141         Time::TimeStamp last_tick;
142         float camera_angle;
143         bool camera_stopped;
144         float cube_angle;
145         bool cube_stopped;
146         unsigned cube_shape;
147         float cube_phase;
148         bool cube_frozen;
149
150         static const char texture_src[];
151         static const char skybox_src[];
152         static const char ground_src[];
153         static const char cube_src[];
154         static const char cube_shadow_src_tail[];
155         static const float cube_shapes[];
156
157 public:
158         DesertPillars(int, char **);
159         ~DesertPillars();
160
161 private:
162         void setup_view();
163         void create_pipeline();
164         void create_skybox();
165         static void create_skybox_face(GL::TextureCube &, GL::TextureCubeFace);
166         void create_tiles_texture();
167         void create_sand_texture();
168         static void gaussian_blur(unsigned char *, unsigned, unsigned);
169         static void create_normalmap(const unsigned char *, unsigned char *, unsigned, unsigned, float);
170         void create_ground();
171         static float ground_height(float, float);
172         void create_pillars();
173         void create_cube();
174         static void create_cube_face(GL::MeshBuilder &, const GL::Vector3 &, const GL::Vector3 &, const GL::Vector3 &, unsigned);
175
176 public:
177         virtual int main();
178 private:
179         virtual void tick();
180
181         void key_press(unsigned);
182 };
183
184 const char DesertPillars::texture_src[] =
185         "import msp_interface;\n"
186         "#pragma MSP stage(vertex)\n"
187         "void main()\n"
188         "{\n"
189         "       gl_Position = vec4(vertex.xy*2.0-1.0, -vertex.z*2.0, 1.0);\n"
190         "       passthrough;\n"
191         "}\n"
192         "#pragma MSP stage(fragment)\n"
193         "layout(location=1) out vec4 frag_normal;\n"
194         "void main()\n"
195         "{\n"
196         "       frag_color = color;\n"
197         "       frag_normal = vec4(normal*0.5+0.5, 1.0);\n"
198         "}\n";
199
200 const char DesertPillars::skybox_src[] =
201         "import msp_interface;\n"
202         "uniform samplerCube sky;\n"
203         "#pragma MSP stage(vertex)\n"
204         "void main()\n"
205         "{\n"
206         "       gl_Position = projection_matrix*vec4(mat3(eye_obj_matrix)*vertex.xyz, 1.0);\n"
207         "       passthrough;\n"
208         "}\n"
209         "#pragma MSP stage(fragment)\n"
210         "void main()\n"
211         "{\n"
212         "       frag_color = texture(sky, vertex.xyz);\n"
213         "}\n";
214
215 const char DesertPillars::ground_src[] =
216         "import phong;\n"
217         "uniform sampler2D texture1;\n"
218         "uniform sampler2D normalmap1;\n"
219         "uniform sampler2D texture2;\n"
220         "uniform sampler2D normalmap2;\n"
221         "const bool use_normal_map = true;\n"
222         "const bool use_shadow_map = true;\n"
223         "#pragma MSP stage(vertex)\n"
224         "layout(location=7) in float ground_type;\n"
225         "#pragma MSP stage(fragment)\n"
226         "vec4 get_diffuse_color()\n"
227         "{\n"
228         "       return mix(texture(texture1, texcoord.xy*3.0), texture(texture2, texcoord.xy), ground_type);\n"
229         "}\n"
230         "vec4 get_normal_sample()\n"
231         "{\n"
232         "       return mix(texture(normalmap1, texcoord.xy*3.0).rgb, texture(normalmap2, texcoord.xy).rgb, ground_type);\n"
233         "}\n";
234
235 const char DesertPillars::cube_src[] =
236         "import phong;\n"
237         "const bool use_specular = true;\n"
238         "const bool use_reflectivity = true;\n"
239         "uniform float spherify;\n"
240         "#pragma MSP stage(vertex)\n"
241         "layout(location=7) in vec3 sphere_coord;\n"
242         "vec4 transform_position(vec4 pos)\n"
243         "{\n"
244         "       return eye_obj_matrix*vec4(mix(vertex.xyz, sphere_coord, spherify), 1.0);\n"
245         "}\n"
246         "vec3 transform_normal(vec3 pos)\n"
247         "{\n"
248         "       return eye_obj_normal_matrix*normalize(mix(normal, normalize(sphere_coord), spherify));\n"
249         "}\n";
250
251 const char DesertPillars::cube_shadow_src_tail[] =
252         "#pragma MSP stage(fragment)\n"
253         "void main()\n"
254         "{\n"
255         "       frag_color = vec4(1.0);\n"
256         "}\n";
257
258 const float DesertPillars::cube_shapes[] = { -0.4, 0.5, 1.0, 0.3 };
259
260
261 DesertPillars::Options::Options(const Graphics::Display &dpy, int argc, char **argv)
262 {
263         GetOpt getopt;
264         getopt.add_option('f', "fullscreen", window_opts.fullscreen, GetOpt::NO_ARG).set_help("Run in fullscreen mode");
265         getopt(argc, argv);
266
267         if(window_opts.fullscreen)
268         {
269                 const Graphics::VideoMode &mode = dpy.get_desktop_mode();
270                 window_opts.width = mode.width;
271                 window_opts.height = mode.height;
272         }
273         else
274         {
275                 window_opts.width = 800;
276                 window_opts.height = 600;
277         }
278 }
279
280
281 DesertPillars::DesertPillars(int argc, char **argv):
282         options(display, argc, argv),
283         window(display, options.window_opts),
284         gl_context(window),
285         keyboard(window),
286         skybox_shprog(skybox_src),
287         shadow_shprog("occluder.glsl"),
288         ground_shprog(ground_src),
289         cube_shprog(cube_src),
290         cube_shadow_shprog(string(cube_src)+cube_shadow_src_tail),
291         view(window, gl_context),
292         pipeline(view),
293         shadow_scene(2048, scene, light),
294         bloom(window.get_width(), window.get_height()),
295         env_pipeline(512, 512),
296         camera_angle(0),
297         camera_stopped(false),
298         cube_angle(0),
299         cube_stopped(false),
300         cube_shape(0),
301         cube_phase(0),
302         cube_frozen(false)
303 {
304         window.set_title("Desert Pillars");
305         window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &DesertPillars::exit), 0));
306         if(options.window_opts.fullscreen)
307                 window.show_cursor(false);
308         keyboard.signal_button_press.connect(sigc::bind_return(sigc::mem_fun(this, &DesertPillars::key_press), false));
309
310         setup_view();
311         create_pipeline();
312         create_skybox();
313         create_ground();
314         create_pillars();
315         create_cube();
316 }
317
318 DesertPillars::~DesertPillars()
319 {
320         delete env_cube;
321         delete cube;
322         for(vector<GL::AnimatedObject *>::iterator i=pillars.begin(); i!=pillars.end(); ++i)
323                 delete *i;
324 }
325
326 void DesertPillars::setup_view()
327 {
328         camera.set_aspect_ratio(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         view.set_camera(&camera);
332         view.set_content(&pipeline);
333 }
334
335 void DesertPillars::create_pipeline()
336 {
337         pipeline.set_multisample(8);
338
339         /* The shadow map is focused on the part of the scene that contains the
340         pillars and the cube.  Making the ground cast shadows as well would result
341         either in a very low spatial resolution of the shadow map, or ugly artifacts
342         as the ground crosses the shadow map boundary. */
343         shadow_scene.set_target(GL::Vector3(0, 0, 0), 10);
344
345         // Put the sun pretty high in the sky
346         light.set_position(GL::Vector4(0.5, -2, 3, 0));
347         lighting.attach(0, light);
348         lighting.set_ambient(GL::Color(0.5));
349
350         // The skybox is rendered first
351         pipeline.add_pass(0, sky_scene);
352
353         GL::Pipeline::Pass *pass = &pipeline.add_pass(0, shadow_scene);
354         pass->set_lighting(&lighting);
355         pass->set_depth_test(&GL::DepthTest::lequal());
356
357         /* A bloom filter enhances the realism of bright surfaces, even if there
358         isn't anything really glowy in the scene. */
359         bloom.set_strength(0.3);
360         pipeline.add_postprocessor(bloom);
361
362         /* Initialize a second pipeline to render the environment map.  It has the
363         same renderables and passes, but no postprocessors or camera. */
364         env_pipeline.add_pass(0, sky_scene);
365         pass = &env_pipeline.add_pass(0, shadow_scene);
366         pass->set_lighting(&lighting);
367         pass->set_depth_test(&GL::DepthTest::lequal());
368 }
369
370 void DesertPillars::create_skybox()
371 {
372         skybox_tex.storage(GL::RGB, 128);
373         skybox_tex.set_min_filter(GL::LINEAR);
374         skybox_tex.set_wrap(GL::CLAMP_TO_EDGE);
375         for(unsigned i=0; i<6; ++i)
376                 create_skybox_face(skybox_tex, skybox_tex.enumerate_faces(i));
377
378         GL::RenderPass &pass = skybox_tech.add_pass(0);
379         pass.set_shader_program(&skybox_shprog, 0);
380         pass.set_texture(0, &skybox_tex);
381
382         // The shader will use the vertex coordinates to initialize texture coordinates as well
383         skybox_data.mesh = new GL::Mesh(GL::VERTEX3);
384         GL::BoxBuilder(10, 10, 10).build(*skybox_data.mesh);
385         skybox_data.object = new GL::Object(skybox_data.mesh, &skybox_tech);
386
387         sky_scene.add(*skybox_data.object);
388 }
389
390 void DesertPillars::create_skybox_face(GL::TextureCube &texture, GL::TextureCubeFace face)
391 {
392         unsigned char *pixels = new unsigned char[128*128*3];
393         for(int y=0; y<128; ++y)
394                 for(int x=0; x<128; ++x)
395                 {
396                         unsigned i = (x+y*128)*3;
397                         GL::Vector3 v = texture.get_texel_direction(face, x, y);
398                         if(v.z>0)
399                         {
400                                 float l = sqrt(v.x*v.x+v.y*v.y+v.z*v.z);
401                                 // Render a sky-like gradient, with deeper blue at the zenith
402                                 pixels[i] = 96-48*v.z/l;
403                                 pixels[i+1] = 168-84*v.z/l;
404                                 pixels[i+2] = 255;
405                         }
406                         else
407                         {
408                                 // Fill with a desert-y color below horizon
409                                 pixels[i] = 240;
410                                 pixels[i+1] = 224;
411                                 pixels[i+2] = 160;
412                         }
413                 }
414         texture.image(face, 0, GL::RGB, GL::UNSIGNED_BYTE, pixels);
415         delete[] pixels;
416 }
417
418 void DesertPillars::create_tiles_texture()
419 {
420         unsigned width = 256;
421         unsigned height = 256;
422         tiles_texture.storage(GL::RGB, width, height);
423         tiles_texture.set_min_filter(GL::LINEAR);
424         tiles_normalmap.storage(GL::RGB, width, height);
425         tiles_normalmap.set_min_filter(GL::LINEAR);
426
427         GL::Mesh tiles((GL::VERTEX3, GL::NORMAL3, GL::COLOR4_UBYTE));
428
429         // Prepare some lookup tables for rendering the tiles
430         float split = 1.0f/3;
431         float spacing = split*0.02f;
432         float bevel = split*0.1f;
433         float coords[] = { 0.0f, spacing, spacing+bevel, split-bevel, split, split+spacing, split+spacing+bevel, 1.0f-bevel, 1.0f };
434         unsigned order[] = { 4, 1, 3, 2, 1, 1, 2, 2,
435                 1, 4, 2, 3, 4, 4, 3, 3,
436                 1, 1, 2, 2, 1, 4, 2, 3,
437                 4, 4, 3, 3, 4, 1, 3, 2,
438                 2, 3, 2, 2, 3, 3, 3, 2 };
439
440         GL::MeshBuilder bld(tiles);
441
442         // Create a dark background
443         bld.color(0.2f, 0.2f, 0.2f);
444         bld.normal(0.0f, 0.0f, 1.0f);
445         bld.begin(GL::TRIANGLE_STRIP);
446         bld.vertex(0.0f, 1.0f);
447         bld.vertex(0.0f, 0.0f);
448         bld.vertex(1.0f, 1.0f);
449         bld.vertex(1.0f, 0.0f);
450         bld.end();
451
452         // Create the four tiles
453         bld.color(0.95f, 0.8f, 0.65f);
454         for(unsigned i=0; i<2; ++i)
455                 for(unsigned j=0; j<2; ++j)
456                 {
457                         for(unsigned k=0; k<4; ++k)
458                         {
459                                 bld.begin(GL::TRIANGLE_STRIP);
460                                 float facing = (k%2)*2-1.0f;
461                                 if(k<2)
462                                         bld.normal(0.0f, 0.7071f*facing, 0.7071f);
463                                 else
464                                         bld.normal(0.7071f*facing, 0.0f, 0.7071f);
465                                 for(unsigned l=0; l<4; ++l)
466                                         bld.vertex(coords[i*4+order[k*8+l*2]], coords[j*4+order[k*8+l*2+1]], (l%2 ? bevel : 0.0f));
467                                 bld.end();
468                         }
469
470                         bld.begin(GL::TRIANGLE_STRIP);
471                         bld.normal(0.0f, 0.0f, 1.0f);
472                         for(unsigned l=0; l<4; ++l)
473                                 bld.vertex(coords[i*4+order[32+l*2]], coords[j*4+order[32+l*2+1]], bevel);
474                         bld.end();
475                 }
476
477         GL::Program shprog(texture_src);
478
479         // Use an FBO to turn the geometry into a normalmapped texture
480         GL::Framebuffer fbo;
481         fbo.attach(GL::COLOR_ATTACHMENT0, tiles_texture);
482         fbo.attach(GL::COLOR_ATTACHMENT1, tiles_normalmap);
483         GL::Bind bind_fbo(fbo);
484         GL::Renderer renderer;
485         renderer.set_shader_program(&shprog, 0);
486         tiles.draw(renderer);
487 }
488
489 void DesertPillars::create_sand_texture()
490 {
491         unsigned width = 512;
492         unsigned height = 512;
493
494         sand_texture.storage(GL::RGB, width/16, height/16);
495         sand_texture.set_min_filter(GL::LINEAR_MIPMAP_LINEAR);
496         sand_texture.set_max_anisotropy(4);
497         sand_texture.set_auto_generate_mipmap(true);
498         sand_normalmap.storage(GL::RGB, width, height);
499         sand_normalmap.set_min_filter(GL::LINEAR_MIPMAP_LINEAR);
500         sand_normalmap.set_max_anisotropy(4);
501         sand_normalmap.set_auto_generate_mipmap(true);
502
503         unsigned char *pixels = new unsigned char[width*height*3];
504         unsigned char *bump = new unsigned char[width*height];
505         for(unsigned y=0; y<height; ++y)
506                 for(unsigned x=0; x<width; ++x)
507                 {
508                         unsigned i = (x+y*width)*3;
509                         unsigned c = rand()%16;
510                         pixels[i] = 224+c;
511                         pixels[i+1] = 208+c;
512                         pixels[i+2] = 160;
513                         bump[x+y*width] = rand();
514                 }
515         sand_texture.image(0, GL::RGB, GL::UNSIGNED_BYTE, pixels);
516         gaussian_blur(bump, width, height);
517         create_normalmap(bump, pixels, width, height, 4);
518         sand_normalmap.image(0, GL::RGB, GL::UNSIGNED_BYTE, pixels);
519         delete[] pixels;
520         delete[] bump;
521 }
522
523 void DesertPillars::gaussian_blur(unsigned char *data, unsigned width, unsigned height)
524 {
525         /* Create a gaussian blur kernel for Ïƒ=2.  Gaussian blur is separable, so a
526         1-dimensional kernel is enough. */
527         float kernel[9];
528         float sum = 0;
529         for(int i=-4; i<=4; ++i)
530                 sum += (kernel[4+i] = exp(-i*i*0.5));
531         for(unsigned i=0; i<9; ++i)
532                 kernel[i] /= sum;
533
534         unsigned char *line = new unsigned char[max(width, height)];
535         // Perform the blur in the X direction
536         for(unsigned y=0; y<height; ++y)
537         {
538                 for(unsigned x=0; x<width; ++x)
539                 {
540                         float value = 0;
541                         for(int i=-4; i<=4; ++i)
542                                 value += data[(x+width+i)%width+y*width]*kernel[4+i];
543                         line[x] = value;
544                 }
545                 copy(line, line+width, data+y*width);
546         }
547         // And then in the Y direction
548         for(unsigned x=0; x<width; ++x)
549         {
550                 for(unsigned y=0; y<height; ++y)
551                 {
552                         float value = 0;
553                         for(int i=-4; i<=4; ++i)
554                                 value += data[x+((y+height+i)%height)*width]*kernel[4+i];
555                         line[y] = value;
556                 }
557                 for(unsigned y=0; y<height; ++y)
558                         data[x+y*width] = line[y];
559         }
560         delete[] line;
561 }
562
563 void DesertPillars::create_normalmap(const unsigned char *bump, unsigned char *normals, unsigned width, unsigned height, float depth)
564 {
565         for(unsigned y=0; y<height; ++y)
566                 for(unsigned x=0; x<width; ++x)
567                 {
568                         float dz_x = (bump[(x+1)%width+y*width]-bump[(x+width-1)%width+y*width])*depth/510;
569                         float dz_y = (bump[x+((y+1)%height)*width]-bump[x+((y+height-1)%height)*width])*depth/510;
570                         float l = sqrt(dz_x*dz_x+dz_y*dz_y+1);
571                         unsigned i = (x+y*width)*3;
572                         normals[i] = (0.5-0.5*dz_x/l)*255;
573                         normals[i+1] = (0.5-0.5*dz_y/l)*255;
574                         normals[i+2] = (0.5+0.5/l)*255;
575                 }
576 }
577
578 void DesertPillars::create_ground()
579 {
580         create_tiles_texture();
581         create_sand_texture();
582
583         ground_shdata.uniform("texture1", 0);
584         ground_shdata.uniform("normalmap1", 1);
585         ground_shdata.uniform("texture2", 2);
586         ground_shdata.uniform("normalmap2", 3);
587
588         GL::RenderPass *pass = &ground_tech.add_pass(0);
589         pass->set_shader_program(&ground_shprog, &ground_shdata);
590         pass->set_texture(0, &tiles_texture);
591         pass->set_texture(1, &tiles_normalmap);
592         pass->set_texture(2, &sand_texture);
593         pass->set_texture(3, &sand_normalmap);
594
595         /* No shadow pass here; the ground only receives shadows, but doesn't cast
596         them. */
597
598         GL::VertexFormat vfmt = (GL::VERTEX3, GL::NORMAL3, GL::TANGENT3, GL::BINORMAL3, GL::TEXCOORD2, GL::ATTRIB1,7);
599         ground_data.mesh = new GL::Mesh(vfmt);
600
601         // Create a base grid
602         GL::GridBuilder(80, 80, 200, 200).tbn().build(*ground_data.mesh);
603
604         // And modify it with a heightmap
605         unsigned n_vertices = ground_data.mesh->get_n_vertices();
606         unsigned pos = vfmt.offset(GL::VERTEX3);
607         unsigned nor = vfmt.offset(GL::NORMAL3);
608         unsigned tan = vfmt.offset(GL::TANGENT3);
609         unsigned bin = vfmt.offset(GL::BINORMAL3);
610         unsigned tex = vfmt.offset(GL::TEXCOORD2);
611         unsigned gt = vfmt.offset(GL::make_indexed_component(GL::ATTRIB1, 7));
612         for(unsigned i=0; i<n_vertices; ++i)
613         {
614                 float *v = ground_data.mesh->modify_vertex(i);
615                 v[pos+2] = ground_height(v[pos], v[pos+1]);
616
617                 float dz_x = (ground_height(v[pos]+0.01, v[pos+1])-ground_height(v[pos]-0.01, v[pos+1]))/0.02;
618                 float dz_y = (ground_height(v[pos], v[pos+1]+0.01)-ground_height(v[pos], v[pos+1]-0.01))/0.02;
619                 float l = sqrt(dz_x*dz_x+dz_y*dz_y+1);
620                 v[nor] = -dz_x/l;
621                 v[nor+1] = -dz_y/l;
622                 v[nor+2] = 1/l;
623
624                 l = sqrt(dz_x*dz_x+1);
625                 v[tan] = 1/l;
626                 v[tan+2] = dz_x/l;
627
628                 l = sqrt(dz_y*dz_y+1);
629                 v[bin+1] = 1/l;
630                 v[bin+2] = dz_y/l;
631
632                 v[gt] = min(v[pos+2]*100, 1.0f);
633
634                 v[tex] *= 20;
635                 v[tex+1] *= 20;
636         }
637         ground_data.object = new GL::Object(ground_data.mesh, &ground_tech);
638
639         scene.add(*ground_data.object);
640 }
641
642 float DesertPillars::ground_height(float x, float y)
643 {
644         // Leave a flat area in the middle
645         float d = sqrt(x*x+y*y);
646         if(d<=6.0f)
647                 return 0;
648
649         // This results in concentric rings of low hills
650         int i = (d-6)/(M_PI*2);
651         float a = atan2(y, x);
652         a *= i*2+5;
653         a += M_PI*(i%3)/2;
654         float h = (i%2) ? 0.5 : 0.3;
655         return (d-6)*0.001f+(1-cos(d-6))*(1-cos(a))*h;
656 }
657
658 void DesertPillars::create_pillars()
659 {
660         // The pillars are a matt off-white
661         pillar_material.set_diffuse(GL::Color(0.9, 0.88, 0.8));
662         pillar_material.set_receive_shadows(true);
663
664         GL::RenderPass *pass = &pillar_tech.add_pass(0);
665         pass->set_material(&pillar_material);
666
667         pass = &pillar_tech.add_pass("shadow");
668         pass->set_shader_program(&shadow_shprog, 0);
669
670         pillar_data.reserve(7);
671         for(unsigned i=3; i<=20; ++i)
672         {
673                 unsigned height = 2;
674                 for(unsigned j=2; j<i; ++j)
675                         if(i%j==0)
676                                 ++height;
677
678                 if(pillar_data.size()<=height)
679                         pillar_data.resize(height+1);
680
681                 ObjectData &pd = pillar_data[height];
682                 if(!pd.object)
683                 {
684                         pd.mesh = new GL::Mesh((GL::VERTEX3, GL::NORMAL3));
685                         GL::MeshBuilder bld(*pd.mesh);
686
687                         // Produce a fluted cylinder
688                         unsigned n_flutes = 12;
689                         float r_bottom = cos(M_PI/n_flutes)*0.4;
690                         float flute_depth = (0.4-r_bottom)*2;
691                         float half_w = sin(M_PI/n_flutes)*0.4;
692                         for(unsigned j=0; j<n_flutes; ++j)
693                         {
694                                 float a = j*M_PI*2/n_flutes;
695                                 bld.begin(GL::TRIANGLE_STRIP);
696                                 for(int k=-3; k<=3; k+=2)
697                                 {
698                                         float t = k/3.0f;
699                                         float x = cos(a)*(r_bottom-(1-t*t)*flute_depth)-sin(a)*t*half_w;
700                                         float y = sin(a)*(r_bottom-(1-t*t)*flute_depth)+cos(a)*t*half_w;
701                                         float d = -t*2*flute_depth;
702                                         float l = sqrt(d*d+1);
703                                         bld.normal(cos(a)/l-sin(a)*d/l, sin(a)/l+cos(a)*d/l, 0);
704                                         bld.vertex(x, y, 0.6+height);
705                                         bld.vertex(x, y, 0.6);
706                                 }
707                                 bld.end();
708                         }
709
710                         // Create a square plinth and capitel
711                         bld.set_matrix(GL::Matrix::translation(0, 0, 0.3));
712                         GL::BoxBuilder(1.0, 1.0, 0.6).build(bld);
713                         bld.set_matrix(GL::Matrix::translation(0, 0, height+0.8));
714                         GL::BoxBuilder(1.0, 1.0, 0.4).build(bld);
715
716                         pd.object = new GL::Object(pd.mesh, &pillar_tech);
717                 }
718
719                 GL::AnimatedObject *pillar = new GL::AnimatedObject(*pd.object);
720                 GL::Matrix matrix;
721                 float a = (i-3)*2*M_PI/18;
722                 matrix.translate(cos(a)*5, sin(a)*5, 0);
723                 matrix.rotate(a, 0, 0, 1);
724                 pillar->set_matrix(matrix);
725
726                 pillars.push_back(pillar);
727                 scene.add(*pillar);
728         }
729 }
730
731 void DesertPillars::create_cube()
732 {
733         /* The cube is bluish-gray, with a hard specular reflection to produce a
734         sun-like spot */
735         cube_material.set_diffuse(GL::Color(0.5, 0.5, 0.55));
736         cube_material.set_specular(GL::Color(1.0));
737         cube_material.set_shininess(120);
738         cube_material.set_reflectivity(0.5);
739
740         GL::RenderPass *pass = &cube_tech.add_pass(0);
741         pass->set_material(&cube_material);
742         pass->set_shader_program(&cube_shprog, 0);
743
744         pass = &cube_tech.add_pass("shadow");
745         pass->set_shader_program(&cube_shadow_shprog, 0);
746
747         cube_data.mesh = new GL::Mesh((GL::VERTEX3, GL::NORMAL3, GL::ATTRIB3,7));
748         GL::MeshBuilder bld(*cube_data.mesh);
749         create_cube_face(bld, GL::Vector3(-1, -1, -1), GL::Vector3(2, 0, 0), GL::Vector3(0, 2, 0), 16);
750         bld.offset(cube_data.mesh->get_n_vertices());
751         create_cube_face(bld, GL::Vector3(-1, 1, -1), GL::Vector3(2, 0, 0), GL::Vector3(0, 0, 2), 16);
752         bld.offset(cube_data.mesh->get_n_vertices());
753         create_cube_face(bld, GL::Vector3(-1, 1, 1), GL::Vector3(2, 0, 0), GL::Vector3(0, -2, 0), 16);
754         bld.offset(cube_data.mesh->get_n_vertices());
755         create_cube_face(bld, GL::Vector3(1, -1, -1), GL::Vector3(-2, 0, 0), GL::Vector3(0, 0, 2), 16);
756         bld.offset(cube_data.mesh->get_n_vertices());
757         create_cube_face(bld, GL::Vector3(-1, -1, 1), GL::Vector3(0, 0, -2), GL::Vector3(0, 2, 0), 16);
758         bld.offset(cube_data.mesh->get_n_vertices());
759         create_cube_face(bld, GL::Vector3(1, -1, -1), GL::Vector3(0, 0, 2), GL::Vector3(0, 2, 0), 16);
760         cube_data.object = new GL::Object(cube_data.mesh, &cube_tech);
761
762         cube = new Cube(*cube_data.object);
763         env_cube = new GL::EnvironmentMap(512, *cube, env_pipeline);
764         scene.add(*env_cube);
765 }
766
767 void DesertPillars::create_cube_face(GL::MeshBuilder &bld, const GL::Vector3 &base, const GL::Vector3 &side1, const GL::Vector3 &side2, unsigned div)
768 {
769         /* The sides follow the cube map convention where the cross product points
770         inwards.  Since the normal has to point outwards, reverse the order. */
771         GL::Vector3 n;
772         n.x = side2.y*side1.z-side2.z*side1.y;
773         n.y = side2.z*side1.x-side2.x*side1.z;
774         n.z = side2.x*side1.y-side2.y*side1.x;
775         float l = sqrt(n.x*n.x+n.y*n.y+n.z*n.z);
776         bld.normal(n.x/l, n.y/l, n.z/l);
777
778         // Create vertices, with precomputed spherified coordinates
779         for(unsigned i=0; i<=div; ++i)
780                 for(unsigned j=0; j<=div; ++j)
781                 {
782                         GL::Vector3 v;
783                         v.x = base.x+side1.x*i/div+side2.x*j/div;
784                         v.y = base.y+side1.y*i/div+side2.y*j/div;
785                         v.z = base.z+side1.z*i/div+side2.z*j/div;
786
787                         l = sqrt(v.x*v.x+v.y*v.y+v.z*v.z);
788                         l /= 1.732;
789                         bld.attrib(7, v.x/l, v.y/l, v.z/l);
790
791                         bld.vertex(v);
792                 }
793
794         for(unsigned i=0; i<div; ++i)
795         {
796                 bld.begin(GL::TRIANGLE_STRIP);
797                 for(unsigned j=0; j<=div; ++j)
798                 {
799                         bld.element(i*(div+1)+j);
800                         bld.element((i+1)*(div+1)+j);
801                 }
802                 bld.end();
803         }
804 }
805
806 int DesertPillars::main()
807 {
808         window.show();
809         return Application::main();
810 }
811
812 void DesertPillars::tick()
813 {
814         Time::TimeStamp t = Time::now();
815         Time::TimeDelta dt;
816         if(last_tick)
817                 dt = t-last_tick;
818         last_tick = t;
819
820         if(!camera_stopped)
821         {
822                 camera_angle += (dt/Time::sec)*M_PI*2/30;
823                 if(camera_angle>M_PI*4)
824                         camera_angle -= M_PI*4;
825                 float h = 3+(1-cos(camera_angle*1.5))*3;
826                 float r = sqrt(225-h*h);
827                 camera.set_position(GL::Vector3(cos(camera_angle)*r, sin(camera_angle)*r, 1.5+h));
828                 camera.look_at(GL::Vector3(0, 0, 2));
829         }
830
831         if(!cube_stopped)
832         {
833                 cube_angle += (dt/Time::sec)*M_PI*2/20;
834                 GL::Matrix cube_matrix;
835                 cube_matrix.translate(0, 0, 2.5);
836                 cube_matrix.rotate(cube_angle, 0, 0, 1);
837                 cube_matrix.rotate(cube_angle*0.5, 1, 0, 0);
838                 cube->set_matrix(cube_matrix);
839         }
840
841         if(!cube_frozen)
842         {
843                 cube_phase += (dt/Time::sec)/5;
844                 if(cube_phase>1)
845                 {
846                         cube_phase -= 1;
847                         ++cube_shape;
848                         if(cube_shape>=4)
849                                 cube_shape -= 4;
850                 }
851                 if(cube_phase<0.2)
852                 {
853                         float x = cube_phase*5;
854                         x = (3-2*x)*x*x;
855                         cube->set_spherify((1-x)*cube_shapes[(cube_shape+3)%4]+x*cube_shapes[cube_shape]);
856                 }
857                 else
858                         cube->set_spherify(cube_shapes[cube_shape]);
859         }
860
861         display.tick();
862         view.render();
863 }
864
865 void DesertPillars::key_press(unsigned key)
866 {
867         if(key==Input::KEY_ESC)
868                 exit(0);
869         else if(key==Input::KEY_SPACE)
870                 camera_stopped = !camera_stopped;
871         else if(key==Input::KEY_F)
872                 cube_frozen = !cube_frozen;
873         else if(key==Input::KEY_S)
874                 cube_stopped = !cube_stopped;
875 }
876
877
878 DesertPillars::ObjectData::ObjectData():
879         mesh(0),
880         object(0)
881 { }
882
883 DesertPillars::ObjectData::~ObjectData()
884 {
885         delete object;
886         delete mesh;
887 }
888
889
890 DesertPillars::Cube::Cube(const GL::Object &obj):
891         GL::AnimatedObject(obj)
892 { }
893
894 void DesertPillars::Cube::set_spherify(float s)
895 {
896         shdata.uniform("spherify", s);
897 }
898
899 void DesertPillars::Cube::setup_render(GL::Renderer &renderer, const GL::Tag &tag) const
900 {
901         AnimatedObject::setup_render(renderer, tag);
902         renderer.add_shader_data(shdata);
903 }