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