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