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