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