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