From: Mikko Rasa Date: Wed, 12 Sep 2012 18:58:34 +0000 (+0300) Subject: A complex demo program to show off features of the engine X-Git-Url: http://git.tdb.fi/?p=libs%2Fgl.git;a=commitdiff_plain;h=7f08a54a61db86c60f06f4ddef529aa37e0d3d9c A complex demo program to show off features of the engine --- diff --git a/.gitignore b/.gitignore index c7a1afbd..62e48cab 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .config temp /cubemap +/desertpillars /libmspgl.a /libmspgl.so /mesh2c diff --git a/Build b/Build index 4ea0aa79..8c537eb7 100644 --- a/Build +++ b/Build @@ -63,6 +63,15 @@ package "mspgl" }; }; + program "desertpillars" + { + source "demos/desertpillars.cpp"; + build_info + { + library "mspgl"; + }; + }; + source_tarball { source "License.txt"; diff --git a/demos/desertpillars.cpp b/demos/desertpillars.cpp new file mode 100644 index 00000000..61db0bad --- /dev/null +++ b/demos/desertpillars.cpp @@ -0,0 +1,951 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace Msp; + +/** +This application demonstrates a variety of features of the mspgl library, +including: +- Creating meshes from multiple parts +- Creating a mesh and then modifying it +- Shadow mapping +- Environment mapped reflections +- Skybox +- Nested scenes and pipelines +- Complex multitexturing +- Shader-based deformations +- Creating a normalmapped texture through rendering + +To run the program in fullscreen mode, specify --fullscreen on the command +line. + +During execution the following keys are available: +esc exit +space stop camera movement +s stop cube rotation +f freeze cube shape +*/ +class DesertPillars: public RegisteredApplication +{ +private: + struct Options + { + Graphics::WindowOptions window_opts; + + Options(const Graphics::Display &, int, char **); + }; + + struct ObjectData + { + GL::Mesh *mesh; + GL::Object *object; + + ObjectData(); + ~ObjectData(); + }; + + class Cube: public GL::AnimatedObject + { + private: + GL::ProgramData shdata; + + public: + Cube(const GL::Object &); + + void set_spherify(float); + + virtual void setup_render(GL::Renderer &, const GL::Tag &) const; + }; + + Msp::Graphics::Display display; + Options options; + Msp::Graphics::Window window; + Msp::Graphics::GLContext gl_context; + Msp::Input::Keyboard keyboard; + + GL::Program skybox_shprog; + GL::Technique skybox_tech; + GL::TextureCube skybox_tex; + ObjectData skybox_data; + + GL::Program shadow_shprog; + + GL::Program ground_shprog; + GL::ProgramData ground_shdata; + GL::Texture2D tiles_texture; + GL::Texture2D tiles_normalmap; + GL::Texture2D sand_texture; + GL::Texture2D sand_normalmap; + GL::Technique ground_tech; + ObjectData ground_data; + + GL::Program pillar_shprog; + GL::Material pillar_material; + GL::Technique pillar_tech; + std::vector pillar_data; + std::vector pillars; + + GL::VertexShader cube_transform; + GL::Program cube_shprog; + GL::Program cube_shadow_shprog; + GL::Material cube_material; + GL::Technique cube_tech; + ObjectData cube_data; + Cube *cube; + GL::EnvironmentMap *env_cube; + + GL::Pipeline pipeline; + GL::Camera camera; + GL::SimpleScene sky_scene; + GL::InstanceScene scene; + GL::Lighting lighting; + GL::Light light; + GL::ShadowMap shadow_scene; + GL::Bloom bloom; + + GL::Pipeline env_pipeline; + + Time::TimeStamp last_tick; + float camera_angle; + bool camera_stopped; + float cube_angle; + bool cube_stopped; + unsigned cube_shape; + float cube_phase; + bool cube_frozen; + + static const char texture_vertex_src[]; + static const char texture_fragment_src[]; + static const char skybox_vertex_src[]; + static const char skybox_fragment_src[]; + static const char ground_transform_src[]; + static const char ground_colorify_src[]; + static const char cube_transform_src[]; + static const float cube_shapes[]; + +public: + DesertPillars(int, char **); + ~DesertPillars(); + +private: + void create_pipeline(); + void create_skybox(); + static void create_skybox_face(GL::TextureCube &, GL::TextureCubeFace); + void create_tiles_texture(); + void create_sand_texture(); + static void gaussian_blur(unsigned char *, unsigned, unsigned); + static void create_normalmap(const unsigned char *, unsigned char *, unsigned, unsigned, float); + void create_ground(); + static float ground_height(float, float); + void create_pillars(); + void create_cube(); + static void create_cube_face(GL::MeshBuilder &, const GL::Vector3 &, const GL::Vector3 &, const GL::Vector3 &, unsigned); + +public: + virtual int main(); +private: + virtual void tick(); + + void key_press(unsigned); +}; + +const char DesertPillars::texture_vertex_src[] = + "varying vec3 v_normal;\n" + "varying vec3 v_color;\n" + "void main()\n" + "{\n" + " gl_Position = vec4(gl_Vertex.xy*2.0-1.0, -gl_Vertex.z*2.0, 1.0);\n" + " v_normal = gl_Normal;\n" + " v_color = gl_Color.rgb;\n" + "}\n"; + +const char DesertPillars::texture_fragment_src[] = + "varying vec3 v_normal;\n" + "varying vec3 v_color;\n" + "void main()\n" + "{\n" + " gl_FragData[0] = vec4(v_color, 1.0);\n" + " gl_FragData[1] = vec4(v_normal*0.5+0.5, 1.0);\n" + "}\n"; + +const char DesertPillars::skybox_vertex_src[] = + "varying vec3 v_texcoord;\n" + "void main()\n" + "{\n" + " gl_Position = gl_ProjectionMatrix*vec4(mat3(gl_ModelViewMatrix)*gl_Vertex.xyz, 1.0);\n" + " v_texcoord = gl_Vertex;\n" + "}"; + +const char DesertPillars::skybox_fragment_src[] = + "uniform samplerCube sky;\n" + "varying vec3 v_texcoord;\n" + "void main()\n" + "{\n" + " gl_FragColor = textureCube(sky, v_texcoord);\n" + "}"; + +// This exists only to transfer the ground type to fragment shader +const char DesertPillars::ground_transform_src[] = + "attribute float ground_type;\n" + "varying float v_ground_type;\n" + "vec4 transform_vertex(vec4 vertex)\n" + "{\n" + " v_ground_type = ground_type;\n" + " return gl_ModelViewMatrix*vertex;\n" + "}\n" + "vec3 transform_normal(vec3 normal)\n" + "{\n" + " return gl_NormalMatrix*normal;\n" + "}\n"; + +const char DesertPillars::ground_colorify_src[] = + "uniform sampler2D texture1;\n" + "uniform sampler2D normalmap1;\n" + "uniform sampler2D texture2;\n" + "uniform sampler2D normalmap2;\n" + "varying float v_ground_type;\n" + "vec4 sample_texture(vec2 coord)\n" + "{\n" + " return mix(texture2D(texture1, coord*3), texture2D(texture2, coord), v_ground_type);\n" + "}\n" + "vec3 sample_normalmap(vec2 coord)\n" + "{\n" + " return mix(texture2D(normalmap1, coord*3), texture2D(normalmap2, coord), v_ground_type);\n" + "}\n"; + +const char DesertPillars::cube_transform_src[] = + "uniform float spherify;\n" + "attribute vec3 sphere_coord;\n" + "vec4 transform_vertex(vec4 vertex)\n" + "{\n" + " return gl_ModelViewMatrix*vec4(mix(vertex, sphere_coord, spherify), 1.0);\n" + "}\n" + "vec3 transform_normal(vec3 normal)\n" + "{\n" + " return gl_NormalMatrix*normalize(mix(normal, normalize(sphere_coord), spherify));\n" + "}\n"; + +const float DesertPillars::cube_shapes[] = { -0.4, 0.5, 1.0, 0.3 }; + + +DesertPillars::Options::Options(const Graphics::Display &display, int argc, char **argv) +{ + GetOpt getopt; + getopt.add_option('f', "fullscreen", window_opts.fullscreen, GetOpt::NO_ARG).set_help("Run in fullscreen mode"); + getopt(argc, argv); + + if(window_opts.fullscreen) + { + const Graphics::VideoMode &mode = display.get_desktop_mode(); + window_opts.width = mode.width; + window_opts.height = mode.height; + } + else + { + window_opts.width = 800; + window_opts.height = 600; + } +} + + +DesertPillars::DesertPillars(int argc, char **argv): + options(display, argc, argv), + window(display, options.window_opts), + gl_context(window), + keyboard(window), + shadow_shprog(GL::ProgramBuilder::StandardFeatures()), + pipeline(window.get_width(), window.get_height()), + shadow_scene(2048, scene, light), + bloom(window.get_width(), window.get_height()), + env_pipeline(512, 512), + camera_angle(0), + camera_stopped(false), + cube_angle(0), + cube_stopped(false), + cube_shape(0), + cube_phase(0), + cube_frozen(false) +{ + window.set_title("Desert Pillars"); + window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &DesertPillars::exit), 0)); + if(options.window_opts.fullscreen) + window.show_cursor(false); + keyboard.signal_button_press.connect(sigc::mem_fun(this, &DesertPillars::key_press)); + + create_pipeline(); + create_skybox(); + create_ground(); + create_pillars(); + create_cube(); +} + +DesertPillars::~DesertPillars() +{ + delete env_cube; + delete cube; + for(vector::iterator i=pillars.begin(); i!=pillars.end(); ++i) + delete *i; +} + +void DesertPillars::create_pipeline() +{ + pipeline.set_multisample(8); + + camera.set_aspect(float(window.get_width())/window.get_height()); + camera.set_up_direction(GL::Vector3(0, 0, 1)); + camera.set_depth_clip(1, 50); + pipeline.set_camera(&camera); + + /* The shadow map is focused on the part of the scene that contains the + pillars and the cube. Making the ground cast shadows as well would result + either in a very low spatial resolution of the shadow map, or ugly artifacts + as the ground crosses the shadow map boundary. */ + shadow_scene.set_target(GL::Vector3(0, 0, 0), 10); + shadow_scene.set_texture_unit(5); + sky_scene.add(shadow_scene); + pipeline.add_renderable(sky_scene); + + // Put the sun pretty high in the sky + light.set_position(GL::Vector4(0.5, -2, 3, 0)); + lighting.attach(0, light); + lighting.set_ambient(GL::Color(0.5)); + + // The skybox is rendered first + pipeline.add_pass("sky"); + + GL::Pipeline::Pass *pass = &pipeline.add_pass(0); + pass->set_lighting(&lighting); + pass->set_depth_test(&GL::DepthTest::lequal()); + + /* A bloom filter enhances the realism of bright surfaces, even if there + isn't anything really glowy in the scene. */ + bloom.set_strength(0.3); + pipeline.add_postprocessor(bloom); + + /* Initialize a second pipeline to render the environment map. It has the + same renderables and passes, but no postprocessors or camera. */ + env_pipeline.add_renderable(sky_scene); + env_pipeline.add_pass("sky"); + pass = &env_pipeline.add_pass(0); + pass->set_lighting(&lighting); + pass->set_depth_test(&GL::DepthTest::lequal()); +} + +void DesertPillars::create_skybox() +{ + skybox_tex.storage(GL::RGB, 128); + skybox_tex.set_min_filter(GL::LINEAR); + skybox_tex.set_wrap(GL::CLAMP_TO_EDGE); + for(unsigned i=0; i<6; ++i) + create_skybox_face(skybox_tex, skybox_tex.enumerate_faces(i)); + + skybox_shprog.attach_shader_owned(new GL::VertexShader(skybox_vertex_src)); + skybox_shprog.attach_shader_owned(new GL::FragmentShader(skybox_fragment_src)); + skybox_shprog.link(); + + GL::RenderPass &pass = skybox_tech.add_pass("sky"); + pass.set_shader_program(&skybox_shprog, 0); + pass.set_texture(0, &skybox_tex); + + // The shader will use the vertex coordinates to initialize texture coordinates as well + skybox_data.mesh = new GL::Mesh(GL::VERTEX3); + GL::BoxBuilder(10, 10, 10).build(*skybox_data.mesh); + skybox_data.object = new GL::Object(skybox_data.mesh, &skybox_tech); + + sky_scene.add(*skybox_data.object); +} + +void DesertPillars::create_skybox_face(GL::TextureCube &texture, GL::TextureCubeFace face) +{ + unsigned char *pixels = new unsigned char[128*128*3]; + for(int y=0; y<128; ++y) + for(int x=0; x<128; ++x) + { + unsigned i = (x+y*128)*3; + GL::Vector3 v = texture.get_texel_direction(face, x, y); + if(v.z>0) + { + float l = sqrt(v.x*v.x+v.y*v.y+v.z*v.z); + // Render a sky-like gradient, with deeper blue at the zenith + pixels[i] = 96-48*v.z/l; + pixels[i+1] = 168-84*v.z/l; + pixels[i+2] = 255; + } + else + { + // Fill with a desert-y color below horizon + pixels[i] = 240; + pixels[i+1] = 224; + pixels[i+2] = 160; + } + } + texture.image(face, 0, GL::RGB, GL::UNSIGNED_BYTE, pixels); + delete[] pixels; +} + +void DesertPillars::create_tiles_texture() +{ + unsigned width = 256; + unsigned height = 256; + tiles_texture.storage(GL::RGB, width, height); + tiles_texture.set_min_filter(GL::LINEAR); + tiles_normalmap.storage(GL::RGB, width, height); + tiles_normalmap.set_min_filter(GL::LINEAR); + + GL::Mesh tiles((GL::VERTEX3, GL::NORMAL3, GL::COLOR4_UBYTE)); + + // Prepare some lookup tables for rendering the tiles + float split = 1.0f/3; + float spacing = split*0.02f; + float bevel = split*0.1f; + float coords[] = { 0.0f, spacing, spacing+bevel, split-bevel, split, split+spacing, split+spacing+bevel, 1.0f-bevel, 1.0f }; + unsigned order[] = { 4, 1, 3, 2, 1, 1, 2, 2, + 1, 4, 2, 3, 4, 4, 3, 3, + 1, 1, 2, 2, 1, 4, 2, 3, + 4, 4, 3, 3, 4, 1, 3, 2, + 2, 3, 2, 2, 3, 3, 3, 2 }; + + GL::MeshBuilder bld(tiles); + + // Create a dark background + bld.color(0.2f, 0.2f, 0.2f); + bld.normal(0.0f, 0.0f, 1.0f); + bld.begin(GL::TRIANGLE_STRIP); + bld.vertex(0.0f, 1.0f); + bld.vertex(0.0f, 0.0f); + bld.vertex(1.0f, 1.0f); + bld.vertex(1.0f, 0.0f); + bld.end(); + + // Create the four tiles + bld.color(0.95f, 0.8f, 0.65f); + for(unsigned i=0; i<2; ++i) + for(unsigned j=0; j<2; ++j) + { + for(unsigned k=0; k<4; ++k) + { + bld.begin(GL::TRIANGLE_STRIP); + float facing = (k%2)*2-1.0f; + if(k<2) + bld.normal(0.0f, 0.7071f*facing, 0.7071f); + else + bld.normal(0.7071f*facing, 0.0f, 0.7071f); + for(unsigned l=0; l<4; ++l) + bld.vertex(coords[i*4+order[k*8+l*2]], coords[j*4+order[k*8+l*2+1]], (l%2 ? bevel : 0.0f)); + bld.end(); + } + + bld.begin(GL::TRIANGLE_STRIP); + bld.normal(0.0f, 0.0f, 1.0f); + for(unsigned l=0; l<4; ++l) + bld.vertex(coords[i*4+order[32+l*2]], coords[j*4+order[32+l*2+1]], bevel); + bld.end(); + } + + GL::Program shprog(texture_vertex_src, texture_fragment_src); + + // Use an FBO to turn the geometry into a normalmapped texture + GL::Framebuffer fbo; + fbo.attach(GL::COLOR_ATTACHMENT0, tiles_texture); + fbo.attach(GL::COLOR_ATTACHMENT1, tiles_normalmap); + GL::Bind bind_fbo(fbo); + GL::Renderer renderer(0); + renderer.set_shader_program(&shprog, 0); + tiles.draw(renderer); +} + +void DesertPillars::create_sand_texture() +{ + unsigned width = 512; + unsigned height = 512; + + sand_texture.storage(GL::RGB, width/16, height/16); + sand_texture.set_min_filter(GL::LINEAR_MIPMAP_LINEAR); + sand_texture.set_max_anisotropy(4); + sand_texture.set_generate_mipmap(true); + sand_normalmap.storage(GL::RGB, width, height); + sand_normalmap.set_min_filter(GL::LINEAR_MIPMAP_LINEAR); + sand_normalmap.set_max_anisotropy(4); + sand_normalmap.set_generate_mipmap(true); + + unsigned char *pixels = new unsigned char[width*height*3]; + unsigned char *bump = new unsigned char[width*height]; + for(unsigned y=0; yset_shader_program(&ground_shprog, &ground_shdata); + pass->set_texture(0, &tiles_texture); + pass->set_texture(1, &tiles_normalmap); + pass->set_texture(2, &sand_texture); + pass->set_texture(3, &sand_normalmap); + + /* No shadow pass here; the ground only receives shadows, but doesn't cast + them. */ + + GL::VertexFormat vfmt = (GL::VERTEX3, GL::NORMAL3, GL::TANGENT3, GL::BINORMAL3, GL::TEXCOORD2, GL::ATTRIB1,7); + ground_data.mesh = new GL::Mesh(vfmt); + + // Create a base grid + GL::GridBuilder(80, 80, 200, 200).tbn().build(*ground_data.mesh); + + // And modify it with a heightmap + unsigned n_vertices = ground_data.mesh->get_n_vertices(); + unsigned pos = vfmt.offset(GL::VERTEX3); + unsigned nor = vfmt.offset(GL::NORMAL3); + unsigned tan = vfmt.offset(GL::TANGENT3); + unsigned bin = vfmt.offset(GL::BINORMAL3); + unsigned tex = vfmt.offset(GL::TEXCOORD2); + unsigned gt = vfmt.offset(GL::make_indexed_component(GL::ATTRIB1, 7)); + for(unsigned i=0; imodify_vertex(i); + v[pos+2] = ground_height(v[pos], v[pos+1]); + + float dz_x = (ground_height(v[pos]+0.01, v[pos+1])-ground_height(v[pos]-0.01, v[pos+1]))/0.02; + float dz_y = (ground_height(v[pos], v[pos+1]+0.01)-ground_height(v[pos], v[pos+1]-0.01))/0.02; + float l = sqrt(dz_x*dz_x+dz_y*dz_y+1); + v[nor] = -dz_x/l; + v[nor+1] = -dz_y/l; + v[nor+2] = 1/l; + + l = sqrt(dz_x*dz_x+1); + v[tan] = 1/l; + v[tan+2] = dz_x/l; + + l = sqrt(dz_y*dz_y+1); + v[bin+1] = 1/l; + v[bin+2] = dz_y/l; + + v[gt] = min(v[pos+2]*100, 1.0f); + + v[tex] *= 20; + v[tex+1] *= 20; + } + ground_data.object = new GL::Object(ground_data.mesh, &ground_tech); + + scene.add(*ground_data.object); +} + +float DesertPillars::ground_height(float x, float y) +{ + // Leave a flat area in the middle + float d = sqrt(x*x+y*y); + if(d<=6.0f) + return 0; + + // This results in concentric rings of low hills + int i = (d-6)/(M_PI*2); + float a = atan2(y, x); + a *= i*2+5; + a += M_PI*(i%3)/2; + float h = (i%2) ? 0.5 : 0.3; + return (d-6)*0.001f+(1-cos(d-6))*(1-cos(a))*h; +} + +void DesertPillars::create_pillars() +{ + // The pillars are a matt off-white + pillar_material.set_diffuse(GL::Color(0.9, 0.88, 0.8)); + pillar_material.set_ambient(GL::Color(0.9, 0.88, 0.8)); + + GL::ProgramBuilder::StandardFeatures features; + features.lighting = true; + features.material = true; + features.shadow = true; + GL::ProgramBuilder(features).add_shaders(pillar_shprog); + pillar_shprog.link(); + + GL::RenderPass *pass = &pillar_tech.add_pass(0); + pass->set_material(&pillar_material); + pass->set_shader_program(&pillar_shprog, 0); + + pass = &pillar_tech.add_pass("shadow"); + pass->set_shader_program(&shadow_shprog, 0); + + pillar_data.reserve(7); + for(unsigned i=3; i<=20; ++i) + { + unsigned height = 2; + for(unsigned j=2; jset_matrix(matrix); + + pillars.push_back(pillar); + scene.add(*pillar); + } +} + +void DesertPillars::create_cube() +{ + /* The cube is bluish-gray, with a hard specular reflection to produce a + sun-like spot */ + cube_material.set_diffuse(GL::Color(0.5, 0.5, 0.55)); + cube_material.set_ambient(GL::Color(0.5, 0.5, 0.55)); + cube_material.set_specular(GL::Color(1.0)); + cube_material.set_shininess(150); + + cube_transform.source(cube_transform_src); + cube_transform.compile(); + + // First create a simplified shader for rendering the shadow map + GL::ProgramBuilder::StandardFeatures features; + features.transform = true; + GL::ProgramBuilder(features).add_shaders(cube_shadow_shprog); + cube_shadow_shprog.attach_shader(cube_transform); + cube_shadow_shprog.bind_attribute(7, "sphere_coord"); + cube_shadow_shprog.link(); + + // Then add the rest of the features for normal rendering + features.lighting = true; + features.specular = true; + features.material = true; + features.shadow = true; + features.reflection = true; + GL::ProgramBuilder(features).add_shaders(cube_shprog); + cube_shprog.attach_shader(cube_transform); + cube_shprog.bind_attribute(7, "sphere_coord"); + cube_shprog.link(); + + GL::RenderPass *pass = &cube_tech.add_pass(0); + pass->set_material(&cube_material); + pass->set_shader_program(&cube_shprog, 0); + + pass = &cube_tech.add_pass("shadow"); + pass->set_shader_program(&cube_shadow_shprog, 0); + + cube_data.mesh = new GL::Mesh((GL::VERTEX3, GL::NORMAL3, GL::ATTRIB3,7)); + GL::MeshBuilder bld(*cube_data.mesh); + create_cube_face(bld, GL::Vector3(-1, -1, -1), GL::Vector3(2, 0, 0), GL::Vector3(0, 2, 0), 16); + bld.offset(cube_data.mesh->get_n_vertices()); + create_cube_face(bld, GL::Vector3(-1, 1, -1), GL::Vector3(2, 0, 0), GL::Vector3(0, 0, 2), 16); + bld.offset(cube_data.mesh->get_n_vertices()); + create_cube_face(bld, GL::Vector3(-1, 1, 1), GL::Vector3(2, 0, 0), GL::Vector3(0, -2, 0), 16); + bld.offset(cube_data.mesh->get_n_vertices()); + create_cube_face(bld, GL::Vector3(1, -1, -1), GL::Vector3(-2, 0, 0), GL::Vector3(0, 0, 2), 16); + bld.offset(cube_data.mesh->get_n_vertices()); + create_cube_face(bld, GL::Vector3(-1, -1, 1), GL::Vector3(0, 0, -2), GL::Vector3(0, 2, 0), 16); + bld.offset(cube_data.mesh->get_n_vertices()); + create_cube_face(bld, GL::Vector3(1, -1, -1), GL::Vector3(0, 0, 2), GL::Vector3(0, 2, 0), 16); + cube_data.object = new GL::Object(cube_data.mesh, &cube_tech); + + cube = new Cube(*cube_data.object); + env_cube = new GL::EnvironmentMap(512, *cube, env_pipeline); + scene.add(*env_cube); +} + +void DesertPillars::create_cube_face(GL::MeshBuilder &bld, const GL::Vector3 &base, const GL::Vector3 &side1, const GL::Vector3 &side2, unsigned div) +{ + /* The sides follow the cube map convention where the cross product points + inwards. Since the normal has to point outwards, reverse the order. */ + GL::Vector3 n; + n.x = side2.y*side1.z-side2.z*side1.y; + n.y = side2.z*side1.x-side2.x*side1.z; + n.z = side2.x*side1.y-side2.y*side1.x; + float l = sqrt(n.x*n.x+n.y*n.y+n.z*n.z); + bld.normal(n.x/l, n.y/l, n.z/l); + + // Create vertices, with precomputed spherified coordinates + for(unsigned i=0; i<=div; ++i) + for(unsigned j=0; j<=div; ++j) + { + GL::Vector3 v; + v.x = base.x+side1.x*i/div+side2.x*j/div; + v.y = base.y+side1.y*i/div+side2.y*j/div; + v.z = base.z+side1.z*i/div+side2.z*j/div; + + l = sqrt(v.x*v.x+v.y*v.y+v.z*v.z); + l /= 1.732; + bld.attrib(7, v.x/l, v.y/l, v.z/l); + + bld.vertex(v); + } + + for(unsigned i=0; iM_PI*4) + camera_angle -= M_PI*4; + float h = 3+(1-cos(camera_angle*1.5))*3; + float r = sqrt(225-h*h); + camera.set_position(GL::Vector3(cos(camera_angle)*r, sin(camera_angle)*r, 1.5+h)); + camera.look_at(GL::Vector3(0, 0, 2)); + } + + if(!cube_stopped) + { + cube_angle += (dt/Time::sec)*M_PI*2/20; + GL::Matrix cube_matrix; + cube_matrix.translate(0, 0, 2.5); + cube_matrix.rotate(cube_angle, 0, 0, 1); + cube_matrix.rotate(cube_angle*0.5, 1, 0, 0); + cube->set_matrix(cube_matrix); + } + + if(!cube_frozen) + { + cube_phase += (dt/Time::sec)/5; + if(cube_phase>1) + { + cube_phase -= 1; + ++cube_shape; + if(cube_shape>=4) + cube_shape -= 4; + } + if(cube_phase<0.2) + { + float x = cube_phase*5; + x = (3-2*x)*x*x; + cube->set_spherify((1-x)*cube_shapes[(cube_shape+3)%4]+x*cube_shapes[cube_shape]); + } + else + cube->set_spherify(cube_shapes[cube_shape]); + } + + display.tick(); + GL::Framebuffer::system().clear(GL::COLOR_BUFFER_BIT|GL::DEPTH_BUFFER_BIT); + pipeline.render(); + gl_context.swap_buffers(); +} + +void DesertPillars::key_press(unsigned key) +{ + if(key==Input::KEY_ESC) + exit(0); + else if(key==Input::KEY_SPACE) + camera_stopped = !camera_stopped; + else if(key==Input::KEY_F) + cube_frozen = !cube_frozen; + else if(key==Input::KEY_S) + cube_stopped = !cube_stopped; +} + + +DesertPillars::ObjectData::ObjectData(): + mesh(0), + object(0) +{ } + +DesertPillars::ObjectData::~ObjectData() +{ + delete object; + delete mesh; +} + + +DesertPillars::Cube::Cube(const GL::Object &obj): + GL::AnimatedObject(obj) +{ + shdata.uniform("reflectivity", 0.5f); +} + +void DesertPillars::Cube::set_spherify(float s) +{ + shdata.uniform("spherify", s); +} + +void DesertPillars::Cube::setup_render(GL::Renderer &renderer, const GL::Tag &tag) const +{ + AnimatedObject::setup_render(renderer, tag); + renderer.add_shader_data(shdata); +}