]> git.tdb.fi Git - libs/gl.git/commitdiff
Rearrange soucre files into subdirectories
authorMikko Rasa <tdb@tdb.fi>
Sun, 14 Feb 2021 00:52:21 +0000 (02:52 +0200)
committerMikko Rasa <tdb@tdb.fi>
Sun, 14 Feb 2021 00:52:21 +0000 (02:52 +0200)
403 files changed:
Build
source/ambientocclusion.cpp [deleted file]
source/ambientocclusion.h [deleted file]
source/animatedobject.cpp [deleted file]
source/animatedobject.h [deleted file]
source/animation.cpp [deleted file]
source/animation.h [deleted file]
source/animation/animatedobject.cpp [new file with mode: 0644]
source/animation/animatedobject.h [new file with mode: 0644]
source/animation/animation.cpp [new file with mode: 0644]
source/animation/animation.h [new file with mode: 0644]
source/animation/animationeventobserver.h [new file with mode: 0644]
source/animation/animationplayer.cpp [new file with mode: 0644]
source/animation/animationplayer.h [new file with mode: 0644]
source/animation/armature.cpp [new file with mode: 0644]
source/animation/armature.h [new file with mode: 0644]
source/animation/keyframe.cpp [new file with mode: 0644]
source/animation/keyframe.h [new file with mode: 0644]
source/animation/pose.cpp [new file with mode: 0644]
source/animation/pose.h [new file with mode: 0644]
source/animation/transform.cpp [new file with mode: 0644]
source/animation/transform.h [new file with mode: 0644]
source/animationeventobserver.h [deleted file]
source/animationplayer.cpp [deleted file]
source/animationplayer.h [deleted file]
source/armature.cpp [deleted file]
source/armature.h [deleted file]
source/basicmaterial.cpp [deleted file]
source/basicmaterial.h [deleted file]
source/batch.cpp [deleted file]
source/batch.h [deleted file]
source/bindable.h [deleted file]
source/blend.cpp [deleted file]
source/blend.h [deleted file]
source/bloom.cpp [deleted file]
source/bloom.h [deleted file]
source/box.cpp [deleted file]
source/box.h [deleted file]
source/buffer.cpp [deleted file]
source/buffer.h [deleted file]
source/bufferable.cpp [deleted file]
source/bufferable.h [deleted file]
source/builders/box.cpp [new file with mode: 0644]
source/builders/box.h [new file with mode: 0644]
source/builders/capsule.cpp [new file with mode: 0644]
source/builders/capsule.h [new file with mode: 0644]
source/builders/cylinder.cpp [new file with mode: 0644]
source/builders/cylinder.h [new file with mode: 0644]
source/builders/font.cpp [new file with mode: 0644]
source/builders/font.h [new file with mode: 0644]
source/builders/geometrybuilder.cpp [new file with mode: 0644]
source/builders/geometrybuilder.h [new file with mode: 0644]
source/builders/grid.cpp [new file with mode: 0644]
source/builders/grid.h [new file with mode: 0644]
source/builders/meshbuilder.cpp [new file with mode: 0644]
source/builders/meshbuilder.h [new file with mode: 0644]
source/builders/pipelinebuilder.cpp [new file with mode: 0644]
source/builders/pipelinebuilder.h [new file with mode: 0644]
source/builders/pipelinetemplate.cpp [new file with mode: 0644]
source/builders/pipelinetemplate.h [new file with mode: 0644]
source/builders/primitivebuilder.cpp [new file with mode: 0644]
source/builders/primitivebuilder.h [new file with mode: 0644]
source/builders/sphere.cpp [new file with mode: 0644]
source/builders/sphere.h [new file with mode: 0644]
source/builders/vertexarraybuilder.cpp [new file with mode: 0644]
source/builders/vertexarraybuilder.h [new file with mode: 0644]
source/builders/vertexbuilder.h [new file with mode: 0644]
source/camera.cpp [deleted file]
source/camera.h [deleted file]
source/capsule.cpp [deleted file]
source/capsule.h [deleted file]
source/clipping.cpp [deleted file]
source/clipping.h [deleted file]
source/clipplane.cpp [deleted file]
source/clipplane.h [deleted file]
source/color.h [deleted file]
source/colorcurve.cpp [deleted file]
source/colorcurve.h [deleted file]
source/core/batch.cpp [new file with mode: 0644]
source/core/batch.h [new file with mode: 0644]
source/core/bindable.h [new file with mode: 0644]
source/core/blend.cpp [new file with mode: 0644]
source/core/blend.h [new file with mode: 0644]
source/core/buffer.cpp [new file with mode: 0644]
source/core/buffer.h [new file with mode: 0644]
source/core/bufferable.cpp [new file with mode: 0644]
source/core/bufferable.h [new file with mode: 0644]
source/core/clipping.cpp [new file with mode: 0644]
source/core/clipping.h [new file with mode: 0644]
source/core/clipplane.cpp [new file with mode: 0644]
source/core/clipplane.h [new file with mode: 0644]
source/core/color.h [new file with mode: 0644]
source/core/datatype.cpp [new file with mode: 0644]
source/core/datatype.h [new file with mode: 0644]
source/core/error.h [new file with mode: 0644]
source/core/extension.cpp [new file with mode: 0644]
source/core/extension.h [new file with mode: 0644]
source/core/framebuffer.cpp [new file with mode: 0644]
source/core/framebuffer.h [new file with mode: 0644]
source/core/gl.h [new file with mode: 0644]
source/core/matrix.cpp [new file with mode: 0644]
source/core/matrix.h [new file with mode: 0644]
source/core/mesh.cpp [new file with mode: 0644]
source/core/mesh.h [new file with mode: 0644]
source/core/misc.cpp [new file with mode: 0644]
source/core/misc.h [new file with mode: 0644]
source/core/pixelformat.cpp [new file with mode: 0644]
source/core/pixelformat.h [new file with mode: 0644]
source/core/pixelstore.cpp [new file with mode: 0644]
source/core/pixelstore.h [new file with mode: 0644]
source/core/predicate.cpp [new file with mode: 0644]
source/core/predicate.h [new file with mode: 0644]
source/core/primitivetype.cpp [new file with mode: 0644]
source/core/primitivetype.h [new file with mode: 0644]
source/core/program.cpp [new file with mode: 0644]
source/core/program.h [new file with mode: 0644]
source/core/renderbuffer.cpp [new file with mode: 0644]
source/core/renderbuffer.h [new file with mode: 0644]
source/core/sampler.cpp [new file with mode: 0644]
source/core/sampler.h [new file with mode: 0644]
source/core/shader.cpp [new file with mode: 0644]
source/core/shader.h [new file with mode: 0644]
source/core/stencil.cpp [new file with mode: 0644]
source/core/stencil.h [new file with mode: 0644]
source/core/tests.cpp [new file with mode: 0644]
source/core/tests.h [new file with mode: 0644]
source/core/texture.cpp [new file with mode: 0644]
source/core/texture.h [new file with mode: 0644]
source/core/texture1d.cpp [new file with mode: 0644]
source/core/texture1d.h [new file with mode: 0644]
source/core/texture2d.cpp [new file with mode: 0644]
source/core/texture2d.h [new file with mode: 0644]
source/core/texture2darray.cpp [new file with mode: 0644]
source/core/texture2darray.h [new file with mode: 0644]
source/core/texture3d.cpp [new file with mode: 0644]
source/core/texture3d.h [new file with mode: 0644]
source/core/texturecube.cpp [new file with mode: 0644]
source/core/texturecube.h [new file with mode: 0644]
source/core/texunit.cpp [new file with mode: 0644]
source/core/texunit.h [new file with mode: 0644]
source/core/uniform.cpp [new file with mode: 0644]
source/core/uniform.h [new file with mode: 0644]
source/core/uniformblock.cpp [new file with mode: 0644]
source/core/uniformblock.h [new file with mode: 0644]
source/core/vector.h [new file with mode: 0644]
source/core/vertexarray.cpp [new file with mode: 0644]
source/core/vertexarray.h [new file with mode: 0644]
source/core/vertexformat.cpp [new file with mode: 0644]
source/core/vertexformat.h [new file with mode: 0644]
source/core/vertexsetup.cpp [new file with mode: 0644]
source/core/vertexsetup.h [new file with mode: 0644]
source/core/windingtest.cpp [new file with mode: 0644]
source/core/windingtest.h [new file with mode: 0644]
source/cylinder.cpp [deleted file]
source/cylinder.h [deleted file]
source/datatype.cpp [deleted file]
source/datatype.h [deleted file]
source/effect.cpp [deleted file]
source/effect.h [deleted file]
source/effects/ambientocclusion.cpp [new file with mode: 0644]
source/effects/ambientocclusion.h [new file with mode: 0644]
source/effects/bloom.cpp [new file with mode: 0644]
source/effects/bloom.h [new file with mode: 0644]
source/effects/colorcurve.cpp [new file with mode: 0644]
source/effects/colorcurve.h [new file with mode: 0644]
source/effects/effect.cpp [new file with mode: 0644]
source/effects/effect.h [new file with mode: 0644]
source/effects/environmentmap.cpp [new file with mode: 0644]
source/effects/environmentmap.h [new file with mode: 0644]
source/effects/postprocessor.cpp [new file with mode: 0644]
source/effects/postprocessor.h [new file with mode: 0644]
source/effects/shadowmap.cpp [new file with mode: 0644]
source/effects/shadowmap.h [new file with mode: 0644]
source/environmentmap.cpp [deleted file]
source/environmentmap.h [deleted file]
source/error.h [deleted file]
source/extension.cpp [deleted file]
source/extension.h [deleted file]
source/font.cpp [deleted file]
source/font.h [deleted file]
source/framebuffer.cpp [deleted file]
source/framebuffer.h [deleted file]
source/geometrybuilder.cpp [deleted file]
source/geometrybuilder.h [deleted file]
source/gl.h [deleted file]
source/glsl/programcompiler.cpp [new file with mode: 0644]
source/glsl/programcompiler.h [new file with mode: 0644]
source/glsl/programparser.cpp [new file with mode: 0644]
source/glsl/programparser.h [new file with mode: 0644]
source/glsl/programsyntax.cpp [new file with mode: 0644]
source/glsl/programsyntax.h [new file with mode: 0644]
source/grid.cpp [deleted file]
source/grid.h [deleted file]
source/instancearray.cpp [deleted file]
source/instancearray.h [deleted file]
source/instancescene.cpp [deleted file]
source/instancescene.h [deleted file]
source/keyframe.cpp [deleted file]
source/keyframe.h [deleted file]
source/light.cpp [deleted file]
source/light.h [deleted file]
source/lighting.cpp [deleted file]
source/lighting.h [deleted file]
source/material.cpp [deleted file]
source/material.h [deleted file]
source/materials/basicmaterial.cpp [new file with mode: 0644]
source/materials/basicmaterial.h [new file with mode: 0644]
source/materials/light.cpp [new file with mode: 0644]
source/materials/light.h [new file with mode: 0644]
source/materials/lighting.cpp [new file with mode: 0644]
source/materials/lighting.h [new file with mode: 0644]
source/materials/material.cpp [new file with mode: 0644]
source/materials/material.h [new file with mode: 0644]
source/materials/pbrmaterial.cpp [new file with mode: 0644]
source/materials/pbrmaterial.h [new file with mode: 0644]
source/materials/renderpass.cpp [new file with mode: 0644]
source/materials/renderpass.h [new file with mode: 0644]
source/materials/technique.cpp [new file with mode: 0644]
source/materials/technique.h [new file with mode: 0644]
source/matrix.cpp [deleted file]
source/matrix.h [deleted file]
source/mesh.cpp [deleted file]
source/mesh.h [deleted file]
source/meshbuilder.cpp [deleted file]
source/meshbuilder.h [deleted file]
source/misc.cpp [deleted file]
source/misc.h [deleted file]
source/object.cpp [deleted file]
source/object.h [deleted file]
source/objectinstance.cpp [deleted file]
source/objectinstance.h [deleted file]
source/occludedscene.cpp [deleted file]
source/occludedscene.h [deleted file]
source/offscreenview.cpp [deleted file]
source/offscreenview.h [deleted file]
source/orderedscene.cpp [deleted file]
source/orderedscene.h [deleted file]
source/pbrmaterial.cpp [deleted file]
source/pbrmaterial.h [deleted file]
source/pipeline.cpp [deleted file]
source/pipeline.h [deleted file]
source/pipelinebuilder.cpp [deleted file]
source/pipelinebuilder.h [deleted file]
source/pipelinetemplate.cpp [deleted file]
source/pipelinetemplate.h [deleted file]
source/pixelformat.cpp [deleted file]
source/pixelformat.h [deleted file]
source/pixelstore.cpp [deleted file]
source/pixelstore.h [deleted file]
source/placeable.cpp [deleted file]
source/placeable.h [deleted file]
source/pose.cpp [deleted file]
source/pose.h [deleted file]
source/postprocessor.cpp [deleted file]
source/postprocessor.h [deleted file]
source/predicate.cpp [deleted file]
source/predicate.h [deleted file]
source/primitivebuilder.cpp [deleted file]
source/primitivebuilder.h [deleted file]
source/primitivetype.cpp [deleted file]
source/primitivetype.h [deleted file]
source/program.cpp [deleted file]
source/program.h [deleted file]
source/programcompiler.cpp [deleted file]
source/programcompiler.h [deleted file]
source/programdata.cpp [deleted file]
source/programdata.h [deleted file]
source/programparser.cpp [deleted file]
source/programparser.h [deleted file]
source/programsyntax.cpp [deleted file]
source/programsyntax.h [deleted file]
source/render/camera.cpp [new file with mode: 0644]
source/render/camera.h [new file with mode: 0644]
source/render/instancearray.cpp [new file with mode: 0644]
source/render/instancearray.h [new file with mode: 0644]
source/render/instancescene.cpp [new file with mode: 0644]
source/render/instancescene.h [new file with mode: 0644]
source/render/object.cpp [new file with mode: 0644]
source/render/object.h [new file with mode: 0644]
source/render/objectinstance.cpp [new file with mode: 0644]
source/render/objectinstance.h [new file with mode: 0644]
source/render/occludedscene.cpp [new file with mode: 0644]
source/render/occludedscene.h [new file with mode: 0644]
source/render/offscreenview.cpp [new file with mode: 0644]
source/render/offscreenview.h [new file with mode: 0644]
source/render/orderedscene.cpp [new file with mode: 0644]
source/render/orderedscene.h [new file with mode: 0644]
source/render/pipeline.cpp [new file with mode: 0644]
source/render/pipeline.h [new file with mode: 0644]
source/render/placeable.cpp [new file with mode: 0644]
source/render/placeable.h [new file with mode: 0644]
source/render/programdata.cpp [new file with mode: 0644]
source/render/programdata.h [new file with mode: 0644]
source/render/renderable.h [new file with mode: 0644]
source/render/renderer.cpp [new file with mode: 0644]
source/render/renderer.h [new file with mode: 0644]
source/render/rendertarget.cpp [new file with mode: 0644]
source/render/rendertarget.h [new file with mode: 0644]
source/render/scene.cpp [new file with mode: 0644]
source/render/scene.h [new file with mode: 0644]
source/render/simplescene.cpp [new file with mode: 0644]
source/render/simplescene.h [new file with mode: 0644]
source/render/slot.cpp [new file with mode: 0644]
source/render/slot.h [new file with mode: 0644]
source/render/tag.cpp [new file with mode: 0644]
source/render/tag.h [new file with mode: 0644]
source/render/text.cpp [new file with mode: 0644]
source/render/text.h [new file with mode: 0644]
source/render/texturing.cpp [new file with mode: 0644]
source/render/texturing.h [new file with mode: 0644]
source/render/view.cpp [new file with mode: 0644]
source/render/view.h [new file with mode: 0644]
source/render/windowview.cpp [new file with mode: 0644]
source/render/windowview.h [new file with mode: 0644]
source/render/zsortedscene.cpp [new file with mode: 0644]
source/render/zsortedscene.h [new file with mode: 0644]
source/renderable.h [deleted file]
source/renderbuffer.cpp [deleted file]
source/renderbuffer.h [deleted file]
source/renderer.cpp [deleted file]
source/renderer.h [deleted file]
source/renderpass.cpp [deleted file]
source/renderpass.h [deleted file]
source/rendertarget.cpp [deleted file]
source/rendertarget.h [deleted file]
source/resource.cpp [deleted file]
source/resource.h [deleted file]
source/resourcemanager.cpp [deleted file]
source/resourcemanager.h [deleted file]
source/resourceobserver.h [deleted file]
source/resources.cpp [deleted file]
source/resources.h [deleted file]
source/resources/resource.cpp [new file with mode: 0644]
source/resources/resource.h [new file with mode: 0644]
source/resources/resourcemanager.cpp [new file with mode: 0644]
source/resources/resourcemanager.h [new file with mode: 0644]
source/resources/resourceobserver.h [new file with mode: 0644]
source/resources/resources.cpp [new file with mode: 0644]
source/resources/resources.h [new file with mode: 0644]
source/sampler.cpp [deleted file]
source/sampler.h [deleted file]
source/scene.cpp [deleted file]
source/scene.h [deleted file]
source/shader.cpp [deleted file]
source/shader.h [deleted file]
source/shadowmap.cpp [deleted file]
source/shadowmap.h [deleted file]
source/simplescene.cpp [deleted file]
source/simplescene.h [deleted file]
source/slot.cpp [deleted file]
source/slot.h [deleted file]
source/sphere.cpp [deleted file]
source/sphere.h [deleted file]
source/stencil.cpp [deleted file]
source/stencil.h [deleted file]
source/tag.cpp [deleted file]
source/tag.h [deleted file]
source/technique.cpp [deleted file]
source/technique.h [deleted file]
source/tests.cpp [deleted file]
source/tests.h [deleted file]
source/text.cpp [deleted file]
source/text.h [deleted file]
source/texture.cpp [deleted file]
source/texture.h [deleted file]
source/texture1d.cpp [deleted file]
source/texture1d.h [deleted file]
source/texture2d.cpp [deleted file]
source/texture2d.h [deleted file]
source/texture2darray.cpp [deleted file]
source/texture2darray.h [deleted file]
source/texture3d.cpp [deleted file]
source/texture3d.h [deleted file]
source/texturecube.cpp [deleted file]
source/texturecube.h [deleted file]
source/texturing.cpp [deleted file]
source/texturing.h [deleted file]
source/texunit.cpp [deleted file]
source/texunit.h [deleted file]
source/transform.cpp [deleted file]
source/transform.h [deleted file]
source/uniform.cpp [deleted file]
source/uniform.h [deleted file]
source/uniformblock.cpp [deleted file]
source/uniformblock.h [deleted file]
source/vector.h [deleted file]
source/vertexarray.cpp [deleted file]
source/vertexarray.h [deleted file]
source/vertexarraybuilder.cpp [deleted file]
source/vertexarraybuilder.h [deleted file]
source/vertexbuilder.h [deleted file]
source/vertexformat.cpp [deleted file]
source/vertexformat.h [deleted file]
source/vertexsetup.cpp [deleted file]
source/vertexsetup.h [deleted file]
source/view.cpp [deleted file]
source/view.h [deleted file]
source/windingtest.cpp [deleted file]
source/windingtest.h [deleted file]
source/windowview.cpp [deleted file]
source/windowview.h [deleted file]
source/zsortedscene.cpp [deleted file]
source/zsortedscene.h [deleted file]

diff --git a/Build b/Build
index 87da59f5fe6726d5db821e6f283e736eb6c958d5..f193a780a224a2c5cde340fb10424705460f7710 100644 (file)
--- a/Build
+++ b/Build
@@ -41,15 +41,39 @@ package "mspgl"
 
        library "mspgl"
        {
-               source "source";
+               source "source/core";
+               source "source/materials";
+               source "source/render";
+               source "source/effects";
+               source "source/animation";
+               source "source/resources";
+               source "source/glsl";
+               source "source/builders";
                source "extensions";
                source "shaderlib";
+               build_info
+               {
+                       incpath "source/core";
+                       incpath "source/materials";
+                       incpath "source/render";
+                       incpath "source/effects";
+                       incpath "source/animation";
+                       incpath "source/resources";
+                       incpath "source/glsl";
+                       incpath "source/builders";
+               };
                install true;
                install_map
                {
-                       map "source" "include/msp/gl";
+                       map "source/core" "include/msp/gl";
+                       map "source/materials" "include/msp/gl";
+                       map "source/render" "include/msp/gl";
+                       map "source/effects" "include/msp/gl";
+                       map "source/animation" "include/msp/gl";
+                       map "source/resources" "include/msp/gl";
+                       map "source/glsl" "include/msp/gl";
+                       map "source/builders" "include/msp/gl";
                        map "extensions" "include/msp/gl/extensions";
-                       map "shaderlib" "include/msp/gl/resources";
                };
        };
 
diff --git a/source/ambientocclusion.cpp b/source/ambientocclusion.cpp
deleted file mode 100644 (file)
index 8c1dd61..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-#include <algorithm>
-#include "ambientocclusion.h"
-#include "blend.h"
-#include "camera.h"
-#include "renderer.h"
-#include "shader.h"
-#include "tests.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-AmbientOcclusion::AmbientOcclusion(unsigned w, unsigned h, float):
-       occlude_target(w, h, (RENDER_COLOR,R8)),
-       occlude_shader("ambientocclusion_occlude.glsl"),
-       combine_shader("ambientocclusion_combine.glsl"),
-       quad(get_fullscreen_quad()),
-       linear_sampler(get_linear_sampler()),
-       nearest_sampler(get_nearest_sampler())
-{
-       texturing.attach(2, occlude_target.get_target_texture(RENDER_COLOR), linear_sampler.get());
-
-       unsigned seed = 1;
-       rotate_lookup.storage(RGBA8, 4, 4, 1);
-       unsigned char data[64];
-       for(unsigned i=0; i<16; ++i)
-       {
-               Geometry::Angle<float> a = Geometry::Angle<float>::from_turns(random(seed));
-               unsigned char c = (cos(a)*0.5f+0.5f)*255;
-               unsigned char s = (sin(a)*0.5f+0.5f)*255;
-               data[i*4  ] = c;
-               data[i*4+1] = s;
-               data[i*4+2] = 255-s;
-               data[i*4+3] = ((i+i/4)%2)*255;
-       }
-       rotate_lookup.image(0, data);
-
-       texturing.attach(3, rotate_lookup, nearest_sampler.get());
-
-       shdata.uniform("source", 0);
-       shdata.uniform("depth", 1);
-       shdata.uniform("occlusion", 2);
-       shdata.uniform("rotate", 3);
-       shdata.uniform("inverse_projection", Matrix());
-
-       set_n_samples(16);
-       set_occlusion_radius(0.5f);
-       set_darkness(1.0f);
-       set_edge_depth_threshold(0.1f);
-}
-
-float AmbientOcclusion::random(unsigned &seed)
-{
-       static const unsigned modulus = (1U<<31)-1;
-       seed = (static_cast<UInt64>(seed)*48271)%modulus;  // minstd
-       return static_cast<float>(seed)/(modulus-1);
-}
-
-void AmbientOcclusion::set_n_samples(unsigned n)
-{
-       if(n<1 || n>32)
-               throw out_of_range("AmbientOcclusion::set_n_samples");
-
-       unsigned seed = 1;
-       float radius_divisor = (n-1)*(n-1);
-       Vector3 sample_points[32];
-       for(unsigned i=0; i<n; ++i)
-       {
-               Vector3 v(random(seed)-0.5f, random(seed)-0.5f, random(seed)-0.5f);
-               sample_points[i] = normalize(v)*(0.1f+0.9f*i*i/radius_divisor);
-       }
-       shdata.uniform3_array("sample_points", n, &sample_points[0].x);
-       shdata.uniform("n_samples", static_cast<int>(n));
-}
-
-void AmbientOcclusion::set_occlusion_radius(float r)
-{
-       shdata.uniform("occlusion_radius", r);
-}
-
-void AmbientOcclusion::set_darkness(float darkness)
-{
-       shdata.uniform("darkness", darkness);
-}
-
-void AmbientOcclusion::set_edge_depth_threshold(float edt)
-{
-       shdata.uniform("edge_depth_threshold", edt);
-}
-
-void AmbientOcclusion::render(Renderer &renderer, const Texture2D &color, const Texture2D &depth)
-{
-       texturing.attach(0, color, nearest_sampler.get());
-       texturing.attach(1, depth, nearest_sampler.get());
-
-       if(renderer.get_camera())
-               shdata.uniform("inverse_projection", invert(renderer.get_camera()->get_projection_matrix()));
-
-       Renderer::Push push(renderer);
-       renderer.set_texturing(&texturing);
-       renderer.set_shader_program(&occlude_shader, &shdata);
-
-       {
-               BindRestore bind_fbo(occlude_target.get_framebuffer());
-               quad->draw(renderer);
-       }
-
-       renderer.set_shader_program(&combine_shader);
-       quad->draw(renderer);
-}
-
-
-AmbientOcclusion::Template::Template():
-       n_samples(16),
-       occlusion_radius(0.5f),
-       darkness(1.0f),
-       edge_depth_threshold(0.1f)
-{ }
-
-AmbientOcclusion *AmbientOcclusion::Template::create(unsigned width, unsigned height) const
-{
-       RefPtr<AmbientOcclusion> ao = new AmbientOcclusion(width/size_divisor, height/size_divisor);
-       ao->set_n_samples(n_samples);
-       ao->set_occlusion_radius(occlusion_radius);
-       ao->set_darkness(darkness);
-       ao->set_edge_depth_threshold(edge_depth_threshold);
-       return ao.release();
-}
-
-
-AmbientOcclusion::Template::Loader::Loader(Template &t):
-       DataFile::DerivedObjectLoader<Template, PostProcessor::Template::Loader>(t)
-{
-       add("darkness", &Template::darkness);
-       add("edge_depth_threshold", &Template::edge_depth_threshold);
-       add("occlusion_radius", &Template::occlusion_radius);
-       add("samples", &Template::n_samples);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/ambientocclusion.h b/source/ambientocclusion.h
deleted file mode 100644 (file)
index 6c2c5d7..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-#ifndef MSP_GL_AMBIENTOCCLUSION_H_
-#define MSP_GL_AMBIENTOCCLUSION_H_
-
-#include "framebuffer.h"
-#include "mesh.h"
-#include "postprocessor.h"
-#include "program.h"
-#include "programdata.h"
-#include "rendertarget.h"
-#include "texture2d.h"
-#include "texturing.h"
-
-namespace Msp {
-namespace GL {
-
-/**
-Implements screen-space ambient occlusion.
-
-http://en.wikipedia.org/wiki/Screen_Space_Ambient_Occlusion
-*/
-class AmbientOcclusion: public PostProcessor
-{
-public:
-       struct Template: PostProcessor::Template
-       {
-               class Loader: public DataFile::DerivedObjectLoader<Template, PostProcessor::Template::Loader>
-               {
-               public:
-                       Loader(Template &);
-               };
-
-               unsigned n_samples;
-               float occlusion_radius;
-               float darkness;
-               float edge_depth_threshold;
-
-               Template();
-
-               virtual AmbientOcclusion *create(unsigned, unsigned) const;
-       };
-
-private:
-       Texture2D rotate_lookup;
-       RenderTarget occlude_target;
-       Texturing texturing;
-       Program occlude_shader;
-       Program combine_shader;
-       mutable ProgramData shdata;
-       RefPtr<Mesh> quad;
-       RefPtr<Sampler> linear_sampler;
-       RefPtr<Sampler> nearest_sampler;
-
-public:
-       AmbientOcclusion(unsigned, unsigned, float = 1.0f);
-
-private:
-       static float random(unsigned &);
-
-public:
-       void set_n_samples(unsigned);
-       void set_occlusion_radius(float);
-       void set_edge_depth_threshold(float);
-
-       void set_darkness(float);
-
-       virtual void render(Renderer &, const Texture2D &, const Texture2D &);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/animatedobject.cpp b/source/animatedobject.cpp
deleted file mode 100644 (file)
index 390b90b..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-#include <algorithm>
-#include <msp/strings/format.h>
-#include "animatedobject.h"
-#include "error.h"
-#include "object.h"
-#include "programdata.h"
-#include "renderer.h"
-#include "technique.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-AnimatedObject::AnimatedObject(const Object &o):
-       ObjectInstance(o),
-       shdata(0)
-{
-       if(const Technique *tech = object.get_technique())
-               if(tech->has_shaders())
-                       shdata = new ProgramData;
-}
-
-AnimatedObject::~AnimatedObject()
-{
-       delete shdata;
-}
-
-void AnimatedObject::set_pose_matrix(unsigned link, const Matrix &m)
-{
-       if(shdata)
-       {
-               if(link*16>=pose_data.size())
-                       pose_data.resize((link+1)*16);
-               copy(m.data(), m.data()+16, &pose_data[link*16]);
-               shdata->uniform_matrix4_array("pose", pose_data.size()/16, &pose_data[0]);
-       }
-}
-
-ProgramData &AnimatedObject::get_shader_data()
-{
-       if(!shdata)
-               throw invalid_operation("AnimatedObject::get_shader_data");
-       return *shdata;
-}
-
-const ProgramData &AnimatedObject::get_shader_data() const
-{
-       if(!shdata)
-               throw invalid_operation("AnimatedObject::get_shader_data");
-       return *shdata;
-}
-
-void AnimatedObject::set_uniform(const string &name, const KeyFrame::AnimatedUniform &uni)
-{
-       if(!shdata)
-               throw invalid_operation("AnimatedObject::set_uniform");
-
-       if(uni.size==1)
-               shdata->uniform(name, uni.values[0]);
-       else if(uni.size==2)
-               shdata->uniform2(name, uni.values);
-       else if(uni.size==3)
-               shdata->uniform3(name, uni.values);
-       else if(uni.size==4)
-               shdata->uniform4(name, uni.values);
-       else
-               throw invalid_argument("AnimatedObject::set_uniform");
-}
-
-void AnimatedObject::setup_render(Renderer &renderer, const Tag &) const
-{
-       renderer.transform(matrix);
-       if(shdata)
-               renderer.add_shader_data(*shdata);
-}
-
-
-AnimatedObject::Loader::Loader(AnimatedObject &o):
-       DataFile::DerivedObjectLoader<AnimatedObject, ObjectInstance::Loader>(o)
-{
-       // Deprecated; Use the transform statement defined in ObjectInstance instead
-       add("position", &Loader::position);
-       add("rotation", &Loader::rotation);
-       add("scale", &Loader::scale);
-       add("scale", &Loader::scale_uniform);
-}
-
-void AnimatedObject::Loader::position(float x, float y, float z)
-{
-       obj.matrix.translate(x, y, z);
-}
-
-void AnimatedObject::Loader::rotation(float a, float x, float y, float z)
-{
-       obj.matrix.rotate_deg(a, x, y, z);
-}
-
-void AnimatedObject::Loader::scale(float x, float y, float z)
-{
-       obj.matrix.scale(x, y, z);
-}
-
-void AnimatedObject::Loader::scale_uniform(float s)
-{
-       obj.matrix.scale(s);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/animatedobject.h b/source/animatedobject.h
deleted file mode 100644 (file)
index f9b8824..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef MSP_GL_ANIMATEDOBJECT_H_
-#define MSP_GL_ANIMATEDOBJECT_H_
-
-#include <vector>
-#include <msp/core/attributes.h>
-#include <msp/datafile/objectloader.h>
-#include "keyframe.h"
-#include "matrix.h"
-#include "objectinstance.h"
-
-namespace Msp {
-namespace GL {
-
-/**
-An object instance that can be animated by an AnimationPlayer.
-*/
-class AnimatedObject: public ObjectInstance
-{
-public:
-       class Loader: public DataFile::DerivedObjectLoader<AnimatedObject, ObjectInstance::Loader>
-       {
-       public:
-               Loader(AnimatedObject &);
-
-       private:
-               void position(float, float, float);
-               void rotation(float, float, float, float);
-               void scale(float, float, float);
-               void scale_uniform(float);
-       };
-
-private:
-       std::vector<float> pose_data;
-       ProgramData *shdata;
-
-public:
-       AnimatedObject(const Object &);
-       ~AnimatedObject();
-
-       void set_pose_matrix(unsigned, const Matrix &);
-       ProgramData &get_shader_data();
-       const ProgramData &get_shader_data() const;
-
-       DEPRECATED void set_uniform(const std::string &, const KeyFrame::AnimatedUniform &);
-
-       virtual const Matrix *get_matrix() const { return &matrix; }
-
-       virtual void setup_render(Renderer &, const Tag &) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/animation.cpp b/source/animation.cpp
deleted file mode 100644 (file)
index a8d9e96..0000000
+++ /dev/null
@@ -1,633 +0,0 @@
-#include <cmath>
-#include <msp/core/maputils.h>
-#include <msp/datafile/collection.h>
-#include <msp/interpolate/bezierspline.h>
-#include "animation.h"
-#include "animationeventobserver.h"
-#include "armature.h"
-#include "error.h"
-#include "pose.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Animation::Animation():
-       armature(0),
-       looping(false)
-{ }
-
-Animation::~Animation()
-{
-       for(vector<Curve *>::iterator i=curves.begin(); i!=curves.end(); ++i)
-               delete *i;
-}
-
-void Animation::set_armature(const Armature &a)
-{
-       if(!keyframes.empty() && &a!=armature)
-               throw invalid_operation("Animation::set_armature");
-       armature = &a;
-}
-
-unsigned Animation::get_slot_for_uniform(const string &n) const
-{
-       for(unsigned i=0; i<uniforms.size(); ++i)
-               if(uniforms[i].name==n)
-                       return i;
-       throw key_error(n);
-}
-
-const string &Animation::get_uniform_name(unsigned i) const
-{
-       if(i>=uniforms.size())
-               throw out_of_range("Animation::get_uniform_name");
-       return uniforms[i].name;
-}
-
-void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf)
-{
-       add_keyframe(t, &kf, false, false);
-       create_curves();
-}
-
-void Animation::add_keyframe_owned(const Time::TimeDelta &t, const KeyFrame *kf)
-{
-       add_keyframe(t, kf, false, true);
-       create_curves();
-}
-
-void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf, float slope)
-{
-       add_keyframe(t, &kf, slope, slope, false);
-       create_curves();
-}
-
-void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf, float ss, float es)
-{
-       add_keyframe(t, &kf, ss, es, false);
-       create_curves();
-}
-
-void Animation::add_control_keyframe(const KeyFrame &kf)
-{
-       if(keyframes.empty())
-               throw invalid_operation("Animation::add_control_keyframe");
-
-       add_keyframe(keyframes.back().time, &kf, true, false);
-}
-
-void Animation::add_control_keyframe_owned(const KeyFrame *kf)
-{
-       if(keyframes.empty())
-               throw invalid_operation("Animation::add_control_keyframe_owned");
-
-       add_keyframe(keyframes.back().time, kf, true, true);
-}
-
-void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame *kf, float ss, float es, bool owned)
-{
-       if(keyframes.empty())
-               return add_keyframe(t, kf, false, owned);
-
-       if(keyframes.back().control)
-               throw invalid_operation("Animation::add_keyframe");
-
-       const KeyFrame &last = *keyframes.back().keyframe;
-       const Transform &trn = kf->get_transform();
-       const Transform &last_trn = last.get_transform();
-       const KeyFrame::UniformMap &kf_unis = kf->get_uniforms();
-       const KeyFrame::UniformMap &last_unis = last.get_uniforms();
-       for(unsigned i=1; i<=2; ++i)
-       {
-               float x = (i==1 ? ss/3 : 1-es/3);
-               KeyFrame *ckf = new KeyFrame;
-               Transform ctrn;
-               ctrn.set_position(last_trn.get_position()*(1-x)+trn.get_position()*x);
-               const Transform::AngleVector3 &e1 = last_trn.get_euler();
-               const Transform::AngleVector3 &e2 = trn.get_euler();
-               ctrn.set_euler(Transform::AngleVector3(e1.x*(1-x)+e2.x*x, e1.y*(1-x)+e2.y*x, e1.z*(1-x)+e2.z*x));
-               ctrn.set_scale(last_trn.get_scale()*(1-x)+trn.get_scale()*x);
-               ckf->set_transform(ctrn);
-
-               for(KeyFrame::UniformMap::const_iterator j=kf_unis.begin(); j!=kf_unis.end(); ++j)
-               {
-                       KeyFrame::UniformMap::const_iterator k = last_unis.find(j->first);
-                       if(k==last_unis.end())
-                               continue;
-
-                       KeyFrame::AnimatedUniform uni(j->second.size, 0.0f);
-                       for(unsigned c=0; c<uni.size; ++c)
-                               uni.values[c] = k->second.values[c]*(1-x)+j->second.values[c]*x;
-
-                       ckf->set_uniform(j->first, uni);
-               }
-
-               add_keyframe(t, ckf, true, true);
-       }
-
-       add_keyframe(t, kf, false, owned);
-}
-
-void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame *kf, bool c, bool owned)
-{
-       if(c && keyframes.empty())
-               throw invalid_argument("Animation::add_keyframe");
-       if(keyframes.empty() && t!=Time::zero)
-               throw invalid_argument("Animation::add_keyframe");
-       if(!keyframes.empty() && t<keyframes.back().time)
-               throw invalid_argument("Animation::add_keyframe");
-       if(kf->get_pose() && armature && kf->get_pose()->get_armature()!=armature)
-               throw invalid_argument("Animation::add_keyframe");
-
-       const KeyFrame::UniformMap &kf_uniforms = kf->get_uniforms();
-       for(vector<UniformInfo>::const_iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
-       {
-               KeyFrame::UniformMap::const_iterator j = kf_uniforms.find(i->name);
-               if(j!=kf_uniforms.end() && j->second.size!=i->size)
-                       throw invalid_argument("Animation::add_keyframe");
-       }
-
-       if(kf->get_pose() && !armature)
-               armature = kf->get_pose()->get_armature();
-
-       TimedKeyFrame tkf;
-       tkf.time = t;
-       tkf.keyframe = kf;
-       if(!owned)
-               tkf.keyframe.keep();
-       tkf.control = c;
-
-       keyframes.push_back(tkf);
-
-       for(KeyFrame::UniformMap::const_iterator i=kf_uniforms.begin(); i!=kf_uniforms.end(); ++i)
-       {
-               bool found = false;
-               for(vector<UniformInfo>::const_iterator j=uniforms.begin(); (!found && j!=uniforms.end()); ++j)
-                       found = (j->name==i->first);
-
-               if(!found)
-                       uniforms.push_back(UniformInfo(i->first, i->second.size));
-       }
-}
-
-void Animation::create_curves()
-{
-       for(vector<Curve *>::iterator i=curves.begin(); i!=curves.end(); ++i)
-               delete *i;
-       curves.clear();
-
-       curves.reserve(6+uniforms.size());
-       create_curve(POSITION, Transform::POSITION, &extract_position);
-       create_curve(EULER, Transform::EULER, &extract_euler);
-       create_curve(SCALE, Transform::SCALE, &extract_scale);
-
-       uniform_curve_offset = curves.size();
-       for(vector<UniformInfo>::const_iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
-       {
-               if(i->size==1)
-                       create_curve<1>(UNIFORM, -1, ExtractUniform<1>(i->name));
-               else if(i->size==2)
-                       create_curve<2>(UNIFORM, -1, ExtractUniform<2>(i->name));
-               else if(i->size==3)
-                       create_curve<3>(UNIFORM, -1, ExtractUniform<3>(i->name));
-               else if(i->size==4)
-                       create_curve<4>(UNIFORM, -1, ExtractUniform<4>(i->name));
-       }
-}
-
-void Animation::create_curve(CurveTarget target, Transform::ComponentMask mask, ExtractComponent::Extract extract)
-{
-       Transform::ComponentMask all = mask;
-       Transform::ComponentMask any = Transform::NONE;
-       for(vector<TimedKeyFrame>::const_iterator i=keyframes.begin(); i!=keyframes.end(); ++i)
-       {
-               all = all&i->keyframe->get_transform().get_mask();
-               any = any|i->keyframe->get_transform().get_mask();
-       }
-
-       if(all==mask)
-               create_curve<3>(target, -1, extract);
-       else if(any&mask)
-       {
-               unsigned low_bit = mask&(mask>>2);
-               for(unsigned i=3; i-->0; )
-               {
-                       Transform::ComponentMask bit = static_cast<Transform::ComponentMask>(low_bit<<i);
-                       if(any&bit)
-                               create_curve<1>(target, i, ExtractComponent(extract, i, bit));
-               }
-       }
-}
-
-template<unsigned N, typename T>
-void Animation::create_curve(CurveTarget target, int component, const T &extract)
-{
-       typedef typename ValueCurve<N>::Knot Knot;
-
-       vector<Knot> knots;
-       unsigned n_control = 0;
-       for(vector<TimedKeyFrame>::const_iterator i=keyframes.begin(); i!=keyframes.end(); ++i)
-       {
-               if(i->control && knots.empty())
-                       continue;
-
-               typename Interpolate::SplineValue<float, N>::Type value;
-               if(extract(*i->keyframe, value))
-               {
-                       typename Knot::Value dvalue = value;
-                       float x = i->time/Time::sec;
-                       if(i->control)
-                       {
-                               ++n_control;
-                               if(n_control>2)
-                                       throw logic_error("too many control keyframes");
-                       }
-                       else
-                       {
-                               if(n_control==1)
-                               {
-                                       typename Knot::Value cv = knots.back().y;
-                                       knots.back().y = (knots[knots.size()-2].y+cv*2.0)/3.0;
-                                       knots.push_back(Knot(x, (dvalue+cv*2.0)/3.0));
-                               }
-                               else if(n_control==0 && !knots.empty())
-                               {
-                                       typename Knot::Value prev = knots.back().y;
-                                       knots.push_back(Knot(knots.back().x, (prev*2.0+dvalue)/3.0));
-                                       knots.push_back(Knot(x, (prev+dvalue*2.0)/3.0));
-                               }
-                               n_control = 0;
-                       }
-                       knots.push_back(Knot(x, value));
-               }
-       }
-       
-       while(n_control--)
-               knots.pop_back();
-
-       if(knots.size()==1)
-       {
-               knots.push_back(knots.back());
-               knots.push_back(knots.back());
-               knots.back().x += 1;
-               knots.push_back(knots.back());
-       }
-
-       curves.push_back(new ValueCurve<N>(target, component, knots));
-}
-
-bool Animation::extract_position(const KeyFrame &kf, Vector3 &value)
-{
-       value = kf.get_transform().get_position();
-       return true;
-}
-
-bool Animation::extract_euler(const KeyFrame &kf, Vector3 &value)
-{
-       const Transform::AngleVector3 &euler = kf.get_transform().get_euler();
-       value = Vector3(euler.x.radians(), euler.y.radians(), euler.z.radians());
-       return true;
-}
-
-bool Animation::extract_scale(const KeyFrame &kf, Vector3 &value)
-{
-       value = kf.get_transform().get_scale();
-       return true;
-}
-
-void Animation::add_event(const Time::TimeDelta &t, const string &n, const Variant &v)
-{
-       Event event;
-       event.time = t;
-       event.name = n;
-       event.value = v;
-       events.push_back(event);
-}
-
-const Time::TimeDelta &Animation::get_duration() const
-{
-       if(keyframes.empty())
-               return Time::zero;
-
-       return keyframes.back().time;
-}
-
-void Animation::set_looping(bool l)
-{
-       looping = l;
-}
-
-
-Animation::Curve::Curve(CurveTarget t, int c):
-       target(t),
-       component(c)
-{ }
-
-
-template<unsigned N>
-Animation::ValueCurve<N>::ValueCurve(CurveTarget t, int c, const vector<Knot> &k):
-       Curve(t, c),
-       spline(Interpolate::BezierSpline<double, 3, N>(k))
-{ }
-
-template<unsigned N>
-void Animation::ValueCurve<N>::apply(float, Matrix &) const
-{
-       throw invalid_operation("ValueCurve::apply");
-}
-
-template<>
-void Animation::ValueCurve<1>::apply(float x, Matrix &matrix) const
-{
-       float value = spline(x);
-       if(target==POSITION || target==SCALE)
-       {
-               if(target==POSITION)
-               {
-                       Vector3 vec;
-                       vec[component] = value;
-                       matrix.translate(vec);
-               }
-               else
-               {
-                       Vector3 vec(1.0f, 1.0f, 1.0f);
-                       vec[component] = value;
-                       matrix.scale(vec);
-               }
-       }
-       else if(target==EULER)
-       {
-               Vector3 vec;
-               vec[component] = 1.0f;
-               matrix.rotate(Geometry::Angle<float>::from_radians(value), vec);
-       }
-       else
-               throw invalid_operation("ValueCurve::apply");
-}
-
-template<>
-void Animation::ValueCurve<3>::apply(float x, Matrix &matrix) const
-{
-       Vector3 value = spline(x);
-       if(target==POSITION)
-               matrix.translate(value);
-       else if(target==EULER)
-       {
-               matrix.rotate(Geometry::Angle<float>::from_radians(value.z), Vector3(0, 0, 1));
-               matrix.rotate(Geometry::Angle<float>::from_radians(value.y), Vector3(0, 1, 0));
-               matrix.rotate(Geometry::Angle<float>::from_radians(value.x), Vector3(1, 0, 0));
-       }
-       else if(target==SCALE)
-               matrix.scale(value);
-       else
-               throw invalid_operation("ValueCurve::apply");
-}
-
-template<unsigned N>
-void Animation::ValueCurve<N>::apply(float x, KeyFrame::AnimatedUniform &uni) const
-{
-       uni.size = N;
-       typename Interpolate::Spline<double, 3, N>::Value value = spline(x);
-       for(unsigned i=0; i<N; ++i)
-               uni.values[i] = Interpolate::SplineValue<double, N>::get(value, i);
-}
-
-
-bool Animation::ExtractComponent::operator()(const KeyFrame &kf, float &value) const
-{
-       Vector3 vec;
-       if(!extract(kf, vec))
-               return false;
-
-       value = vec[index];
-       return kf.get_transform().get_mask()&mask;
-}
-
-
-template<unsigned N>
-bool Animation::ExtractUniform<N>::operator()(const KeyFrame &kf, typename Interpolate::SplineValue<float, N>::Type &value) const
-{
-       const KeyFrame::UniformMap &kf_uniforms = kf.get_uniforms();
-       const KeyFrame::UniformMap::const_iterator i = kf_uniforms.find(name);
-       if(i==kf_uniforms.end())
-               return false;
-
-       value = Interpolate::SplineValue<float, N>::make(i->second.values);
-       return true;
-}
-
-
-Animation::UniformInfo::UniformInfo(const string &n, unsigned s):
-       name(n),
-       size(s)
-{ }
-
-
-Animation::Iterator::Iterator(const Animation &a):
-       animation(&a),
-       event_iter(animation->events.begin()),
-       end(false)
-{
-}
-
-Animation::Iterator &Animation::Iterator::operator+=(const Time::TimeDelta &t)
-{
-       const Time::TimeDelta &duration = animation->get_duration();
-       if(!duration)
-               return *this;
-
-       elapsed += t;
-       if(animation->looping)
-       {
-               while(elapsed>=duration)
-                       elapsed -= duration;
-       }
-       else if(elapsed>=duration)
-       {
-               end = true;
-               elapsed = duration;
-       }
-
-       return *this;
-}
-
-void Animation::Iterator::dispatch_events(AnimationEventObserver &observer)
-{
-       for(; (event_iter!=animation->events.end() && event_iter->time<=elapsed); ++event_iter)
-               observer.animation_event(0, event_iter->name, event_iter->value);
-}
-
-Matrix Animation::Iterator::get_matrix() const
-{
-       Matrix matrix;
-       for(unsigned i=0; i<animation->uniform_curve_offset; ++i)
-               animation->curves[i]->apply(elapsed/Time::sec, matrix);
-       return matrix;
-}
-
-KeyFrame::AnimatedUniform Animation::Iterator::get_uniform(unsigned i) const
-{
-       if(i>=animation->uniforms.size())
-               throw out_of_range("Animation::Iterator::get_uniform");
-
-       KeyFrame::AnimatedUniform uni(animation->uniforms[i].size, 0.0f);
-       animation->curves[animation->uniform_curve_offset+i]->apply(elapsed/Time::sec, uni);
-       return uni;
-}
-
-Matrix Animation::Iterator::get_pose_matrix(unsigned link) const
-{
-       if(!animation->armature)
-               throw invalid_operation("Animation::Iterator::get_pose_matrix");
-       if(link>animation->armature->get_max_link_index())
-               throw out_of_range("Animation::Iterator::get_pose_matrix");
-
-       throw logic_error("pose animations are currently unimplemented");
-}
-
-
-Animation::Loader::Loader(Animation &a):
-       DataFile::CollectionObjectLoader<Animation>(a, 0)
-{
-       init();
-}
-
-Animation::Loader::Loader(Animation &a, Collection &c):
-       DataFile::CollectionObjectLoader<Animation>(a, &c)
-{
-       init();
-}
-
-void Animation::Loader::init()
-{
-       start_slope = 1;
-       end_slope = 1;
-       slopes_set = 0;
-       add("armature", &Animation::armature);
-       add("control_keyframe", &Loader::control_keyframe);
-       add("control_keyframe", &Loader::control_keyframe_inline);
-       add("event", &Loader::event);
-       add("event", &Loader::event1i);
-       add("event", &Loader::event1f);
-       add("event", &Loader::event2f);
-       add("event", &Loader::event3f);
-       add("event", &Loader::event4f);
-       add("interval", &Loader::interval);
-       add("keyframe", &Loader::keyframe);
-       add("keyframe", &Loader::keyframe_inline);
-       add("looping", &Animation::looping);
-       add("slopes", &Loader::slopes);
-}
-
-void Animation::Loader::finish()
-{
-       obj.create_curves();
-}
-
-void Animation::Loader::check_slopes_and_control(bool s, bool c)
-{
-       if(s && c)
-               throw logic_error("can't use both slopes and control keyframes in same segment");
-}
-
-void Animation::Loader::add_kf(const KeyFrame *kf, bool c, bool owned)
-{
-       if(slopes_set && !c)
-               obj.add_keyframe(current_time, kf, start_slope, end_slope, owned);
-       else
-               obj.add_keyframe(current_time, kf, c, owned);
-
-       start_slope = end_slope;
-       end_slope = 1;
-       slopes_set = (slopes_set<<1)&3;
-}
-
-void Animation::Loader::load_kf(const string &n, bool c)
-{
-       add_kf(&get_collection().get<KeyFrame>(n), c, false);
-}
-
-void Animation::Loader::load_kf_inline(bool c)
-{
-       RefPtr<KeyFrame> kf = new KeyFrame;
-       if(coll)
-               load_sub(*kf, get_collection());
-       else
-               load_sub(*kf);
-
-       add_kf(kf.get(), c, true);
-       kf.release();
-}
-
-void Animation::Loader::control_keyframe(const string &n)
-{
-       slopes_set &= 1;
-       check_slopes_and_control(slopes_set, true);
-       load_kf(n, true);
-}
-
-void Animation::Loader::control_keyframe_inline()
-{
-       slopes_set &= 1;
-       check_slopes_and_control(slopes_set, true);
-       load_kf_inline(true);
-}
-
-void Animation::Loader::event(const string &n)
-{
-       obj.add_event(current_time, n);
-}
-
-void Animation::Loader::event1i(const string &n, int v)
-{
-       obj.add_event(current_time, n, v);
-}
-
-void Animation::Loader::event1f(const string &n, float v)
-{
-       obj.add_event(current_time, n, v);
-}
-
-void Animation::Loader::event2f(const string &n, float v0, float v1)
-{
-       obj.add_event(current_time, n, LinAl::Vector<float, 2>(v0, v1));
-}
-
-void Animation::Loader::event3f(const string &n, float v0, float v1, float v2)
-{
-       obj.add_event(current_time, n, Vector3(v0, v1, v2));
-}
-
-void Animation::Loader::event4f(const string &n, float v0, float v1, float v2, float v3)
-{
-       obj.add_event(current_time, n, Vector4(v0, v1, v2, v3));
-}
-
-void Animation::Loader::interval(float t)
-{
-       current_time += t*Time::sec;
-}
-
-void Animation::Loader::keyframe(const string &n)
-{
-       load_kf(n, false);
-}
-
-void Animation::Loader::keyframe_inline()
-{
-       load_kf_inline(false);
-}
-
-void Animation::Loader::slopes(float s, float e)
-{
-       check_slopes_and_control(true, (!obj.keyframes.empty() && obj.keyframes.back().control));
-
-       start_slope = s;
-       end_slope = e;
-       slopes_set = 1;
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/animation.h b/source/animation.h
deleted file mode 100644 (file)
index 6845d23..0000000
+++ /dev/null
@@ -1,213 +0,0 @@
-#ifndef MSP_GL_ANIMATION_H_
-#define MSP_GL_ANIMATION_H_
-
-#include <msp/core/refptr.h>
-#include <msp/datafile/objectloader.h>
-#include <msp/interpolate/spline.h>
-#include <msp/time/timedelta.h>
-#include "keyframe.h"
-
-namespace Msp {
-namespace GL {
-
-class AnimationEventObserver;
-class Armature;
-class Matrix;
-class Pose;
-
-/**
-An Animation is a sequence of KeyFrames combined with timing information.  The
-state at any point in the animation can be interpolated from the keyframes.
-*/
-class Animation
-{
-public:
-       class Loader: public DataFile::CollectionObjectLoader<Animation>
-       {
-       private:
-               Time::TimeDelta current_time;
-               float start_slope;
-               float end_slope;
-               int slopes_set;
-
-       public:
-               Loader(Animation &);
-               Loader(Animation &, Collection &);
-       private:
-               void init();
-               virtual void finish();
-
-               void check_slopes_and_control(bool, bool);
-               void add_kf(const KeyFrame *, bool, bool);
-               void load_kf(const std::string &, bool);
-               void load_kf_inline(bool);
-
-               void control_keyframe(const std::string &);
-               void control_keyframe_inline();
-               void event(const std::string &);
-               void event1i(const std::string &, int);
-               void event1f(const std::string &, float);
-               void event2f(const std::string &, float, float);
-               void event3f(const std::string &, float, float, float);
-               void event4f(const std::string &, float, float, float, float);
-               void interval(float);
-               void keyframe(const std::string &);
-               void keyframe_inline();
-               void slopes(float, float);
-       };
-
-private:
-       enum CurveTarget
-       {
-               POSITION,
-               EULER,
-               SCALE,
-               UNIFORM
-       };
-
-       class Curve
-       {
-       protected:
-               CurveTarget target;
-               int component;
-
-               Curve(CurveTarget, int);
-       public:
-               virtual ~Curve() { }
-
-               virtual void apply(float, Matrix &) const = 0;
-               virtual void apply(float, KeyFrame::AnimatedUniform &) const = 0;
-       };
-
-       template<unsigned N>
-       class ValueCurve: public Curve
-       {
-       public:
-               typedef typename Interpolate::SplineKnot<double, N> Knot;
-
-       private:
-               Interpolate::Spline<double, 3, N> spline;
-
-       public:
-               ValueCurve(CurveTarget, int, const std::vector<Knot> &);
-
-               virtual void apply(float, Matrix &) const;
-               virtual void apply(float, KeyFrame::AnimatedUniform &) const;
-       };
-
-       struct ExtractComponent
-       {
-               typedef bool (*Extract)(const KeyFrame &, Vector3 &);
-
-               Extract extract;
-               unsigned index;
-               Transform::ComponentMask mask;
-
-               ExtractComponent(Extract e, unsigned i, Transform::ComponentMask m): extract(e), index(i), mask(m) { }
-
-               bool operator()(const KeyFrame &, float &) const;
-       };
-
-       template<unsigned N>
-       struct ExtractUniform
-       {
-               const std::string &name;
-
-               ExtractUniform(const std::string &n): name(n) { }
-
-               bool operator()(const KeyFrame &, typename Interpolate::SplineValue<float, N>::Type &) const;
-       };
-
-       struct TimedKeyFrame
-       {
-               Time::TimeDelta time;
-               RefPtr<const KeyFrame> keyframe;
-               bool control;
-       };
-
-       struct Event
-       {
-               Time::TimeDelta time;
-               std::string name;
-               Variant value;
-       };
-
-       struct UniformInfo
-       {
-               std::string name;
-               unsigned size;
-
-               UniformInfo(const std::string &, unsigned);
-       };
-
-public:
-       class Iterator
-       {
-       private:
-               const Animation *animation;
-               Time::TimeDelta elapsed;
-               std::vector<Event>::const_iterator event_iter;
-               bool end;
-
-       public:
-               Iterator(const Animation &);
-
-               Iterator &operator+=(const Time::TimeDelta &);
-               void dispatch_events(AnimationEventObserver &);
-
-               bool is_end() const { return end; }
-               Matrix get_matrix() const;
-               KeyFrame::AnimatedUniform get_uniform(unsigned) const;
-               Matrix get_pose_matrix(unsigned) const;
-       };
-
-private:
-       const Armature *armature;
-       std::vector<TimedKeyFrame> keyframes;
-       std::vector<Event> events;
-       bool looping;
-       std::vector<UniformInfo> uniforms;
-       std::vector<Curve *> curves;
-       unsigned uniform_curve_offset;
-
-public:
-       Animation();
-       ~Animation();
-
-       void set_armature(const Armature &);
-       const Armature *get_armature() const { return armature; }
-
-       unsigned get_n_uniforms() const { return uniforms.size(); }
-       unsigned get_slot_for_uniform(const std::string &) const;
-       const std::string &get_uniform_name(unsigned) const;
-
-       void add_keyframe(const Time::TimeDelta &, const KeyFrame &);
-       void add_keyframe_owned(const Time::TimeDelta &, const KeyFrame *);
-       DEPRECATED void add_keyframe(const Time::TimeDelta &, const KeyFrame &, float);
-       DEPRECATED void add_keyframe(const Time::TimeDelta &, const KeyFrame &, float, float);
-       void add_control_keyframe(const KeyFrame &);
-       void add_control_keyframe_owned(const KeyFrame *);
-private:
-       void add_keyframe(const Time::TimeDelta &, const KeyFrame *, float, float, bool);
-       void add_keyframe(const Time::TimeDelta &, const KeyFrame *, bool, bool);
-       void prepare_keyframe(TimedKeyFrame &);
-       void create_curves();
-       void create_curve(CurveTarget, Transform::ComponentMask, ExtractComponent::Extract);
-       template<unsigned N, typename T>
-       void create_curve(CurveTarget target, int, const T &);
-       static bool extract_position(const KeyFrame &, Vector3 &);
-       static bool extract_euler(const KeyFrame &, Vector3 &);
-       static bool extract_scale(const KeyFrame &, Vector3 &);
-public:
-       void add_event(const Time::TimeDelta &, const std::string &, const Variant & = Variant());
-
-       const Msp::Time::TimeDelta &get_duration() const;
-
-       void set_looping(bool);
-       bool is_looping() const { return looping; }
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/animation/animatedobject.cpp b/source/animation/animatedobject.cpp
new file mode 100644 (file)
index 0000000..390b90b
--- /dev/null
@@ -0,0 +1,110 @@
+#include <algorithm>
+#include <msp/strings/format.h>
+#include "animatedobject.h"
+#include "error.h"
+#include "object.h"
+#include "programdata.h"
+#include "renderer.h"
+#include "technique.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+AnimatedObject::AnimatedObject(const Object &o):
+       ObjectInstance(o),
+       shdata(0)
+{
+       if(const Technique *tech = object.get_technique())
+               if(tech->has_shaders())
+                       shdata = new ProgramData;
+}
+
+AnimatedObject::~AnimatedObject()
+{
+       delete shdata;
+}
+
+void AnimatedObject::set_pose_matrix(unsigned link, const Matrix &m)
+{
+       if(shdata)
+       {
+               if(link*16>=pose_data.size())
+                       pose_data.resize((link+1)*16);
+               copy(m.data(), m.data()+16, &pose_data[link*16]);
+               shdata->uniform_matrix4_array("pose", pose_data.size()/16, &pose_data[0]);
+       }
+}
+
+ProgramData &AnimatedObject::get_shader_data()
+{
+       if(!shdata)
+               throw invalid_operation("AnimatedObject::get_shader_data");
+       return *shdata;
+}
+
+const ProgramData &AnimatedObject::get_shader_data() const
+{
+       if(!shdata)
+               throw invalid_operation("AnimatedObject::get_shader_data");
+       return *shdata;
+}
+
+void AnimatedObject::set_uniform(const string &name, const KeyFrame::AnimatedUniform &uni)
+{
+       if(!shdata)
+               throw invalid_operation("AnimatedObject::set_uniform");
+
+       if(uni.size==1)
+               shdata->uniform(name, uni.values[0]);
+       else if(uni.size==2)
+               shdata->uniform2(name, uni.values);
+       else if(uni.size==3)
+               shdata->uniform3(name, uni.values);
+       else if(uni.size==4)
+               shdata->uniform4(name, uni.values);
+       else
+               throw invalid_argument("AnimatedObject::set_uniform");
+}
+
+void AnimatedObject::setup_render(Renderer &renderer, const Tag &) const
+{
+       renderer.transform(matrix);
+       if(shdata)
+               renderer.add_shader_data(*shdata);
+}
+
+
+AnimatedObject::Loader::Loader(AnimatedObject &o):
+       DataFile::DerivedObjectLoader<AnimatedObject, ObjectInstance::Loader>(o)
+{
+       // Deprecated; Use the transform statement defined in ObjectInstance instead
+       add("position", &Loader::position);
+       add("rotation", &Loader::rotation);
+       add("scale", &Loader::scale);
+       add("scale", &Loader::scale_uniform);
+}
+
+void AnimatedObject::Loader::position(float x, float y, float z)
+{
+       obj.matrix.translate(x, y, z);
+}
+
+void AnimatedObject::Loader::rotation(float a, float x, float y, float z)
+{
+       obj.matrix.rotate_deg(a, x, y, z);
+}
+
+void AnimatedObject::Loader::scale(float x, float y, float z)
+{
+       obj.matrix.scale(x, y, z);
+}
+
+void AnimatedObject::Loader::scale_uniform(float s)
+{
+       obj.matrix.scale(s);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/animation/animatedobject.h b/source/animation/animatedobject.h
new file mode 100644 (file)
index 0000000..f9b8824
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef MSP_GL_ANIMATEDOBJECT_H_
+#define MSP_GL_ANIMATEDOBJECT_H_
+
+#include <vector>
+#include <msp/core/attributes.h>
+#include <msp/datafile/objectloader.h>
+#include "keyframe.h"
+#include "matrix.h"
+#include "objectinstance.h"
+
+namespace Msp {
+namespace GL {
+
+/**
+An object instance that can be animated by an AnimationPlayer.
+*/
+class AnimatedObject: public ObjectInstance
+{
+public:
+       class Loader: public DataFile::DerivedObjectLoader<AnimatedObject, ObjectInstance::Loader>
+       {
+       public:
+               Loader(AnimatedObject &);
+
+       private:
+               void position(float, float, float);
+               void rotation(float, float, float, float);
+               void scale(float, float, float);
+               void scale_uniform(float);
+       };
+
+private:
+       std::vector<float> pose_data;
+       ProgramData *shdata;
+
+public:
+       AnimatedObject(const Object &);
+       ~AnimatedObject();
+
+       void set_pose_matrix(unsigned, const Matrix &);
+       ProgramData &get_shader_data();
+       const ProgramData &get_shader_data() const;
+
+       DEPRECATED void set_uniform(const std::string &, const KeyFrame::AnimatedUniform &);
+
+       virtual const Matrix *get_matrix() const { return &matrix; }
+
+       virtual void setup_render(Renderer &, const Tag &) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/animation/animation.cpp b/source/animation/animation.cpp
new file mode 100644 (file)
index 0000000..a8d9e96
--- /dev/null
@@ -0,0 +1,633 @@
+#include <cmath>
+#include <msp/core/maputils.h>
+#include <msp/datafile/collection.h>
+#include <msp/interpolate/bezierspline.h>
+#include "animation.h"
+#include "animationeventobserver.h"
+#include "armature.h"
+#include "error.h"
+#include "pose.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Animation::Animation():
+       armature(0),
+       looping(false)
+{ }
+
+Animation::~Animation()
+{
+       for(vector<Curve *>::iterator i=curves.begin(); i!=curves.end(); ++i)
+               delete *i;
+}
+
+void Animation::set_armature(const Armature &a)
+{
+       if(!keyframes.empty() && &a!=armature)
+               throw invalid_operation("Animation::set_armature");
+       armature = &a;
+}
+
+unsigned Animation::get_slot_for_uniform(const string &n) const
+{
+       for(unsigned i=0; i<uniforms.size(); ++i)
+               if(uniforms[i].name==n)
+                       return i;
+       throw key_error(n);
+}
+
+const string &Animation::get_uniform_name(unsigned i) const
+{
+       if(i>=uniforms.size())
+               throw out_of_range("Animation::get_uniform_name");
+       return uniforms[i].name;
+}
+
+void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf)
+{
+       add_keyframe(t, &kf, false, false);
+       create_curves();
+}
+
+void Animation::add_keyframe_owned(const Time::TimeDelta &t, const KeyFrame *kf)
+{
+       add_keyframe(t, kf, false, true);
+       create_curves();
+}
+
+void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf, float slope)
+{
+       add_keyframe(t, &kf, slope, slope, false);
+       create_curves();
+}
+
+void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame &kf, float ss, float es)
+{
+       add_keyframe(t, &kf, ss, es, false);
+       create_curves();
+}
+
+void Animation::add_control_keyframe(const KeyFrame &kf)
+{
+       if(keyframes.empty())
+               throw invalid_operation("Animation::add_control_keyframe");
+
+       add_keyframe(keyframes.back().time, &kf, true, false);
+}
+
+void Animation::add_control_keyframe_owned(const KeyFrame *kf)
+{
+       if(keyframes.empty())
+               throw invalid_operation("Animation::add_control_keyframe_owned");
+
+       add_keyframe(keyframes.back().time, kf, true, true);
+}
+
+void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame *kf, float ss, float es, bool owned)
+{
+       if(keyframes.empty())
+               return add_keyframe(t, kf, false, owned);
+
+       if(keyframes.back().control)
+               throw invalid_operation("Animation::add_keyframe");
+
+       const KeyFrame &last = *keyframes.back().keyframe;
+       const Transform &trn = kf->get_transform();
+       const Transform &last_trn = last.get_transform();
+       const KeyFrame::UniformMap &kf_unis = kf->get_uniforms();
+       const KeyFrame::UniformMap &last_unis = last.get_uniforms();
+       for(unsigned i=1; i<=2; ++i)
+       {
+               float x = (i==1 ? ss/3 : 1-es/3);
+               KeyFrame *ckf = new KeyFrame;
+               Transform ctrn;
+               ctrn.set_position(last_trn.get_position()*(1-x)+trn.get_position()*x);
+               const Transform::AngleVector3 &e1 = last_trn.get_euler();
+               const Transform::AngleVector3 &e2 = trn.get_euler();
+               ctrn.set_euler(Transform::AngleVector3(e1.x*(1-x)+e2.x*x, e1.y*(1-x)+e2.y*x, e1.z*(1-x)+e2.z*x));
+               ctrn.set_scale(last_trn.get_scale()*(1-x)+trn.get_scale()*x);
+               ckf->set_transform(ctrn);
+
+               for(KeyFrame::UniformMap::const_iterator j=kf_unis.begin(); j!=kf_unis.end(); ++j)
+               {
+                       KeyFrame::UniformMap::const_iterator k = last_unis.find(j->first);
+                       if(k==last_unis.end())
+                               continue;
+
+                       KeyFrame::AnimatedUniform uni(j->second.size, 0.0f);
+                       for(unsigned c=0; c<uni.size; ++c)
+                               uni.values[c] = k->second.values[c]*(1-x)+j->second.values[c]*x;
+
+                       ckf->set_uniform(j->first, uni);
+               }
+
+               add_keyframe(t, ckf, true, true);
+       }
+
+       add_keyframe(t, kf, false, owned);
+}
+
+void Animation::add_keyframe(const Time::TimeDelta &t, const KeyFrame *kf, bool c, bool owned)
+{
+       if(c && keyframes.empty())
+               throw invalid_argument("Animation::add_keyframe");
+       if(keyframes.empty() && t!=Time::zero)
+               throw invalid_argument("Animation::add_keyframe");
+       if(!keyframes.empty() && t<keyframes.back().time)
+               throw invalid_argument("Animation::add_keyframe");
+       if(kf->get_pose() && armature && kf->get_pose()->get_armature()!=armature)
+               throw invalid_argument("Animation::add_keyframe");
+
+       const KeyFrame::UniformMap &kf_uniforms = kf->get_uniforms();
+       for(vector<UniformInfo>::const_iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
+       {
+               KeyFrame::UniformMap::const_iterator j = kf_uniforms.find(i->name);
+               if(j!=kf_uniforms.end() && j->second.size!=i->size)
+                       throw invalid_argument("Animation::add_keyframe");
+       }
+
+       if(kf->get_pose() && !armature)
+               armature = kf->get_pose()->get_armature();
+
+       TimedKeyFrame tkf;
+       tkf.time = t;
+       tkf.keyframe = kf;
+       if(!owned)
+               tkf.keyframe.keep();
+       tkf.control = c;
+
+       keyframes.push_back(tkf);
+
+       for(KeyFrame::UniformMap::const_iterator i=kf_uniforms.begin(); i!=kf_uniforms.end(); ++i)
+       {
+               bool found = false;
+               for(vector<UniformInfo>::const_iterator j=uniforms.begin(); (!found && j!=uniforms.end()); ++j)
+                       found = (j->name==i->first);
+
+               if(!found)
+                       uniforms.push_back(UniformInfo(i->first, i->second.size));
+       }
+}
+
+void Animation::create_curves()
+{
+       for(vector<Curve *>::iterator i=curves.begin(); i!=curves.end(); ++i)
+               delete *i;
+       curves.clear();
+
+       curves.reserve(6+uniforms.size());
+       create_curve(POSITION, Transform::POSITION, &extract_position);
+       create_curve(EULER, Transform::EULER, &extract_euler);
+       create_curve(SCALE, Transform::SCALE, &extract_scale);
+
+       uniform_curve_offset = curves.size();
+       for(vector<UniformInfo>::const_iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
+       {
+               if(i->size==1)
+                       create_curve<1>(UNIFORM, -1, ExtractUniform<1>(i->name));
+               else if(i->size==2)
+                       create_curve<2>(UNIFORM, -1, ExtractUniform<2>(i->name));
+               else if(i->size==3)
+                       create_curve<3>(UNIFORM, -1, ExtractUniform<3>(i->name));
+               else if(i->size==4)
+                       create_curve<4>(UNIFORM, -1, ExtractUniform<4>(i->name));
+       }
+}
+
+void Animation::create_curve(CurveTarget target, Transform::ComponentMask mask, ExtractComponent::Extract extract)
+{
+       Transform::ComponentMask all = mask;
+       Transform::ComponentMask any = Transform::NONE;
+       for(vector<TimedKeyFrame>::const_iterator i=keyframes.begin(); i!=keyframes.end(); ++i)
+       {
+               all = all&i->keyframe->get_transform().get_mask();
+               any = any|i->keyframe->get_transform().get_mask();
+       }
+
+       if(all==mask)
+               create_curve<3>(target, -1, extract);
+       else if(any&mask)
+       {
+               unsigned low_bit = mask&(mask>>2);
+               for(unsigned i=3; i-->0; )
+               {
+                       Transform::ComponentMask bit = static_cast<Transform::ComponentMask>(low_bit<<i);
+                       if(any&bit)
+                               create_curve<1>(target, i, ExtractComponent(extract, i, bit));
+               }
+       }
+}
+
+template<unsigned N, typename T>
+void Animation::create_curve(CurveTarget target, int component, const T &extract)
+{
+       typedef typename ValueCurve<N>::Knot Knot;
+
+       vector<Knot> knots;
+       unsigned n_control = 0;
+       for(vector<TimedKeyFrame>::const_iterator i=keyframes.begin(); i!=keyframes.end(); ++i)
+       {
+               if(i->control && knots.empty())
+                       continue;
+
+               typename Interpolate::SplineValue<float, N>::Type value;
+               if(extract(*i->keyframe, value))
+               {
+                       typename Knot::Value dvalue = value;
+                       float x = i->time/Time::sec;
+                       if(i->control)
+                       {
+                               ++n_control;
+                               if(n_control>2)
+                                       throw logic_error("too many control keyframes");
+                       }
+                       else
+                       {
+                               if(n_control==1)
+                               {
+                                       typename Knot::Value cv = knots.back().y;
+                                       knots.back().y = (knots[knots.size()-2].y+cv*2.0)/3.0;
+                                       knots.push_back(Knot(x, (dvalue+cv*2.0)/3.0));
+                               }
+                               else if(n_control==0 && !knots.empty())
+                               {
+                                       typename Knot::Value prev = knots.back().y;
+                                       knots.push_back(Knot(knots.back().x, (prev*2.0+dvalue)/3.0));
+                                       knots.push_back(Knot(x, (prev+dvalue*2.0)/3.0));
+                               }
+                               n_control = 0;
+                       }
+                       knots.push_back(Knot(x, value));
+               }
+       }
+       
+       while(n_control--)
+               knots.pop_back();
+
+       if(knots.size()==1)
+       {
+               knots.push_back(knots.back());
+               knots.push_back(knots.back());
+               knots.back().x += 1;
+               knots.push_back(knots.back());
+       }
+
+       curves.push_back(new ValueCurve<N>(target, component, knots));
+}
+
+bool Animation::extract_position(const KeyFrame &kf, Vector3 &value)
+{
+       value = kf.get_transform().get_position();
+       return true;
+}
+
+bool Animation::extract_euler(const KeyFrame &kf, Vector3 &value)
+{
+       const Transform::AngleVector3 &euler = kf.get_transform().get_euler();
+       value = Vector3(euler.x.radians(), euler.y.radians(), euler.z.radians());
+       return true;
+}
+
+bool Animation::extract_scale(const KeyFrame &kf, Vector3 &value)
+{
+       value = kf.get_transform().get_scale();
+       return true;
+}
+
+void Animation::add_event(const Time::TimeDelta &t, const string &n, const Variant &v)
+{
+       Event event;
+       event.time = t;
+       event.name = n;
+       event.value = v;
+       events.push_back(event);
+}
+
+const Time::TimeDelta &Animation::get_duration() const
+{
+       if(keyframes.empty())
+               return Time::zero;
+
+       return keyframes.back().time;
+}
+
+void Animation::set_looping(bool l)
+{
+       looping = l;
+}
+
+
+Animation::Curve::Curve(CurveTarget t, int c):
+       target(t),
+       component(c)
+{ }
+
+
+template<unsigned N>
+Animation::ValueCurve<N>::ValueCurve(CurveTarget t, int c, const vector<Knot> &k):
+       Curve(t, c),
+       spline(Interpolate::BezierSpline<double, 3, N>(k))
+{ }
+
+template<unsigned N>
+void Animation::ValueCurve<N>::apply(float, Matrix &) const
+{
+       throw invalid_operation("ValueCurve::apply");
+}
+
+template<>
+void Animation::ValueCurve<1>::apply(float x, Matrix &matrix) const
+{
+       float value = spline(x);
+       if(target==POSITION || target==SCALE)
+       {
+               if(target==POSITION)
+               {
+                       Vector3 vec;
+                       vec[component] = value;
+                       matrix.translate(vec);
+               }
+               else
+               {
+                       Vector3 vec(1.0f, 1.0f, 1.0f);
+                       vec[component] = value;
+                       matrix.scale(vec);
+               }
+       }
+       else if(target==EULER)
+       {
+               Vector3 vec;
+               vec[component] = 1.0f;
+               matrix.rotate(Geometry::Angle<float>::from_radians(value), vec);
+       }
+       else
+               throw invalid_operation("ValueCurve::apply");
+}
+
+template<>
+void Animation::ValueCurve<3>::apply(float x, Matrix &matrix) const
+{
+       Vector3 value = spline(x);
+       if(target==POSITION)
+               matrix.translate(value);
+       else if(target==EULER)
+       {
+               matrix.rotate(Geometry::Angle<float>::from_radians(value.z), Vector3(0, 0, 1));
+               matrix.rotate(Geometry::Angle<float>::from_radians(value.y), Vector3(0, 1, 0));
+               matrix.rotate(Geometry::Angle<float>::from_radians(value.x), Vector3(1, 0, 0));
+       }
+       else if(target==SCALE)
+               matrix.scale(value);
+       else
+               throw invalid_operation("ValueCurve::apply");
+}
+
+template<unsigned N>
+void Animation::ValueCurve<N>::apply(float x, KeyFrame::AnimatedUniform &uni) const
+{
+       uni.size = N;
+       typename Interpolate::Spline<double, 3, N>::Value value = spline(x);
+       for(unsigned i=0; i<N; ++i)
+               uni.values[i] = Interpolate::SplineValue<double, N>::get(value, i);
+}
+
+
+bool Animation::ExtractComponent::operator()(const KeyFrame &kf, float &value) const
+{
+       Vector3 vec;
+       if(!extract(kf, vec))
+               return false;
+
+       value = vec[index];
+       return kf.get_transform().get_mask()&mask;
+}
+
+
+template<unsigned N>
+bool Animation::ExtractUniform<N>::operator()(const KeyFrame &kf, typename Interpolate::SplineValue<float, N>::Type &value) const
+{
+       const KeyFrame::UniformMap &kf_uniforms = kf.get_uniforms();
+       const KeyFrame::UniformMap::const_iterator i = kf_uniforms.find(name);
+       if(i==kf_uniforms.end())
+               return false;
+
+       value = Interpolate::SplineValue<float, N>::make(i->second.values);
+       return true;
+}
+
+
+Animation::UniformInfo::UniformInfo(const string &n, unsigned s):
+       name(n),
+       size(s)
+{ }
+
+
+Animation::Iterator::Iterator(const Animation &a):
+       animation(&a),
+       event_iter(animation->events.begin()),
+       end(false)
+{
+}
+
+Animation::Iterator &Animation::Iterator::operator+=(const Time::TimeDelta &t)
+{
+       const Time::TimeDelta &duration = animation->get_duration();
+       if(!duration)
+               return *this;
+
+       elapsed += t;
+       if(animation->looping)
+       {
+               while(elapsed>=duration)
+                       elapsed -= duration;
+       }
+       else if(elapsed>=duration)
+       {
+               end = true;
+               elapsed = duration;
+       }
+
+       return *this;
+}
+
+void Animation::Iterator::dispatch_events(AnimationEventObserver &observer)
+{
+       for(; (event_iter!=animation->events.end() && event_iter->time<=elapsed); ++event_iter)
+               observer.animation_event(0, event_iter->name, event_iter->value);
+}
+
+Matrix Animation::Iterator::get_matrix() const
+{
+       Matrix matrix;
+       for(unsigned i=0; i<animation->uniform_curve_offset; ++i)
+               animation->curves[i]->apply(elapsed/Time::sec, matrix);
+       return matrix;
+}
+
+KeyFrame::AnimatedUniform Animation::Iterator::get_uniform(unsigned i) const
+{
+       if(i>=animation->uniforms.size())
+               throw out_of_range("Animation::Iterator::get_uniform");
+
+       KeyFrame::AnimatedUniform uni(animation->uniforms[i].size, 0.0f);
+       animation->curves[animation->uniform_curve_offset+i]->apply(elapsed/Time::sec, uni);
+       return uni;
+}
+
+Matrix Animation::Iterator::get_pose_matrix(unsigned link) const
+{
+       if(!animation->armature)
+               throw invalid_operation("Animation::Iterator::get_pose_matrix");
+       if(link>animation->armature->get_max_link_index())
+               throw out_of_range("Animation::Iterator::get_pose_matrix");
+
+       throw logic_error("pose animations are currently unimplemented");
+}
+
+
+Animation::Loader::Loader(Animation &a):
+       DataFile::CollectionObjectLoader<Animation>(a, 0)
+{
+       init();
+}
+
+Animation::Loader::Loader(Animation &a, Collection &c):
+       DataFile::CollectionObjectLoader<Animation>(a, &c)
+{
+       init();
+}
+
+void Animation::Loader::init()
+{
+       start_slope = 1;
+       end_slope = 1;
+       slopes_set = 0;
+       add("armature", &Animation::armature);
+       add("control_keyframe", &Loader::control_keyframe);
+       add("control_keyframe", &Loader::control_keyframe_inline);
+       add("event", &Loader::event);
+       add("event", &Loader::event1i);
+       add("event", &Loader::event1f);
+       add("event", &Loader::event2f);
+       add("event", &Loader::event3f);
+       add("event", &Loader::event4f);
+       add("interval", &Loader::interval);
+       add("keyframe", &Loader::keyframe);
+       add("keyframe", &Loader::keyframe_inline);
+       add("looping", &Animation::looping);
+       add("slopes", &Loader::slopes);
+}
+
+void Animation::Loader::finish()
+{
+       obj.create_curves();
+}
+
+void Animation::Loader::check_slopes_and_control(bool s, bool c)
+{
+       if(s && c)
+               throw logic_error("can't use both slopes and control keyframes in same segment");
+}
+
+void Animation::Loader::add_kf(const KeyFrame *kf, bool c, bool owned)
+{
+       if(slopes_set && !c)
+               obj.add_keyframe(current_time, kf, start_slope, end_slope, owned);
+       else
+               obj.add_keyframe(current_time, kf, c, owned);
+
+       start_slope = end_slope;
+       end_slope = 1;
+       slopes_set = (slopes_set<<1)&3;
+}
+
+void Animation::Loader::load_kf(const string &n, bool c)
+{
+       add_kf(&get_collection().get<KeyFrame>(n), c, false);
+}
+
+void Animation::Loader::load_kf_inline(bool c)
+{
+       RefPtr<KeyFrame> kf = new KeyFrame;
+       if(coll)
+               load_sub(*kf, get_collection());
+       else
+               load_sub(*kf);
+
+       add_kf(kf.get(), c, true);
+       kf.release();
+}
+
+void Animation::Loader::control_keyframe(const string &n)
+{
+       slopes_set &= 1;
+       check_slopes_and_control(slopes_set, true);
+       load_kf(n, true);
+}
+
+void Animation::Loader::control_keyframe_inline()
+{
+       slopes_set &= 1;
+       check_slopes_and_control(slopes_set, true);
+       load_kf_inline(true);
+}
+
+void Animation::Loader::event(const string &n)
+{
+       obj.add_event(current_time, n);
+}
+
+void Animation::Loader::event1i(const string &n, int v)
+{
+       obj.add_event(current_time, n, v);
+}
+
+void Animation::Loader::event1f(const string &n, float v)
+{
+       obj.add_event(current_time, n, v);
+}
+
+void Animation::Loader::event2f(const string &n, float v0, float v1)
+{
+       obj.add_event(current_time, n, LinAl::Vector<float, 2>(v0, v1));
+}
+
+void Animation::Loader::event3f(const string &n, float v0, float v1, float v2)
+{
+       obj.add_event(current_time, n, Vector3(v0, v1, v2));
+}
+
+void Animation::Loader::event4f(const string &n, float v0, float v1, float v2, float v3)
+{
+       obj.add_event(current_time, n, Vector4(v0, v1, v2, v3));
+}
+
+void Animation::Loader::interval(float t)
+{
+       current_time += t*Time::sec;
+}
+
+void Animation::Loader::keyframe(const string &n)
+{
+       load_kf(n, false);
+}
+
+void Animation::Loader::keyframe_inline()
+{
+       load_kf_inline(false);
+}
+
+void Animation::Loader::slopes(float s, float e)
+{
+       check_slopes_and_control(true, (!obj.keyframes.empty() && obj.keyframes.back().control));
+
+       start_slope = s;
+       end_slope = e;
+       slopes_set = 1;
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/animation/animation.h b/source/animation/animation.h
new file mode 100644 (file)
index 0000000..6845d23
--- /dev/null
@@ -0,0 +1,213 @@
+#ifndef MSP_GL_ANIMATION_H_
+#define MSP_GL_ANIMATION_H_
+
+#include <msp/core/refptr.h>
+#include <msp/datafile/objectloader.h>
+#include <msp/interpolate/spline.h>
+#include <msp/time/timedelta.h>
+#include "keyframe.h"
+
+namespace Msp {
+namespace GL {
+
+class AnimationEventObserver;
+class Armature;
+class Matrix;
+class Pose;
+
+/**
+An Animation is a sequence of KeyFrames combined with timing information.  The
+state at any point in the animation can be interpolated from the keyframes.
+*/
+class Animation
+{
+public:
+       class Loader: public DataFile::CollectionObjectLoader<Animation>
+       {
+       private:
+               Time::TimeDelta current_time;
+               float start_slope;
+               float end_slope;
+               int slopes_set;
+
+       public:
+               Loader(Animation &);
+               Loader(Animation &, Collection &);
+       private:
+               void init();
+               virtual void finish();
+
+               void check_slopes_and_control(bool, bool);
+               void add_kf(const KeyFrame *, bool, bool);
+               void load_kf(const std::string &, bool);
+               void load_kf_inline(bool);
+
+               void control_keyframe(const std::string &);
+               void control_keyframe_inline();
+               void event(const std::string &);
+               void event1i(const std::string &, int);
+               void event1f(const std::string &, float);
+               void event2f(const std::string &, float, float);
+               void event3f(const std::string &, float, float, float);
+               void event4f(const std::string &, float, float, float, float);
+               void interval(float);
+               void keyframe(const std::string &);
+               void keyframe_inline();
+               void slopes(float, float);
+       };
+
+private:
+       enum CurveTarget
+       {
+               POSITION,
+               EULER,
+               SCALE,
+               UNIFORM
+       };
+
+       class Curve
+       {
+       protected:
+               CurveTarget target;
+               int component;
+
+               Curve(CurveTarget, int);
+       public:
+               virtual ~Curve() { }
+
+               virtual void apply(float, Matrix &) const = 0;
+               virtual void apply(float, KeyFrame::AnimatedUniform &) const = 0;
+       };
+
+       template<unsigned N>
+       class ValueCurve: public Curve
+       {
+       public:
+               typedef typename Interpolate::SplineKnot<double, N> Knot;
+
+       private:
+               Interpolate::Spline<double, 3, N> spline;
+
+       public:
+               ValueCurve(CurveTarget, int, const std::vector<Knot> &);
+
+               virtual void apply(float, Matrix &) const;
+               virtual void apply(float, KeyFrame::AnimatedUniform &) const;
+       };
+
+       struct ExtractComponent
+       {
+               typedef bool (*Extract)(const KeyFrame &, Vector3 &);
+
+               Extract extract;
+               unsigned index;
+               Transform::ComponentMask mask;
+
+               ExtractComponent(Extract e, unsigned i, Transform::ComponentMask m): extract(e), index(i), mask(m) { }
+
+               bool operator()(const KeyFrame &, float &) const;
+       };
+
+       template<unsigned N>
+       struct ExtractUniform
+       {
+               const std::string &name;
+
+               ExtractUniform(const std::string &n): name(n) { }
+
+               bool operator()(const KeyFrame &, typename Interpolate::SplineValue<float, N>::Type &) const;
+       };
+
+       struct TimedKeyFrame
+       {
+               Time::TimeDelta time;
+               RefPtr<const KeyFrame> keyframe;
+               bool control;
+       };
+
+       struct Event
+       {
+               Time::TimeDelta time;
+               std::string name;
+               Variant value;
+       };
+
+       struct UniformInfo
+       {
+               std::string name;
+               unsigned size;
+
+               UniformInfo(const std::string &, unsigned);
+       };
+
+public:
+       class Iterator
+       {
+       private:
+               const Animation *animation;
+               Time::TimeDelta elapsed;
+               std::vector<Event>::const_iterator event_iter;
+               bool end;
+
+       public:
+               Iterator(const Animation &);
+
+               Iterator &operator+=(const Time::TimeDelta &);
+               void dispatch_events(AnimationEventObserver &);
+
+               bool is_end() const { return end; }
+               Matrix get_matrix() const;
+               KeyFrame::AnimatedUniform get_uniform(unsigned) const;
+               Matrix get_pose_matrix(unsigned) const;
+       };
+
+private:
+       const Armature *armature;
+       std::vector<TimedKeyFrame> keyframes;
+       std::vector<Event> events;
+       bool looping;
+       std::vector<UniformInfo> uniforms;
+       std::vector<Curve *> curves;
+       unsigned uniform_curve_offset;
+
+public:
+       Animation();
+       ~Animation();
+
+       void set_armature(const Armature &);
+       const Armature *get_armature() const { return armature; }
+
+       unsigned get_n_uniforms() const { return uniforms.size(); }
+       unsigned get_slot_for_uniform(const std::string &) const;
+       const std::string &get_uniform_name(unsigned) const;
+
+       void add_keyframe(const Time::TimeDelta &, const KeyFrame &);
+       void add_keyframe_owned(const Time::TimeDelta &, const KeyFrame *);
+       DEPRECATED void add_keyframe(const Time::TimeDelta &, const KeyFrame &, float);
+       DEPRECATED void add_keyframe(const Time::TimeDelta &, const KeyFrame &, float, float);
+       void add_control_keyframe(const KeyFrame &);
+       void add_control_keyframe_owned(const KeyFrame *);
+private:
+       void add_keyframe(const Time::TimeDelta &, const KeyFrame *, float, float, bool);
+       void add_keyframe(const Time::TimeDelta &, const KeyFrame *, bool, bool);
+       void prepare_keyframe(TimedKeyFrame &);
+       void create_curves();
+       void create_curve(CurveTarget, Transform::ComponentMask, ExtractComponent::Extract);
+       template<unsigned N, typename T>
+       void create_curve(CurveTarget target, int, const T &);
+       static bool extract_position(const KeyFrame &, Vector3 &);
+       static bool extract_euler(const KeyFrame &, Vector3 &);
+       static bool extract_scale(const KeyFrame &, Vector3 &);
+public:
+       void add_event(const Time::TimeDelta &, const std::string &, const Variant & = Variant());
+
+       const Msp::Time::TimeDelta &get_duration() const;
+
+       void set_looping(bool);
+       bool is_looping() const { return looping; }
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/animation/animationeventobserver.h b/source/animation/animationeventobserver.h
new file mode 100644 (file)
index 0000000..1eafc4d
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef MSP_GL_ANIMATIONEVENTOBSERVER_H_
+#define MSP_GL_ANIMATIONEVENTOBSERVER_H_
+
+#include <string>
+#include <msp/core/variant.h>
+
+namespace Msp {
+namespace GL {
+
+class Placeable;
+
+class AnimationEventObserver
+{
+protected:
+       AnimationEventObserver() { }
+public:
+       virtual ~AnimationEventObserver() { }
+
+       virtual void animation_event(Placeable *, const std::string &, const Variant &) { }
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/animation/animationplayer.cpp b/source/animation/animationplayer.cpp
new file mode 100644 (file)
index 0000000..ac16321
--- /dev/null
@@ -0,0 +1,240 @@
+#include <msp/core/algorithm.h>
+#include "animatedobject.h"
+#include "animationplayer.h"
+#include "armature.h"
+#include "programdata.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+AnimationPlayer::Target &AnimationPlayer::get_slot(Placeable &obj)
+{
+       ObjectMap::iterator i = objects.find(&obj);
+       if(i!=objects.end())
+               return i->second;
+
+       return objects.insert(ObjectMap::value_type(&obj, Target(obj))).first->second;
+}
+
+AnimationPlayer::Target &AnimationPlayer::play_(Placeable &obj, const Animation &anim, bool stacked, float speed)
+{
+       Target &target = get_slot(obj);
+       if(!stacked)
+       {
+               target.animations.clear();
+               target.base_matrix = Matrix();
+       }
+       else if(target.animations.empty())
+               target.base_matrix = *obj.get_matrix();
+       target.stacked = stacked;
+       // TODO check for incompatible armature
+       target.armature = anim.get_armature();
+       target.animations.push_back(PlayingAnimation(anim, speed));
+       return target;
+}
+
+void AnimationPlayer::play(AnimatedObject &obj, const Animation &anim, float speed)
+{
+       Target &target = play_(obj, anim, false, speed);
+       target.object = &obj;
+}
+
+void AnimationPlayer::play(Placeable &obj, const Animation &anim, float speed)
+{
+       play_(obj, anim, false, speed);
+}
+
+void AnimationPlayer::play_stacked(AnimatedObject &obj, const Animation &anim, float speed)
+{
+       Target &target = play_(obj, anim, true, speed);
+       target.object = &obj;
+}
+
+void AnimationPlayer::play_stacked(Placeable &obj, const Animation &anim, float speed)
+{
+       play_(obj, anim, true, speed);
+}
+
+unsigned AnimationPlayer::get_n_active_animations(const AnimatedObject &obj) const
+{
+       ObjectMap::const_iterator i = objects.find(&obj);
+       return (i!=objects.end() ? i->second.animations.size() : 0);
+}
+
+void AnimationPlayer::observe_events(AnimatedObject &obj, AnimationEventObserver &observer)
+{
+       Target &target = get_slot(obj);
+       if(find(target.event_observers, &observer)==target.event_observers.end())
+               target.event_observers.push_back(&observer);
+}
+
+void AnimationPlayer::unobserve_events(AnimatedObject &obj, AnimationEventObserver &observer)
+{
+       ObjectMap::iterator i = objects.find(&obj);
+       if(i==objects.end())
+               return;
+
+       vector<AnimationEventObserver *>::iterator j = find(i->second.event_observers, &observer);
+       if(j!=i->second.event_observers.end())
+               i->second.event_observers.erase(j);
+}
+
+void AnimationPlayer::unobserve_events(AnimationEventObserver &observer)
+{
+       for(ObjectMap::iterator i=objects.begin(); i!=objects.end(); ++i)
+       {
+               vector<AnimationEventObserver *>::iterator j = find(i->second.event_observers, &observer);
+               if(j!=i->second.event_observers.end())
+                       i->second.event_observers.erase(j);
+       }
+}
+
+void AnimationPlayer::stop(Placeable &obj)
+{
+       objects.erase(&obj);
+}
+
+void AnimationPlayer::stop(Placeable &obj, const Animation &anim)
+{
+       ObjectMap::iterator i = objects.find(&obj);
+       if(i==objects.end())
+               return;
+
+       for(vector<PlayingAnimation>::iterator j=i->second.animations.begin(); j!=i->second.animations.end(); ++j)
+               if(j->animation==&anim)
+               {
+                       i->second.animations.erase(j);
+                       break;
+               }
+
+       if(i->second.animations.empty())
+               objects.erase(i);
+}
+
+void AnimationPlayer::tick(const Time::TimeDelta &dt)
+{
+       for(ObjectMap::iterator i=objects.begin(); i!=objects.end(); )
+       {
+               if(i->second.stacked)
+                       tick_stacked(i->second, dt);
+               else if(!i->second.animations.empty())
+                       tick_single(i->second, dt);
+
+               if(i->second.animations.empty() && i->second.event_observers.empty())
+                       objects.erase(i++);
+               else
+                       ++i;
+       }
+}
+
+void AnimationPlayer::tick_single(Target &target, const Time::TimeDelta &dt)
+{
+       PlayingAnimation &anim = target.animations.front();
+       anim.iterator += dt*anim.speed;
+       target.placeable.set_matrix(anim.iterator.get_matrix());
+
+       if(target.object)
+       {
+               unsigned n_uniforms = anim.animation->get_n_uniforms();
+               for(unsigned i=0; i<n_uniforms; ++i)
+                       set_object_uniform(*target.object, anim.animation->get_uniform_name(i), anim.iterator.get_uniform(i));
+
+               if(target.armature)
+               {
+                       unsigned max_index = target.armature->get_max_link_index();
+                       for(unsigned i=0; i<=max_index; ++i)
+                               target.object->set_pose_matrix(i, anim.iterator.get_pose_matrix(i));
+               }
+       }
+
+       anim.iterator.dispatch_events(target);
+
+       if(anim.iterator.is_end())
+               target.animations.clear();
+}
+
+void AnimationPlayer::tick_stacked(Target &target, const Time::TimeDelta &dt)
+{
+       Matrix matrix = target.base_matrix;
+       for(vector<PlayingAnimation>::iterator i=target.animations.begin(); i!=target.animations.end(); ++i)
+       {
+               i->iterator += dt*i->speed;
+               matrix *= i->iterator.get_matrix();
+
+               if(target.object)
+               {
+                       unsigned n_uniforms = i->animation->get_n_uniforms();
+                       for(unsigned j=0; j<n_uniforms; ++j)
+                               set_object_uniform(*target.object, i->animation->get_uniform_name(j), i->iterator.get_uniform(j));
+               }
+       }
+       target.placeable.set_matrix(matrix);
+
+       if(target.object && target.armature)
+       {
+               unsigned max_index = target.armature->get_max_link_index();
+               for(unsigned i=0; i<=max_index; ++i)
+               {
+                       matrix = Matrix();
+                       /* XXX This is in all likelihood incorrect.  The stacking should be
+                       performed on local matrices. */
+                       for(vector<PlayingAnimation>::iterator j=target.animations.begin(); j!=target.animations.end(); ++j)
+                               if(j->animation->get_armature())
+                                       matrix *= j->iterator.get_pose_matrix(i);
+                       target.object->set_pose_matrix(i, matrix);
+               }
+       }
+
+       for(vector<PlayingAnimation>::iterator i=target.animations.begin(); i!=target.animations.end(); )
+       {
+               i->iterator.dispatch_events(target);
+
+               if(i->iterator.is_end())
+                       i = target.animations.erase(i);
+               else
+                       ++i;
+       }
+
+       if(target.animations.empty())
+               target.stacked = false;
+}
+
+void AnimationPlayer::set_object_uniform(AnimatedObject &obj, const string &name, const KeyFrame::AnimatedUniform &uni)
+{
+       ProgramData &shdata = obj.get_shader_data();
+
+       if(uni.size==1)
+               shdata.uniform(name, uni.values[0]);
+       else if(uni.size==2)
+               shdata.uniform2(name, uni.values);
+       else if(uni.size==3)
+               shdata.uniform3(name, uni.values);
+       else if(uni.size==4)
+               shdata.uniform4(name, uni.values);
+}
+
+
+AnimationPlayer::PlayingAnimation::PlayingAnimation(const Animation &a, float s):
+       animation(&a),
+       speed(s),
+       iterator(*animation)
+{ }
+
+
+AnimationPlayer::Target::Target(Placeable &p):
+       placeable(p),
+       object(0),
+       armature(0),
+       stacked(false)
+{ }
+
+void AnimationPlayer::Target::animation_event(Placeable *, const string &name, const Variant &value)
+{
+       for(vector<AnimationEventObserver *>::const_iterator i=event_observers.begin(); i!=event_observers.end(); ++i)
+               (*i)->animation_event(&placeable, name, value);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/animation/animationplayer.h b/source/animation/animationplayer.h
new file mode 100644 (file)
index 0000000..c45027d
--- /dev/null
@@ -0,0 +1,98 @@
+#ifndef MSP_GL_ANIMATIONPLAYER_H_
+#define MSP_GL_ANIMATIONPLAYER_H_
+
+#include <msp/time/timedelta.h>
+#include "animation.h"
+#include "animationeventobserver.h"
+#include "matrix.h"
+
+namespace Msp {
+namespace GL {
+
+class AnimatedObject;
+
+/**
+The bridge between Animations and AnimatedObjects.  A single AnimationPlayer
+can handle an arbitrary number of animations simultaneously.
+*/
+class AnimationPlayer
+{
+private:
+       struct PlayingAnimation
+       {
+               const Animation *animation;
+               float speed;
+               Animation::Iterator iterator;
+
+               PlayingAnimation(const Animation &, float);
+       };
+
+       struct Target: AnimationEventObserver
+       {
+               Placeable &placeable;
+               AnimatedObject *object;
+               Matrix base_matrix;
+               const Armature *armature;
+               std::vector<PlayingAnimation> animations;
+               bool stacked;
+               std::vector<AnimationEventObserver *> event_observers;
+
+               Target(Placeable &);
+
+               virtual void animation_event(Placeable *, const std::string &, const Variant &);
+       };
+
+       typedef std::map<const Placeable *, Target> ObjectMap;
+
+       ObjectMap objects;
+
+private:
+       Target &get_slot(Placeable &);
+
+       Target &play_(Placeable &, const Animation &, bool, float);
+public:
+       /// Plays an animation on an object.  Any previous animations are replaced.
+       void play(AnimatedObject &, const Animation &, float = 1.0f);
+
+       void play(Placeable &, const Animation &, float = 1.0f);
+
+       /** Plays an animation, stacked with other animations.  If no animations are
+       playing yet, the object's current matrix is used as the base. */
+       void play_stacked(AnimatedObject &, const Animation &, float = 1.0f);
+
+       void play_stacked(Placeable &, const Animation &, float = 1.0f);
+
+       /// Returns the number of animations currently affecting an object.
+       unsigned get_n_active_animations(const AnimatedObject &) const;
+
+       /** Request delivery of animation events for the given object.  Events will
+       be delivered from all current and future animations until the observer is
+       removed. */
+       void observe_events(AnimatedObject &, AnimationEventObserver &);
+
+       /// Remove an event observer from one object.
+       void unobserve_events(AnimatedObject &, AnimationEventObserver &);
+
+       /// Remove an event observer from all objects.
+       void unobserve_events(AnimationEventObserver &);
+
+       /// Stops all animations affecting an object.
+       void stop(Placeable &);
+
+       /// Stops a single animation affecting an object.
+       void stop(Placeable &, const Animation &);
+
+       /** Advances all playing animations.  Should be called in a regular manner,
+       preferably just before rendering. */
+       void tick(const Time::TimeDelta &);
+
+private:
+       void tick_single(Target &, const Time::TimeDelta &);
+       void tick_stacked(Target &, const Time::TimeDelta &);
+       static void set_object_uniform(AnimatedObject &, const std::string &, const KeyFrame::AnimatedUniform &);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/animation/armature.cpp b/source/animation/armature.cpp
new file mode 100644 (file)
index 0000000..6e921e3
--- /dev/null
@@ -0,0 +1,91 @@
+#include <msp/core/maputils.h>
+#include "armature.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Armature::Link &Armature::add_link()
+{
+       links.push_back(Link(string(), links.size()));
+       return links.back();
+}
+
+const Armature::Link &Armature::get_link(unsigned index) const
+{
+       for(vector<Link>::const_iterator i=links.begin(); i!=links.end(); ++i)
+               if(i->get_index()==index)
+                       return *i;
+       throw key_error(typeid(list<Link>));
+}
+
+const Armature::Link &Armature::get_link(const string &name) const
+{
+       for(vector<Link>::const_iterator i=links.begin(); i!=links.end(); ++i)
+               if(i->get_name()==name)
+                       return *i;
+       throw key_error(typeid(list<Link>));
+}
+
+unsigned Armature::get_max_link_index() const
+{
+       unsigned max_index = 0;
+       for(vector<Link>::const_iterator i=links.begin(); i!=links.end(); ++i)
+               max_index = max(max_index, i->get_index());
+       return max_index;
+}
+
+
+Armature::Link::Link(const string &n, unsigned i):
+       name(n),
+       index(i),
+       parent(0)
+{ }
+
+void Armature::Link::set_parent(const Link *p)
+{
+       parent = p;
+}
+
+void Armature::Link::set_base(const Vector3 &b)
+{
+       base = b;
+}
+
+
+Armature::Loader::Loader(Armature &a):
+       DataFile::ObjectLoader<Armature>(a)
+{
+       add("link", &Loader::link);
+}
+
+void Armature::Loader::link(const string &n)
+{
+       Link lnk(n, obj.links.size());
+       load_sub(lnk, obj);
+       obj.links.push_back(lnk);
+}
+
+
+Armature::Link::Loader::Loader(Link &l, const Armature &a):
+       DataFile::ObjectLoader<Link>(l),
+       armature(a)
+{
+       add("base",   &Loader::base);
+       add("index",  &Link::index);
+       add("parent", &Loader::parent);
+}
+
+void Armature::Link::Loader::base(float x, float y, float z)
+{
+       obj.base = Vector3(x, y, z);
+}
+
+void Armature::Link::Loader::parent(const string &n)
+{
+       obj.parent = &armature.get_link(n);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/animation/armature.h b/source/animation/armature.h
new file mode 100644 (file)
index 0000000..9dc2f50
--- /dev/null
@@ -0,0 +1,71 @@
+#ifndef MSP_GL_ARMATURE_H_
+#define MSP_GL_ARMATURE_H_
+
+#include <list>
+#include <string>
+#include <msp/datafile/objectloader.h>
+#include "pose.h"
+#include "vector.h"
+
+namespace Msp {
+namespace GL {
+
+class Armature
+{
+public:
+       class Loader: public DataFile::ObjectLoader<Armature>
+       {
+       public:
+               Loader(Armature &);
+       private:
+               void link(const std::string &);
+       };
+
+       class Link
+       {
+       public:
+               class Loader: public DataFile::ObjectLoader<Link>
+               {
+               private:
+                       const Armature &armature;
+
+               public:
+                       Loader(Link &, const Armature &);
+               private:
+                       void base(float, float, float);
+                       void parent(const std::string &);
+               };
+
+       private:
+               std::string name;
+               unsigned index;
+               const Link *parent;
+               Vector3 base;
+
+       public:
+               Link(const std::string &, unsigned);
+
+               void set_parent(const Link *);
+               void set_base(const Vector3 &);
+
+               const std::string &get_name() const { return name; }
+               unsigned get_index() const { return index; }
+               const Link *get_parent() const { return parent; }
+               const Vector3 &get_base() const { return base; }
+       };
+
+private:
+       std::vector<Link> links;
+
+public:
+       Link &add_link();
+
+       const Link &get_link(unsigned) const;
+       const Link &get_link(const std::string &) const;
+       unsigned get_max_link_index() const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/animation/keyframe.cpp b/source/animation/keyframe.cpp
new file mode 100644 (file)
index 0000000..f610de2
--- /dev/null
@@ -0,0 +1,157 @@
+#include <msp/datafile/collection.h>
+#include "keyframe.h"
+#include "pose.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+// Avoid synthesizing RefPtr c'tor and d'tor in files including keyframe.h
+KeyFrame::KeyFrame()
+{ }
+
+KeyFrame::~KeyFrame()
+{ }
+
+void KeyFrame::set_transform(const Transform &t)
+{
+       transform = t;
+}
+
+void KeyFrame::set_matrix(const Matrix &m)
+{
+       transform = Transform::from_matrix(m);
+}
+
+void KeyFrame::set_uniform(const string &n, const AnimatedUniform &u)
+{
+       uniforms.erase(n);
+       uniforms.insert(UniformMap::value_type(n, u));
+}
+
+void KeyFrame::set_pose(const Pose &p)
+{
+       pose = &p;
+       pose.keep();
+}
+
+
+KeyFrame::AnimatedUniform::AnimatedUniform(unsigned s, float v0, float v1, float v2, float v3):
+       size(s)
+{
+       values[0] = v0;
+       values[1] = v1;
+       values[2] = v2;
+       values[3] = v3;
+}
+
+
+KeyFrame::Loader::Loader(KeyFrame &k):
+       DataFile::CollectionObjectLoader<KeyFrame>(k, 0)
+{
+       init();
+}
+
+KeyFrame::Loader::Loader(KeyFrame &k, Collection &c):
+       DataFile::CollectionObjectLoader<KeyFrame>(k, &c)
+{
+       init();
+}
+
+void KeyFrame::Loader::init()
+{
+       add("pose", &Loader::pose);
+       add("pose", &Loader::pose_inline);
+       add("transform", &Loader::transform);
+       add("uniforms", &Loader::uniforms);
+
+       // Deprecated; use the transform statement instead
+       add("position", &Loader::position);
+       add("rotation", &Loader::rotation);
+       add("scaling", &Loader::scaling_uniform);
+       add("scaling", &Loader::scaling);
+}
+
+void KeyFrame::Loader::pose(const string &n)
+{
+       obj.pose = &get_collection().get<Pose>(n);
+       obj.pose.keep();
+}
+
+void KeyFrame::Loader::pose_inline()
+{
+       RefPtr<Pose> p = new Pose;
+       load_sub(*p, get_collection());
+       obj.pose = p;
+}
+
+void KeyFrame::Loader::position(float x, float y, float z)
+{
+       obj.transform.set_position(Vector3(x, y, z));
+}
+
+void KeyFrame::Loader::rotation(float a, float x, float y, float z)
+{
+       obj.transform.set_rotation(Transform::Angle::from_degrees(a), Vector3(x, y, z));
+}
+
+void KeyFrame::Loader::scaling_uniform(float s)
+{
+       obj.transform.set_scale(s);
+}
+
+void KeyFrame::Loader::scaling(float x, float y, float z)
+{
+       obj.transform.set_scale(Vector3(x, y, z));
+}
+
+void KeyFrame::Loader::transform()
+{
+       load_sub(obj.transform);
+}
+
+void KeyFrame::Loader::uniforms()
+{
+       UniformsLoader ldr(obj);
+       load_sub_with(ldr);
+}
+
+
+KeyFrame::UniformsLoader::UniformsLoader(KeyFrame &k):
+       DataFile::ObjectLoader<KeyFrame>(k)
+{
+       add("uniform", &UniformsLoader::uniform1f);
+       add("uniform", &UniformsLoader::uniform2f);
+       add("uniform", &UniformsLoader::uniform3f);
+       add("uniform", &UniformsLoader::uniform4f);
+
+       // Deprecated
+       add("uniform1f", &UniformsLoader::uniform1f);
+       add("uniform2f", &UniformsLoader::uniform2f);
+       add("uniform3f", &UniformsLoader::uniform3f);
+       add("uniform4f", &UniformsLoader::uniform4f);
+}
+
+void KeyFrame::UniformsLoader::uniform1f(const string &n, float v)
+{
+       obj.uniforms.insert(UniformMap::value_type(n, AnimatedUniform(1, v)));
+}
+
+void KeyFrame::UniformsLoader::uniform2f(const string &n, float v0, float v1)
+{
+       obj.uniforms.insert(UniformMap::value_type(n, AnimatedUniform(2, v0, v1)));
+}
+
+void KeyFrame::UniformsLoader::uniform3f(const string &n, float v0, float v1, float v2)
+{
+       obj.uniforms.insert(UniformMap::value_type(n, AnimatedUniform(3, v0, v1, v2)));
+}
+
+void KeyFrame::UniformsLoader::uniform4f(const string &n, float v0, float v1, float v2, float v3)
+{
+       obj.uniforms.insert(UniformMap::value_type(n, AnimatedUniform(4, v0, v1, v2, v3)));
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/animation/keyframe.h b/source/animation/keyframe.h
new file mode 100644 (file)
index 0000000..5b748ad
--- /dev/null
@@ -0,0 +1,82 @@
+#ifndef MSP_GL_KEYFRAME_H_
+#define MSP_GL_KEYFRAME_H_
+
+#include <msp/core/refptr.h>
+#include <msp/datafile/objectloader.h>
+#include "matrix.h"
+#include "transform.h"
+
+namespace Msp {
+namespace GL {
+
+class Pose;
+
+/**
+Keyframes are used to encapsulate object state for animation.
+*/
+class KeyFrame
+{
+public:
+       class Loader: public DataFile::CollectionObjectLoader<KeyFrame>
+       {
+       public:
+               Loader(KeyFrame &);
+               Loader(KeyFrame &, Collection &);
+       private:
+               void init();
+
+               void pose(const std::string &);
+               void pose_inline();
+               void position(float, float, float);
+               void rotation(float, float, float, float);
+               void scaling_uniform(float);
+               void scaling(float, float, float);
+               void transform();
+               void uniforms();
+       };
+
+       class UniformsLoader: public DataFile::ObjectLoader<KeyFrame>
+       {
+       public:
+               UniformsLoader(KeyFrame &);
+
+       private:
+               void uniform1f(const std::string &, float);
+               void uniform2f(const std::string &, float, float);
+               void uniform3f(const std::string &, float, float, float);
+               void uniform4f(const std::string &, float, float, float, float);
+       };
+
+       struct AnimatedUniform
+       {
+               unsigned size;
+               float values[4];
+
+               AnimatedUniform(unsigned, float, float = 0.0f, float = 0.0f, float = 0.0f);
+       };
+
+       typedef std::map<std::string, AnimatedUniform> UniformMap;
+
+private:
+       Transform transform;
+       UniformMap uniforms;
+       RefPtr<const Pose> pose;
+
+public:
+       KeyFrame();
+       ~KeyFrame();
+
+       void set_transform(const Transform &);
+       void set_matrix(const Matrix &);
+       void set_uniform(const std::string &, const AnimatedUniform &);
+       void set_pose(const Pose &);
+       const Transform &get_transform() const { return transform; }
+       Matrix get_matrix() const { return transform.to_matrix(); }
+       const UniformMap &get_uniforms() const { return uniforms; }
+       const Pose *get_pose() const { return pose.get(); }
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/animation/pose.cpp b/source/animation/pose.cpp
new file mode 100644 (file)
index 0000000..99fa6bb
--- /dev/null
@@ -0,0 +1,90 @@
+#include <msp/datafile/collection.h>
+#include "armature.h"
+#include "error.h"
+#include "pose.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Pose::Pose():
+       armature(0)
+{ }
+
+Pose::Pose(const Armature &a):
+       armature(0)
+{
+       set_armature(a);
+}
+
+void Pose::set_armature(const Armature &a)
+{
+       if(armature)
+               throw invalid_operation("Pose::set_armature");
+       armature = &a;
+       links.resize(armature->get_max_link_index()+1);
+}
+
+void Pose::rotate_link(unsigned i, float angle, const Vector3 &axis)
+{
+       if(i>=links.size())
+               throw out_of_range("Pose::rotate_link");
+
+       const Armature::Link &arm_link = armature->get_link(i);
+       links[i].local_matrix.rotate(angle, axis);
+
+       // Keep the base point stationary
+       Vector3 base = arm_link.get_base();
+       Vector3 new_base = links[i].local_matrix*base;
+       links[i].local_matrix = Matrix::translation(base-new_base)*links[i].local_matrix;
+
+       if(const Armature::Link *parent = arm_link.get_parent())
+               links[i].matrix = links[parent->get_index()].matrix*links[i].local_matrix;
+
+       // XXX apply matrix to descendants of the link
+}
+
+const Matrix &Pose::get_link_matrix(unsigned i) const
+{
+       if(i>=links.size())
+               throw out_of_range("Pose::get_link_matrix");
+       return links[i].matrix;
+}
+
+
+Pose::Loader::Loader(Pose &p, Collection &c):
+       DataFile::CollectionObjectLoader<Pose>(p, &c)
+{
+       add("armature", &Loader::armature);
+       add("link",     &Loader::link);
+}
+
+void Pose::Loader::armature(const string &n)
+{
+       obj.set_armature(get_collection().get<Armature>(n));
+}
+
+void Pose::Loader::link(const string &n)
+{
+       if(!obj.armature)
+               throw logic_error("Armature must be specified first");
+       LinkLoader ldr(obj, obj.armature->get_link(n).get_index());
+       load_sub_with(ldr);
+}
+
+
+Pose::LinkLoader::LinkLoader(Pose &p, unsigned l):
+       DataFile::ObjectLoader<Pose>(p),
+       link_index(l)
+{
+       add("rotation", &LinkLoader::rotation);
+}
+
+void Pose::LinkLoader::rotation(float a, float x, float y, float z)
+{
+       obj.rotate_link(link_index, a, Vector3(x, y, z));
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/animation/pose.h b/source/animation/pose.h
new file mode 100644 (file)
index 0000000..cbd8137
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef MSP_GL_POSE_H_
+#define MSP_GL_POSE_H_
+
+#include <map>
+#include <msp/datafile/objectloader.h>
+#include "matrix.h"
+
+namespace Msp {
+namespace GL {
+
+class Armature;
+
+class Pose
+{
+public:
+       class Loader: public DataFile::CollectionObjectLoader<Pose>
+       {
+       public:
+               Loader(Pose &, Collection &);
+       private:
+               void armature(const std::string &);
+               void link(const std::string &);
+       };
+
+private:
+       struct Link
+       {
+               Matrix matrix;
+               Matrix local_matrix;
+       };
+
+       class LinkLoader: public DataFile::ObjectLoader<Pose>
+       {
+       private:
+               unsigned link_index;
+
+       public:
+               LinkLoader(Pose &, unsigned);
+       private:
+               void rotation(float, float, float, float);
+       };
+
+       const Armature *armature;
+       std::vector<Link> links;
+
+public:
+       Pose();
+       Pose(const Armature &);
+
+       void set_armature(const Armature &);
+       const Armature *get_armature() const { return armature; }
+       void rotate_link(unsigned, float, const Vector3 &);
+       const Matrix &get_link_matrix(unsigned) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/animation/transform.cpp b/source/animation/transform.cpp
new file mode 100644 (file)
index 0000000..b703a5b
--- /dev/null
@@ -0,0 +1,171 @@
+#include "transform.h"
+
+namespace Msp {
+namespace GL {
+
+Transform::Transform():
+       position(0.0f, 0.0f, 0.0f),
+       euler(Angle::zero(), Angle::zero(), Angle::zero()),
+       scale(1.0f, 1.0f, 1.0f),
+       mask(NONE)
+{ }
+
+Transform Transform::from_matrix(const Matrix &matrix)
+{
+       Transform trn;
+       trn.position = matrix.column(3).slice<3>(0);
+
+       trn.euler.z = Geometry::atan2<float>(matrix(1, 0), matrix(0, 0));
+       Matrix m = Matrix::rotation(-trn.euler.z, Vector3(0.0f, 0.0f, 1.0f))*matrix;
+       trn.euler.y = Geometry::atan2<float>(-m(2, 0), m(0, 0));
+       m = Matrix::rotation(-trn.euler.y, Vector3(0.0f, 1.0f, 0.0f))*m;
+       trn.euler.x = Geometry::atan2<float>(m(2, 1), m(1, 1));
+       m = Matrix::rotation(-trn.euler.x, Vector3(1.0f, 0.0f, 0.0f))*m;
+
+       trn.scale = Vector3(m(0, 0), m(1, 1), m(2, 2));
+
+       trn.mask = POSITION|EULER|SCALE;
+       return trn;
+}
+
+void Transform::set_position(const Vector3 &p)
+{
+       position = p;
+       mask = mask|POSITION;
+}
+
+void Transform::set_euler(const AngleVector3 &e)
+{
+       euler = e;
+       mask = mask|EULER;
+}
+
+void Transform::set_rotation(const Angle &angle, const Vector3 &axis)
+{
+       euler = from_matrix(Matrix::rotation(angle, axis)).euler;
+       mask = mask|EULER;
+}
+
+void Transform::set_scale(float s)
+{
+       set_scale(Vector3(s, s, s));
+}
+
+void Transform::set_scale(const Vector3 &s)
+{
+       scale = s;
+       mask = mask|SCALE;
+}
+
+Matrix Transform::to_matrix() const
+{
+       Matrix result;
+       result.translate(position);
+       result.rotate(euler.z, Vector3(0.0f, 0.0f, 1.0f));
+       result.rotate(euler.y, Vector3(0.0f, 1.0f, 0.0f));
+       result.rotate(euler.x, Vector3(1.0f, 0.0f, 0.0f));
+       result.scale(scale);
+       return result;
+}
+
+
+Transform::Loader::Loader(Transform &t):
+       DataFile::ObjectLoader<Transform>(t)
+{
+       add("position_x", &Loader::position_x);
+       add("position_y", &Loader::position_y);
+       add("position_z", &Loader::position_z);
+       add("position", &Loader::position);
+       add("euler_x", &Loader::euler_x);
+       add("euler_y", &Loader::euler_y);
+       add("euler_z", &Loader::euler_z);
+       add("euler", &Loader::euler);
+       add("rotation", &Loader::rotation);
+       add("scale_x", &Loader::scale_x);
+       add("scale_y", &Loader::scale_y);
+       add("scale_z", &Loader::scale_z);
+       add("scale", &Loader::scale_uniform);
+       add("scale", &Loader::scale);
+}
+
+void Transform::Loader::position_x(float x)
+{
+       obj.position.x = x;
+       obj.mask = obj.mask|POSITION_X;
+}
+
+void Transform::Loader::position_y(float y)
+{
+       obj.position.y = y;
+       obj.mask = obj.mask|POSITION_Y;
+}
+
+void Transform::Loader::position_z(float z)
+{
+       obj.position.z = z;
+       obj.mask = obj.mask|POSITION_Z;
+}
+
+void Transform::Loader::position(float x, float y, float z)
+{
+       obj.set_position(Vector3(x, y, z));
+}
+
+void Transform::Loader::euler_x(float x)
+{
+       obj.euler.x = Angle::from_degrees(x);
+       obj.mask = obj.mask|EULER_X;
+}
+
+void Transform::Loader::euler_y(float y)
+{
+       obj.euler.y = Angle::from_degrees(y);
+       obj.mask = obj.mask|EULER_Y;
+}
+
+void Transform::Loader::euler_z(float z)
+{
+       obj.euler.z = Angle::from_degrees(z);
+       obj.mask = obj.mask|EULER_Z;
+}
+
+void Transform::Loader::euler(float x, float y, float z)
+{
+       obj.set_euler(AngleVector3(Angle::from_degrees(x), Angle::from_degrees(y), Angle::from_degrees(z)));
+}
+
+void Transform::Loader::rotation(float a, float x, float y, float z)
+{
+       obj.set_rotation(Angle::from_degrees(a), Vector3(x, y, z));
+}
+
+void Transform::Loader::scale_x(float x)
+{
+       obj.scale.x = x;
+       obj.mask = obj.mask|SCALE_X;
+}
+
+void Transform::Loader::scale_y(float y)
+{
+       obj.scale.y = y;
+       obj.mask = obj.mask|SCALE_Y;
+}
+
+void Transform::Loader::scale_z(float z)
+{
+       obj.scale.z = z;
+       obj.mask = obj.mask|SCALE_Z;
+}
+
+void Transform::Loader::scale_uniform(float s)
+{
+       obj.set_scale(s);
+}
+
+void Transform::Loader::scale(float x, float y, float z)
+{
+       obj.set_scale(Vector3(x, y, z));
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/animation/transform.h b/source/animation/transform.h
new file mode 100644 (file)
index 0000000..1d0aedd
--- /dev/null
@@ -0,0 +1,99 @@
+#ifndef MSP_GL_TRANSFORM_H_
+#define MSP_GL_TRANSFORM_H_
+
+#include <msp/datafile/objectloader.h>
+#include "matrix.h"
+
+namespace Msp {
+namespace GL {
+
+/**
+Stores a coordinate space transform as individual components.  Primarily
+intended for loading data from external sources.  At runtime transforms
+should generally be stored as matrices.
+*/
+class Transform
+{
+public:
+       class Loader: public DataFile::ObjectLoader<Transform>
+       {
+       public:
+               Loader(Transform &);
+
+       private:
+               void position_x(float);
+               void position_y(float);
+               void position_z(float);
+               void position(float, float, float);
+               void euler_x(float);
+               void euler_y(float);
+               void euler_z(float);
+               void euler(float, float, float);
+               void rotation(float, float, float, float);
+               void scale_x(float);
+               void scale_y(float);
+               void scale_z(float);
+               void scale_uniform(float);
+               void scale(float, float, float);
+       };
+
+       enum ComponentMask
+       {
+               NONE = 0,
+               POSITION_X = 1,
+               POSITION_Y = 2,
+               POSITION_Z = 4,
+               POSITION = POSITION_X|POSITION_Y|POSITION_Z,
+               EULER_X = 8,
+               EULER_Y = 16,
+               EULER_Z = 32,
+               EULER = EULER_X|EULER_Y|EULER_Z,
+               SCALE_X = 64,
+               SCALE_Y = 128,
+               SCALE_Z = 256,
+               SCALE = SCALE_X|SCALE_Y|SCALE_Z
+       };
+
+       typedef Geometry::Angle<float> Angle;
+       typedef LinAl::Vector<Angle, 3> AngleVector3;
+
+private:
+       Vector3 position;
+       AngleVector3 euler;
+       Vector3 scale;
+       ComponentMask mask;
+
+public:
+       Transform();
+
+       static Transform from_matrix(const Matrix &);
+
+       void set_position(const Vector3 &);
+       void set_euler(const AngleVector3 &);
+       void set_rotation(const Angle &, const Vector3 &);
+       void set_scale(float);
+       void set_scale(const Vector3 &);
+       const Vector3 &get_position() const { return position; }
+       const AngleVector3 &get_euler() const { return euler; }
+       const Vector3 &get_scale() const { return scale; }
+       ComponentMask get_mask() const { return mask; }
+
+       Matrix to_matrix() const;
+};
+
+inline Transform::ComponentMask operator&(Transform::ComponentMask m1, Transform::ComponentMask m2)
+{ return static_cast<Transform::ComponentMask>(static_cast<int>(m1)&static_cast<int>(m2)); }
+
+inline Transform::ComponentMask operator|(Transform::ComponentMask m1, Transform::ComponentMask m2)
+{ return static_cast<Transform::ComponentMask>(static_cast<int>(m1)|static_cast<int>(m2)); }
+
+inline Transform::ComponentMask operator^(Transform::ComponentMask m1, Transform::ComponentMask m2)
+{ return static_cast<Transform::ComponentMask>(static_cast<int>(m1)^static_cast<int>(m2)); }
+
+inline Transform::ComponentMask operator~(Transform::ComponentMask m)
+{ return static_cast<Transform::ComponentMask>(~static_cast<int>(m)); }
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/animationeventobserver.h b/source/animationeventobserver.h
deleted file mode 100644 (file)
index 1eafc4d..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef MSP_GL_ANIMATIONEVENTOBSERVER_H_
-#define MSP_GL_ANIMATIONEVENTOBSERVER_H_
-
-#include <string>
-#include <msp/core/variant.h>
-
-namespace Msp {
-namespace GL {
-
-class Placeable;
-
-class AnimationEventObserver
-{
-protected:
-       AnimationEventObserver() { }
-public:
-       virtual ~AnimationEventObserver() { }
-
-       virtual void animation_event(Placeable *, const std::string &, const Variant &) { }
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/animationplayer.cpp b/source/animationplayer.cpp
deleted file mode 100644 (file)
index ac16321..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-#include <msp/core/algorithm.h>
-#include "animatedobject.h"
-#include "animationplayer.h"
-#include "armature.h"
-#include "programdata.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-AnimationPlayer::Target &AnimationPlayer::get_slot(Placeable &obj)
-{
-       ObjectMap::iterator i = objects.find(&obj);
-       if(i!=objects.end())
-               return i->second;
-
-       return objects.insert(ObjectMap::value_type(&obj, Target(obj))).first->second;
-}
-
-AnimationPlayer::Target &AnimationPlayer::play_(Placeable &obj, const Animation &anim, bool stacked, float speed)
-{
-       Target &target = get_slot(obj);
-       if(!stacked)
-       {
-               target.animations.clear();
-               target.base_matrix = Matrix();
-       }
-       else if(target.animations.empty())
-               target.base_matrix = *obj.get_matrix();
-       target.stacked = stacked;
-       // TODO check for incompatible armature
-       target.armature = anim.get_armature();
-       target.animations.push_back(PlayingAnimation(anim, speed));
-       return target;
-}
-
-void AnimationPlayer::play(AnimatedObject &obj, const Animation &anim, float speed)
-{
-       Target &target = play_(obj, anim, false, speed);
-       target.object = &obj;
-}
-
-void AnimationPlayer::play(Placeable &obj, const Animation &anim, float speed)
-{
-       play_(obj, anim, false, speed);
-}
-
-void AnimationPlayer::play_stacked(AnimatedObject &obj, const Animation &anim, float speed)
-{
-       Target &target = play_(obj, anim, true, speed);
-       target.object = &obj;
-}
-
-void AnimationPlayer::play_stacked(Placeable &obj, const Animation &anim, float speed)
-{
-       play_(obj, anim, true, speed);
-}
-
-unsigned AnimationPlayer::get_n_active_animations(const AnimatedObject &obj) const
-{
-       ObjectMap::const_iterator i = objects.find(&obj);
-       return (i!=objects.end() ? i->second.animations.size() : 0);
-}
-
-void AnimationPlayer::observe_events(AnimatedObject &obj, AnimationEventObserver &observer)
-{
-       Target &target = get_slot(obj);
-       if(find(target.event_observers, &observer)==target.event_observers.end())
-               target.event_observers.push_back(&observer);
-}
-
-void AnimationPlayer::unobserve_events(AnimatedObject &obj, AnimationEventObserver &observer)
-{
-       ObjectMap::iterator i = objects.find(&obj);
-       if(i==objects.end())
-               return;
-
-       vector<AnimationEventObserver *>::iterator j = find(i->second.event_observers, &observer);
-       if(j!=i->second.event_observers.end())
-               i->second.event_observers.erase(j);
-}
-
-void AnimationPlayer::unobserve_events(AnimationEventObserver &observer)
-{
-       for(ObjectMap::iterator i=objects.begin(); i!=objects.end(); ++i)
-       {
-               vector<AnimationEventObserver *>::iterator j = find(i->second.event_observers, &observer);
-               if(j!=i->second.event_observers.end())
-                       i->second.event_observers.erase(j);
-       }
-}
-
-void AnimationPlayer::stop(Placeable &obj)
-{
-       objects.erase(&obj);
-}
-
-void AnimationPlayer::stop(Placeable &obj, const Animation &anim)
-{
-       ObjectMap::iterator i = objects.find(&obj);
-       if(i==objects.end())
-               return;
-
-       for(vector<PlayingAnimation>::iterator j=i->second.animations.begin(); j!=i->second.animations.end(); ++j)
-               if(j->animation==&anim)
-               {
-                       i->second.animations.erase(j);
-                       break;
-               }
-
-       if(i->second.animations.empty())
-               objects.erase(i);
-}
-
-void AnimationPlayer::tick(const Time::TimeDelta &dt)
-{
-       for(ObjectMap::iterator i=objects.begin(); i!=objects.end(); )
-       {
-               if(i->second.stacked)
-                       tick_stacked(i->second, dt);
-               else if(!i->second.animations.empty())
-                       tick_single(i->second, dt);
-
-               if(i->second.animations.empty() && i->second.event_observers.empty())
-                       objects.erase(i++);
-               else
-                       ++i;
-       }
-}
-
-void AnimationPlayer::tick_single(Target &target, const Time::TimeDelta &dt)
-{
-       PlayingAnimation &anim = target.animations.front();
-       anim.iterator += dt*anim.speed;
-       target.placeable.set_matrix(anim.iterator.get_matrix());
-
-       if(target.object)
-       {
-               unsigned n_uniforms = anim.animation->get_n_uniforms();
-               for(unsigned i=0; i<n_uniforms; ++i)
-                       set_object_uniform(*target.object, anim.animation->get_uniform_name(i), anim.iterator.get_uniform(i));
-
-               if(target.armature)
-               {
-                       unsigned max_index = target.armature->get_max_link_index();
-                       for(unsigned i=0; i<=max_index; ++i)
-                               target.object->set_pose_matrix(i, anim.iterator.get_pose_matrix(i));
-               }
-       }
-
-       anim.iterator.dispatch_events(target);
-
-       if(anim.iterator.is_end())
-               target.animations.clear();
-}
-
-void AnimationPlayer::tick_stacked(Target &target, const Time::TimeDelta &dt)
-{
-       Matrix matrix = target.base_matrix;
-       for(vector<PlayingAnimation>::iterator i=target.animations.begin(); i!=target.animations.end(); ++i)
-       {
-               i->iterator += dt*i->speed;
-               matrix *= i->iterator.get_matrix();
-
-               if(target.object)
-               {
-                       unsigned n_uniforms = i->animation->get_n_uniforms();
-                       for(unsigned j=0; j<n_uniforms; ++j)
-                               set_object_uniform(*target.object, i->animation->get_uniform_name(j), i->iterator.get_uniform(j));
-               }
-       }
-       target.placeable.set_matrix(matrix);
-
-       if(target.object && target.armature)
-       {
-               unsigned max_index = target.armature->get_max_link_index();
-               for(unsigned i=0; i<=max_index; ++i)
-               {
-                       matrix = Matrix();
-                       /* XXX This is in all likelihood incorrect.  The stacking should be
-                       performed on local matrices. */
-                       for(vector<PlayingAnimation>::iterator j=target.animations.begin(); j!=target.animations.end(); ++j)
-                               if(j->animation->get_armature())
-                                       matrix *= j->iterator.get_pose_matrix(i);
-                       target.object->set_pose_matrix(i, matrix);
-               }
-       }
-
-       for(vector<PlayingAnimation>::iterator i=target.animations.begin(); i!=target.animations.end(); )
-       {
-               i->iterator.dispatch_events(target);
-
-               if(i->iterator.is_end())
-                       i = target.animations.erase(i);
-               else
-                       ++i;
-       }
-
-       if(target.animations.empty())
-               target.stacked = false;
-}
-
-void AnimationPlayer::set_object_uniform(AnimatedObject &obj, const string &name, const KeyFrame::AnimatedUniform &uni)
-{
-       ProgramData &shdata = obj.get_shader_data();
-
-       if(uni.size==1)
-               shdata.uniform(name, uni.values[0]);
-       else if(uni.size==2)
-               shdata.uniform2(name, uni.values);
-       else if(uni.size==3)
-               shdata.uniform3(name, uni.values);
-       else if(uni.size==4)
-               shdata.uniform4(name, uni.values);
-}
-
-
-AnimationPlayer::PlayingAnimation::PlayingAnimation(const Animation &a, float s):
-       animation(&a),
-       speed(s),
-       iterator(*animation)
-{ }
-
-
-AnimationPlayer::Target::Target(Placeable &p):
-       placeable(p),
-       object(0),
-       armature(0),
-       stacked(false)
-{ }
-
-void AnimationPlayer::Target::animation_event(Placeable *, const string &name, const Variant &value)
-{
-       for(vector<AnimationEventObserver *>::const_iterator i=event_observers.begin(); i!=event_observers.end(); ++i)
-               (*i)->animation_event(&placeable, name, value);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/animationplayer.h b/source/animationplayer.h
deleted file mode 100644 (file)
index c45027d..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-#ifndef MSP_GL_ANIMATIONPLAYER_H_
-#define MSP_GL_ANIMATIONPLAYER_H_
-
-#include <msp/time/timedelta.h>
-#include "animation.h"
-#include "animationeventobserver.h"
-#include "matrix.h"
-
-namespace Msp {
-namespace GL {
-
-class AnimatedObject;
-
-/**
-The bridge between Animations and AnimatedObjects.  A single AnimationPlayer
-can handle an arbitrary number of animations simultaneously.
-*/
-class AnimationPlayer
-{
-private:
-       struct PlayingAnimation
-       {
-               const Animation *animation;
-               float speed;
-               Animation::Iterator iterator;
-
-               PlayingAnimation(const Animation &, float);
-       };
-
-       struct Target: AnimationEventObserver
-       {
-               Placeable &placeable;
-               AnimatedObject *object;
-               Matrix base_matrix;
-               const Armature *armature;
-               std::vector<PlayingAnimation> animations;
-               bool stacked;
-               std::vector<AnimationEventObserver *> event_observers;
-
-               Target(Placeable &);
-
-               virtual void animation_event(Placeable *, const std::string &, const Variant &);
-       };
-
-       typedef std::map<const Placeable *, Target> ObjectMap;
-
-       ObjectMap objects;
-
-private:
-       Target &get_slot(Placeable &);
-
-       Target &play_(Placeable &, const Animation &, bool, float);
-public:
-       /// Plays an animation on an object.  Any previous animations are replaced.
-       void play(AnimatedObject &, const Animation &, float = 1.0f);
-
-       void play(Placeable &, const Animation &, float = 1.0f);
-
-       /** Plays an animation, stacked with other animations.  If no animations are
-       playing yet, the object's current matrix is used as the base. */
-       void play_stacked(AnimatedObject &, const Animation &, float = 1.0f);
-
-       void play_stacked(Placeable &, const Animation &, float = 1.0f);
-
-       /// Returns the number of animations currently affecting an object.
-       unsigned get_n_active_animations(const AnimatedObject &) const;
-
-       /** Request delivery of animation events for the given object.  Events will
-       be delivered from all current and future animations until the observer is
-       removed. */
-       void observe_events(AnimatedObject &, AnimationEventObserver &);
-
-       /// Remove an event observer from one object.
-       void unobserve_events(AnimatedObject &, AnimationEventObserver &);
-
-       /// Remove an event observer from all objects.
-       void unobserve_events(AnimationEventObserver &);
-
-       /// Stops all animations affecting an object.
-       void stop(Placeable &);
-
-       /// Stops a single animation affecting an object.
-       void stop(Placeable &, const Animation &);
-
-       /** Advances all playing animations.  Should be called in a regular manner,
-       preferably just before rendering. */
-       void tick(const Time::TimeDelta &);
-
-private:
-       void tick_single(Target &, const Time::TimeDelta &);
-       void tick_stacked(Target &, const Time::TimeDelta &);
-       static void set_object_uniform(AnimatedObject &, const std::string &, const KeyFrame::AnimatedUniform &);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/armature.cpp b/source/armature.cpp
deleted file mode 100644 (file)
index 6e921e3..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-#include <msp/core/maputils.h>
-#include "armature.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Armature::Link &Armature::add_link()
-{
-       links.push_back(Link(string(), links.size()));
-       return links.back();
-}
-
-const Armature::Link &Armature::get_link(unsigned index) const
-{
-       for(vector<Link>::const_iterator i=links.begin(); i!=links.end(); ++i)
-               if(i->get_index()==index)
-                       return *i;
-       throw key_error(typeid(list<Link>));
-}
-
-const Armature::Link &Armature::get_link(const string &name) const
-{
-       for(vector<Link>::const_iterator i=links.begin(); i!=links.end(); ++i)
-               if(i->get_name()==name)
-                       return *i;
-       throw key_error(typeid(list<Link>));
-}
-
-unsigned Armature::get_max_link_index() const
-{
-       unsigned max_index = 0;
-       for(vector<Link>::const_iterator i=links.begin(); i!=links.end(); ++i)
-               max_index = max(max_index, i->get_index());
-       return max_index;
-}
-
-
-Armature::Link::Link(const string &n, unsigned i):
-       name(n),
-       index(i),
-       parent(0)
-{ }
-
-void Armature::Link::set_parent(const Link *p)
-{
-       parent = p;
-}
-
-void Armature::Link::set_base(const Vector3 &b)
-{
-       base = b;
-}
-
-
-Armature::Loader::Loader(Armature &a):
-       DataFile::ObjectLoader<Armature>(a)
-{
-       add("link", &Loader::link);
-}
-
-void Armature::Loader::link(const string &n)
-{
-       Link lnk(n, obj.links.size());
-       load_sub(lnk, obj);
-       obj.links.push_back(lnk);
-}
-
-
-Armature::Link::Loader::Loader(Link &l, const Armature &a):
-       DataFile::ObjectLoader<Link>(l),
-       armature(a)
-{
-       add("base",   &Loader::base);
-       add("index",  &Link::index);
-       add("parent", &Loader::parent);
-}
-
-void Armature::Link::Loader::base(float x, float y, float z)
-{
-       obj.base = Vector3(x, y, z);
-}
-
-void Armature::Link::Loader::parent(const string &n)
-{
-       obj.parent = &armature.get_link(n);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/armature.h b/source/armature.h
deleted file mode 100644 (file)
index 9dc2f50..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-#ifndef MSP_GL_ARMATURE_H_
-#define MSP_GL_ARMATURE_H_
-
-#include <list>
-#include <string>
-#include <msp/datafile/objectloader.h>
-#include "pose.h"
-#include "vector.h"
-
-namespace Msp {
-namespace GL {
-
-class Armature
-{
-public:
-       class Loader: public DataFile::ObjectLoader<Armature>
-       {
-       public:
-               Loader(Armature &);
-       private:
-               void link(const std::string &);
-       };
-
-       class Link
-       {
-       public:
-               class Loader: public DataFile::ObjectLoader<Link>
-               {
-               private:
-                       const Armature &armature;
-
-               public:
-                       Loader(Link &, const Armature &);
-               private:
-                       void base(float, float, float);
-                       void parent(const std::string &);
-               };
-
-       private:
-               std::string name;
-               unsigned index;
-               const Link *parent;
-               Vector3 base;
-
-       public:
-               Link(const std::string &, unsigned);
-
-               void set_parent(const Link *);
-               void set_base(const Vector3 &);
-
-               const std::string &get_name() const { return name; }
-               unsigned get_index() const { return index; }
-               const Link *get_parent() const { return parent; }
-               const Vector3 &get_base() const { return base; }
-       };
-
-private:
-       std::vector<Link> links;
-
-public:
-       Link &add_link();
-
-       const Link &get_link(unsigned) const;
-       const Link &get_link(const std::string &) const;
-       unsigned get_max_link_index() const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/basicmaterial.cpp b/source/basicmaterial.cpp
deleted file mode 100644 (file)
index 362fc9d..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-#include "basicmaterial.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-BasicMaterial::BasicMaterial():
-       receive_shadows(false)
-{
-       set_diffuse(Color(1.0f));
-       set_specular(Color(0.0f));
-       set_emission(Color(0.0f));
-       set_shininess(50.0f);
-       set_reflectivity(0.0f);
-}
-
-string BasicMaterial::create_program_source() const
-{
-       string source = "import phong;\n";
-       if(diffuse.texture)
-               source += "const bool use_diffuse_map = true;\n";
-       if(specular.texture || specular.value.r || specular.value.g || specular.value.b)
-       {
-               source += "const bool use_specular = true;\n";
-               if(specular.texture)
-                       source += "const bool use_specular_map = true;\n";
-               if(shininess.texture)
-                       source += "const bool use_shininess_map = true;\n";
-       }
-       if(normal.texture)
-               source += "const bool use_normal_map = true;\n";
-       if(emission.texture || emission.value.r || emission.value.g || emission.value.b)
-       {
-               source += "const bool use_emission = true;\n";
-               if(emission.texture)
-                       source += "const bool use_emission_map = true;\n";
-       }
-       if(reflectivity.value || reflectivity.texture)
-       {
-               source += "const bool use_reflectivity = true;\n";
-               if (reflectivity.texture)
-                       source += "const bool use_reflectivity_map = true;\n";
-       }
-       if(receive_shadows)
-               source += "const bool use_shadow_map = true;\n";
-       return source;
-}
-
-void BasicMaterial::attach_textures_to(Texturing &texturing, ProgramData &tex_shdata) const
-{
-       attach_texture_to(diffuse.texture, texturing, tex_shdata, "diffuse_map");
-       attach_texture_to(specular.texture, texturing, tex_shdata, "specular_map");
-       attach_texture_to(normal.texture, texturing, tex_shdata, "normal_map");
-       attach_texture_to(emission.texture, texturing, tex_shdata, "emission_map");
-       attach_texture_to(shininess.texture, texturing, tex_shdata, "shininess_map");
-       attach_texture_to(reflectivity.texture, texturing, tex_shdata, "reflectivity_map");
-}
-
-void BasicMaterial::set_diffuse(const Color &color)
-{
-       diffuse.value = color;
-       shdata.uniform("basic_material.diffuse", color);
-}
-
-void BasicMaterial::set_diffuse_map(const Texture *tex)
-{
-       diffuse.texture = tex;
-}
-
-void BasicMaterial::set_specular(const Color &color)
-{
-       specular.value = color;
-       shdata.uniform("basic_material.specular", color);
-}
-
-void BasicMaterial::set_specular_map(const Texture *tex)
-{
-       specular.texture = tex;
-}
-
-void BasicMaterial::set_normal_map(const Texture *tex)
-{
-       normal.texture = tex;
-}
-
-void BasicMaterial::set_emission(const Color &color)
-{
-       emission.value = color;
-       shdata.uniform("basic_material.emission", color);
-}
-
-void BasicMaterial::set_emission_map(const Texture *tex)
-{
-       emission.texture = tex;
-}
-
-void BasicMaterial::set_shininess(float value)
-{
-       shininess.value = value;
-       shdata.uniform("basic_material.shininess", value);
-}
-
-void BasicMaterial::set_shininess_map(const Texture *tex)
-{
-       shininess.texture = tex;
-}
-
-void BasicMaterial::set_reflectivity(float value)
-{
-       reflectivity.value = value;
-       shdata.uniform("basic_material.reflectivity", value);
-}
-
-void BasicMaterial::set_reflectivity_map(const Texture *tex)
-{
-       reflectivity.texture = tex;
-}
-
-void BasicMaterial::set_receive_shadows(bool s)
-{
-       receive_shadows = s;
-}
-
-
-DataFile::Loader::ActionMap BasicMaterial::Loader::shared_actions;
-
-BasicMaterial::Loader::Loader(BasicMaterial &m):
-       DerivedObjectLoader<BasicMaterial, Material::PropertyLoader<BasicMaterial> >(m)
-{
-       set_actions(shared_actions);
-}
-
-BasicMaterial::Loader::Loader(BasicMaterial &m, Collection &c):
-       DerivedObjectLoader<BasicMaterial, Material::PropertyLoader<BasicMaterial> >(m, c)
-{
-       set_actions(shared_actions);
-}
-
-void BasicMaterial::Loader::init_actions()
-{
-       Material::PropertyLoader<BasicMaterial>::init_actions();
-       add_property("diffuse", &BasicMaterial::set_diffuse, &BasicMaterial::set_diffuse_map, true);
-       add_property("specular", &BasicMaterial::set_specular, &BasicMaterial::set_specular_map, false);
-       add_property("normal", &BasicMaterial::set_normal_map);
-       add_property("emission", &BasicMaterial::set_emission, &BasicMaterial::set_emission_map, false);
-       add_property("shininess", &BasicMaterial::set_shininess, &BasicMaterial::set_shininess_map);
-       add_property("reflectivity", &BasicMaterial::set_reflectivity, &BasicMaterial::set_reflectivity_map);
-       add("receive_shadows", &BasicMaterial::receive_shadows);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/basicmaterial.h b/source/basicmaterial.h
deleted file mode 100644 (file)
index 95259f4..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-#ifndef MSP_GL_BASICMATERIAL_H_
-#define MSP_GL_BASICMATERIAL_H_
-
-#include "material.h"
-
-namespace Msp {
-namespace GL {
-
-class BasicMaterial: public Material
-{
-public:
-       class Loader: public DataFile::DerivedObjectLoader<BasicMaterial, Material::PropertyLoader<BasicMaterial> >
-       {
-       private:
-               static ActionMap shared_actions;
-
-       public:
-               Loader(BasicMaterial &);
-               Loader(BasicMaterial &, Collection &);
-
-       private:
-               virtual void init_actions();
-       };
-
-private:
-       Property<Color> diffuse;
-       Property<Color> specular;
-       Property<float> shininess;
-       Property<Vector3> normal;
-       Property<Color> emission;
-       Property<float> reflectivity;
-       bool receive_shadows;
-
-public:
-       BasicMaterial();
-
-protected:
-       virtual std::string create_program_source() const;
-
-public:
-       virtual void attach_textures_to(Texturing &, ProgramData &) const;
-
-       void set_diffuse(const Color &);
-       void set_diffuse_map(const Texture *);
-       void set_specular(const Color &);
-       void set_specular_map(const Texture *);
-       void set_normal_map(const Texture *);
-       void set_emission(const Color &);
-       void set_emission_map(const Texture *);
-       void set_shininess(float);
-       void set_shininess_map(const Texture *);
-       void set_reflectivity(float);
-       void set_reflectivity_map(const Texture *);
-       void set_receive_shadows(bool);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/batch.cpp b/source/batch.cpp
deleted file mode 100644 (file)
index f14cc30..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-#include <msp/gl/extensions/arb_draw_instanced.h>
-#include <msp/gl/extensions/msp_primitive_restart.h>
-#include "batch.h"
-#include "bindable.h"
-#include "buffer.h"
-#include "error.h"
-#include "mesh.h"
-#include "vertexarray.h"
-
-using namespace std;
-
-namespace {
-
-template<typename T>
-void append(vector<Msp::UInt8> &data, T i)
-{
-       data.insert(data.end(), sizeof(T), 0);
-       *(T *)(&data[data.size()-sizeof(T)]) = i;
-}
-
-template<typename T, typename U>
-U convert(T n)
-{
-       if(!static_cast<T>(~n))
-               return ~0;
-       else
-               return n;
-}
-
-template<typename T, typename U>
-void expand(vector<Msp::UInt8> &data)
-{
-       unsigned count = data.size()/sizeof(T);
-       data.resize(count*sizeof(U));
-       for(unsigned i=count; i--;)
-               *(U *)(&data[i*sizeof(U)]) = convert<T, U>(*(T *)(&data[i*sizeof(T)]));
-}
-
-template<typename T, typename U>
-void shrink(vector<Msp::UInt8> &data)
-{
-       unsigned count = data.size()/sizeof(T);
-       for(unsigned i=0; i<count; ++i)
-               *(U *)(&data[i*sizeof(U)]) = convert<T, U>(*(T *)(&data[i*sizeof(T)]));
-       data.resize(count*sizeof(U));
-}
-
-}
-
-namespace Msp {
-namespace GL {
-
-unsigned Batch::restart_index = 0;
-
-Batch::Batch(PrimitiveType t):
-       prim_type(t),
-       index_type(UNSIGNED_SHORT),
-       max_index(0),
-       restart(false)
-{ }
-
-Batch::~Batch()
-{
-}
-
-void Batch::set_index_type(DataType t)
-{
-       if(t!=UNSIGNED_SHORT && t!=UNSIGNED_INT)
-               throw invalid_argument("Batch::set_data_type");
-       if(t==UNSIGNED_SHORT && max_index>0xFFFE)
-               throw invalid_operation("Batch::set_data_type");
-
-       if(index_type==UNSIGNED_SHORT && t==UNSIGNED_INT)
-               expand<UInt16, UInt32>(data);
-       else if(index_type==UNSIGNED_INT && t==UNSIGNED_SHORT)
-               shrink<UInt32, UInt16>(data);
-
-       index_type = t;
-       update_offset();
-       dirty = true;
-}
-
-Batch &Batch::append(unsigned i)
-{
-       append_index(i);
-
-       update_offset();
-       dirty = true;
-
-       return *this;
-}
-
-Batch &Batch::append(const vector<unsigned> &ind)
-{
-       if(ind.empty())
-               return *this;
-
-       data.reserve(data.size()+ind.size()*get_index_size());
-       for(vector<unsigned>::const_iterator i=ind.begin(); i!=ind.end(); ++i)
-               append_index(*i);
-
-       update_offset();
-       dirty = true;
-
-       return *this;
-}
-
-bool Batch::can_append(PrimitiveType other_type)
-{
-       if(other_type!=prim_type)
-               return false;
-       else if(prim_type==LINE_STRIP || prim_type==LINE_LOOP || prim_type==TRIANGLE_FAN)
-               return MSP_primitive_restart;
-       else
-               return true;
-}
-
-Batch &Batch::append(const Batch &other)
-{
-       if(other.prim_type!=prim_type)
-               throw invalid_argument("Batch::append");
-       if(prim_type==LINE_STRIP || prim_type==LINE_LOOP || prim_type==TRIANGLE_FAN)
-               static Require _req(MSP_primitive_restart);
-
-       if(other.data.empty())
-               return *this;
-
-       // TODO allow appending triangles to a triangle strip
-
-       if(prim_type==POINTS || prim_type==LINES || prim_type==TRIANGLES)
-               ;
-       else if(MSP_primitive_restart)
-       {
-               restart = true;
-               if(index_type==UNSIGNED_INT)
-                       ::append<UInt32>(data, 0xFFFFFFFF);
-               else
-                       ::append<UInt16>(data, 0xFFFF);
-       }
-       else if(prim_type==TRIANGLE_STRIP)
-       {
-               append(get_index(size()-1));
-               append(other.get_index(0));
-               if(size()&1)
-                       append(other.get_index(0));
-       }
-
-       unsigned count = other.size();
-       for(unsigned i=0; i<count; ++i)
-               append_index(other.get_index(i));
-
-       update_offset();
-       dirty = true;
-
-       return *this;
-}
-
-void Batch::append_index(unsigned i)
-{
-       if(data.empty())
-               max_index = i;
-       else
-               max_index = max(max_index, i);
-
-       if(index_type==UNSIGNED_SHORT && max_index>0xFFFE)
-               set_index_type(UNSIGNED_INT);
-
-       if(index_type==UNSIGNED_INT)
-               ::append<UInt32>(data, i);
-       else
-               ::append<UInt16>(data, i);
-}
-
-unsigned Batch::get_index_size() const
-{
-       return (index_type==UNSIGNED_INT ? sizeof(UInt32) : sizeof(UInt16));
-}
-
-unsigned Batch::get_index(unsigned i) const
-{
-       if(index_type==UNSIGNED_INT)
-               return *(UInt32 *)&data[i*sizeof(UInt32)];
-       else
-               return *(UInt16 *)&data[i*sizeof(UInt16)];
-}
-
-void Batch::draw() const
-{
-       BindRestore _bind_ibuf(get_buffer(), ELEMENT_ARRAY_BUFFER);
-       const void *data_ptr = setup_draw();
-
-       glDrawElements(prim_type, size(), index_type, data_ptr);
-}
-
-void Batch::draw_instanced(unsigned count) const
-{
-       static Require req(ARB_draw_instanced);
-
-       BindRestore _bind_ibuf(get_buffer(), ELEMENT_ARRAY_BUFFER);
-       const void *data_ptr = setup_draw();
-
-       glDrawElementsInstanced(prim_type, size(), index_type, data_ptr, count);
-}
-
-const void *Batch::setup_draw() const
-{
-       if(!get_buffer())
-               throw invalid_operation("Batch::setup_draw");
-
-       if(restart)
-       {
-               unsigned index = (index_type==UNSIGNED_INT ? 0xFFFFFFFF : 0xFFFF);
-
-               if(index!=restart_index)
-                       set_restart_index(index);
-       }
-       else if(restart_index && restart_index<=max_index)
-               set_restart_index(0);
-
-       refresh();
-
-       return reinterpret_cast<const void *>(get_offset());
-}
-
-void Batch::set_restart_index(unsigned index)
-{
-       if(index>0)
-       {
-               if(!restart_index)
-                       glEnable(GL_PRIMITIVE_RESTART);
-               glPrimitiveRestartIndex(index);
-       }
-       else
-               glDisable(GL_PRIMITIVE_RESTART);
-
-       restart_index = index;
-}
-
-
-Batch::Loader::Loader(Batch &b):
-       DataFile::ObjectLoader<Batch>(b)
-{
-       add("indices", &Loader::indices);
-}
-
-void Batch::Loader::indices(const vector<unsigned> &ind)
-{
-       obj.append(ind);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/batch.h b/source/batch.h
deleted file mode 100644 (file)
index 2e4ad9e..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-#ifndef MSP_GL_BATCH_H_
-#define MSP_GL_BATCH_H_
-
-#include <vector>
-#include <msp/datafile/objectloader.h>
-#include "bufferable.h"
-#include "datatype.h"
-#include "primitivetype.h"
-
-namespace Msp {
-namespace GL {
-
-class Buffer;
-
-/**
-Stores primitive type and element indices for a single GL draw call.  Data
-type for indices is automatically chosen to accommodate the largest index in
-the Batch.
-
-This is a pretty low-level class and mainly intended to be used by the Mesh
-class.
-*/
-class Batch: public Bufferable
-{
-public:
-       class Loader: public DataFile::ObjectLoader<Batch>
-       {
-       public:
-               Loader(Batch &);
-       private:
-               void indices(const std::vector<unsigned> &);
-       };
-
-private:
-       PrimitiveType prim_type;
-       DataType index_type;
-       std::vector<UInt8> data;
-       unsigned max_index;
-       bool restart;
-
-       static unsigned restart_index;
-
-public:
-       Batch(PrimitiveType t);
-       ~Batch();
-
-       PrimitiveType get_type() const { return prim_type; }
-       void set_index_type(DataType);
-       DataType get_index_type() const { return index_type; }
-
-       DEPRECATED void set_data_type(DataType t) { set_index_type(t); }
-       DEPRECATED DataType get_data_type() const { return index_type; }
-
-       Batch &append(unsigned);
-       Batch &append(const std::vector<unsigned> &);
-       bool can_append(PrimitiveType);
-       Batch &append(const Batch &);
-private:
-       void append_index(unsigned);
-       virtual unsigned get_data_size() const { return data.size(); }
-       virtual const void *get_data_pointer() const { return &data[0]; }
-       virtual unsigned get_alignment() const { return get_index_size(); }
-       unsigned get_index_size() const;
-public:
-       unsigned size() const { return data.size()/get_index_size(); }
-
-       unsigned get_index(unsigned) const;
-
-       void draw() const;
-       void draw_instanced(unsigned) const;
-private:
-       const void *setup_draw() const;
-       static void set_restart_index(unsigned);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/bindable.h b/source/bindable.h
deleted file mode 100644 (file)
index b2634ea..0000000
+++ /dev/null
@@ -1,225 +0,0 @@
-#ifndef MSP_GL_BINDABLE_H_
-#define MSP_GL_BINDABLE_H_
-
-namespace Msp {
-namespace GL {
-
-/**
-A helper class for single-point binding.  Provides tracking of the currently
-bound object.
-*/
-template<typename T>
-class Bindable
-{
-protected:
-       static const T *cur_obj;
-
-       Bindable() { }
-       ~Bindable() { if(cur_obj==this) T::unbind(); }
-
-       static bool set_current(const T *obj)
-       {
-               if(obj==cur_obj)
-                       return false;
-
-               cur_obj = obj;
-               return true;
-       }
-
-public:
-       static const T *current() { return cur_obj; }
-};
-
-template<typename T>
-const T *Bindable<T>::cur_obj;
-
-
-/**
-A helper class for Bindables that revert to a default object on unbind.
-*/
-template<typename T>
-class BindableWithDefault: protected Bindable<T>
-{
-       friend class Bindable<T>;
-
-protected:
-       BindableWithDefault() { }
-       ~BindableWithDefault() { if(this==&default_object()) Bindable<T>::set_current(0); }
-
-public:
-       static const T *current()
-       {
-               if(!Bindable<T>::cur_obj)
-                       Bindable<T>::cur_obj = &default_object();
-               return Bindable<T>::cur_obj;
-       }
-
-       static void unbind()
-       {
-               if(Bindable<T>::cur_obj)
-                       default_object().bind();
-       }
-
-       static const T &default_object()
-       {
-               static T obj;
-               return obj;
-       }
-};
-
-
-/**
-RAII class for binding things.  Binds the thing upon construction and unbinds
-it upon destruction.  If a null pointer is given, unbinds upon construction and
-does nothing upon destruction.
-*/
-class Bind
-{
-private:
-       typedef void CleanupFunc(int);
-
-       int slot;
-       CleanupFunc *cleanup;
-
-public:
-       template<typename T>
-       Bind(T *o) { init(o); }
-
-       template<typename T>
-       Bind(const T *o) { init(o); }
-
-       template<typename T>
-       Bind(const T &o) { init(&o); }
-
-       template<typename T, typename S>
-       Bind(T *o, S s) { init(o, s); }
-
-       template<typename T, typename S>
-       Bind(const T *o, S s) { init(o, s); }
-
-       template<typename T, typename S>
-       Bind(const T &o, S s) { init(&o, s); }
-
-private:
-       template<typename T>
-       void init(const T *o)
-       {
-               cleanup = (o ? static_cast<CleanupFunc *>(&unbind<T>) : 0);
-               slot = 0;
-               if(o)
-                       o->bind();
-               else
-                       T::unbind();
-       }
-
-       template<typename T, typename S>
-       void init(const T *o, S s)
-       {
-               cleanup = (o ? static_cast<CleanupFunc *>(&unbind_from<T, S>) : 0);
-               slot = s;
-               if(o)
-                       o->bind_to(s);
-               else
-                       T::unbind_from(s);
-       }
-
-public:
-       ~Bind()
-       { if(cleanup) cleanup(slot); }
-
-private:
-       template<typename T>
-       static void unbind(int)
-       { T::unbind(); }
-
-       template<typename T, typename S>
-       static void unbind_from(int s)
-       { T::unbind_from(static_cast<S>(s)); }
-};
-
-
-/**
-Similar to Bind, but restores previous binding upon destruction.
-*/
-class BindRestore
-{
-private:
-       typedef void CleanupFunc(const void *, int);
-
-       const void *old;
-       int slot;
-       CleanupFunc *cleanup;
-
-public:
-       template<typename T>
-       BindRestore(T *o) { init(o); }
-
-       template<typename T>
-       BindRestore(const T *o) { init(o); }
-
-       template<typename T>
-       BindRestore(const T &o) { init(&o); }
-
-       template<typename T, typename S>
-       BindRestore(T *o, S s) { init(o, s); }
-
-       template<typename T, typename S>
-       BindRestore(const T *o, S s) { init(o, s); }
-
-       template<typename T, typename S>
-       BindRestore(const T &o, S s) { init(&o, s); }
-
-private:
-       template<typename T>
-       void init(T *o)
-       {
-               old = T::current();
-               slot = 0;
-               cleanup = (o!=old ? static_cast<CleanupFunc *>(&restore<T>) : 0);
-               if(o)
-                       o->bind();
-               else if(old)
-                       T::unbind();
-       }
-
-       template<typename T, typename S>
-       void init(T *o, S s)
-       {
-               old = T::current(s);
-               slot = s;
-               cleanup = (o!=old ? static_cast<CleanupFunc *>(&restore_to<T, S>) : 0);
-               if(o)
-                       o->bind_to(s);
-               else if(old)
-                       T::unbind_from(s);
-       }
-
-public:
-       ~BindRestore()
-       { if(cleanup) cleanup(old, slot); }
-
-private:
-       template<typename T>
-       static void restore(const void *o, int)
-       {
-               if(o)
-                       reinterpret_cast<const T *>(o)->bind();
-               else
-                       T::unbind();
-       }
-
-       template<typename T, typename S>
-       static void restore_to(const void *o, int si)
-       {
-               S s = static_cast<S>(si);
-               if(o)
-                       reinterpret_cast<const T *>(o)->bind_to(s);
-               else
-                       T::unbind_from(s);
-       }
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/blend.cpp b/source/blend.cpp
deleted file mode 100644 (file)
index 3327885..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-#include <msp/gl/extensions/ext_blend_minmax.h>
-#include <msp/gl/extensions/ext_blend_subtract.h>
-#include <msp/strings/format.h>
-#include "blend.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Blend::Blend():
-       eq(ADD),
-       src_factor(ONE),
-       dst_factor(ZERO)
-{ }
-
-Blend::Blend(BlendFactor sf, BlendFactor df):
-       eq(ADD),
-       src_factor(sf),
-       dst_factor(df)
-{ }
-
-Blend::Blend(BlendEquation e, BlendFactor sf, BlendFactor df):
-       eq(e),
-       src_factor(sf),
-       dst_factor(df)
-{
-       if(eq==MIN || eq==MAX)
-               static Require _req(EXT_blend_minmax);
-       else if(eq==SUBTRACT || eq==REVERSE_SUBTRACT)
-               static Require _req(EXT_blend_subtract);
-}
-
-void Blend::bind() const
-{
-       if(set_current(this))
-       {
-               glEnable(GL_BLEND);
-               if(EXT_blend_minmax)
-                       glBlendEquation(eq);
-               glBlendFunc(src_factor, dst_factor);
-       }
-}
-
-void Blend::unbind()
-{
-       if(set_current(0))
-               glDisable(GL_BLEND);
-}
-
-const Blend &Blend::alpha()
-{
-       static Blend blend(SRC_ALPHA, ONE_MINUS_SRC_ALPHA);
-       return blend;
-}
-
-const Blend &Blend::additive()
-{
-       static Blend blend(ONE, ONE);
-       return blend;
-}
-
-const Blend &Blend::additive_alpha()
-{
-       static Blend blend(SRC_ALPHA, ONE);
-       return blend;
-}
-
-void operator>>(const LexicalConverter &conv, BlendFactor &factor)
-{
-       const string &str = conv.get();
-       if(str=="ZERO")
-               factor = ZERO;
-       else if(str=="ONE")
-               factor = ONE;
-       else if(str=="SRC_COLOR")
-               factor = SRC_COLOR;
-       else if(str=="ONE_MINUS_SRC_COLOR")
-               factor = ONE_MINUS_SRC_COLOR;
-       else if(str=="SRC_ALPHA")
-               factor = SRC_ALPHA;
-       else if(str=="ONE_MINUS_SRC_ALPHA")
-               factor = ONE_MINUS_SRC_ALPHA;
-       else if(str=="DST_COLOR")
-               factor = DST_COLOR;
-       else if(str=="ONE_MINUS_DST_COLOR")
-               factor = ONE_MINUS_DST_COLOR;
-       else if(str=="DST_ALPHA")
-               factor = DST_ALPHA;
-       else if(str=="ONE_MINUS_DST_ALPHA")
-               factor = ONE_MINUS_DST_ALPHA;
-       else if(str=="CONSTANT_COLOR")
-               factor = CONSTANT_COLOR;
-       else if(str=="ONE_MINUS_CONSTANT_COLOR")
-               factor = ONE_MINUS_CONSTANT_COLOR;
-       else if(str=="CONSTANT_ALPHA")
-               factor = CONSTANT_ALPHA;
-       else if(str=="ONE_MINUS_CONSTANT_ALPHA")
-               factor = ONE_MINUS_CONSTANT_ALPHA;
-       else
-               throw lexical_error(format("conversion of '%s' to BlendFactor", str));
-}
-
-void operator<<(LexicalConverter &conv, BlendFactor factor)
-{
-       switch(factor)
-       {
-       case ZERO: conv.result("ZERO"); break;
-       case ONE: conv.result("ONE"); break;
-       case SRC_COLOR: conv.result("SRC_COLOR"); break;
-       case ONE_MINUS_SRC_COLOR: conv.result("ONE_MINUS_SRC_COLOR"); break;
-       case SRC_ALPHA: conv.result("SRC_ALPHA"); break;
-       case ONE_MINUS_SRC_ALPHA: conv.result("ONE_MINUS_SRC_ALPHA"); break;
-       case DST_COLOR: conv.result("DST_COLOR"); break;
-       case ONE_MINUS_DST_COLOR: conv.result("ONE_MINUS_DST_COLOR"); break;
-       case DST_ALPHA: conv.result("DST_ALPHA"); break;
-       case ONE_MINUS_DST_ALPHA: conv.result("ONE_MINUS_DST_ALPHA"); break;
-       case CONSTANT_COLOR: conv.result("CONSTANT_COLOR"); break;
-       case ONE_MINUS_CONSTANT_COLOR: conv.result("ONE_MINUS_CONSTANT_COLOR"); break;
-       case CONSTANT_ALPHA: conv.result("CONSTANT_ALPHA"); break;
-       case ONE_MINUS_CONSTANT_ALPHA: conv.result("ONE_MINUS_CONSTANT_ALPHA"); break;
-       default: conv.result(format("BlendFactor(%#x)", static_cast<int>(factor)));
-       }
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/blend.h b/source/blend.h
deleted file mode 100644 (file)
index e7b291e..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#ifndef MSP_GL_BLEND_H_
-#define MSP_GL_BLEND_H_
-
-#include <msp/strings/lexicalcast.h>
-#include "bindable.h"
-#include "gl.h"
-#include <msp/gl/extensions/ext_blend_minmax.h>
-
-namespace Msp {
-namespace GL {
-
-enum BlendEquation
-{
-       ADD              = GL_FUNC_ADD,
-       SUBTRACT         = GL_FUNC_SUBTRACT,
-       REVERSE_SUBTRACT = GL_FUNC_REVERSE_SUBTRACT,
-       MIN              = GL_MIN,
-       MAX              = GL_MAX
-};
-
-enum BlendFactor
-{
-       ZERO = GL_ZERO,
-       ONE = GL_ONE,
-       SRC_COLOR = GL_SRC_COLOR,
-       ONE_MINUS_SRC_COLOR = GL_ONE_MINUS_SRC_COLOR,
-       SRC_ALPHA = GL_SRC_ALPHA,
-       ONE_MINUS_SRC_ALPHA = GL_ONE_MINUS_SRC_ALPHA,
-       DST_COLOR = GL_DST_COLOR,
-       ONE_MINUS_DST_COLOR = GL_ONE_MINUS_DST_COLOR,
-       DST_ALPHA = GL_DST_ALPHA,
-       ONE_MINUS_DST_ALPHA = GL_ONE_MINUS_DST_ALPHA,
-       CONSTANT_COLOR = GL_CONSTANT_COLOR,
-       ONE_MINUS_CONSTANT_COLOR = GL_ONE_MINUS_CONSTANT_COLOR,
-       CONSTANT_ALPHA = GL_CONSTANT_ALPHA,
-       ONE_MINUS_CONSTANT_ALPHA = GL_ONE_MINUS_CONSTANT_ALPHA
-};
-
-/**
-Blends incoming fragments with those already in the framebuffer.
-*/
-class Blend: public Bindable<Blend>
-{
-private:
-       BlendEquation eq;
-       BlendFactor src_factor;
-       BlendFactor dst_factor;
-
-public:
-       Blend();
-       Blend(BlendFactor, BlendFactor);
-       Blend(BlendEquation, BlendFactor, BlendFactor);
-
-       void bind() const;
-
-       static void unbind();
-
-       static const Blend &alpha();
-       static const Blend &additive();
-       static const Blend &additive_alpha();
-};
-
-void operator>>(const LexicalConverter &, BlendFactor &);
-void operator<<(LexicalConverter &, BlendFactor);
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/bloom.cpp b/source/bloom.cpp
deleted file mode 100644 (file)
index ee7c76c..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-#include <cmath>
-#include <msp/strings/format.h>
-#include "blend.h"
-#include "bloom.h"
-#include "misc.h"
-#include "renderer.h"
-#include "shader.h"
-#include "tests.h"
-#include "texunit.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Bloom::Bloom(unsigned w, unsigned h):
-       blur_shader("bloom_blur.glsl"),
-       combine_shader("bloom_combine.glsl"),
-       quad(get_fullscreen_quad()),
-       nearest_sampler(get_nearest_sampler()),
-       linear_sampler(get_linear_sampler())
-{
-       blur_shdata[0].uniform("delta", 1.0f/w, 0.0f);
-       blur_shdata[1].uniform("delta", 0.0f, 1.0f/h);
-
-       for(unsigned i=0; i<2; ++i)
-               target[i] = new RenderTarget(w, h, (RENDER_COLOR,RGB16F));
-
-       common_shdata.uniform("source", 0);
-       common_shdata.uniform("blurred", 1);
-
-       combine_texturing.attach(1, target[1]->get_target_texture(RENDER_COLOR), linear_sampler.get());
-
-       set_radius(2.0f);
-       set_strength(0.2f);
-}
-
-Bloom::~Bloom()
-{
-       for(unsigned i=0; i<2; ++i)
-               delete target[i];
-}
-
-void Bloom::set_radius(float r)
-{
-       if(r<=0.0f)
-               throw invalid_argument("Bloom::set_radius");
-
-       int size = min(static_cast<int>(r*3.0f), 9);
-       common_shdata.uniform("size", size);
-
-       vector<float> factors(size*2+1);
-       float sum = 0.0f;
-       r = 2*r*r;
-       for(int i=-size; i<=size; ++i)
-               sum += (factors[size+i] = exp(-i*i/r));
-       for(int i=0; i<=size*2; ++i)
-               factors[i] /= sum;
-
-       common_shdata.uniform1_array("factors", size*2+1, &factors.front());
-}
-
-void Bloom::set_strength(float s)
-{
-       if(s<0.0f || s>1.0f)
-               throw invalid_argument("Bloom::set_strength");
-       common_shdata.uniform("strength", s);
-}
-
-void Bloom::render(Renderer &renderer, const Texture2D &src, const Texture2D &)
-{
-       Renderer::Push push(renderer);
-       renderer.set_shader_program(&blur_shader, &common_shdata);
-       for(unsigned i=0; i<2; ++i)
-       {
-               BindRestore bind_fbo(target[i]->get_framebuffer());
-               Renderer::Push push2(renderer);
-               renderer.set_texture(i ? &target[0]->get_target_texture(RENDER_COLOR) : &src, nearest_sampler.get());
-               renderer.add_shader_data(blur_shdata[i]);
-               quad->draw(renderer);
-       }
-
-       combine_texturing.attach(0, src, nearest_sampler.get());
-       renderer.set_texturing(&combine_texturing);
-       renderer.set_shader_program(&combine_shader);
-       quad->draw(renderer);
-}
-
-
-Bloom::Template::Template():
-       radius(2.0f),
-       strength(0.2f)
-{ }
-
-Bloom *Bloom::Template::create(unsigned width, unsigned height) const
-{
-       RefPtr<Bloom> bloom = new Bloom(width/size_divisor, height/size_divisor);
-       bloom->set_radius(radius);
-       bloom->set_strength(strength);
-       return bloom.release();
-}
-
-
-Bloom::Template::Loader::Loader(Template &t):
-       DataFile::DerivedObjectLoader<Template, PostProcessor::Template::Loader>(t)
-{
-       add("strength", &Template::strength);
-       add("radius", &Template::radius);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/bloom.h b/source/bloom.h
deleted file mode 100644 (file)
index 6b10195..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-#ifndef MSP_GL_BLOOM_H_
-#define MSP_GL_BLOOM_H_
-
-#include "framebuffer.h"
-#include "mesh.h"
-#include "postprocessor.h"
-#include "texture2d.h"
-#include "texturing.h"
-#include "program.h"
-#include "programdata.h"
-#include "rendertarget.h"
-
-namespace Msp {
-namespace GL {
-
-/**
-The Bloom post-processing effect causes very bright areas of the image to bleed
-into surrounding pixels.  Commonly used together with HDR rendering.
-
-The technique used is to gaussian blur the image and then blend the result with
-the original image.  With suitable parameters, this effect may also be used as
-a blur filter.
-*/
-class Bloom: public PostProcessor
-{
-public:
-       struct Template: public PostProcessor::Template
-       {
-               class Loader: public DataFile::DerivedObjectLoader<Template, PostProcessor::Template::Loader>
-               {
-               public:
-                       Loader(Template &);
-               };
-
-               float radius;
-               float strength;
-
-               Template();
-
-               virtual Bloom *create(unsigned, unsigned) const;
-       };
-
-private:
-       RenderTarget *target[2];
-       ProgramData common_shdata;
-       Program blur_shader;
-       ProgramData blur_shdata[2];
-       Program combine_shader;
-       Texturing combine_texturing;
-       RefPtr<Mesh> quad;
-       RefPtr<Sampler> nearest_sampler;
-       RefPtr<Sampler> linear_sampler;
-
-public:
-       Bloom(unsigned, unsigned);
-       ~Bloom();
-
-       /** Sets the Ïƒ value of the gaussian blur.  Values much larger than 4.0 are
-       likely to cause artifacts. */
-       void set_radius(float);
-
-       /** Sets the blend factor between original and blurred images.  Larger
-       values mean more blurriness. */
-       void set_strength(float);
-
-       virtual void render(Renderer &, const Texture2D &, const Texture2D &);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/box.cpp b/source/box.cpp
deleted file mode 100644 (file)
index 46f1b48..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-#include <cmath>
-#include "box.h"
-#include "primitivebuilder.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-BoxBuilder::BoxBuilder(float w, float h, float d):
-       origin(-w/2, -h/2, -d/2),
-       span(w, h, d)
-{ }
-
-BoxBuilder::BoxBuilder(const Vector3 &o, const Vector3 &s):
-       origin(o),
-       span(s)
-{ }
-
-void BoxBuilder::build(PrimitiveBuilder &builder) const
-{
-       builder.normal(1, 0, 0);
-       build_face(builder, Vector3(origin.x+span.x, origin.y, origin.z), Vector3(0, span.y, 0), Vector3(0, 0, span.z));
-       builder.normal(0, 1, 0);
-       build_face(builder, Vector3(origin.x+span.x, origin.y+span.y, origin.z), Vector3(-span.x, 0, 0), Vector3(0, 0, span.z));
-       builder.normal(-1, 0, 0);
-       build_face(builder, Vector3(origin.x, origin.y+span.y, origin.z), Vector3(0, -span.y, 0), Vector3(0, 0, span.z));
-       builder.normal(0, -1, 0);
-       build_face(builder, origin, Vector3(span.x, 0, 0), Vector3(0, 0, span.z));
-       builder.normal(0, 0, 1);
-       build_face(builder, Vector3(origin.x, origin.y, origin.z+span.z), Vector3(span.x, 0, 0), Vector3(0, span.y, 0));
-       builder.normal(0, 0, -1);
-       build_face(builder, Vector3(origin.x+span.x, origin.y, origin.z), Vector3(-span.x, 0, 0), Vector3(0, span.y, 0));
-}
-
-void BoxBuilder::build_face(PrimitiveBuilder &builder, const Vector3 &o, const Vector3 &s1, const Vector3 &s2) const
-{
-       float l1 = 1, l2 = 1;
-       if(generate_tbn || tex_fit!=STRETCH)
-       {
-               l1 = s1.norm();
-               l2 = s2.norm();
-       }
-
-       if(generate_tbn)
-       {
-               builder.tangent(s1/l1);
-               builder.binormal(s2/l2);
-       }
-
-       float u_size = 1;
-       float v_size = 1;
-       adjust_texture_scale(u_size, v_size, l1, l2);
-
-       builder.begin(TRIANGLE_STRIP);
-       builder.texcoord(0, v_size);
-       builder.vertex(o+s2);
-       builder.texcoord(0, 0);
-       builder.vertex(o);
-       builder.texcoord(u_size, v_size);
-       builder.vertex(o+s1+s2);
-       builder.texcoord(u_size, 0);
-       builder.vertex(o+s1);
-       builder.end();
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/box.h b/source/box.h
deleted file mode 100644 (file)
index 1059d21..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef MSP_GL_BOX_H_
-#define MSP_GL_BOX_H_
-
-#include "geometrybuilder.h"
-#include "vector.h"
-
-namespace Msp {
-namespace GL {
-
-class BoxBuilder: public GeometryBuilder
-{
-private:
-       Vector3 origin;
-       Vector3 span;
-
-public:
-       BoxBuilder(float, float, float);
-       BoxBuilder(const Vector3 &, const Vector3 &);
-
-       using GeometryBuilder::build;
-       virtual void build(PrimitiveBuilder &) const;
-private:
-       void build_face(PrimitiveBuilder &, const Vector3 &, const Vector3 &, const Vector3 &) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/buffer.cpp b/source/buffer.cpp
deleted file mode 100644 (file)
index 56705b8..0000000
+++ /dev/null
@@ -1,296 +0,0 @@
-#include <stdexcept>
-#include <msp/gl/extensions/arb_buffer_storage.h>
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include <msp/gl/extensions/arb_map_buffer_range.h>
-#include <msp/strings/format.h>
-#include "buffer.h"
-#include "error.h"
-#include "misc.h"
-#include "vertexsetup.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-const Buffer *Buffer::bound[5] = { 0, 0, 0, 0, 0 };
-BufferType buffer_types[] = { ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, PIXEL_PACK_BUFFER, PIXEL_UNPACK_BUFFER, UNIFORM_BUFFER };
-
-Buffer::Buffer(BufferType t):
-       type(t),
-       size(0)
-{
-       require_buffer_type(type);
-
-       if(ARB_direct_state_access)
-               glCreateBuffers(1, &id);
-       else
-               glGenBuffers(1, &id);
-}
-
-Buffer::~Buffer()
-{
-       for(unsigned i=0; i<5; ++i)
-               if(bound[i]==this)
-                       unbind_from(buffer_types[i]);
-       glDeleteBuffers(1, &id);
-}
-
-void Buffer::require_buffer_type(BufferType type)
-{
-       static Require _req_vbo(ARB_vertex_buffer_object);
-       if(type==PIXEL_PACK_BUFFER || type==PIXEL_UNPACK_BUFFER)
-               static Require _req_pbo(ARB_pixel_buffer_object);
-       else if(type==UNIFORM_BUFFER)
-               static Require _req_ubo(ARB_uniform_buffer_object);
-}
-
-void Buffer::storage(unsigned sz)
-{
-       if(size>0)
-               throw invalid_operation("Buffer::storage");
-       if(sz==0)
-               throw invalid_argument("Buffer::storage");
-
-       size = sz;
-       if(ARB_buffer_storage)
-       {
-               static const int flags = GL_MAP_READ_BIT|GL_MAP_WRITE_BIT|GL_DYNAMIC_STORAGE_BIT;
-               if(ARB_direct_state_access)
-                       glNamedBufferStorage(id, size, 0, flags);
-               else
-               {
-                       BindRestore _bind(this, type);
-                       glBufferStorage(type, size, 0, flags);
-               }
-       }
-}
-
-void Buffer::set_usage(BufferUsage)
-{
-}
-
-void Buffer::data(const void *d)
-{
-       if(size==0)
-               throw invalid_operation("Buffer::data");
-
-       if(ARB_buffer_storage)
-               return sub_data(0, size, d);
-
-       if(ARB_direct_state_access)
-               glNamedBufferData(id, size, d, STATIC_DRAW);
-       else
-       {
-               BindRestore _bind(this, type);
-               glBufferData(type, size, d, STATIC_DRAW);
-       }
-}
-
-void Buffer::data(unsigned sz, const void *d)
-{
-       if(size==0)
-               storage(sz);
-       else if(sz!=size)
-               throw incompatible_data("Buffer::data");
-
-       data(d);
-}
-
-void Buffer::sub_data(unsigned off, unsigned sz, const void *d)
-{
-       if(ARB_direct_state_access)
-               glNamedBufferSubData(id, off, sz, d);
-       else
-       {
-               BindRestore _bind(this, type);
-               glBufferSubData(type, off, sz, d);
-       }
-}
-
-void Buffer::require_size(unsigned req_sz) const
-{
-       if(size<req_sz)
-               throw buffer_too_small(format("buffer has %d bytes; %d required", size, req_sz));
-}
-
-BufferRange *Buffer::create_range(unsigned s, unsigned o)
-{
-       return new BufferRange(*this, s, o);
-}
-
-void *Buffer::map()
-{
-       if(ARB_map_buffer_range)
-       {
-               if(ARB_direct_state_access)
-                       return glMapNamedBufferRange(id, 0, size, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT);
-               else
-               {
-                       BindRestore _bind(this, type);
-                       return glMapBufferRange(type, 0, size, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT);
-               }
-       }
-       else if(ARB_direct_state_access)
-               return glMapNamedBuffer(id, GL_READ_WRITE);
-       else if(OES_mapbuffer)
-       {
-               BindRestore _bind(this, type);
-               return glMapBuffer(type, GL_READ_WRITE);
-       }
-       else
-               throw invalid_operation("Buffer::map");
-}
-
-bool Buffer::unmap()
-{
-       // TODO check if it's mapped
-       if(ARB_direct_state_access)
-               return glUnmapNamedBuffer(id);
-       else if(OES_mapbuffer)
-       {
-               BindRestore _bind(this, type);
-               return glUnmapBuffer(type);
-       }
-       else
-               return true;
-}
-
-void Buffer::bind_to(BufferType t) const
-{
-       if(t!=type)
-               require_buffer_type(t);
-       if(t==ELEMENT_ARRAY_BUFFER)
-               if(const VertexSetup *vs = VertexSetup::current())
-               {
-                       // Don't change the binding in a vertex array object
-                       if(this==vs->get_index_buffer())
-                               return;
-                       throw invalid_operation("Buffer::bind_to(ELEMENT_ARRAY_BUFFER)");
-               }
-       if(set_current(t, this))
-               glBindBuffer(t, id);
-}
-
-const Buffer *Buffer::current(BufferType t)
-{
-       if(t==ELEMENT_ARRAY_BUFFER)
-               if(const VertexSetup *vs = VertexSetup::current())
-                       return vs->get_index_buffer();
-       return binding(t);
-}
-
-void Buffer::unbind_from(BufferType type)
-{
-       if(type==ELEMENT_ARRAY_BUFFER && VertexSetup::current())
-               throw invalid_operation("Buffer::unbind_from(ELEMENT_ARRAY_BUFFER)");
-       if(set_current(type, 0))
-               glBindBuffer(type, 0);
-}
-
-const Buffer *&Buffer::binding(BufferType type)
-{
-       switch(type)
-       {
-       case ARRAY_BUFFER:         return bound[0];
-       case ELEMENT_ARRAY_BUFFER: return bound[1];
-       case PIXEL_PACK_BUFFER:    return bound[2];
-       case PIXEL_UNPACK_BUFFER:  return bound[3];
-       case UNIFORM_BUFFER:       return bound[4];
-       default: throw invalid_argument("Buffer::binding");
-       }
-}
-
-bool Buffer::set_current(BufferType type, const Buffer *buf)
-{
-       const Buffer *&ptr = binding(type);
-       if(ptr==buf)
-               return false;
-
-       ptr = buf;
-       return true;
-}
-
-
-vector<const BufferRange *> BufferRange::bound_uniform;
-
-BufferRange::BufferRange(Buffer &b, unsigned o, unsigned s):
-       buffer(b),
-       offset(o),
-       size(s)
-{
-       if(o>buffer.get_size() || o+s>buffer.get_size())
-               throw out_of_range("BufferRange::BufferRange");
-}
-
-BufferRange::~BufferRange()
-{
-       for(unsigned i=0; i<bound_uniform.size(); ++i)
-               if(bound_uniform[i]==this)
-                       unbind_from(UNIFORM_BUFFER, i);
-}
-
-void BufferRange::data(const void *d)
-{
-       buffer.sub_data(offset, size, d);
-}
-
-void BufferRange::bind_to(BufferType t, unsigned i)
-{
-       if(t!=buffer.type)
-               Buffer::require_buffer_type(t);
-       if(set_current(t, i, this))
-       {
-               // The buffer gets bound as a side effect
-               Buffer::set_current(t, &buffer);
-               glBindBufferRange(t, i, buffer.id, offset, size);
-       }
-}
-
-void BufferRange::unbind_from(BufferType t, unsigned i)
-{
-       if(set_current(t, i, 0))
-       {
-               Buffer::set_current(t, 0);
-               glBindBufferBase(t, i, 0);
-       }
-}
-
-const BufferRange *&BufferRange::binding(BufferType type, unsigned index)
-{
-       if(type==UNIFORM_BUFFER)
-       {
-               if(index>=get_n_uniform_buffer_bindings())
-                       throw out_of_range("BufferRange::binding");
-               if(bound_uniform.size()<=index)
-                       bound_uniform.resize(index+1);
-               return bound_uniform[index];
-       }
-       else
-               throw invalid_argument("BufferRange::binding");
-}
-
-bool BufferRange::set_current(BufferType type, unsigned index, const BufferRange *buf)
-{
-       const BufferRange *&ptr = binding(type, index);
-       if(ptr==buf)
-               return false;
-
-       ptr = buf;
-       return true;
-}
-
-unsigned BufferRange::get_n_uniform_buffer_bindings()
-{
-       static unsigned count = get_i(GL_MAX_UNIFORM_BUFFER_BINDINGS);
-       return count;
-}
-
-unsigned BufferRange::get_uniform_buffer_alignment()
-{
-       static unsigned align = get_i(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
-       return align;
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/buffer.h b/source/buffer.h
deleted file mode 100644 (file)
index cc5e587..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-#ifndef MSP_GL_BUFFER_H_
-#define MSP_GL_BUFFER_H_
-
-#include <stdexcept>
-#include <string>
-#include <vector>
-#include <msp/core/attributes.h>
-#include "gl.h"
-#include <msp/gl/extensions/arb_pixel_buffer_object.h>
-#include <msp/gl/extensions/arb_vertex_buffer_object.h>
-#include <msp/gl/extensions/arb_uniform_buffer_object.h>
-#include <msp/gl/extensions/oes_mapbuffer.h>
-
-namespace Msp {
-namespace GL {
-
-class buffer_too_small: public std::logic_error
-{
-public:
-       buffer_too_small(const std::string &w): std::logic_error(w) { }
-       virtual ~buffer_too_small() throw() { }
-};
-
-enum BufferType
-{
-       ARRAY_BUFFER         = GL_ARRAY_BUFFER,
-       ELEMENT_ARRAY_BUFFER = GL_ELEMENT_ARRAY_BUFFER,
-       PIXEL_PACK_BUFFER    = GL_PIXEL_PACK_BUFFER,
-       PIXEL_UNPACK_BUFFER  = GL_PIXEL_UNPACK_BUFFER,
-       UNIFORM_BUFFER       = GL_UNIFORM_BUFFER
-};
-
-enum BufferUsage
-{
-       STREAM_DRAW  = GL_STREAM_DRAW,
-       STREAM_READ  = GL_STREAM_READ,
-       STREAM_COPY  = GL_STREAM_COPY,
-       STATIC_DRAW  = GL_STATIC_DRAW,
-       STATIC_READ  = GL_STATIC_READ,
-       STATIC_COPY  = GL_STATIC_COPY,
-       DYNAMIC_DRAW = GL_DYNAMIC_DRAW,
-       DYNAMIC_READ = GL_DYNAMIC_READ,
-       DYNAMIC_COPY = GL_DYNAMIC_COPY
-};
-
-enum BufferAccess
-{
-       READ_ONLY = GL_READ_ONLY,
-       WRITE_ONLY = GL_WRITE_ONLY,
-       READ_WRITE = GL_READ_WRITE
-};
-
-class BufferRange;
-
-/**
-A buffer for storing data in GL memory.  Putting vertex and index data in
-buffers can improve rendering performance.  The VertexArray, Mesh and
-UniformBlock classes contain built-in support for buffers.
-*/
-class Buffer
-{
-       friend class BufferRange;
-
-private:
-       BufferType type;
-       unsigned id;
-       unsigned size;
-
-       static const Buffer *bound[5];
-
-public:
-       Buffer(BufferType);
-       ~Buffer();
-
-private:
-       static void require_buffer_type(BufferType);
-
-public:
-       /** Returns the OpenGL ID of the buffer.  For internal use only. */
-       unsigned get_id() const { return id; }
-
-       /** Returns the default binding type for the buffer. */
-       BufferType get_type() const { return type; }
-
-       /** Defines the storage size of the buffer.  Must be called before data can
-       be uploaded.  Storage cannot be changed once set. */
-       void storage(unsigned);
-
-       /** Sets the usage hint of the buffer.  It will take effect the next time
-       the buffer's contents are defined. */
-       DEPRECATED void set_usage(BufferUsage);
-
-       /** Uploads data into the buffer, completely replacing any previous
-       contents.  Storage must be defined beforehand.  The data must have size
-       matching the defined storage. */
-       void data(const void *);
-
-       DEPRECATED void data(unsigned, const void *);
-
-       /** Overwrites part of the buffer data with new data.  Storage must be
-       defined beforehand. */
-       void sub_data(unsigned, unsigned, const void *);
-
-       unsigned get_size() const { return size; }
-
-       void require_size(unsigned) const;
-
-       BufferRange *create_range(unsigned, unsigned);
-
-       void *map();
-       DEPRECATED void *map(BufferAccess) { return map(); }
-       bool unmap();
-
-       /** Binds the buffer in its default slot. */
-       void bind() const { bind_to(type); }
-
-       /** Binds the buffer in an alternate slot. */
-       void bind_to(BufferType) const;
-
-       /** Unbinds the buffer from its default slot. */
-       void unbind() const { unbind_from(type); }
-
-       static const Buffer *current(BufferType);
-       static void unbind_from(BufferType);
-private:
-       static const Buffer *&binding(BufferType);
-       static bool set_current(BufferType, const Buffer *);
-};
-
-
-/**
-A proxy for a subset of a buffer.  Can be bound for use with uniform blocks.
-*/
-class BufferRange
-{
-private:
-       Buffer &buffer;
-       unsigned offset;
-       unsigned size;
-
-       static std::vector<const BufferRange *> bound_uniform;
-
-public:
-       BufferRange(Buffer &, unsigned, unsigned);
-       ~BufferRange();
-
-       void data(const void *);
-
-       void bind_to(BufferType, unsigned);
-
-       static const BufferRange *current(BufferType t, unsigned i) { return binding(t, i); }
-       static void unbind_from(BufferType, unsigned);
-private:
-       static const BufferRange *&binding(BufferType, unsigned);
-       static bool set_current(BufferType, unsigned, const BufferRange *);
-
-public:
-       static unsigned get_n_uniform_buffer_bindings();
-       static unsigned get_uniform_buffer_alignment();
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/bufferable.cpp b/source/bufferable.cpp
deleted file mode 100644 (file)
index d07f57c..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-#include <stdexcept>
-#include <msp/core/raii.h>
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include "bindable.h"
-#include "buffer.h"
-#include "bufferable.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Bufferable::Bufferable():
-       buffer(0),
-       offset(0),
-       next_in_buffer(0),
-       prev_in_buffer(0),
-       location_dirty(false),
-       dirty(false)
-{ }
-
-Bufferable::~Bufferable()
-{
-       unlink_from_buffer();
-}
-
-void Bufferable::use_buffer(Buffer *buf, Bufferable *prev)
-{
-       if(prev && buf!=prev->buffer)
-               throw invalid_argument("Bufferable::use_buffer");
-
-       if(buffer)
-               unlink_from_buffer();
-
-       buffer = buf;
-       if(buffer)
-       {
-               prev_in_buffer = prev;
-               if(prev_in_buffer)
-               {
-                       next_in_buffer = prev_in_buffer->next_in_buffer;
-                       prev_in_buffer->next_in_buffer = this;
-               }
-       }
-
-       location_dirty = true;
-       dirty = true;
-       update_offset();
-}
-
-void Bufferable::change_buffer(Buffer *buf)
-{
-       for(Bufferable *b=this; b; b=b->next_in_buffer)
-       {
-               b->buffer = buf;
-               b->location_dirty = true;
-               b->dirty = true;
-       }
-       for(Bufferable *b=prev_in_buffer; b; b=b->prev_in_buffer)
-       {
-               b->buffer = buf;
-               b->location_dirty = true;
-               b->dirty = true;
-       }
-}
-
-unsigned Bufferable::get_required_buffer_size() const
-{
-       const Bufferable *last = this;
-       for(; last->next_in_buffer; last=last->next_in_buffer) ;
-       return last->offset+last->get_data_size();
-}
-
-Bufferable::AsyncUpdater *Bufferable::refresh_async() const
-{
-       return dirty ? new AsyncUpdater(*this) : 0;
-}
-
-void Bufferable::unlink_from_buffer()
-{
-       if(prev_in_buffer)
-               prev_in_buffer->next_in_buffer = next_in_buffer;
-       if(next_in_buffer)
-       {
-               next_in_buffer->prev_in_buffer = prev_in_buffer;
-               next_in_buffer->update_offset();
-       }
-       prev_in_buffer = 0;
-       next_in_buffer = 0;
-       buffer = 0;
-       offset = 0;
-}
-
-void Bufferable::update_offset()
-{
-       unsigned new_offset = 0;
-       if(prev_in_buffer)
-               new_offset = prev_in_buffer->offset+prev_in_buffer->get_data_size();
-
-       unsigned align = get_alignment();
-       new_offset += align-1;
-       new_offset -= new_offset%align;
-       if(new_offset!=offset)
-       {
-               offset = new_offset;
-               location_dirty = true;
-               dirty = true;
-       }
-
-       if(next_in_buffer)
-               next_in_buffer->update_offset();
-       else if(buffer && offset+get_data_size()>buffer->get_size())
-       {
-               location_dirty = true;
-               dirty = true;
-       }
-}
-
-void Bufferable::upload_data(char *target) const
-{
-       unsigned data_size = get_data_size();
-       if(location_dirty)
-       {
-               buffer->require_size(offset+data_size);
-               location_changed(buffer, offset, data_size);
-               location_dirty = false;
-       }
-
-       if(target)
-       {
-               const char *source = reinterpret_cast<const char *>(get_data_pointer());
-               copy(source, source+data_size, target);
-       }
-       else
-               buffer->sub_data(offset, data_size, get_data_pointer());
-       dirty = false;
-}
-
-
-Bufferable::AsyncUpdater::AsyncUpdater(const Bufferable &b):
-       bufferable(b)
-{
-       bufferable.buffer->require_size(bufferable.get_required_buffer_size());
-       mapped_address = reinterpret_cast<char *>(bufferable.buffer->map());
-}
-
-Bufferable::AsyncUpdater::~AsyncUpdater()
-{
-       bufferable.buffer->unmap();
-}
-
-void Bufferable::AsyncUpdater::upload_data()
-{
-       bufferable.upload_data(mapped_address+bufferable.offset);
-       // Update all bufferables in the same buffer at once
-       for(const Bufferable *b=bufferable.prev_in_buffer; b; b=b->prev_in_buffer)
-               if(b->dirty)
-                       b->upload_data(mapped_address+b->offset);
-       for(const Bufferable *b=bufferable.next_in_buffer; b; b=b->next_in_buffer)
-               if(b->dirty)
-                       b->upload_data(mapped_address+b->offset);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/bufferable.h b/source/bufferable.h
deleted file mode 100644 (file)
index cbbde4e..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-#ifndef MSP_GL_BUFFERABLE_H_
-#define MSP_GL_BUFFERABLE_H_
-
-namespace Msp {
-namespace GL {
-
-class Buffer;
-
-/**
-Base class for things that can store data in buffers.  Supports buffer sharing.
-A dirty flag is provided for derived classes.  It should be set when the data
-in the buffer is considered out of date, and is cleared by Bufferable after
-uploading fresh data to the buffer.
-*/
-class Bufferable
-{
-public:
-       class AsyncUpdater
-       {
-       private:
-               const Bufferable &bufferable;
-               char *mapped_address;
-
-       public:
-               AsyncUpdater(const Bufferable &);
-               ~AsyncUpdater();
-
-               void upload_data();
-       };
-
-private:
-       Buffer *buffer;
-       unsigned offset;
-       Bufferable *next_in_buffer;
-       Bufferable *prev_in_buffer;
-       mutable bool location_dirty;
-protected:
-       mutable bool dirty;
-
-       Bufferable();
-public:
-       virtual ~Bufferable();
-
-       /** Sets the buffer to use.  If prev is not null, it must use the same
-       buffer, and this object is inserted after it. */
-       void use_buffer(Buffer *buf, Bufferable *prev = 0);
-
-       /** Sets the buffer for the entire chain of objects. */
-       void change_buffer(Buffer *);
-
-       /** Returns the total amount of storage required by this object and others
-       in the same chain, including any alignment between objects. */
-       unsigned get_required_buffer_size() const;
-
-       /** Uploads new data into the buffer if necessary. */
-       void refresh() const { if(buffer && dirty) upload_data(0); }
-
-       /** Returns an object which can be used to upload data to the buffer using
-       mapped memory. */
-       AsyncUpdater *refresh_async() const;
-
-private:
-       void unlink_from_buffer();
-
-public:
-       /** Returns the buffer in which the data is stored. */
-       const Buffer *get_buffer() const { return buffer; }
-
-protected:
-       /** Returns the amount of data to be stored in the buffer, in bytes. */
-       virtual unsigned get_data_size() const = 0;
-
-       /** Returns a pointer to the start of data in client memory. */
-       virtual const void *get_data_pointer() const = 0;
-
-       /** Returns the alignment required for the data, in bytes.  The offset is
-       guaranteed to be a multiple of this. */
-       virtual unsigned get_alignment() const { return 1; }
-
-       /** Updates the offsets for the chain so that data from different objects
-       does not overlap.  Should be called if either data size or alignment
-       changes. */
-       void update_offset();
-
-       /** Returns the offset where the data should be uploaded. */
-       unsigned get_offset() const { return offset; }
-
-       /** Called when the target buffer or offset within it has changed. */
-       virtual void location_changed(Buffer *, unsigned, unsigned) const { }
-
-private:
-       /** Uploads data to the buffer.  Receives pointer to mapped buffer memory as
-       parameter, or null to use the buffer upload interface. */
-       void upload_data(char *) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/builders/box.cpp b/source/builders/box.cpp
new file mode 100644 (file)
index 0000000..46f1b48
--- /dev/null
@@ -0,0 +1,68 @@
+#include <cmath>
+#include "box.h"
+#include "primitivebuilder.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+BoxBuilder::BoxBuilder(float w, float h, float d):
+       origin(-w/2, -h/2, -d/2),
+       span(w, h, d)
+{ }
+
+BoxBuilder::BoxBuilder(const Vector3 &o, const Vector3 &s):
+       origin(o),
+       span(s)
+{ }
+
+void BoxBuilder::build(PrimitiveBuilder &builder) const
+{
+       builder.normal(1, 0, 0);
+       build_face(builder, Vector3(origin.x+span.x, origin.y, origin.z), Vector3(0, span.y, 0), Vector3(0, 0, span.z));
+       builder.normal(0, 1, 0);
+       build_face(builder, Vector3(origin.x+span.x, origin.y+span.y, origin.z), Vector3(-span.x, 0, 0), Vector3(0, 0, span.z));
+       builder.normal(-1, 0, 0);
+       build_face(builder, Vector3(origin.x, origin.y+span.y, origin.z), Vector3(0, -span.y, 0), Vector3(0, 0, span.z));
+       builder.normal(0, -1, 0);
+       build_face(builder, origin, Vector3(span.x, 0, 0), Vector3(0, 0, span.z));
+       builder.normal(0, 0, 1);
+       build_face(builder, Vector3(origin.x, origin.y, origin.z+span.z), Vector3(span.x, 0, 0), Vector3(0, span.y, 0));
+       builder.normal(0, 0, -1);
+       build_face(builder, Vector3(origin.x+span.x, origin.y, origin.z), Vector3(-span.x, 0, 0), Vector3(0, span.y, 0));
+}
+
+void BoxBuilder::build_face(PrimitiveBuilder &builder, const Vector3 &o, const Vector3 &s1, const Vector3 &s2) const
+{
+       float l1 = 1, l2 = 1;
+       if(generate_tbn || tex_fit!=STRETCH)
+       {
+               l1 = s1.norm();
+               l2 = s2.norm();
+       }
+
+       if(generate_tbn)
+       {
+               builder.tangent(s1/l1);
+               builder.binormal(s2/l2);
+       }
+
+       float u_size = 1;
+       float v_size = 1;
+       adjust_texture_scale(u_size, v_size, l1, l2);
+
+       builder.begin(TRIANGLE_STRIP);
+       builder.texcoord(0, v_size);
+       builder.vertex(o+s2);
+       builder.texcoord(0, 0);
+       builder.vertex(o);
+       builder.texcoord(u_size, v_size);
+       builder.vertex(o+s1+s2);
+       builder.texcoord(u_size, 0);
+       builder.vertex(o+s1);
+       builder.end();
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/builders/box.h b/source/builders/box.h
new file mode 100644 (file)
index 0000000..1059d21
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef MSP_GL_BOX_H_
+#define MSP_GL_BOX_H_
+
+#include "geometrybuilder.h"
+#include "vector.h"
+
+namespace Msp {
+namespace GL {
+
+class BoxBuilder: public GeometryBuilder
+{
+private:
+       Vector3 origin;
+       Vector3 span;
+
+public:
+       BoxBuilder(float, float, float);
+       BoxBuilder(const Vector3 &, const Vector3 &);
+
+       using GeometryBuilder::build;
+       virtual void build(PrimitiveBuilder &) const;
+private:
+       void build_face(PrimitiveBuilder &, const Vector3 &, const Vector3 &, const Vector3 &) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/builders/capsule.cpp b/source/builders/capsule.cpp
new file mode 100644 (file)
index 0000000..65a18eb
--- /dev/null
@@ -0,0 +1,74 @@
+#define _USE_MATH_DEFINES
+#include <cmath>
+#include "capsule.h"
+#include "primitivebuilder.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+CapsuleBuilder::CapsuleBuilder(float r, float l, unsigned s, unsigned n):
+       radius(r),
+       length(l),
+       segments(s),
+       rings(n)
+{
+       if(segments<3)
+               segments = 3;
+       rings |= 1;
+       if(rings<3)
+               rings = 3;
+}
+
+void CapsuleBuilder::build(PrimitiveBuilder &builder) const
+{
+       float u_scale = 1.0/segments;
+       float v_scale = 1/(length+radius*M_PI);
+       adjust_texture_scale(u_scale, v_scale, radius*M_PI*2, length+radius*M_PI);
+
+       builder.normal(0, 0, -1);
+       builder.texcoord(0.5, 0);
+       builder.vertex(0, 0, -length/2-radius);
+       for(unsigned i=1; i<rings; ++i)
+       {
+               float cz = length*(i>rings/2 ? 0.5 : -0.5);
+               float v = ((i>rings/2 ? i-1 : i)*radius*M_PI/(rings-1)+(i>rings/2 ? length : 0))*v_scale;
+               float ra = (i>rings/2 ? i-1 : i)*M_PI/(rings-1);
+               float rc = cos(ra);
+               float rs = sin(ra);
+               for(unsigned j=0; j<=segments; ++j)
+               {
+                       float sa = j*M_PI*2/segments;
+                       float sc = cos(sa);
+                       float ss = sin(sa);
+                       builder.normal(rs*sc, rs*ss, -rc);
+                       if(generate_tbn)
+                       {
+                               builder.tangent(-ss, sc, 0);
+                               builder.binormal(rc*sc, rc*ss, rs);
+                       }
+                       builder.texcoord(j*u_scale, v);
+                       builder.vertex(rs*sc*radius, rs*ss*radius, cz-rc*radius);
+               }
+       }
+       builder.normal(0, 0, 1);
+       builder.texcoord(0.5, (length+radius*M_PI)*v_scale);
+       builder.vertex(0, 0, length/2+radius);
+
+       for(unsigned i=0; i<segments; ++i)
+       {
+               builder.begin(TRIANGLE_STRIP);
+               builder.element(0);
+               for(unsigned j=0; j+1<rings; ++j)
+               {
+                       builder.element(1+j*(segments+1)+i+1);
+                       builder.element(1+j*(segments+1)+i);
+               }
+               builder.element((segments+1)*(rings-1)+1);
+               builder.end();
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/builders/capsule.h b/source/builders/capsule.h
new file mode 100644 (file)
index 0000000..375f4a7
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef MSP_GL_CAPSULE_H_
+#define MSP_GL_CAPSULE_H_
+
+#include "geometrybuilder.h"
+
+namespace Msp {
+namespace GL {
+
+class CapsuleBuilder: public GeometryBuilder
+{
+private:
+       float radius;
+       float length;
+       unsigned segments;
+       unsigned rings;
+
+public:
+       CapsuleBuilder(float, float, unsigned = 16, unsigned = 9);
+
+       using GeometryBuilder::build;
+       virtual void build(PrimitiveBuilder &) const;
+};
+
+} // namepace GL
+} // namespace Msp
+
+#endif
diff --git a/source/builders/cylinder.cpp b/source/builders/cylinder.cpp
new file mode 100644 (file)
index 0000000..901a0f4
--- /dev/null
@@ -0,0 +1,86 @@
+#define _USE_MATH_DEFINES
+#include <cmath>
+#include "cylinder.h"
+#include "primitivebuilder.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+CylinderBuilder::CylinderBuilder(float r, float l, unsigned s):
+       radius(r),
+       length(l),
+       segments(s)
+{
+       if(segments<3)
+               segments = 3;
+}
+
+void CylinderBuilder::build(PrimitiveBuilder &builder) const
+{
+       if(generate_tbn)
+               builder.binormal(0, 1, 0);
+       for(unsigned i=0; i<2; ++i)
+       {
+               float z = (i-0.5)*length;
+               builder.normal(0, 0, i*2.0-1.0);
+               builder.texcoord(0.5, 0.5);
+               if(generate_tbn)
+                       builder.tangent((i ? 1 : -1), 0, 0);
+               builder.vertex(0, 0, z);
+               for(unsigned j=0; j<segments; ++j)
+               {
+                       float a = j*M_PI*2/segments;
+                       float c = cos(a);
+                       float s = sin(a);
+                       builder.texcoord(0.5+(i ? 0.5 : -0.5)*c, 0.5+0.5*s);
+                       builder.vertex(radius*c, radius*s, z);
+               }
+       }
+
+       float u_scale = 1.0/segments;
+       float v_scale = 1;
+       adjust_texture_scale(u_scale, v_scale, radius*M_PI*2, length);
+
+       if(generate_tbn)
+               builder.binormal(0, 0, 1);
+       for(unsigned i=0; i<2; ++i)
+       {
+               float z = (i-0.5)*length;
+               for(unsigned j=0; j<=segments; ++j)
+               {
+                       float a = j*M_PI*2/segments;
+                       float c = cos(a);
+                       float s = sin(a);
+                       builder.normal(c, s, 0);
+                       builder.texcoord(j*u_scale, i*v_scale);
+                       if(generate_tbn)
+                               builder.tangent(-s, c, 0);
+                       builder.vertex(radius*c, radius*s, z);
+               }
+       }
+
+       unsigned base = 0;
+       for(unsigned i=0; i<2; ++i)
+       {
+               builder.begin(TRIANGLE_FAN);
+               builder.element(base);
+               for(unsigned j=0; j<=segments; ++j)
+                       builder.element(base+1+j%segments);
+               builder.end();
+
+               base += segments+1;
+       }
+
+       builder.begin(TRIANGLE_STRIP);
+       for(unsigned j=0; j<=segments; ++j)
+       {
+               builder.element(base+segments+1+j);
+               builder.element(base+j);
+       }
+       builder.end();
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/builders/cylinder.h b/source/builders/cylinder.h
new file mode 100644 (file)
index 0000000..dee4c4f
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef MSP_GL_CYLINDER_H_
+#define MSP_GL_CYLINDER_H_
+
+#include "geometrybuilder.h"
+
+namespace Msp {
+namespace GL {
+
+class CylinderBuilder: public GeometryBuilder
+{
+private:
+       float radius;
+       float length;
+       unsigned segments;
+
+public:
+       CylinderBuilder(float, float, unsigned = 16);
+
+       using GeometryBuilder::build;
+       virtual void build(PrimitiveBuilder &) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/builders/font.cpp b/source/builders/font.cpp
new file mode 100644 (file)
index 0000000..5990c57
--- /dev/null
@@ -0,0 +1,224 @@
+#include <msp/core/maputils.h>
+#include <msp/datafile/collection.h>
+#include "bindable.h"
+#include "gl.h"
+#include "font.h"
+#include "primitivebuilder.h"
+#include "texture2d.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Font::Font():
+       native_size(1),
+       ascent(1),
+       descent(0),
+       cap_height(1),
+       x_height(0.5)
+{ }
+
+// Avoid synthesizing ~RefPtr in files including font.h
+Font::~Font()
+{ }
+
+void Font::set_texture(const Texture2D &t)
+{
+       texture = &t;
+       texture.keep();
+}
+
+const Texture2D &Font::get_texture() const
+{
+       if(!texture)
+               throw logic_error("No texture");
+       return *texture;
+}
+
+void Font::add_glyph(const Glyph &g)
+{
+       insert_unique(glyphs, g.code, g);
+}
+
+void Font::set_kerning(unsigned l, unsigned r, float d)
+{
+       kerning[CodePair(l, r)] = d;
+}
+
+float Font::get_string_width(const string &str, StringCodec::Decoder &dec) const
+{
+       float x = 0;
+
+       unsigned prev = 0;
+       for(string::const_iterator i=str.begin(); i!=str.end();)
+       {
+               unsigned c = dec.decode_char(str, i);
+               if(prev)
+                       x += get_glyph_advance(prev, c);
+               prev = c;
+       }
+       x += get_glyph_advance(prev);
+
+       return x;
+}
+
+void Font::build_string(const string &str, StringCodec::Decoder &dec, PrimitiveBuilder &bld) const
+{
+       VertexBuilder::PushMatrix push_mtx(bld);
+
+       unsigned prev = 0;
+       unsigned next = 0;
+       for(string::const_iterator i=str.begin(); (next || i!=str.end());)
+       {
+               unsigned c = (next ? next : dec.decode_char(str, i));
+               next = (i!=str.end() ? dec.decode_char(str, i) : 0);
+
+               if(unsigned lig = get_ligature(c, next))
+               {
+                       c = lig;
+                       next = 0;
+               }
+
+               GlyphMap::const_iterator j = glyphs.find(c);
+               if(j==glyphs.end())
+                       continue;
+
+               if(prev)
+                       bld.transform(Matrix::translation(get_glyph_advance(prev, c), 0, 0));
+
+               create_glyph_quad(j->second, bld);
+               prev = c;
+       }
+}
+
+void Font::create_glyph_quad(const Glyph &glyph, PrimitiveBuilder &bld) const
+{
+       bld.begin(TRIANGLE_STRIP);
+       bld.texcoord(glyph.x1, glyph.y2);
+       bld.vertex(glyph.off_x, glyph.off_y+glyph.h);
+       bld.texcoord(glyph.x1, glyph.y1);
+       bld.vertex(glyph.off_x, glyph.off_y);
+       bld.texcoord(glyph.x2, glyph.y2);
+       bld.vertex(glyph.off_x+glyph.w, glyph.off_y+glyph.h);
+       bld.texcoord(glyph.x2, glyph.y1);
+       bld.vertex(glyph.off_x+glyph.w, glyph.off_y);
+       bld.end();
+}
+
+float Font::get_glyph_advance(unsigned code, unsigned next) const
+{
+       GlyphMap::const_iterator i = glyphs.find(code);
+       if(i==glyphs.end())
+               return 0;
+
+       float advance = i->second.advance;
+
+       if(next)
+       {
+               KerningMap::const_iterator j = kerning.find(CodePair(code, next));
+               if(j!=kerning.end())
+                       advance += j->second;
+       }
+
+       return advance;
+}
+
+unsigned Font::get_ligature(unsigned code, unsigned next) const
+{
+       LigatureMap::const_iterator i = ligatures.find(CodePair(code, next));
+       return (i!=ligatures.end() ? i->second : 0);
+}
+
+
+Font::Glyph::Glyph():
+       code(0),
+       x1(0),
+       y1(0),
+       x2(1),
+       y2(1),
+       w(1),
+       h(1),
+       off_x(0),
+       off_y(0),
+       advance(1)
+{ }
+
+
+Font::Loader::Loader(Font &f):
+       DataFile::CollectionObjectLoader<Font>(f, 0)
+{
+       init();
+}
+
+Font::Loader::Loader(Font &f, Collection &c):
+       DataFile::CollectionObjectLoader<Font>(f, &c)
+{
+       init();
+}
+
+void Font::Loader::init()
+{
+       add("native_size", &Font::native_size);
+       add("ascent",      &Font::ascent);
+       add("cap_height",  &Font::cap_height);
+       add("descent",     &Font::descent);
+       add("texture",     &Loader::texture);
+       add("texture",     &Loader::texture_ref);
+       add("glyph",       &Loader::glyph);
+       add("kerning",     &Loader::kerning);
+       add("ligature",    &Loader::ligature);
+       add("x_height",    &Font::x_height);
+}
+
+void Font::Loader::glyph(unsigned c)
+{
+       Glyph gl;
+       gl.code = c;
+       load_sub(gl);
+       obj.glyphs.insert(GlyphMap::value_type(c, gl));
+}
+
+void Font::Loader::kerning(unsigned l, unsigned r, float d)
+{
+       obj.kerning[CodePair(l, r)] = d;
+}
+
+void Font::Loader::ligature(unsigned l, unsigned r, unsigned g)
+{
+       obj.ligatures[CodePair(l, r)] = g;
+}
+
+void Font::Loader::texture()
+{
+       RefPtr<Texture2D> tex = new Texture2D;
+       load_sub(*tex);
+       obj.texture = tex;
+}
+
+void Font::Loader::texture_ref(const string &name)
+{
+       obj.texture = &get_collection().get<Texture2D>(name);
+       obj.texture.keep();
+}
+
+
+Font::Glyph::Loader::Loader(Glyph &g):
+       DataFile::ObjectLoader<Glyph>(g)
+{
+       add("texcoords", &Loader::texcoords);
+       add("size",      &Glyph::w,     &Glyph::h);
+       add("offset",    &Glyph::off_x, &Glyph::off_y);
+       add("advance",   &Glyph::advance);
+}
+
+void Font::Glyph::Loader::texcoords(float x1_, float y1_, float x2_, float y2_)
+{
+       obj.x1 = x1_;
+       obj.y1 = y1_;
+       obj.x2 = x2_;
+       obj.y2 = y2_;
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/builders/font.h b/source/builders/font.h
new file mode 100644 (file)
index 0000000..9f040dc
--- /dev/null
@@ -0,0 +1,133 @@
+#ifndef MSP_GL_FONT_H_
+#define MSP_GL_FONT_H_
+
+#include <map>
+#include <string>
+#include <msp/datafile/objectloader.h>
+#include <msp/stringcodec/utf8.h>
+#include "vertexarray.h"
+
+namespace Msp {
+namespace GL {
+
+class PrimitiveBuilder;
+class Texture2D;
+
+/**
+Stores a set of glyphs and creates strings out of them.
+*/
+class Font
+{
+public:
+       class Loader: public DataFile::CollectionObjectLoader<Font>
+       {
+       public:
+               Loader(Font &);
+               Loader(Font &, Collection &);
+
+       private:
+               void init();
+               void glyph(unsigned);
+               void kerning(unsigned, unsigned, float);
+               void ligature(unsigned, unsigned, unsigned);
+               void texture();
+               void texture_ref(const std::string &);
+       };
+
+       struct Glyph
+       {
+               class Loader: public Msp::DataFile::ObjectLoader<Glyph>
+               {
+               public:
+                       Loader(Glyph &);
+               private:
+                       void texcoords(float, float, float, float);
+               };
+
+               unsigned code;
+               float x1, y1;
+               float x2, y2;
+               float w, h;
+               float off_x, off_y;
+               float advance;
+
+               Glyph();
+       };
+
+private:
+       typedef std::map<unsigned, Glyph> GlyphMap;
+       typedef std::pair<unsigned, unsigned> CodePair;
+       typedef std::map<CodePair, float> KerningMap;
+       typedef std::map<CodePair, unsigned> LigatureMap;
+
+       RefPtr<const Texture2D> texture;
+       float native_size;
+       float ascent;
+       float descent;
+       float cap_height;
+       float x_height;
+       GlyphMap glyphs;
+       KerningMap kerning;
+       LigatureMap ligatures;
+
+public:
+       Font();
+       ~Font();
+
+       void set_texture(const Texture2D &);
+       const Texture2D &get_texture() const;
+
+       /** Adds a glyph to the font.  There must not be an existing glyph with the
+       same code. */
+       void add_glyph(const Glyph &);
+       void set_kerning(unsigned, unsigned, float);
+
+       /** Returns the size used to generate the font texture.  This serves as a
+       hint for obtaining the best quality when rendering strings. */
+       float get_native_size() const { return native_size; }
+
+       float get_ascent() const { return ascent; }
+       float get_descent() const { return descent; }
+       float get_cap_height() const { return cap_height; }
+       float get_x_height() const { return x_height; }
+
+       /** Returns the width of a string, in multiples of the font size.  Scale the
+       result according to the size used in rendering. */
+       float get_string_width(const std::string &, StringCodec::Decoder &) const;
+
+       template<class C>
+       float get_string_width(const std::string &str) const
+       {
+               typename C::Decoder dec;
+               return get_string_width(str, dec);
+       }
+
+       float get_string_width(const std::string &str) const
+       { return get_string_width<StringCodec::Utf8>(str); }
+
+       /** Builds the primitives for a string.  Two-dimensional vertex and texture
+       coordinates are generated.  Size 1.0 is used for building; set up the
+       builder's matrix before the call.  The texture is not bound, to avoid
+       unnecessary bindings when creating meshes. */
+       void build_string(const std::string &, StringCodec::Decoder &, PrimitiveBuilder &) const;
+
+       template<class C>
+       void build_string(const std::string &str, PrimitiveBuilder &pb) const
+       {
+               typename C::Decoder dec;
+               build_string(str, dec, pb);
+       }
+
+       void build_string(const std::string &str, PrimitiveBuilder &pb) const
+       { return build_string<StringCodec::Utf8>(str, pb); }
+
+private:
+       void create_glyph_quad(const Glyph &, PrimitiveBuilder &) const;
+       float get_glyph_advance(unsigned, unsigned = 0) const;
+       unsigned get_ligature(unsigned, unsigned) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/builders/geometrybuilder.cpp b/source/builders/geometrybuilder.cpp
new file mode 100644 (file)
index 0000000..729b40f
--- /dev/null
@@ -0,0 +1,43 @@
+#include "geometrybuilder.h"
+#include "meshbuilder.h"
+
+namespace Msp {
+namespace GL {
+
+GeometryBuilder::GeometryBuilder():
+       generate_tbn(false),
+       tex_fit(STRETCH)
+{ }
+
+GeometryBuilder &GeometryBuilder::tbn(bool t)
+{
+       generate_tbn = t;
+       return *this;
+}
+
+GeometryBuilder &GeometryBuilder::texture_fit(TextureFit tf)
+{
+       tex_fit = tf;
+       return *this;
+}
+
+void GeometryBuilder::adjust_texture_scale(float &u_scale, float &v_scale, float width, float height) const
+{
+       if(tex_fit!=STRETCH)
+       {
+               if((width<height)==(tex_fit==CROP))
+                       u_scale *= width/height;
+               else
+                       v_scale *= height/width;
+       }
+}
+
+void GeometryBuilder::build(Mesh &mesh) const
+{
+       MeshBuilder builder(mesh);
+       builder.auto_offset();
+       build(builder);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/builders/geometrybuilder.h b/source/builders/geometrybuilder.h
new file mode 100644 (file)
index 0000000..40bcd13
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef MSP_GL_GEOMETRYBUILDER_H_
+#define MSP_GL_GEOMETRYBUILDER_H_
+
+namespace Msp {
+namespace GL {
+
+class Mesh;
+class PrimitiveBuilder;
+
+class GeometryBuilder
+{
+public:
+       enum TextureFit
+       {
+               STRETCH,
+               CROP,
+               WRAP
+       };
+
+protected:
+       bool generate_tbn;
+       TextureFit tex_fit;
+
+       GeometryBuilder();
+
+public:
+       GeometryBuilder &tbn(bool = true);
+       GeometryBuilder &texture_fit(TextureFit);
+protected:
+       void adjust_texture_scale(float &, float &, float, float) const;
+
+public:
+       virtual void build(PrimitiveBuilder &) const = 0;
+       void build(Mesh &) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/builders/grid.cpp b/source/builders/grid.cpp
new file mode 100644 (file)
index 0000000..3f4d5e2
--- /dev/null
@@ -0,0 +1,110 @@
+#include <cmath>
+#include "grid.h"
+#include "primitivebuilder.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+GridBuilder::GridBuilder(float w, float h, unsigned u, unsigned v):
+       origin(-w/2, -h/2, 0),
+       side1(w, 0, 0),
+       side2(0, h, 0),
+       norm(0, 0, 1),
+       binorm(0, 1, 0),
+       u_div(u),
+       v_div(v)
+{
+       init(false);
+}
+
+GridBuilder::GridBuilder(const Vector3 &o, const Vector3 &s, unsigned u, unsigned v):
+       origin(o),
+       u_div(u),
+       v_div(v)
+{
+       if(abs(s.z)<abs(s.x) && abs(s.z)<abs(s.y))
+       {
+               side1 = Vector3(s.x, 0, 0);
+               side2 = Vector3(0, s.y, 0);
+       }
+       else if(abs(s.y)<abs(s.x))
+       {
+               side1 = Vector3(0, 0, s.z);
+               side2 = Vector3(s.x, 0, 0);
+       }
+       else
+       {
+               side1 = Vector3(0, s.y, 0);
+               side2 = Vector3(0, 0, s.z);
+       }
+       init(true);
+}
+
+GridBuilder::GridBuilder(const Vector3 &o, const Vector3 &s1, const Vector3 &s2, unsigned u, unsigned v):
+       origin(o),
+       side1(s1),
+       side2(s2),
+       u_div(u),
+       v_div(v)
+{
+       init(true);
+}
+
+void GridBuilder::init(bool compute_normals)
+{
+       if(u_div<1)
+               u_div = 1;
+       if(v_div<1)
+               v_div = 1;
+
+       if(compute_normals)
+       {
+               norm = normalize(cross(side1, side2));
+               binorm = normalize(cross(norm, side1));
+       }
+}
+
+void GridBuilder::build(PrimitiveBuilder &builder) const
+{
+       float l1_sq = dot(side1, side1);
+       float l2 = dot(side2, binorm);
+       float u_scale = 1/l1_sq;
+       float v_scale = 1/l2;
+       adjust_texture_scale(u_scale, v_scale, sqrt(l1_sq), l2);
+
+       builder.normal(norm.x, norm.y, norm.z);
+       if(generate_tbn)
+       {
+               builder.tangent(side1);
+               builder.binormal(binorm);
+       }
+
+       for(unsigned j=0; j<=v_div; ++j)
+       {
+               Vector3 row = side2*(j*1.0f/v_div);
+               float v = dot(row, binorm)*v_scale;
+               for(unsigned i=0; i<=u_div; ++i)
+               {
+                       Vector3 p = row+side1*(i*1.0f/u_div);
+                       float u = dot(p, side1)*u_scale;
+                       builder.texcoord(u, v);
+                       builder.vertex(origin+p);
+               }
+       }
+
+       for(unsigned j=0; j<v_div; ++j)
+       {
+               builder.begin(TRIANGLE_STRIP);
+               for(unsigned i=0; i<=u_div; ++i)
+               {
+                       builder.element((j+1)*(u_div+1)+i);
+                       builder.element(j*(u_div+1)+i);
+               }
+               builder.end();
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/builders/grid.h b/source/builders/grid.h
new file mode 100644 (file)
index 0000000..ed22ce7
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef MSP_GL_GRID_H_
+#define MSP_GL_GRID_H_
+
+#include "geometrybuilder.h"
+#include "vector.h"
+
+namespace Msp {
+namespace GL {
+
+class GridBuilder: public GeometryBuilder
+{
+private:
+       Vector3 origin;
+       Vector3 side1;
+       Vector3 side2;
+       Vector3 norm;
+       Vector3 binorm;
+       unsigned u_div;
+       unsigned v_div;
+
+public:
+       GridBuilder(float, float, unsigned = 4, unsigned = 4);
+       GridBuilder(const Vector3 &, const Vector3 &, unsigned = 4, unsigned = 4);
+       GridBuilder(const Vector3 &, const Vector3 &, const Vector3 &, unsigned = 4, unsigned = 4);
+private:
+       void init(bool);
+
+public:
+       using GeometryBuilder::build;
+       virtual void build(PrimitiveBuilder &) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/builders/meshbuilder.cpp b/source/builders/meshbuilder.cpp
new file mode 100644 (file)
index 0000000..92e2ee0
--- /dev/null
@@ -0,0 +1,41 @@
+#include "mesh.h"
+#include "meshbuilder.h"
+
+namespace Msp {
+namespace GL {
+
+MeshBuilder::MeshBuilder(Mesh &m):
+       PrimitiveBuilder(m.vertices),
+       mesh(m),
+       batch(0)
+{ }
+
+MeshBuilder::~MeshBuilder()
+{
+       mesh.check_buffers(Mesh::VERTEX_BUFFER);
+}
+
+void MeshBuilder::auto_offset()
+{
+       offset(mesh.get_vertices().size());
+}
+
+void MeshBuilder::begin_()
+{
+       batch = new Batch(type);
+}
+
+void MeshBuilder::end_()
+{
+       mesh.add_batch(*batch);
+       delete batch;
+       batch = 0;
+}
+
+void MeshBuilder::element_(unsigned i)
+{
+       batch->append(i);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/builders/meshbuilder.h b/source/builders/meshbuilder.h
new file mode 100644 (file)
index 0000000..5f4a9dc
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef MSP_GL_MESHBUILDER_H_
+#define MSP_GL_MESHBUILDER_H_
+
+#include "primitivebuilder.h"
+
+namespace Msp {
+namespace GL {
+
+class Batch;
+class Mesh;
+
+class MeshBuilder: public PrimitiveBuilder
+{
+private:
+       Mesh &mesh;
+       Batch *batch;
+
+public:
+       MeshBuilder(Mesh &);
+       ~MeshBuilder();
+
+       void auto_offset();
+private:
+       virtual void begin_();
+       virtual void end_();
+       virtual void element_(unsigned);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/builders/pipelinebuilder.cpp b/source/builders/pipelinebuilder.cpp
new file mode 100644 (file)
index 0000000..51e9cc8
--- /dev/null
@@ -0,0 +1,98 @@
+#include <msp/core/algorithm.h>
+#include <msp/core/maputils.h>
+#include "error.h"
+#include "pipeline.h"
+#include "pipelinebuilder.h"
+#include "pipelinetemplate.h"
+#include "renderbuffer.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+PipelineBuilder::PipelineBuilder(const PipelineTemplate &t):
+       tmpl(t)
+{
+       const vector<PipelineTemplate::Pass> &passes = tmpl.get_passes();
+       for(vector<PipelineTemplate::Pass>::const_iterator i=passes.begin(); i!=passes.end(); ++i)
+               renderables[i->renderable_name] = 0;
+       const vector<PipelineTemplate::PostProcessor> &postprocs = tmpl.get_postprocessors();
+       for(PipelineTemplate::PostProcessorArray::const_iterator i=postprocs.begin(); i!=postprocs.end(); ++i)
+               if(!i->slot_name.empty())
+                       postprocessors[i->slot_name] = 0;
+}
+
+void PipelineBuilder::set_renderable(const string &name, Renderable &rend)
+{
+       get_item(renderables, name) = &rend;
+}
+
+void PipelineBuilder::set_postprocessor(const string &name, PostProcessor &pproc)
+{
+       get_item(postprocessors, name) = &pproc;
+}
+
+void PipelineBuilder::build(Pipeline &pipeline) const
+{
+       pipeline.set_hdr(tmpl.get_hdr());
+       pipeline.set_alpha(tmpl.get_alpha());
+       unsigned samples = min(tmpl.get_maximum_multisample(), Renderbuffer::get_max_samples());
+       if(samples<tmpl.get_required_multisample())
+               throw invalid_operation("PipelineBuilder::build");
+
+       pipeline.set_multisample(samples);
+
+       const PipelineTemplate::PassArray &passes = tmpl.get_passes();
+       for(PipelineTemplate::PassArray::const_iterator i=passes.begin(); i!=passes.end(); ++i)
+       {
+               Renderable *renderable = get_item(renderables, i->renderable_name);
+               if(!renderable)
+                       continue;
+
+               Pipeline::Pass &pass = pipeline.add_pass(i->tag, *renderable);
+               pass.set_blend(i->blend.get());
+               pass.set_depth_test(i->depth_test.get());
+               pass.set_lighting(i->lighting.get());
+       }
+
+       const PipelineTemplate::PostProcessorArray &postprocs = tmpl.get_postprocessors();
+       for(PipelineTemplate::PostProcessorArray::const_iterator i=postprocs.begin(); i!=postprocs.end(); ++i)
+       {
+               PostProcessor *proc = 0;
+               if(!i->slot_name.empty())
+                       proc = get_item(postprocessors, i->slot_name);
+               if(proc)
+                       pipeline.add_postprocessor(*proc);
+               else if(i->postprocessor_template)
+               {
+                       proc = i->postprocessor_template->create(pipeline.get_width(), pipeline.get_height());
+                       if(proc)
+                               pipeline.add_postprocessor_owned(proc);
+               }
+       }
+}
+
+Pipeline *PipelineBuilder::build(unsigned w, unsigned h) const
+{
+       RefPtr<Pipeline> pipeline = new Pipeline(w, h);
+       build(*pipeline);
+       return pipeline.release();
+}
+
+Pipeline *PipelineBuilder::build(const View &view) const
+{
+       RefPtr<Pipeline> pipeline = new Pipeline(view);
+       build(*pipeline);
+       return pipeline.release();
+}
+
+Pipeline *PipelineBuilder::build(const Framebuffer &fbo) const
+{
+       RefPtr<Pipeline> pipeline = new Pipeline(fbo);
+       build(*pipeline);
+       return pipeline.release();
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/builders/pipelinebuilder.h b/source/builders/pipelinebuilder.h
new file mode 100644 (file)
index 0000000..ab0dc4b
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef PIPELINEBUILDER_H_
+#define PIPELINEBUILDER_H_
+
+#include <map>
+#include <string>
+
+namespace Msp {
+namespace GL {
+
+class Framebuffer;
+class Pipeline;
+class PipelineTemplate;
+class PostProcessor;
+class Renderable;
+class View;
+
+class PipelineBuilder
+{
+private:
+       const PipelineTemplate &tmpl;
+       std::map<std::string, Renderable *> renderables;
+       std::map<std::string, PostProcessor *> postprocessors;
+
+public:
+       PipelineBuilder(const PipelineTemplate &);
+
+       void set_renderable(const std::string &, Renderable &);
+       void set_postprocessor(const std::string &, PostProcessor &);
+
+       void build(Pipeline &) const;
+       Pipeline *build(unsigned, unsigned) const;
+       Pipeline *build(const View &) const;
+       Pipeline *build(const Framebuffer &) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/builders/pipelinetemplate.cpp b/source/builders/pipelinetemplate.cpp
new file mode 100644 (file)
index 0000000..b78b4c4
--- /dev/null
@@ -0,0 +1,202 @@
+#include <msp/core/maputils.h>
+#include <msp/datafile/collection.h>
+#include "ambientocclusion.h"
+#include "blend.h"
+#include "bloom.h"
+#include "colorcurve.h"
+#include "lighting.h"
+#include "pipelinetemplate.h"
+#include "tests.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+PipelineTemplate::PipelineTemplate():
+       hdr(false),
+       alpha(false),
+       required_multisample(0),
+       max_multisample(0)
+{ }
+
+PipelineTemplate::~PipelineTemplate()
+{
+       for(PostProcessorArray::iterator i=postprocessors.begin(); i!=postprocessors.end(); ++i)
+               delete i->postprocessor_template;
+}
+
+
+PipelineTemplate::PostProcessorRegistry &PipelineTemplate::get_postprocessor_registry()
+{
+       static PostProcessorRegistry registry;
+       static bool initialized = false;
+       if(!initialized)
+       {
+               registry.register_type<AmbientOcclusion>("ambient_occlusion");
+               registry.register_type<Bloom>("bloom");
+               registry.register_type<ColorCurve>("colorcurve");
+               initialized = true;
+       }
+       return registry;
+}
+
+
+PipelineTemplate::Pass::~Pass()
+{ }
+
+
+PipelineTemplate::PostProcessor::PostProcessor(GL::PostProcessor::Template *ppt):
+       postprocessor_template(ppt)
+{ }
+
+
+PipelineTemplate::PostProcLoader::PostProcLoader()
+{
+       get_postprocessor_registry().add_all(*this);
+}
+
+
+PipelineTemplate::Loader::Loader(PipelineTemplate &t):
+       DataFile::CollectionObjectLoader<PipelineTemplate>(t, 0)
+{
+       init();
+}
+
+PipelineTemplate::Loader::Loader(PipelineTemplate &t, Collection &c):
+       DataFile::CollectionObjectLoader<PipelineTemplate>(t, &c)
+{
+       init();
+}
+
+void PipelineTemplate::Loader::init()
+{
+       add("hdr", &PipelineTemplate::hdr);
+       add("alpha", &PipelineTemplate::alpha);
+       add("multisample", &Loader::multisample);
+       add("multisample", &Loader::multisample_range);
+       add("pass", &Loader::pass);
+       add("postprocessor", &Loader::postprocessor);
+}
+
+void PipelineTemplate::Loader::postprocessor_loaded()
+{
+       obj.postprocessors.push_back(get_postprocessor_template());
+}
+
+void PipelineTemplate::Loader::multisample(unsigned samples)
+{
+       obj.required_multisample = samples;
+       obj.max_multisample = samples;
+}
+
+void PipelineTemplate::Loader::multisample_range(unsigned req, unsigned max)
+{
+       obj.required_multisample = req;
+       obj.max_multisample = max;
+}
+
+void PipelineTemplate::Loader::pass(const string &tag, const string &rend)
+{
+       Pass pss;;
+       pss.tag = tag;
+       pss.renderable_name = rend;
+       if(coll)
+               load_sub(pss, *coll);
+       else
+               load_sub(pss);
+
+       obj.passes.push_back(pss);
+}
+
+void PipelineTemplate::Loader::postprocessor(const std::string &slot)
+{
+       PostProcLoader ldr;
+       load_sub_with(ldr);
+       PostProcessor pp;
+       pp.postprocessor_template = ldr.get_postprocessor_template();
+       pp.slot_name = slot;
+       obj.postprocessors.push_back(pp);
+}
+
+
+PipelineTemplate::Pass::Loader::Loader(Pass &p):
+       DataFile::CollectionObjectLoader<Pass>(p, 0)
+{
+       init();
+}
+
+PipelineTemplate::Pass::Loader::Loader(Pass &p, Collection &c):
+       DataFile::CollectionObjectLoader<Pass>(p, &c)
+{
+       init();
+}
+
+void PipelineTemplate::Pass::Loader::init()
+{
+       add("blend", &Loader::blend);
+       add("blend", &Loader::blend_predefined);
+       add("depth_test", &Loader::depth_test);
+       add("depth_test", &Loader::depth_test_predefined);
+       add("lighting", &Loader::lighting);
+       add("lighting", &Loader::lighting_inline);
+}
+
+void PipelineTemplate::Pass::Loader::blend_predefined(const string &name)
+{
+       const Blend *bln = 0;
+       if(name=="alpha")
+               bln = &Blend::alpha();
+       else if(name=="additive")
+               bln = &Blend::additive();
+       else if(name=="additive_alpha")
+               bln = &Blend::additive_alpha();
+       else
+               throw key_error(name);
+
+       obj.blend = bln;
+       obj.blend.keep();
+}
+
+void PipelineTemplate::Pass::Loader::blend(BlendFactor src, BlendFactor dest)
+{
+       obj.blend = new Blend(src, dest);
+}
+
+void PipelineTemplate::Pass::Loader::depth_test_predefined(const string &name)
+{
+       const DepthTest *dtest = 0;
+       if(name=="lequal")
+               dtest = &DepthTest::lequal();
+       else
+               throw key_error(name);
+
+       obj.depth_test = dtest;
+       obj.depth_test.keep();
+}
+
+void PipelineTemplate::Pass::Loader::depth_test(Predicate pred)
+{
+       obj.depth_test = new DepthTest(pred);
+}
+
+void PipelineTemplate::Pass::Loader::lighting_inline()
+{
+       RefPtr<Lighting> lightn = new Lighting;
+       load_sub(*lightn);
+       obj.lighting = lightn;
+}
+
+void PipelineTemplate::Pass::Loader::lighting(const string &name)
+{
+       obj.lighting = &get_collection().get<Lighting>(name);
+       obj.lighting.keep();
+}
+
+/*void PipelineTemplate::Pass::Loader::scene(const string &name)
+{
+       obj.default_renderable = get_collection().get<Scene>(name);
+}*/
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/builders/pipelinetemplate.h b/source/builders/pipelinetemplate.h
new file mode 100644 (file)
index 0000000..fb24ea2
--- /dev/null
@@ -0,0 +1,153 @@
+#ifndef PIPELINETEMPLATE_H_
+#define PIPELINETEMPLATE_H_
+
+#include <string>
+#include <vector>
+#include <msp/datafile/loadabletyperegistry.h>
+#include <msp/datafile/objectloader.h>
+#include "blend.h"
+#include "postprocessor.h"
+#include "predicate.h"
+
+namespace Msp {
+namespace GL {
+
+class DepthTest;
+class Lighting;
+
+class PipelineTemplate
+{
+private:
+       class PostProcLoader: virtual public DataFile::Loader
+       {
+       private:
+               template<typename T>
+               struct AddPostProc
+               {
+                       static void add(PostProcLoader &ldr, const std::string &kw) { ldr.add(kw, &PostProcLoader::postprocessor<T>); }
+               };
+
+       protected:
+               RefPtr<PostProcessor::Template> postproc;
+
+       public:
+               PostProcLoader();
+
+               PostProcessor::Template *get_postprocessor_template() { return postproc.release(); }
+
+       protected:
+               virtual void postprocessor_loaded() { }
+
+       private:
+               template<typename T>
+               void postprocessor();
+
+               friend class PipelineTemplate;
+       };
+
+public:
+       class Loader: public DataFile::CollectionObjectLoader<PipelineTemplate>, public PostProcLoader
+       {
+       public:
+               Loader(PipelineTemplate &);
+               Loader(PipelineTemplate &, Collection &);
+       private:
+               void init();
+
+               virtual void postprocessor_loaded();
+               void multisample(unsigned);
+               void multisample_range(unsigned, unsigned);
+               void pass(const std::string &, const std::string &);
+               void postprocessor(const std::string &);
+       };
+
+       struct Pass
+       {
+               class Loader: public DataFile::CollectionObjectLoader<Pass>
+               {
+               public:
+                       Loader(Pass &);
+                       Loader(Pass &, Collection &);
+               private:
+                       void init();
+
+                       void blend(BlendFactor, BlendFactor);
+                       void blend_predefined(const std::string &);
+                       void depth_test(Predicate);
+                       void depth_test_predefined(const std::string &);
+                       void lighting(const std::string &);
+                       void lighting_inline();
+                       // TODO requires support for scenes in Resources
+                       //void scene(const std::string &);
+               };
+
+               std::string tag;
+               RefPtr<Lighting> lighting;
+               RefPtr<const DepthTest> depth_test;
+               RefPtr<const Blend> blend;
+               std::string renderable_name;
+               //Renderable *default_renderable;
+
+               ~Pass();
+       };
+
+       struct PostProcessor
+       {
+               GL::PostProcessor::Template *postprocessor_template;
+               std::string slot_name;
+
+               PostProcessor(GL::PostProcessor::Template * = 0);
+       };
+
+       typedef std::vector<Pass> PassArray;
+       typedef std::vector<PostProcessor> PostProcessorArray;
+
+private:
+       typedef DataFile::LoadableTypeRegistry<PostProcLoader, PostProcLoader::AddPostProc> PostProcessorRegistry;
+
+       bool hdr;
+       bool alpha;
+       unsigned required_multisample;
+       unsigned max_multisample;
+       PassArray passes;
+       PostProcessorArray postprocessors;
+
+public:
+       PipelineTemplate();
+       ~PipelineTemplate();
+
+       bool get_hdr() const { return hdr; }
+       bool get_alpha() const { return alpha; }
+       unsigned get_required_multisample() const { return required_multisample; }
+       unsigned get_maximum_multisample() const { return max_multisample; }
+       const PassArray &get_passes() const { return passes; }
+       const PostProcessorArray &get_postprocessors() const { return postprocessors; }
+
+       template<typename T>
+       static void register_postprocessor(const std::string &);
+private:
+       static PostProcessorRegistry &get_postprocessor_registry();
+};
+
+template<typename T>
+void PipelineTemplate::register_postprocessor(const std::string &kw)
+{
+       get_postprocessor_registry().register_type<T>(kw);
+}
+
+template<typename T>
+void PipelineTemplate::PostProcLoader::postprocessor()
+{
+       if(postproc)
+               throw std::logic_error("Only one postprocessor allowed per slot");
+       RefPtr<typename T::Template> pp = new typename T::Template;
+       load_sub(*pp);
+       postproc = pp;
+       pp = 0;
+       postprocessor_loaded();
+}
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/builders/primitivebuilder.cpp b/source/builders/primitivebuilder.cpp
new file mode 100644 (file)
index 0000000..dd86990
--- /dev/null
@@ -0,0 +1,75 @@
+#include "error.h"
+#include "primitivebuilder.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+PrimitiveBuilder::PrimitiveBuilder(VertexArray &a):
+       array(a),
+       vab(array),
+       in_batch(false),
+       offs(0)
+{ }
+
+void PrimitiveBuilder::begin(PrimitiveType t)
+{
+       if(in_batch)
+               throw invalid_operation("PrimitiveBuilder::begin");
+
+       type = t;
+       in_batch = true;
+
+       begin_();
+}
+
+void PrimitiveBuilder::end()
+{
+       if(!in_batch)
+               throw invalid_operation("PrimitiveBuilder::end");
+
+       in_batch = false;
+
+       end_();
+}
+
+void PrimitiveBuilder::offset(unsigned o)
+{
+       if(o>array.size())
+               throw out_of_range("PrimitiveBuilder::offset");
+       offs = o;
+}
+
+void PrimitiveBuilder::element(unsigned i)
+{
+       if(!in_batch)
+               throw invalid_operation("PrimitiveBuilder::element");
+       if(offs+i>=array.size())
+               throw out_of_range("PrimitiveBuilder::element");
+       element_(offs+i);
+}
+
+PrimitiveType PrimitiveBuilder::get_type() const
+{
+       if(!in_batch)
+               throw invalid_operation("PrimitiveBuilder::get_type");
+       return type;
+}
+
+void PrimitiveBuilder::vertex_(const Vector4 &v)
+{
+       vab.color(col);
+       vab.normal(nor);
+       for(std::map<unsigned, Vector4>::iterator i=texc.begin(); i!=texc.end(); ++i)
+               vab.multitexcoord(i->first, i->second);
+       for(std::map<unsigned, Vector4>::iterator i=attr.begin(); i!=attr.end(); ++i)
+               vab.attrib(i->first, i->second);
+       vab.vertex(v);
+
+       if(in_batch)
+               element_(array.size()-1);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/builders/primitivebuilder.h b/source/builders/primitivebuilder.h
new file mode 100644 (file)
index 0000000..0ffd170
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef MSP_GL_PRIMITIVEBUILDER_H_
+#define MSP_GL_PRIMITIVEBUILDER_H_
+
+#include "primitivetype.h"
+#include "vertexarray.h"
+#include "vertexbuilder.h"
+
+namespace Msp {
+namespace GL {
+
+class VertexArray;
+class VertexArrayBuilder;
+
+/**
+Base class for primitive builders.  This is derived from VertexBuilder and adds
+begin() and end() functions for specifying batches of primitives instead of
+just vertices.
+*/
+class PrimitiveBuilder: public VertexBuilder
+{
+protected:
+       VertexArray &array;
+       VertexArrayBuilder vab;
+       PrimitiveType type;
+       bool in_batch;
+       unsigned offs;
+
+       PrimitiveBuilder(VertexArray &);
+public:
+       void begin(PrimitiveType);
+       void end();
+       void offset(unsigned);
+       void element(unsigned);
+       PrimitiveType get_type() const;
+protected:
+       virtual void vertex_(const Vector4 &);
+       virtual void begin_() = 0;
+       virtual void end_() = 0;
+       virtual void element_(unsigned) = 0;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/builders/sphere.cpp b/source/builders/sphere.cpp
new file mode 100644 (file)
index 0000000..dd27fad
--- /dev/null
@@ -0,0 +1,261 @@
+#define _USE_MATH_DEFINES
+#include <cmath>
+#include <vector>
+#include "primitivebuilder.h"
+#include "sphere.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+UvSphereBuilder::UvSphereBuilder(float r, unsigned s, unsigned n):
+       radius(r),
+       segments(s),
+       rings(n)
+{
+       if(segments<3)
+               segments = 3;
+
+       if(rings==0)
+               rings = (segments+1)/2;
+       else if(rings<2)
+               rings = 2;
+}
+
+void UvSphereBuilder::build(PrimitiveBuilder &builder) const
+{
+       float u_scale = 1.0f/segments;
+       float v_scale = 1.0f/rings;
+       adjust_texture_scale(u_scale, v_scale, radius*M_PI*2, radius*M_PI);
+
+       for(unsigned i=0; i<=rings; ++i)
+       {
+               float av = i*M_PI/rings-M_PI/2;
+               float cv = cos(av);
+               float sv = sin(av);
+
+               for(unsigned j=0; j<=segments; ++j)
+               {
+                       float au = j*M_PI*2/segments;
+                       float cu = cos(au);
+                       float su = sin(au);
+
+                       builder.normal(cv*cu, cv*su, sv);
+                       builder.texcoord(j*u_scale, i*v_scale);
+                       if(generate_tbn)
+                       {
+                               builder.tangent(-su, cu, 0);
+                               builder.binormal(-sv*cu, -sv*su, cv);
+                       }
+                       builder.vertex(cv*cu*radius, cv*su*radius, sv*radius);
+               }
+       }
+
+       builder.begin(TRIANGLES);
+       for(unsigned j=0; j<segments; ++j)
+       {
+               builder.element(j);
+               builder.element(segments+2+j);
+               builder.element(segments+1+j);
+       }
+       unsigned top = (rings+1)*(segments+1)-1;
+       for(unsigned j=0; j<segments; ++j)
+       {
+               builder.element(top-j);
+               builder.element(top-(segments+2+j));
+               builder.element(top-(segments+1+j));
+       }
+       builder.end();
+
+       for(unsigned i=1; i<rings; ++i)
+       {
+               builder.begin(TRIANGLE_STRIP);
+               unsigned base = i*(segments+1);
+               for(unsigned j=0; j<=segments; ++j)
+               {
+                       builder.element(base+segments+1+j);
+                       builder.element(base+j);
+               }
+               builder.end();
+       }
+}
+
+
+// https://en.wikipedia.org/wiki/Regular_icosahedron#Cartesian_coordinates
+float IcoSphereBuilder::base_vertices[12*3] =
+{
+        0.0f,       -0.5257311f, -0.8506508f,
+        0.0f,        0.5257311f, -0.8506508f,
+        0.0f,       -0.5257311f,  0.8506508f,
+        0.0f,        0.5257311f,  0.8506508f,
+       -0.8506508f,  0.0f,       -0.5257311f,
+       -0.8506508f,  0.0f,        0.5257311f,
+        0.8506508f,  0.0f,       -0.5257311f,
+        0.8506508f,  0.0f,        0.5257311f,
+       -0.5257311f, -0.8506508f,  0.0f,
+        0.5257311f, -0.8506508f,  0.0f,
+       -0.5257311f,  0.8506508f,  0.0f,
+        0.5257311f,  0.8506508f,  0.0f
+};
+
+unsigned IcoSphereBuilder::base_triangles[20*3] =
+{
+       0, 1, 6,
+       1, 0, 4,
+       2, 3, 5,
+       3, 2, 7,
+       4, 5, 10,
+       5, 4, 8,
+       6, 7, 9,
+       7, 6, 11,
+       8, 9, 2,
+       9, 8, 0,
+       10, 11, 1,
+       11, 10, 3,
+       0, 8, 4,
+       0, 6, 9,
+       1, 4, 10,
+       1, 11, 6,
+       2, 5, 8,
+       2, 9, 7,
+       3, 10, 5,
+       3, 7, 11
+};
+
+unsigned IcoSphereBuilder::base_edges[30*2] = { 0, 0 };
+unsigned IcoSphereBuilder::base_tri_edges[20*3] = { 0, 0, 0 };
+
+IcoSphereBuilder::IcoSphereBuilder(float r, unsigned s):
+       radius(r),
+       subdivision(s)
+{
+       if(base_edges[0]==base_edges[1])
+               initialize_edges();
+}
+
+void IcoSphereBuilder::initialize_edges()
+{
+       vector<int> edge_map(12*12, -1);
+       unsigned next_edge = 0;
+       for(unsigned i=0; i<20; ++i)
+               for(unsigned j=0; j<3; ++j)
+               {
+                       unsigned v1 = base_triangles[i*3+j];
+                       unsigned v2 = base_triangles[i*3+(j+1)%3];
+                       int e = edge_map[v1*12+v2];
+                       if(e<0)
+                       {
+                               e = next_edge++;
+                               base_edges[e*2] = v1;
+                               base_edges[e*2+1] = v2;
+                               // The other triangle using this edge will have the vertices swapped
+                               edge_map[v2*12+v1] = e|32;
+                       }
+                       base_tri_edges[i*3+j] = e;
+               }
+}
+
+void IcoSphereBuilder::build(PrimitiveBuilder &bld) const
+{
+       for(unsigned i=0; i<12; ++i)
+       {
+               const float *v = base_vertices+i*3;
+               bld.normal(v[0], v[1], v[2]);
+               bld.vertex(v[0]*radius, v[1]*radius, v[2]*radius);
+       }
+
+       if(subdivision>1)
+       {
+               vector<Vector3> edge_subdiv(30*(subdivision+1));
+               for(unsigned i=0; i<30; ++i)
+               {
+                       Vector3 v1(base_vertices+base_edges[i*2]*3);
+                       Vector3 v2(base_vertices+base_edges[i*2+1]*3);
+                       for(unsigned j=1; j<subdivision; ++j)
+                       {
+                               float t = static_cast<float>(j)/subdivision;
+                               Vector3 v = v1*(1.0f-t)+v2*t;
+                               edge_subdiv[i*(subdivision-1)+j-1] = v;
+                               v.normalize();
+                               bld.normal(v);
+                               bld.vertex(v*radius);
+                       }
+               }
+
+               for(unsigned i=0; i<20; ++i)
+                       for(unsigned j=1; j<subdivision; ++j)
+                       {
+                               unsigned e = base_tri_edges[i*3];
+                               Vector3 v1 = edge_subdiv[edge_vertex(e, subdivision-j)];
+                               e = base_tri_edges[i*3+1];
+                               Vector3 v2 = edge_subdiv[edge_vertex(e, j)];
+                               for(unsigned k=1; k<j; ++k)
+                               {
+                                       float t = static_cast<float>(k)/j;
+                                       Vector3 v = normalize(v1*(1.0f-t)+v2*t);
+                                       bld.normal(v);
+                                       bld.vertex(v*radius);
+                               }
+                       }
+
+               for(unsigned i=0; i<20; ++i)
+               {
+                       unsigned mid = 12+30*(subdivision-1)+i*(subdivision-1)*(subdivision-2)/2;
+                       for(unsigned j=1; j<=subdivision; ++j)
+                       {
+                               bld.begin(TRIANGLE_STRIP);
+                               if(j==subdivision)
+                                       bld.element(base_triangles[i*3]);
+                               else
+                                       bld.element(12+edge_vertex(base_tri_edges[i*3], subdivision-j));
+
+                               if(j==1)
+                               {
+                                       bld.element(base_triangles[i*3+1]);
+                                       bld.element(12+edge_vertex(base_tri_edges[i*3+1], j));
+                               }
+                               else
+                               {
+                                       bld.element(12+edge_vertex(base_tri_edges[i*3], subdivision-j+1));
+
+                                       if(j==subdivision)
+                                               bld.element(12+edge_vertex(base_tri_edges[i*3+2], subdivision-1));
+                                       else
+                                               bld.element(mid+(j-1)*(j-2)/2);
+
+                                       for(unsigned k=2; k<j; ++k)
+                                       {
+                                               bld.element(mid+(j-2)*(j-3)/2+k-2);
+                                               if(j==subdivision)
+                                                       bld.element(12+edge_vertex(base_tri_edges[i*3+2], subdivision-k));
+                                               else
+                                                       bld.element(mid+(j-1)*(j-2)/2+k-1);
+                                       }
+
+                                       bld.element(12+edge_vertex(base_tri_edges[i*3+1], j-1));
+                                       if(j==subdivision)
+                                               bld.element(base_triangles[i*3+2]);
+                                       else
+                                               bld.element(12+edge_vertex(base_tri_edges[i*3+1], j));
+                               }
+                               bld.end();
+                       }
+               }
+       }
+       else
+       {
+               bld.begin(TRIANGLES);
+               for(unsigned i=0; i<20*3; ++i)
+                       bld.element(base_triangles[i]);
+               bld.end();
+       }
+}
+
+unsigned IcoSphereBuilder::edge_vertex(unsigned e, unsigned i) const
+{
+       return (e&31)*(subdivision-1)+((e&32)?subdivision-i:i)-1;
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/builders/sphere.h b/source/builders/sphere.h
new file mode 100644 (file)
index 0000000..b20ff54
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef MSP_GL_SPHERE_H_
+#define MSP_GL_SPHERE_H_
+
+#include "geometrybuilder.h"
+
+namespace Msp {
+namespace GL {
+
+class UvSphereBuilder: public GeometryBuilder
+{
+private:
+       float radius;
+       unsigned segments;
+       unsigned rings;
+
+public:
+       UvSphereBuilder(float, unsigned, unsigned = 0);
+
+       using GeometryBuilder::build;
+       virtual void build(PrimitiveBuilder &) const;
+};
+
+
+class IcoSphereBuilder: public GeometryBuilder
+{
+private:
+       float radius;
+       unsigned subdivision;
+
+       static float base_vertices[];
+       static unsigned base_triangles[];
+       static unsigned base_edges[];
+       static unsigned base_tri_edges[];
+
+public:
+       IcoSphereBuilder(float, unsigned);
+private:
+       static void initialize_edges();
+
+public:
+       using GeometryBuilder::build;
+       virtual void build(PrimitiveBuilder &) const;
+
+private:
+       unsigned edge_vertex(unsigned, unsigned) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/builders/vertexarraybuilder.cpp b/source/builders/vertexarraybuilder.cpp
new file mode 100644 (file)
index 0000000..55b2a6a
--- /dev/null
@@ -0,0 +1,60 @@
+#include "vertexarray.h"
+#include "vertexarraybuilder.h"
+
+namespace Msp {
+namespace GL {
+
+VertexArrayBuilder::VertexArrayBuilder(VertexArray &a):
+       array(a)
+{ }
+
+void VertexArrayBuilder::vertex_(const Vector4 &ver)
+{
+       float *ptr = array.append();
+       for(const unsigned char *c=array.get_format().begin(); c!=array.get_format().end(); ++c)
+       {
+               unsigned sz = get_component_size(*c);
+               unsigned t = get_component_type(*c);
+               if(*c==COLOR4_UBYTE)
+               {
+                       union { unsigned char c[4]; float f; } u;
+                       u.c[0] = static_cast<unsigned char>(col.r*255);
+                       u.c[1] = static_cast<unsigned char>(col.g*255);
+                       u.c[2] = static_cast<unsigned char>(col.b*255);
+                       u.c[3] = static_cast<unsigned char>(col.a*255);
+                       *ptr++ = u.f;
+               }
+               else if(*c==NORMAL3)
+               {
+                       *ptr++ = nor.x;
+                       *ptr++ = nor.y;
+                       *ptr++ = nor.z;
+               }
+               else if(t==get_component_type(COLOR4_FLOAT))
+               {
+                       *ptr++ = col.r;
+                       *ptr++ = col.g;
+                       *ptr++ = col.b;
+                       if(sz>=4) *ptr++ = col.a;
+               }
+               else
+               {
+                       const Vector4 *v = 0;
+                       if(t==get_component_type(VERTEX3))
+                               v = &ver;
+                       else if(*c>=TEXCOORD1 && *c<=TEXCOORD4+12)
+                               v = &texc[t-get_component_type(TEXCOORD1)];
+                       else if(*c>=ATTRIB1)
+                               v = &attr[t-get_component_type(ATTRIB1)];
+                       else
+                               v = &attr[t];
+                       *ptr++ = v->x;
+                       if(sz>=2) *ptr++ = v->y;
+                       if(sz>=3) *ptr++ = v->z;
+                       if(sz>=4) *ptr++ = v->w;
+               }
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/builders/vertexarraybuilder.h b/source/builders/vertexarraybuilder.h
new file mode 100644 (file)
index 0000000..575a475
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef MSP_GL_VERTEXARRAYBUIDER_H_
+#define MSP_GL_VERTEXARRAYBUIDER_H_
+
+#include <vector>
+#include "vertexbuilder.h"
+#include "vertexformat.h"
+
+namespace Msp {
+namespace GL {
+
+class VertexArray;
+
+class VertexArrayBuilder: public VertexBuilder
+{
+private:
+       VertexArray &array;
+
+       VertexArrayBuilder(const VertexArrayBuilder &);
+public:
+       VertexArrayBuilder(VertexArray &);
+
+private:
+       virtual void vertex_(const Vector4 &);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/builders/vertexbuilder.h b/source/builders/vertexbuilder.h
new file mode 100644 (file)
index 0000000..55f914a
--- /dev/null
@@ -0,0 +1,167 @@
+#ifndef MSP_GL_VERTEXBUILDER_H_
+#define MSP_GL_VERTEXBUILDER_H_
+
+#include <map>
+#include "color.h"
+#include "matrix.h"
+#include "vector.h"
+#include "vertexformat.h"
+
+namespace Msp {
+namespace GL {
+
+/**
+Base class for classes that build vertices from a series of function calls.
+The operating model closely follows that of OpenGL immediate mode: vertex
+attributes can be specified at any time, and when a vertex() function is
+called, a vertex is created with the active attribute values.
+
+A derived class must overload the 4-argument vertex_() function to process the
+data.  Attributes can be read from protected member variables.
+*/
+class VertexBuilder
+{
+public:
+       class PushMatrix
+       {
+       private:
+               VertexBuilder &bld;
+               Matrix mtx;
+
+       public:
+               PushMatrix(VertexBuilder &b): bld(b), mtx(bld.mtx) { }
+               ~PushMatrix() { bld.mtx = mtx; }
+       };
+
+protected:
+       Matrix mtx;
+       Vector3 nor;
+       Color col;
+       std::map<unsigned, Vector4> texc;
+       std::map<unsigned, Vector4> attr;
+
+public:
+       VertexBuilder(): nor(0, 0, 1) { }
+
+       virtual ~VertexBuilder() { }
+
+       void set_matrix(const Matrix &m)
+       { mtx = m; }
+
+       void transform(const Matrix &m)
+       { mtx *= m; }
+
+       const Matrix &get_matrix() const
+       { return mtx; }
+
+       void vertex(float x, float y)
+       { vertex(x, y, 0, 1); }
+
+       void vertex(float x, float y, float z)
+       { vertex(x, y, z, 1); }
+
+       void vertex(float x, float y, float z, float w)
+       { vertex(Vector4(x, y, z, w)); }
+
+       void vertex(const Vector3 &v)
+       { vertex(Vector4(v.x, v.y, v.z, 1)); }
+
+       void vertex(const Vector4 &v)
+       { vertex_(mtx*v); }
+
+protected:
+       virtual void vertex_(const Vector4 &) = 0;
+
+public:
+       void normal(float x, float y, float z)
+       { normal(Vector3(x, y, z)); }
+
+       void normal(const Vector3 &n)
+       {
+               Vector4 tn = mtx*Vector4(n.x, n.y, n.z, 0);
+               nor = Vector3(tn.x, tn.y, tn.z);
+       }
+
+       void tangent(float x, float y, float z)
+       { tangent(Vector3(x, y, z)); }
+
+       void tangent(const Vector3 &t)
+       {
+               Vector4 tt = mtx*Vector4(t.x, t.y, t.z, 0);
+               attrib(get_component_type(TANGENT3), tt);
+       }
+
+       void binormal(float x, float y, float z)
+       { binormal(Vector3(x, y, z)); }
+
+       void binormal(const Vector3 &b)
+       {
+               Vector4 tb = mtx*Vector4(b.x, b.y, b.z, 0);
+               attrib(get_component_type(BINORMAL3), tb);
+       }
+
+       void texcoord(float s)
+       { texcoord(s, 0, 0, 1); }
+
+       void texcoord(float s, float t)
+       { texcoord(s, t, 0, 1); }
+
+       void texcoord(float s, float t, float r)
+       { texcoord(s, t, r, 1); }
+
+       void texcoord(float s, float t, float r, float q)
+       { texcoord(Vector4(s, t, r, q)); }
+
+       void texcoord(const Vector4 &t)
+       { multitexcoord(0, t); }
+
+       void multitexcoord(unsigned i, float s)
+       { multitexcoord(i, s, 0, 0, 1); }
+
+       void multitexcoord(unsigned i, float s, float t)
+       { multitexcoord(i, s, t, 0, 1); }
+
+       void multitexcoord(unsigned i, float s, float t, float r)
+       { multitexcoord(i, s, t, r, 1); }
+
+       void multitexcoord(unsigned i, float s, float t, float r, float q)
+       { multitexcoord(i, Vector4(s, t, r, q)); }
+
+       void multitexcoord(unsigned i, const Vector4 &t)
+       { texc[i] = t; }
+
+       void color(unsigned char r, unsigned char g, unsigned char b)
+       { color(r, g, b, 255); }
+
+       void color(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
+       { color(r/255.f, g/255.f, b/255.f, a/255.f); }
+
+       void color(float r, float g, float b)
+       { color(r, g, b, 1); }
+
+       void color(float r, float g, float b, float a)
+       { color(Color(r, g, b, a)); }
+
+       void color(const Color &c)
+       { col = c; }
+
+       void attrib(unsigned i, float x)
+       { attrib(i, x, 0, 0, 1); }
+
+       void attrib(unsigned i, float x, float y)
+       { attrib(i, x, y, 0, 1); }
+
+       void attrib(unsigned i, float x, float y, float z)
+       { attrib(i, x, y, z, 1); }
+
+       void attrib(unsigned i, float x, float y, float z, float w)
+       { attrib(i, Vector4(x, y, z, w)); }
+
+       void attrib(unsigned i, const Vector4 &a)
+       { attr[i] = a; }
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/camera.cpp b/source/camera.cpp
deleted file mode 100644 (file)
index 3bba0ea..0000000
+++ /dev/null
@@ -1,201 +0,0 @@
-#include <cmath>
-#include "camera.h"
-#include "matrix.h"
-
-namespace Msp {
-namespace GL {
-
-Camera::Camera():
-       fov(Geometry::Angle<float>::from_turns(0.125)),
-       height(0),
-       aspect(4.0/3.0),
-       clip_near(0.1),
-       clip_far(10),
-       frustum_x(0),
-       frustum_y(0),
-       position(0, 0, 0),
-       look_dir(0, 0, -1),
-       up_dir(0, 1, 0)
-{
-       update_projection_matrix();
-       update_object_matrix();
-}
-
-void Camera::set_field_of_view(const Geometry::Angle<float> &f)
-{
-       fov = f;
-       update_projection_matrix();
-}
-
-void Camera::set_orthographic(float w, float h)
-{
-       fov = Geometry::Angle<float>::zero();
-       height = h;
-       if(w)
-               aspect = w/h;
-       update_projection_matrix();
-}
-
-void Camera::set_aspect_ratio(float a)
-{
-       aspect = a;
-       update_projection_matrix();
-}
-
-void Camera::set_depth_clip(float n, float f)
-{
-       clip_near = n;
-       clip_far = f;
-       update_projection_matrix();
-}
-
-void Camera::set_frustum_axis(float x, float y)
-{
-       frustum_x = x;
-       frustum_y = y;
-       update_projection_matrix();
-}
-
-void Camera::set_frustum_rotation(const Geometry::Angle<float> &r)
-{
-       rotate = r;
-       update_projection_matrix();
-}
-
-void Camera::set_position(const Vector3 &p)
-{
-       position = p;
-       update_object_matrix();
-}
-
-void Camera::set_look_direction(const Vector3 &l)
-{
-       look_dir = normalize(l);
-       update_object_matrix();
-}
-
-void Camera::look_at(const Vector3 &p)
-{
-       set_look_direction(p-position);
-}
-
-void Camera::set_up_direction(const Vector3 &u)
-{
-       up_dir = normalize(u);
-       update_object_matrix();
-}
-
-void Camera::set_object_matrix(const Matrix &m)
-{
-       position = m.column(3).slice<3>(0);
-       look_dir = normalize(-m.column(2).slice<3>(0));
-       up_dir = normalize(m.column(1).slice<3>(0));
-       update_object_matrix();
-}
-
-Vector3 Camera::project(const Vector4 &p) const
-{
-       Vector4 r = proj_matrix*(view_matrix*p);
-       return r.slice<3>(0)/r.w;
-}
-
-Vector3 Camera::project(const Vector3 &p) const
-{
-       return project(Vector4(p.x, p.y, p.z, 1.0));
-}
-
-Vector4 Camera::unproject(const Vector4 &p) const
-{
-       Vector4 r = invert(proj_matrix)*Vector4(p.x, p.y, p.z, 1.0f);
-       r = matrix*Vector4(r.x/r.w, r.y/r.w, r.z/r.w, p.w);
-       return r;
-}
-
-Vector3 Camera::unproject(const Vector3 &p) const
-{
-       return unproject(Vector4(p.x, p.y, p.z, 1.0f)).slice<3>(0);
-}
-
-void Camera::update_projection_matrix()
-{
-       float frustum_h = (fov!=Geometry::Angle<float>::zero() ? tan(fov/2.0f)*clip_near : height/2);
-       float frustum_w = frustum_h*aspect;
-       float left = frustum_w*(frustum_x-1.0f);
-       float right = frustum_w*(frustum_x+1.0f);
-       float bottom = frustum_h*(frustum_y-1.0f);
-       float top = frustum_h*(frustum_y+1.0f);
-       if(fov>Geometry::Angle<float>::zero())
-               proj_matrix = Matrix::frustum(left, right, bottom, top, clip_near, clip_far);
-       else
-               proj_matrix = Matrix::ortho(left, right, bottom, top, clip_near, clip_far);
-       proj_matrix = Matrix::rotation(rotate, Vector3(0, 0, 1))*proj_matrix;
-}
-
-void Camera::update_object_matrix()
-{
-       Vector3 right_dir = normalize(cross(look_dir, up_dir));
-       Vector4 columns[4];
-       columns[0] = compose(right_dir, 0.0f);
-       columns[1] = compose(cross(right_dir, look_dir), 0.0f);
-       columns[2] = compose(-look_dir, 0.0f);
-       columns[3] = compose(position, 1.0f);
-       matrix = Matrix::from_columns(columns);
-       view_matrix = invert(matrix);
-}
-
-
-Camera::Loader::Loader(Camera &c):
-       DataFile::ObjectLoader<Camera>(c)
-{
-       add("aspect_ratio", &Loader::aspect_ratio);
-       add("depth_clip", &Loader::depth_clip);
-       add("field_of_view", &Loader::field_of_view);
-       add("look_at", &Loader::look_at);
-       add("look_direction", &Loader::look_direction);
-       add("orthographic", &Loader::orthographic);
-       add("position", &Loader::position);
-       add("up_direction", &Loader::up_direction);
-}
-
-void Camera::Loader::aspect_ratio(float a)
-{
-       obj.set_aspect_ratio(a);
-}
-
-void Camera::Loader::depth_clip(float n, float f)
-{
-       obj.set_depth_clip(n, f);
-}
-
-void Camera::Loader::field_of_view(float a)
-{
-       obj.set_field_of_view(Geometry::Angle<float>::from_degrees(a));
-}
-
-void Camera::Loader::look_at(float x, float y, float z)
-{
-       obj.look_at(Vector3(x, y, z));
-}
-
-void Camera::Loader::look_direction(float x, float y, float z)
-{
-       obj.set_look_direction(Vector3(x, y, z));
-}
-
-void Camera::Loader::orthographic(float w, float h)
-{
-       obj.set_orthographic(w, h);
-}
-
-void Camera::Loader::position(float x, float y, float z)
-{
-       obj.set_position(Vector3(x, y, z));
-}
-
-void Camera::Loader::up_direction(float x, float y, float z)
-{
-       obj.set_up_direction(Vector3(x, y, z));
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/camera.h b/source/camera.h
deleted file mode 100644 (file)
index b10ccb9..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-#ifndef MSP_GL_CAMERA_H_
-#define MSP_GL_CAMERA_H_
-
-#include <msp/datafile/objectloader.h>
-#include "placeable.h"
-
-namespace Msp {
-namespace GL {
-
-class Camera: public Placeable
-{
-public:
-       class Loader: public DataFile::ObjectLoader<Camera>
-       {
-       public:
-               Loader(Camera &);
-
-       private:
-               void aspect_ratio(float);
-               void depth_clip(float, float);
-               void field_of_view(float);
-               void look_at(float, float, float);
-               void look_direction(float, float, float);
-               void orthographic(float, float);
-               void position(float, float, float);
-               void up_direction(float, float, float);
-       };
-
-private:
-       Geometry::Angle<float> fov;
-       float height;
-       float aspect;
-       // Some compilers have "near" and "far" keywords
-       float clip_near;
-       float clip_far;
-       float frustum_x;
-       float frustum_y;
-       Geometry::Angle<float> rotate;
-       Vector3 position;
-       Vector3 look_dir;
-       Vector3 up_dir;
-       Matrix view_matrix;
-       Matrix proj_matrix;
-
-public:
-       Camera();
-
-       void set_field_of_view(const Geometry::Angle<float> &);
-       void set_orthographic(float, float);
-       void set_aspect_ratio(float);
-       void set_depth_clip(float, float);
-       void set_frustum_axis(float, float);
-       void set_frustum_rotation(const Geometry::Angle<float> &);
-       const Geometry::Angle<float> &get_field_of_view() const { return fov; }
-       bool is_orthographic() const { return fov==Geometry::Angle<float>::zero(); }
-       float get_orthographic_width() const { return height*aspect; }
-       float get_orthographic_height() const { return height; }
-       float get_aspect_ratio() const { return aspect; }
-       float get_near_clip() const { return clip_near; }
-       float get_far_clip() const { return clip_far; }
-       const Geometry::Angle<float> &get_frustum_rotation() const { return rotate; }
-
-       void set_position(const Vector3 &);
-       void set_look_direction(const Vector3 &);
-       void look_at(const Vector3 &);
-       void set_up_direction(const Vector3 &);
-       const Vector3 &get_position() const { return position; }
-       const Vector3 &get_look_direction() const { return look_dir; }
-       const Vector3 &get_up_direction() const { return up_dir; }
-
-       virtual void set_matrix(const Matrix &m) { set_object_matrix(m); }
-
-       /** Sets the position and orientation of the camera from an object matrix. */
-       void set_object_matrix(const Matrix &);
-
-       /** Returns the view matrix, used to transform coordinates from world space
-       to eye space. */
-       const Matrix &get_view_matrix() const { return view_matrix; }
-
-       /** Returns the object matrix, used to transform coordinates from eye space
-       to world space. */
-       const Matrix &get_object_matrix() const { return matrix; }
-
-       /** Returns the projection matrix. */
-       const Matrix &get_projection_matrix() const { return proj_matrix; }
-
-       Vector3 project(const Vector4 &) const;
-       Vector3 project(const Vector3 &) const;
-       Vector4 unproject(const Vector4 &) const;
-       Vector3 unproject(const Vector3 &) const;
-
-private:
-       void update_projection_matrix();
-       void update_object_matrix();
-};
-
-} // namespace GL
-} // namespcae Msp
-
-#endif
diff --git a/source/capsule.cpp b/source/capsule.cpp
deleted file mode 100644 (file)
index 65a18eb..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-#define _USE_MATH_DEFINES
-#include <cmath>
-#include "capsule.h"
-#include "primitivebuilder.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-CapsuleBuilder::CapsuleBuilder(float r, float l, unsigned s, unsigned n):
-       radius(r),
-       length(l),
-       segments(s),
-       rings(n)
-{
-       if(segments<3)
-               segments = 3;
-       rings |= 1;
-       if(rings<3)
-               rings = 3;
-}
-
-void CapsuleBuilder::build(PrimitiveBuilder &builder) const
-{
-       float u_scale = 1.0/segments;
-       float v_scale = 1/(length+radius*M_PI);
-       adjust_texture_scale(u_scale, v_scale, radius*M_PI*2, length+radius*M_PI);
-
-       builder.normal(0, 0, -1);
-       builder.texcoord(0.5, 0);
-       builder.vertex(0, 0, -length/2-radius);
-       for(unsigned i=1; i<rings; ++i)
-       {
-               float cz = length*(i>rings/2 ? 0.5 : -0.5);
-               float v = ((i>rings/2 ? i-1 : i)*radius*M_PI/(rings-1)+(i>rings/2 ? length : 0))*v_scale;
-               float ra = (i>rings/2 ? i-1 : i)*M_PI/(rings-1);
-               float rc = cos(ra);
-               float rs = sin(ra);
-               for(unsigned j=0; j<=segments; ++j)
-               {
-                       float sa = j*M_PI*2/segments;
-                       float sc = cos(sa);
-                       float ss = sin(sa);
-                       builder.normal(rs*sc, rs*ss, -rc);
-                       if(generate_tbn)
-                       {
-                               builder.tangent(-ss, sc, 0);
-                               builder.binormal(rc*sc, rc*ss, rs);
-                       }
-                       builder.texcoord(j*u_scale, v);
-                       builder.vertex(rs*sc*radius, rs*ss*radius, cz-rc*radius);
-               }
-       }
-       builder.normal(0, 0, 1);
-       builder.texcoord(0.5, (length+radius*M_PI)*v_scale);
-       builder.vertex(0, 0, length/2+radius);
-
-       for(unsigned i=0; i<segments; ++i)
-       {
-               builder.begin(TRIANGLE_STRIP);
-               builder.element(0);
-               for(unsigned j=0; j+1<rings; ++j)
-               {
-                       builder.element(1+j*(segments+1)+i+1);
-                       builder.element(1+j*(segments+1)+i);
-               }
-               builder.element((segments+1)*(rings-1)+1);
-               builder.end();
-       }
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/capsule.h b/source/capsule.h
deleted file mode 100644 (file)
index 375f4a7..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef MSP_GL_CAPSULE_H_
-#define MSP_GL_CAPSULE_H_
-
-#include "geometrybuilder.h"
-
-namespace Msp {
-namespace GL {
-
-class CapsuleBuilder: public GeometryBuilder
-{
-private:
-       float radius;
-       float length;
-       unsigned segments;
-       unsigned rings;
-
-public:
-       CapsuleBuilder(float, float, unsigned = 16, unsigned = 9);
-
-       using GeometryBuilder::build;
-       virtual void build(PrimitiveBuilder &) const;
-};
-
-} // namepace GL
-} // namespace Msp
-
-#endif
diff --git a/source/clipping.cpp b/source/clipping.cpp
deleted file mode 100644 (file)
index 3bdeee3..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-#include <msp/gl/extensions/msp_clipping.h>
-#include "clipping.h"
-#include "clipplane.h"
-#include "matrix.h"
-#include "misc.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-unsigned Clipping::get_n_attach_points()
-{
-       static Require _req(MSP_clipping);
-       static int count = get_i(GL_MAX_CLIP_PLANES);
-       return count;
-}
-
-void Clipping::attach(unsigned i, const ClipPlane &p)
-{
-       if(i>=get_n_attach_points())
-               throw out_of_range("Clipping::attach");
-
-       if(i>=planes.size())
-               planes.resize(i+1);
-
-       planes[i] = &p;
-       if(current()==this)
-               glEnable(GL_CLIP_PLANE0+i);
-}
-
-void Clipping::detach(unsigned i)
-{
-       if(i>=planes.size())
-               return;
-
-       planes[i] = 0;
-       if(current()==this)
-               disable(GL_CLIP_PLANE0+i);
-}
-
-void Clipping::update_shader_data(ProgramData &shdata, const Matrix &view_matrix) const
-{
-       Matrix view_inverse = invert(view_matrix);
-       for(unsigned i=0; i<planes.size(); ++i)
-               if(planes[i])
-                       planes[i]->update_shader_data(shdata, view_inverse, i);
-}
-
-void Clipping::bind() const
-{
-       static Require _req(MSP_clipping);
-
-       const Clipping *old = current();
-       if(!set_current(this))
-               return;
-
-       for(unsigned i=0; i<planes.size(); ++i)
-       {
-               if(planes[i])
-                       enable(GL_CLIP_PLANE0+i);
-               else
-                       disable(GL_CLIP_PLANE0+i);
-       }
-
-       if(old)
-       {
-               for(unsigned i=planes.size(); i<old->planes.size(); ++i)
-                       disable(GL_CLIP_PLANE0+i);
-       }
-}
-
-void Clipping::unbind()
-{
-       const Clipping *old = current();
-       if(!set_current(0))
-               return;
-
-       for(unsigned i=0; i<old->planes.size(); ++i)
-               if(old->planes[i])
-                       disable(GL_CLIP_PLANE0+i);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/clipping.h b/source/clipping.h
deleted file mode 100644 (file)
index e42c28e..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#ifndef MSP_GL_CLIPPING_H_
-#define MSP_GL_CLIPPING_H_
-
-#include <vector>
-#include "bindable.h"
-
-namespace Msp {
-namespace GL {
-
-class ClipPlane;
-class Matrix;
-class ProgramData;
-
-class Clipping: public Bindable<Clipping>
-{
-private:
-       std::vector<const ClipPlane *> planes;
-
-public:
-       static unsigned get_n_attach_points();
-
-       void attach(unsigned, const ClipPlane &);
-       void detach(unsigned);
-
-       void update_shader_data(ProgramData &, const Matrix &) const;
-
-       void bind() const;
-
-       static void unbind();
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/clipplane.cpp b/source/clipplane.cpp
deleted file mode 100644 (file)
index 4adf2bf..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-#include <msp/strings/format.h>
-#include "clipplane.h"
-#include "gl.h"
-#include "matrix.h"
-#include "misc.h"
-#include "programdata.h"
-
-namespace Msp {
-namespace GL {
-
-ClipPlane::ClipPlane():
-       eq(0, 0, 0, 1)
-{ }
-
-ClipPlane::ClipPlane(const Vector4 &e):
-       eq(e)
-{ }
-
-ClipPlane::ClipPlane(const Vector3 &p, const Vector3 &d):
-       eq(compose(d, -dot(p, d)))
-{ }
-
-void ClipPlane::set_equation(const Vector4 &e)
-{
-       eq = e;
-}
-
-void ClipPlane::set_plane(const Vector3 &p, const Vector3 &d)
-{
-       Vector3 nd = normalize(d);
-       set_equation(compose(nd, -dot(p, nd)));
-}
-
-void ClipPlane::update_shader_data(ProgramData &shdata, const Matrix &view_inverse, unsigned i) const
-{
-       shdata.uniform(format("clip_planes[%d].equation", i), eq*view_inverse);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/clipplane.h b/source/clipplane.h
deleted file mode 100644 (file)
index 98c633b..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifndef MSP_GL_CLIP_H_
-#define MSP_GL_CLIP_H_
-
-#include "vector.h"
-
-namespace Msp {
-namespace GL {
-
-class Matrix;
-class ProgramData;
-
-class ClipPlane
-{
-private:
-       Vector4 eq;
-
-public:
-       ClipPlane();
-       ClipPlane(const Vector4 &);
-       ClipPlane(const Vector3 &, const Vector3 &);
-
-       void set_equation(const Vector4 &);
-       void set_plane(const Vector3 &, const Vector3 &);
-       void update_shader_data(ProgramData &, const Matrix &, unsigned) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/color.h b/source/color.h
deleted file mode 100644 (file)
index e57689b..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#ifndef MSP_GL_COLOR_H_
-#define MSP_GL_COLOR_H_
-
-#include <cmath>
-
-namespace Msp {
-namespace GL {
-
-inline float to_srgb(float c)
-{ return (c<=0.0031308f ? 12.92f*c : 1.055f*std::pow(c, 1/2.4f)-0.055f); }
-
-inline float to_linear(float c)
-{ return (c<=0.04045 ? c/12.92f : std::pow((c+0.055f)/1.055f, 2.4f)); }
-
-struct Color
-{
-       float r, g, b, a;
-
-       Color(): r(1), g(1), b(1), a(1) { }
-       Color(float v): r(v), g(v), b(v), a(1) { }
-       Color(float r_, float g_, float b_): r(r_), g(g_), b(b_), a(1) { }
-       Color(float r_, float g_, float b_, float a_): r(r_), g(g_), b(b_), a(a_) { }
-       Color operator*(float f) const { return Color(r*f, g*f, b*f, a); }
-       Color operator+(const Color &c) const { return Color(r+c.r, g+c.g, b+c.b, 1-(1-a)*(1-c.a)); }
-       bool operator==(const Color &c) const { return (r==c.r && g==c.g && b==c.b && a==c.a); }
-       bool operator!=(const Color &c) const { return !operator==(c); }
-
-       Color to_srgb() const { return Color(GL::to_srgb(r), GL::to_srgb(g), GL::to_srgb(b), a); }
-       Color to_linear() const { return Color(GL::to_linear(r), GL::to_linear(g), GL::to_linear(b), a); }
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/colorcurve.cpp b/source/colorcurve.cpp
deleted file mode 100644 (file)
index 8811b06..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-#include <cmath>
-#include "color.h"
-#include "colorcurve.h"
-#include "mesh.h"
-#include "renderer.h"
-#include "shader.h"
-#include "texture2d.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-ColorCurve::ColorCurve():
-       shprog("colorcurve.glsl"),
-       quad(get_fullscreen_quad()),
-       linear_sampler(get_linear_sampler()),
-       nearest_sampler(get_nearest_sampler())
-{
-       shdata.uniform("source", 0);
-       shdata.uniform("curve", 1);
-
-       curve.storage(LUMINANCE8, 256, 1);
-       texturing.attach(1, curve, linear_sampler.get());
-
-       set_exposure_adjust(0.0f);
-       set_brightness_response(0.4f);
-       set_linear();
-}
-
-void ColorCurve::set_exposure_adjust(float e)
-{
-       shdata.uniform("exposure", pow(2.0f, e));
-}
-
-void ColorCurve::set_brightness_response(float b)
-{
-       if(b<=0 || b>1)
-               throw invalid_argument("ColorCurve::set_brightness_response");
-       float t = (b<1 ? pow(b, 1/(1-b)) : 0.0f);
-       shdata.uniform("brightness_response", b, t, pow(t, b));
-}
-
-void ColorCurve::set_gamma(float g)
-{
-       if(g<0.1 || g>10)
-               throw invalid_argument("ColorCurve::set_gamma");
-
-       unsigned char curve_data[256];
-       for(unsigned i=0; i<256; ++i)
-               curve_data[i] = pow(i/255.0f, 1/g)*255+0.5f;
-       curve.image(0, curve_data);
-}
-
-void ColorCurve::set_srgb()
-{
-       unsigned char curve_data[256];
-       curve_data[0] = 0;
-       for(unsigned i=1; i<256; ++i)
-               curve_data[i] = to_srgb(i/255.0f)*255+0.5f;
-       curve.image(0, curve_data);
-}
-
-void ColorCurve::set_linear()
-{
-       unsigned char curve_data[256];
-       for(unsigned i=0; i<256; ++i)
-               curve_data[i] = i;
-       curve.image(0, curve_data);
-}
-
-void ColorCurve::render(Renderer &renderer, const Texture2D &color_buf, const Texture2D &)
-{
-       texturing.attach(0, color_buf, nearest_sampler.get());
-
-       Renderer::Push push(renderer);
-       renderer.set_shader_program(&shprog, &shdata);
-       renderer.set_texturing(&texturing);
-       quad->draw(renderer);
-}
-
-
-ColorCurve::Template::Template():
-       exposure_adjust(0.0f),
-       brightness_response(0.4f),
-       gamma(1.0f),
-       srgb(false)
-{ }
-
-ColorCurve *ColorCurve::Template::create(unsigned, unsigned) const
-{
-       RefPtr<ColorCurve> colorcurve = new ColorCurve;
-       colorcurve->set_exposure_adjust(exposure_adjust);
-       colorcurve->set_brightness_response(brightness_response);
-       if(srgb)
-               colorcurve->set_srgb();
-       else
-               colorcurve->set_gamma(gamma);
-       return colorcurve.release();
-}
-
-
-ColorCurve::Template::Loader::Loader(Template &t):
-       DataFile::DerivedObjectLoader<Template, PostProcessor::Template::Loader>(t)
-{
-       add("brightness_response", &Template::brightness_response);
-       add("exposure_adjust", &Template::exposure_adjust);
-       add("gamma", &Loader::gamma);
-       add("srgb", &Loader::srgb);
-}
-
-void ColorCurve::Template::Loader::gamma(float g)
-{
-       obj.gamma = g;
-       obj.srgb = false;
-}
-
-void ColorCurve::Template::Loader::srgb()
-{
-       obj.srgb = true;
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/colorcurve.h b/source/colorcurve.h
deleted file mode 100644 (file)
index 3f65b04..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-#ifndef MSP_GL_COLORCURVE_H_
-#define MSP_GL_COLORCURVE_H_
-
-#include "postprocessor.h"
-#include "program.h"
-#include "programdata.h"
-#include "texture1d.h"
-#include "texturing.h"
-
-namespace Msp {
-namespace GL {
-
-/**
-Processes oversaturated colors to preserve hues.  When one color component
-exceeds 1.0, the overflow is distributed to the other components, scaling the
-color towards white.
-
-Gamma or sRGB correction can also be applied to the output.  It can be used to
-improve color reproduction by performing lighting calculations in linear color
-space and converting to sRGB for display.
-*/
-class ColorCurve: public PostProcessor
-{
-public:
-       struct Template: public PostProcessor::Template
-       {
-               class Loader: public DataFile::DerivedObjectLoader<Template, PostProcessor::Template::Loader>
-               {
-               public:
-                       Loader(Template &);
-
-               private:
-                       void gamma(float);
-                       void srgb();
-               };
-
-               float exposure_adjust;
-               float brightness_response;
-               float gamma;
-               bool srgb;
-
-               Template();
-
-               virtual ColorCurve *create(unsigned, unsigned) const;
-       };
-
-private:
-       Program shprog;
-       ProgramData shdata;
-       Texture1D curve;
-       Texturing texturing;
-       RefPtr<Mesh> quad;
-       RefPtr<Sampler> linear_sampler;
-       RefPtr<Sampler> nearest_sampler;
-
-public:
-       ColorCurve();
-
-       /** Set exposure adjustment in EV units.  Positive values brighten the
-       image, negative values darken it.  Zero is neutral. */
-       void set_exposure_adjust(float);
-
-       /** Sets the exponent of the */
-       void set_brightness_response(float);
-
-       /** Sets the gamma value used for mapping output colors.  Allowed range is
-       from 0.1 to 10. */
-       void set_gamma(float);
-
-       /** Sets output mapping to sRGB.  This is almost, but not exactly equivalent
-       to set_gamma(2.2). */
-       void set_srgb();
-
-       /// Sets output mapping to linear.  This is equivalent to set_gamma(1).
-       void set_linear();
-
-       virtual void render(Renderer &, const Texture2D &, const Texture2D &);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/core/batch.cpp b/source/core/batch.cpp
new file mode 100644 (file)
index 0000000..f14cc30
--- /dev/null
@@ -0,0 +1,252 @@
+#include <msp/gl/extensions/arb_draw_instanced.h>
+#include <msp/gl/extensions/msp_primitive_restart.h>
+#include "batch.h"
+#include "bindable.h"
+#include "buffer.h"
+#include "error.h"
+#include "mesh.h"
+#include "vertexarray.h"
+
+using namespace std;
+
+namespace {
+
+template<typename T>
+void append(vector<Msp::UInt8> &data, T i)
+{
+       data.insert(data.end(), sizeof(T), 0);
+       *(T *)(&data[data.size()-sizeof(T)]) = i;
+}
+
+template<typename T, typename U>
+U convert(T n)
+{
+       if(!static_cast<T>(~n))
+               return ~0;
+       else
+               return n;
+}
+
+template<typename T, typename U>
+void expand(vector<Msp::UInt8> &data)
+{
+       unsigned count = data.size()/sizeof(T);
+       data.resize(count*sizeof(U));
+       for(unsigned i=count; i--;)
+               *(U *)(&data[i*sizeof(U)]) = convert<T, U>(*(T *)(&data[i*sizeof(T)]));
+}
+
+template<typename T, typename U>
+void shrink(vector<Msp::UInt8> &data)
+{
+       unsigned count = data.size()/sizeof(T);
+       for(unsigned i=0; i<count; ++i)
+               *(U *)(&data[i*sizeof(U)]) = convert<T, U>(*(T *)(&data[i*sizeof(T)]));
+       data.resize(count*sizeof(U));
+}
+
+}
+
+namespace Msp {
+namespace GL {
+
+unsigned Batch::restart_index = 0;
+
+Batch::Batch(PrimitiveType t):
+       prim_type(t),
+       index_type(UNSIGNED_SHORT),
+       max_index(0),
+       restart(false)
+{ }
+
+Batch::~Batch()
+{
+}
+
+void Batch::set_index_type(DataType t)
+{
+       if(t!=UNSIGNED_SHORT && t!=UNSIGNED_INT)
+               throw invalid_argument("Batch::set_data_type");
+       if(t==UNSIGNED_SHORT && max_index>0xFFFE)
+               throw invalid_operation("Batch::set_data_type");
+
+       if(index_type==UNSIGNED_SHORT && t==UNSIGNED_INT)
+               expand<UInt16, UInt32>(data);
+       else if(index_type==UNSIGNED_INT && t==UNSIGNED_SHORT)
+               shrink<UInt32, UInt16>(data);
+
+       index_type = t;
+       update_offset();
+       dirty = true;
+}
+
+Batch &Batch::append(unsigned i)
+{
+       append_index(i);
+
+       update_offset();
+       dirty = true;
+
+       return *this;
+}
+
+Batch &Batch::append(const vector<unsigned> &ind)
+{
+       if(ind.empty())
+               return *this;
+
+       data.reserve(data.size()+ind.size()*get_index_size());
+       for(vector<unsigned>::const_iterator i=ind.begin(); i!=ind.end(); ++i)
+               append_index(*i);
+
+       update_offset();
+       dirty = true;
+
+       return *this;
+}
+
+bool Batch::can_append(PrimitiveType other_type)
+{
+       if(other_type!=prim_type)
+               return false;
+       else if(prim_type==LINE_STRIP || prim_type==LINE_LOOP || prim_type==TRIANGLE_FAN)
+               return MSP_primitive_restart;
+       else
+               return true;
+}
+
+Batch &Batch::append(const Batch &other)
+{
+       if(other.prim_type!=prim_type)
+               throw invalid_argument("Batch::append");
+       if(prim_type==LINE_STRIP || prim_type==LINE_LOOP || prim_type==TRIANGLE_FAN)
+               static Require _req(MSP_primitive_restart);
+
+       if(other.data.empty())
+               return *this;
+
+       // TODO allow appending triangles to a triangle strip
+
+       if(prim_type==POINTS || prim_type==LINES || prim_type==TRIANGLES)
+               ;
+       else if(MSP_primitive_restart)
+       {
+               restart = true;
+               if(index_type==UNSIGNED_INT)
+                       ::append<UInt32>(data, 0xFFFFFFFF);
+               else
+                       ::append<UInt16>(data, 0xFFFF);
+       }
+       else if(prim_type==TRIANGLE_STRIP)
+       {
+               append(get_index(size()-1));
+               append(other.get_index(0));
+               if(size()&1)
+                       append(other.get_index(0));
+       }
+
+       unsigned count = other.size();
+       for(unsigned i=0; i<count; ++i)
+               append_index(other.get_index(i));
+
+       update_offset();
+       dirty = true;
+
+       return *this;
+}
+
+void Batch::append_index(unsigned i)
+{
+       if(data.empty())
+               max_index = i;
+       else
+               max_index = max(max_index, i);
+
+       if(index_type==UNSIGNED_SHORT && max_index>0xFFFE)
+               set_index_type(UNSIGNED_INT);
+
+       if(index_type==UNSIGNED_INT)
+               ::append<UInt32>(data, i);
+       else
+               ::append<UInt16>(data, i);
+}
+
+unsigned Batch::get_index_size() const
+{
+       return (index_type==UNSIGNED_INT ? sizeof(UInt32) : sizeof(UInt16));
+}
+
+unsigned Batch::get_index(unsigned i) const
+{
+       if(index_type==UNSIGNED_INT)
+               return *(UInt32 *)&data[i*sizeof(UInt32)];
+       else
+               return *(UInt16 *)&data[i*sizeof(UInt16)];
+}
+
+void Batch::draw() const
+{
+       BindRestore _bind_ibuf(get_buffer(), ELEMENT_ARRAY_BUFFER);
+       const void *data_ptr = setup_draw();
+
+       glDrawElements(prim_type, size(), index_type, data_ptr);
+}
+
+void Batch::draw_instanced(unsigned count) const
+{
+       static Require req(ARB_draw_instanced);
+
+       BindRestore _bind_ibuf(get_buffer(), ELEMENT_ARRAY_BUFFER);
+       const void *data_ptr = setup_draw();
+
+       glDrawElementsInstanced(prim_type, size(), index_type, data_ptr, count);
+}
+
+const void *Batch::setup_draw() const
+{
+       if(!get_buffer())
+               throw invalid_operation("Batch::setup_draw");
+
+       if(restart)
+       {
+               unsigned index = (index_type==UNSIGNED_INT ? 0xFFFFFFFF : 0xFFFF);
+
+               if(index!=restart_index)
+                       set_restart_index(index);
+       }
+       else if(restart_index && restart_index<=max_index)
+               set_restart_index(0);
+
+       refresh();
+
+       return reinterpret_cast<const void *>(get_offset());
+}
+
+void Batch::set_restart_index(unsigned index)
+{
+       if(index>0)
+       {
+               if(!restart_index)
+                       glEnable(GL_PRIMITIVE_RESTART);
+               glPrimitiveRestartIndex(index);
+       }
+       else
+               glDisable(GL_PRIMITIVE_RESTART);
+
+       restart_index = index;
+}
+
+
+Batch::Loader::Loader(Batch &b):
+       DataFile::ObjectLoader<Batch>(b)
+{
+       add("indices", &Loader::indices);
+}
+
+void Batch::Loader::indices(const vector<unsigned> &ind)
+{
+       obj.append(ind);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/batch.h b/source/core/batch.h
new file mode 100644 (file)
index 0000000..2e4ad9e
--- /dev/null
@@ -0,0 +1,79 @@
+#ifndef MSP_GL_BATCH_H_
+#define MSP_GL_BATCH_H_
+
+#include <vector>
+#include <msp/datafile/objectloader.h>
+#include "bufferable.h"
+#include "datatype.h"
+#include "primitivetype.h"
+
+namespace Msp {
+namespace GL {
+
+class Buffer;
+
+/**
+Stores primitive type and element indices for a single GL draw call.  Data
+type for indices is automatically chosen to accommodate the largest index in
+the Batch.
+
+This is a pretty low-level class and mainly intended to be used by the Mesh
+class.
+*/
+class Batch: public Bufferable
+{
+public:
+       class Loader: public DataFile::ObjectLoader<Batch>
+       {
+       public:
+               Loader(Batch &);
+       private:
+               void indices(const std::vector<unsigned> &);
+       };
+
+private:
+       PrimitiveType prim_type;
+       DataType index_type;
+       std::vector<UInt8> data;
+       unsigned max_index;
+       bool restart;
+
+       static unsigned restart_index;
+
+public:
+       Batch(PrimitiveType t);
+       ~Batch();
+
+       PrimitiveType get_type() const { return prim_type; }
+       void set_index_type(DataType);
+       DataType get_index_type() const { return index_type; }
+
+       DEPRECATED void set_data_type(DataType t) { set_index_type(t); }
+       DEPRECATED DataType get_data_type() const { return index_type; }
+
+       Batch &append(unsigned);
+       Batch &append(const std::vector<unsigned> &);
+       bool can_append(PrimitiveType);
+       Batch &append(const Batch &);
+private:
+       void append_index(unsigned);
+       virtual unsigned get_data_size() const { return data.size(); }
+       virtual const void *get_data_pointer() const { return &data[0]; }
+       virtual unsigned get_alignment() const { return get_index_size(); }
+       unsigned get_index_size() const;
+public:
+       unsigned size() const { return data.size()/get_index_size(); }
+
+       unsigned get_index(unsigned) const;
+
+       void draw() const;
+       void draw_instanced(unsigned) const;
+private:
+       const void *setup_draw() const;
+       static void set_restart_index(unsigned);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/bindable.h b/source/core/bindable.h
new file mode 100644 (file)
index 0000000..b2634ea
--- /dev/null
@@ -0,0 +1,225 @@
+#ifndef MSP_GL_BINDABLE_H_
+#define MSP_GL_BINDABLE_H_
+
+namespace Msp {
+namespace GL {
+
+/**
+A helper class for single-point binding.  Provides tracking of the currently
+bound object.
+*/
+template<typename T>
+class Bindable
+{
+protected:
+       static const T *cur_obj;
+
+       Bindable() { }
+       ~Bindable() { if(cur_obj==this) T::unbind(); }
+
+       static bool set_current(const T *obj)
+       {
+               if(obj==cur_obj)
+                       return false;
+
+               cur_obj = obj;
+               return true;
+       }
+
+public:
+       static const T *current() { return cur_obj; }
+};
+
+template<typename T>
+const T *Bindable<T>::cur_obj;
+
+
+/**
+A helper class for Bindables that revert to a default object on unbind.
+*/
+template<typename T>
+class BindableWithDefault: protected Bindable<T>
+{
+       friend class Bindable<T>;
+
+protected:
+       BindableWithDefault() { }
+       ~BindableWithDefault() { if(this==&default_object()) Bindable<T>::set_current(0); }
+
+public:
+       static const T *current()
+       {
+               if(!Bindable<T>::cur_obj)
+                       Bindable<T>::cur_obj = &default_object();
+               return Bindable<T>::cur_obj;
+       }
+
+       static void unbind()
+       {
+               if(Bindable<T>::cur_obj)
+                       default_object().bind();
+       }
+
+       static const T &default_object()
+       {
+               static T obj;
+               return obj;
+       }
+};
+
+
+/**
+RAII class for binding things.  Binds the thing upon construction and unbinds
+it upon destruction.  If a null pointer is given, unbinds upon construction and
+does nothing upon destruction.
+*/
+class Bind
+{
+private:
+       typedef void CleanupFunc(int);
+
+       int slot;
+       CleanupFunc *cleanup;
+
+public:
+       template<typename T>
+       Bind(T *o) { init(o); }
+
+       template<typename T>
+       Bind(const T *o) { init(o); }
+
+       template<typename T>
+       Bind(const T &o) { init(&o); }
+
+       template<typename T, typename S>
+       Bind(T *o, S s) { init(o, s); }
+
+       template<typename T, typename S>
+       Bind(const T *o, S s) { init(o, s); }
+
+       template<typename T, typename S>
+       Bind(const T &o, S s) { init(&o, s); }
+
+private:
+       template<typename T>
+       void init(const T *o)
+       {
+               cleanup = (o ? static_cast<CleanupFunc *>(&unbind<T>) : 0);
+               slot = 0;
+               if(o)
+                       o->bind();
+               else
+                       T::unbind();
+       }
+
+       template<typename T, typename S>
+       void init(const T *o, S s)
+       {
+               cleanup = (o ? static_cast<CleanupFunc *>(&unbind_from<T, S>) : 0);
+               slot = s;
+               if(o)
+                       o->bind_to(s);
+               else
+                       T::unbind_from(s);
+       }
+
+public:
+       ~Bind()
+       { if(cleanup) cleanup(slot); }
+
+private:
+       template<typename T>
+       static void unbind(int)
+       { T::unbind(); }
+
+       template<typename T, typename S>
+       static void unbind_from(int s)
+       { T::unbind_from(static_cast<S>(s)); }
+};
+
+
+/**
+Similar to Bind, but restores previous binding upon destruction.
+*/
+class BindRestore
+{
+private:
+       typedef void CleanupFunc(const void *, int);
+
+       const void *old;
+       int slot;
+       CleanupFunc *cleanup;
+
+public:
+       template<typename T>
+       BindRestore(T *o) { init(o); }
+
+       template<typename T>
+       BindRestore(const T *o) { init(o); }
+
+       template<typename T>
+       BindRestore(const T &o) { init(&o); }
+
+       template<typename T, typename S>
+       BindRestore(T *o, S s) { init(o, s); }
+
+       template<typename T, typename S>
+       BindRestore(const T *o, S s) { init(o, s); }
+
+       template<typename T, typename S>
+       BindRestore(const T &o, S s) { init(&o, s); }
+
+private:
+       template<typename T>
+       void init(T *o)
+       {
+               old = T::current();
+               slot = 0;
+               cleanup = (o!=old ? static_cast<CleanupFunc *>(&restore<T>) : 0);
+               if(o)
+                       o->bind();
+               else if(old)
+                       T::unbind();
+       }
+
+       template<typename T, typename S>
+       void init(T *o, S s)
+       {
+               old = T::current(s);
+               slot = s;
+               cleanup = (o!=old ? static_cast<CleanupFunc *>(&restore_to<T, S>) : 0);
+               if(o)
+                       o->bind_to(s);
+               else if(old)
+                       T::unbind_from(s);
+       }
+
+public:
+       ~BindRestore()
+       { if(cleanup) cleanup(old, slot); }
+
+private:
+       template<typename T>
+       static void restore(const void *o, int)
+       {
+               if(o)
+                       reinterpret_cast<const T *>(o)->bind();
+               else
+                       T::unbind();
+       }
+
+       template<typename T, typename S>
+       static void restore_to(const void *o, int si)
+       {
+               S s = static_cast<S>(si);
+               if(o)
+                       reinterpret_cast<const T *>(o)->bind_to(s);
+               else
+                       T::unbind_from(s);
+       }
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/blend.cpp b/source/core/blend.cpp
new file mode 100644 (file)
index 0000000..3327885
--- /dev/null
@@ -0,0 +1,127 @@
+#include <msp/gl/extensions/ext_blend_minmax.h>
+#include <msp/gl/extensions/ext_blend_subtract.h>
+#include <msp/strings/format.h>
+#include "blend.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Blend::Blend():
+       eq(ADD),
+       src_factor(ONE),
+       dst_factor(ZERO)
+{ }
+
+Blend::Blend(BlendFactor sf, BlendFactor df):
+       eq(ADD),
+       src_factor(sf),
+       dst_factor(df)
+{ }
+
+Blend::Blend(BlendEquation e, BlendFactor sf, BlendFactor df):
+       eq(e),
+       src_factor(sf),
+       dst_factor(df)
+{
+       if(eq==MIN || eq==MAX)
+               static Require _req(EXT_blend_minmax);
+       else if(eq==SUBTRACT || eq==REVERSE_SUBTRACT)
+               static Require _req(EXT_blend_subtract);
+}
+
+void Blend::bind() const
+{
+       if(set_current(this))
+       {
+               glEnable(GL_BLEND);
+               if(EXT_blend_minmax)
+                       glBlendEquation(eq);
+               glBlendFunc(src_factor, dst_factor);
+       }
+}
+
+void Blend::unbind()
+{
+       if(set_current(0))
+               glDisable(GL_BLEND);
+}
+
+const Blend &Blend::alpha()
+{
+       static Blend blend(SRC_ALPHA, ONE_MINUS_SRC_ALPHA);
+       return blend;
+}
+
+const Blend &Blend::additive()
+{
+       static Blend blend(ONE, ONE);
+       return blend;
+}
+
+const Blend &Blend::additive_alpha()
+{
+       static Blend blend(SRC_ALPHA, ONE);
+       return blend;
+}
+
+void operator>>(const LexicalConverter &conv, BlendFactor &factor)
+{
+       const string &str = conv.get();
+       if(str=="ZERO")
+               factor = ZERO;
+       else if(str=="ONE")
+               factor = ONE;
+       else if(str=="SRC_COLOR")
+               factor = SRC_COLOR;
+       else if(str=="ONE_MINUS_SRC_COLOR")
+               factor = ONE_MINUS_SRC_COLOR;
+       else if(str=="SRC_ALPHA")
+               factor = SRC_ALPHA;
+       else if(str=="ONE_MINUS_SRC_ALPHA")
+               factor = ONE_MINUS_SRC_ALPHA;
+       else if(str=="DST_COLOR")
+               factor = DST_COLOR;
+       else if(str=="ONE_MINUS_DST_COLOR")
+               factor = ONE_MINUS_DST_COLOR;
+       else if(str=="DST_ALPHA")
+               factor = DST_ALPHA;
+       else if(str=="ONE_MINUS_DST_ALPHA")
+               factor = ONE_MINUS_DST_ALPHA;
+       else if(str=="CONSTANT_COLOR")
+               factor = CONSTANT_COLOR;
+       else if(str=="ONE_MINUS_CONSTANT_COLOR")
+               factor = ONE_MINUS_CONSTANT_COLOR;
+       else if(str=="CONSTANT_ALPHA")
+               factor = CONSTANT_ALPHA;
+       else if(str=="ONE_MINUS_CONSTANT_ALPHA")
+               factor = ONE_MINUS_CONSTANT_ALPHA;
+       else
+               throw lexical_error(format("conversion of '%s' to BlendFactor", str));
+}
+
+void operator<<(LexicalConverter &conv, BlendFactor factor)
+{
+       switch(factor)
+       {
+       case ZERO: conv.result("ZERO"); break;
+       case ONE: conv.result("ONE"); break;
+       case SRC_COLOR: conv.result("SRC_COLOR"); break;
+       case ONE_MINUS_SRC_COLOR: conv.result("ONE_MINUS_SRC_COLOR"); break;
+       case SRC_ALPHA: conv.result("SRC_ALPHA"); break;
+       case ONE_MINUS_SRC_ALPHA: conv.result("ONE_MINUS_SRC_ALPHA"); break;
+       case DST_COLOR: conv.result("DST_COLOR"); break;
+       case ONE_MINUS_DST_COLOR: conv.result("ONE_MINUS_DST_COLOR"); break;
+       case DST_ALPHA: conv.result("DST_ALPHA"); break;
+       case ONE_MINUS_DST_ALPHA: conv.result("ONE_MINUS_DST_ALPHA"); break;
+       case CONSTANT_COLOR: conv.result("CONSTANT_COLOR"); break;
+       case ONE_MINUS_CONSTANT_COLOR: conv.result("ONE_MINUS_CONSTANT_COLOR"); break;
+       case CONSTANT_ALPHA: conv.result("CONSTANT_ALPHA"); break;
+       case ONE_MINUS_CONSTANT_ALPHA: conv.result("ONE_MINUS_CONSTANT_ALPHA"); break;
+       default: conv.result(format("BlendFactor(%#x)", static_cast<int>(factor)));
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/blend.h b/source/core/blend.h
new file mode 100644 (file)
index 0000000..e7b291e
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef MSP_GL_BLEND_H_
+#define MSP_GL_BLEND_H_
+
+#include <msp/strings/lexicalcast.h>
+#include "bindable.h"
+#include "gl.h"
+#include <msp/gl/extensions/ext_blend_minmax.h>
+
+namespace Msp {
+namespace GL {
+
+enum BlendEquation
+{
+       ADD              = GL_FUNC_ADD,
+       SUBTRACT         = GL_FUNC_SUBTRACT,
+       REVERSE_SUBTRACT = GL_FUNC_REVERSE_SUBTRACT,
+       MIN              = GL_MIN,
+       MAX              = GL_MAX
+};
+
+enum BlendFactor
+{
+       ZERO = GL_ZERO,
+       ONE = GL_ONE,
+       SRC_COLOR = GL_SRC_COLOR,
+       ONE_MINUS_SRC_COLOR = GL_ONE_MINUS_SRC_COLOR,
+       SRC_ALPHA = GL_SRC_ALPHA,
+       ONE_MINUS_SRC_ALPHA = GL_ONE_MINUS_SRC_ALPHA,
+       DST_COLOR = GL_DST_COLOR,
+       ONE_MINUS_DST_COLOR = GL_ONE_MINUS_DST_COLOR,
+       DST_ALPHA = GL_DST_ALPHA,
+       ONE_MINUS_DST_ALPHA = GL_ONE_MINUS_DST_ALPHA,
+       CONSTANT_COLOR = GL_CONSTANT_COLOR,
+       ONE_MINUS_CONSTANT_COLOR = GL_ONE_MINUS_CONSTANT_COLOR,
+       CONSTANT_ALPHA = GL_CONSTANT_ALPHA,
+       ONE_MINUS_CONSTANT_ALPHA = GL_ONE_MINUS_CONSTANT_ALPHA
+};
+
+/**
+Blends incoming fragments with those already in the framebuffer.
+*/
+class Blend: public Bindable<Blend>
+{
+private:
+       BlendEquation eq;
+       BlendFactor src_factor;
+       BlendFactor dst_factor;
+
+public:
+       Blend();
+       Blend(BlendFactor, BlendFactor);
+       Blend(BlendEquation, BlendFactor, BlendFactor);
+
+       void bind() const;
+
+       static void unbind();
+
+       static const Blend &alpha();
+       static const Blend &additive();
+       static const Blend &additive_alpha();
+};
+
+void operator>>(const LexicalConverter &, BlendFactor &);
+void operator<<(LexicalConverter &, BlendFactor);
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/buffer.cpp b/source/core/buffer.cpp
new file mode 100644 (file)
index 0000000..56705b8
--- /dev/null
@@ -0,0 +1,296 @@
+#include <stdexcept>
+#include <msp/gl/extensions/arb_buffer_storage.h>
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_map_buffer_range.h>
+#include <msp/strings/format.h>
+#include "buffer.h"
+#include "error.h"
+#include "misc.h"
+#include "vertexsetup.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+const Buffer *Buffer::bound[5] = { 0, 0, 0, 0, 0 };
+BufferType buffer_types[] = { ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, PIXEL_PACK_BUFFER, PIXEL_UNPACK_BUFFER, UNIFORM_BUFFER };
+
+Buffer::Buffer(BufferType t):
+       type(t),
+       size(0)
+{
+       require_buffer_type(type);
+
+       if(ARB_direct_state_access)
+               glCreateBuffers(1, &id);
+       else
+               glGenBuffers(1, &id);
+}
+
+Buffer::~Buffer()
+{
+       for(unsigned i=0; i<5; ++i)
+               if(bound[i]==this)
+                       unbind_from(buffer_types[i]);
+       glDeleteBuffers(1, &id);
+}
+
+void Buffer::require_buffer_type(BufferType type)
+{
+       static Require _req_vbo(ARB_vertex_buffer_object);
+       if(type==PIXEL_PACK_BUFFER || type==PIXEL_UNPACK_BUFFER)
+               static Require _req_pbo(ARB_pixel_buffer_object);
+       else if(type==UNIFORM_BUFFER)
+               static Require _req_ubo(ARB_uniform_buffer_object);
+}
+
+void Buffer::storage(unsigned sz)
+{
+       if(size>0)
+               throw invalid_operation("Buffer::storage");
+       if(sz==0)
+               throw invalid_argument("Buffer::storage");
+
+       size = sz;
+       if(ARB_buffer_storage)
+       {
+               static const int flags = GL_MAP_READ_BIT|GL_MAP_WRITE_BIT|GL_DYNAMIC_STORAGE_BIT;
+               if(ARB_direct_state_access)
+                       glNamedBufferStorage(id, size, 0, flags);
+               else
+               {
+                       BindRestore _bind(this, type);
+                       glBufferStorage(type, size, 0, flags);
+               }
+       }
+}
+
+void Buffer::set_usage(BufferUsage)
+{
+}
+
+void Buffer::data(const void *d)
+{
+       if(size==0)
+               throw invalid_operation("Buffer::data");
+
+       if(ARB_buffer_storage)
+               return sub_data(0, size, d);
+
+       if(ARB_direct_state_access)
+               glNamedBufferData(id, size, d, STATIC_DRAW);
+       else
+       {
+               BindRestore _bind(this, type);
+               glBufferData(type, size, d, STATIC_DRAW);
+       }
+}
+
+void Buffer::data(unsigned sz, const void *d)
+{
+       if(size==0)
+               storage(sz);
+       else if(sz!=size)
+               throw incompatible_data("Buffer::data");
+
+       data(d);
+}
+
+void Buffer::sub_data(unsigned off, unsigned sz, const void *d)
+{
+       if(ARB_direct_state_access)
+               glNamedBufferSubData(id, off, sz, d);
+       else
+       {
+               BindRestore _bind(this, type);
+               glBufferSubData(type, off, sz, d);
+       }
+}
+
+void Buffer::require_size(unsigned req_sz) const
+{
+       if(size<req_sz)
+               throw buffer_too_small(format("buffer has %d bytes; %d required", size, req_sz));
+}
+
+BufferRange *Buffer::create_range(unsigned s, unsigned o)
+{
+       return new BufferRange(*this, s, o);
+}
+
+void *Buffer::map()
+{
+       if(ARB_map_buffer_range)
+       {
+               if(ARB_direct_state_access)
+                       return glMapNamedBufferRange(id, 0, size, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT);
+               else
+               {
+                       BindRestore _bind(this, type);
+                       return glMapBufferRange(type, 0, size, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT);
+               }
+       }
+       else if(ARB_direct_state_access)
+               return glMapNamedBuffer(id, GL_READ_WRITE);
+       else if(OES_mapbuffer)
+       {
+               BindRestore _bind(this, type);
+               return glMapBuffer(type, GL_READ_WRITE);
+       }
+       else
+               throw invalid_operation("Buffer::map");
+}
+
+bool Buffer::unmap()
+{
+       // TODO check if it's mapped
+       if(ARB_direct_state_access)
+               return glUnmapNamedBuffer(id);
+       else if(OES_mapbuffer)
+       {
+               BindRestore _bind(this, type);
+               return glUnmapBuffer(type);
+       }
+       else
+               return true;
+}
+
+void Buffer::bind_to(BufferType t) const
+{
+       if(t!=type)
+               require_buffer_type(t);
+       if(t==ELEMENT_ARRAY_BUFFER)
+               if(const VertexSetup *vs = VertexSetup::current())
+               {
+                       // Don't change the binding in a vertex array object
+                       if(this==vs->get_index_buffer())
+                               return;
+                       throw invalid_operation("Buffer::bind_to(ELEMENT_ARRAY_BUFFER)");
+               }
+       if(set_current(t, this))
+               glBindBuffer(t, id);
+}
+
+const Buffer *Buffer::current(BufferType t)
+{
+       if(t==ELEMENT_ARRAY_BUFFER)
+               if(const VertexSetup *vs = VertexSetup::current())
+                       return vs->get_index_buffer();
+       return binding(t);
+}
+
+void Buffer::unbind_from(BufferType type)
+{
+       if(type==ELEMENT_ARRAY_BUFFER && VertexSetup::current())
+               throw invalid_operation("Buffer::unbind_from(ELEMENT_ARRAY_BUFFER)");
+       if(set_current(type, 0))
+               glBindBuffer(type, 0);
+}
+
+const Buffer *&Buffer::binding(BufferType type)
+{
+       switch(type)
+       {
+       case ARRAY_BUFFER:         return bound[0];
+       case ELEMENT_ARRAY_BUFFER: return bound[1];
+       case PIXEL_PACK_BUFFER:    return bound[2];
+       case PIXEL_UNPACK_BUFFER:  return bound[3];
+       case UNIFORM_BUFFER:       return bound[4];
+       default: throw invalid_argument("Buffer::binding");
+       }
+}
+
+bool Buffer::set_current(BufferType type, const Buffer *buf)
+{
+       const Buffer *&ptr = binding(type);
+       if(ptr==buf)
+               return false;
+
+       ptr = buf;
+       return true;
+}
+
+
+vector<const BufferRange *> BufferRange::bound_uniform;
+
+BufferRange::BufferRange(Buffer &b, unsigned o, unsigned s):
+       buffer(b),
+       offset(o),
+       size(s)
+{
+       if(o>buffer.get_size() || o+s>buffer.get_size())
+               throw out_of_range("BufferRange::BufferRange");
+}
+
+BufferRange::~BufferRange()
+{
+       for(unsigned i=0; i<bound_uniform.size(); ++i)
+               if(bound_uniform[i]==this)
+                       unbind_from(UNIFORM_BUFFER, i);
+}
+
+void BufferRange::data(const void *d)
+{
+       buffer.sub_data(offset, size, d);
+}
+
+void BufferRange::bind_to(BufferType t, unsigned i)
+{
+       if(t!=buffer.type)
+               Buffer::require_buffer_type(t);
+       if(set_current(t, i, this))
+       {
+               // The buffer gets bound as a side effect
+               Buffer::set_current(t, &buffer);
+               glBindBufferRange(t, i, buffer.id, offset, size);
+       }
+}
+
+void BufferRange::unbind_from(BufferType t, unsigned i)
+{
+       if(set_current(t, i, 0))
+       {
+               Buffer::set_current(t, 0);
+               glBindBufferBase(t, i, 0);
+       }
+}
+
+const BufferRange *&BufferRange::binding(BufferType type, unsigned index)
+{
+       if(type==UNIFORM_BUFFER)
+       {
+               if(index>=get_n_uniform_buffer_bindings())
+                       throw out_of_range("BufferRange::binding");
+               if(bound_uniform.size()<=index)
+                       bound_uniform.resize(index+1);
+               return bound_uniform[index];
+       }
+       else
+               throw invalid_argument("BufferRange::binding");
+}
+
+bool BufferRange::set_current(BufferType type, unsigned index, const BufferRange *buf)
+{
+       const BufferRange *&ptr = binding(type, index);
+       if(ptr==buf)
+               return false;
+
+       ptr = buf;
+       return true;
+}
+
+unsigned BufferRange::get_n_uniform_buffer_bindings()
+{
+       static unsigned count = get_i(GL_MAX_UNIFORM_BUFFER_BINDINGS);
+       return count;
+}
+
+unsigned BufferRange::get_uniform_buffer_alignment()
+{
+       static unsigned align = get_i(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
+       return align;
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/buffer.h b/source/core/buffer.h
new file mode 100644 (file)
index 0000000..cc5e587
--- /dev/null
@@ -0,0 +1,165 @@
+#ifndef MSP_GL_BUFFER_H_
+#define MSP_GL_BUFFER_H_
+
+#include <stdexcept>
+#include <string>
+#include <vector>
+#include <msp/core/attributes.h>
+#include "gl.h"
+#include <msp/gl/extensions/arb_pixel_buffer_object.h>
+#include <msp/gl/extensions/arb_vertex_buffer_object.h>
+#include <msp/gl/extensions/arb_uniform_buffer_object.h>
+#include <msp/gl/extensions/oes_mapbuffer.h>
+
+namespace Msp {
+namespace GL {
+
+class buffer_too_small: public std::logic_error
+{
+public:
+       buffer_too_small(const std::string &w): std::logic_error(w) { }
+       virtual ~buffer_too_small() throw() { }
+};
+
+enum BufferType
+{
+       ARRAY_BUFFER         = GL_ARRAY_BUFFER,
+       ELEMENT_ARRAY_BUFFER = GL_ELEMENT_ARRAY_BUFFER,
+       PIXEL_PACK_BUFFER    = GL_PIXEL_PACK_BUFFER,
+       PIXEL_UNPACK_BUFFER  = GL_PIXEL_UNPACK_BUFFER,
+       UNIFORM_BUFFER       = GL_UNIFORM_BUFFER
+};
+
+enum BufferUsage
+{
+       STREAM_DRAW  = GL_STREAM_DRAW,
+       STREAM_READ  = GL_STREAM_READ,
+       STREAM_COPY  = GL_STREAM_COPY,
+       STATIC_DRAW  = GL_STATIC_DRAW,
+       STATIC_READ  = GL_STATIC_READ,
+       STATIC_COPY  = GL_STATIC_COPY,
+       DYNAMIC_DRAW = GL_DYNAMIC_DRAW,
+       DYNAMIC_READ = GL_DYNAMIC_READ,
+       DYNAMIC_COPY = GL_DYNAMIC_COPY
+};
+
+enum BufferAccess
+{
+       READ_ONLY = GL_READ_ONLY,
+       WRITE_ONLY = GL_WRITE_ONLY,
+       READ_WRITE = GL_READ_WRITE
+};
+
+class BufferRange;
+
+/**
+A buffer for storing data in GL memory.  Putting vertex and index data in
+buffers can improve rendering performance.  The VertexArray, Mesh and
+UniformBlock classes contain built-in support for buffers.
+*/
+class Buffer
+{
+       friend class BufferRange;
+
+private:
+       BufferType type;
+       unsigned id;
+       unsigned size;
+
+       static const Buffer *bound[5];
+
+public:
+       Buffer(BufferType);
+       ~Buffer();
+
+private:
+       static void require_buffer_type(BufferType);
+
+public:
+       /** Returns the OpenGL ID of the buffer.  For internal use only. */
+       unsigned get_id() const { return id; }
+
+       /** Returns the default binding type for the buffer. */
+       BufferType get_type() const { return type; }
+
+       /** Defines the storage size of the buffer.  Must be called before data can
+       be uploaded.  Storage cannot be changed once set. */
+       void storage(unsigned);
+
+       /** Sets the usage hint of the buffer.  It will take effect the next time
+       the buffer's contents are defined. */
+       DEPRECATED void set_usage(BufferUsage);
+
+       /** Uploads data into the buffer, completely replacing any previous
+       contents.  Storage must be defined beforehand.  The data must have size
+       matching the defined storage. */
+       void data(const void *);
+
+       DEPRECATED void data(unsigned, const void *);
+
+       /** Overwrites part of the buffer data with new data.  Storage must be
+       defined beforehand. */
+       void sub_data(unsigned, unsigned, const void *);
+
+       unsigned get_size() const { return size; }
+
+       void require_size(unsigned) const;
+
+       BufferRange *create_range(unsigned, unsigned);
+
+       void *map();
+       DEPRECATED void *map(BufferAccess) { return map(); }
+       bool unmap();
+
+       /** Binds the buffer in its default slot. */
+       void bind() const { bind_to(type); }
+
+       /** Binds the buffer in an alternate slot. */
+       void bind_to(BufferType) const;
+
+       /** Unbinds the buffer from its default slot. */
+       void unbind() const { unbind_from(type); }
+
+       static const Buffer *current(BufferType);
+       static void unbind_from(BufferType);
+private:
+       static const Buffer *&binding(BufferType);
+       static bool set_current(BufferType, const Buffer *);
+};
+
+
+/**
+A proxy for a subset of a buffer.  Can be bound for use with uniform blocks.
+*/
+class BufferRange
+{
+private:
+       Buffer &buffer;
+       unsigned offset;
+       unsigned size;
+
+       static std::vector<const BufferRange *> bound_uniform;
+
+public:
+       BufferRange(Buffer &, unsigned, unsigned);
+       ~BufferRange();
+
+       void data(const void *);
+
+       void bind_to(BufferType, unsigned);
+
+       static const BufferRange *current(BufferType t, unsigned i) { return binding(t, i); }
+       static void unbind_from(BufferType, unsigned);
+private:
+       static const BufferRange *&binding(BufferType, unsigned);
+       static bool set_current(BufferType, unsigned, const BufferRange *);
+
+public:
+       static unsigned get_n_uniform_buffer_bindings();
+       static unsigned get_uniform_buffer_alignment();
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/bufferable.cpp b/source/core/bufferable.cpp
new file mode 100644 (file)
index 0000000..d07f57c
--- /dev/null
@@ -0,0 +1,165 @@
+#include <stdexcept>
+#include <msp/core/raii.h>
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include "bindable.h"
+#include "buffer.h"
+#include "bufferable.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Bufferable::Bufferable():
+       buffer(0),
+       offset(0),
+       next_in_buffer(0),
+       prev_in_buffer(0),
+       location_dirty(false),
+       dirty(false)
+{ }
+
+Bufferable::~Bufferable()
+{
+       unlink_from_buffer();
+}
+
+void Bufferable::use_buffer(Buffer *buf, Bufferable *prev)
+{
+       if(prev && buf!=prev->buffer)
+               throw invalid_argument("Bufferable::use_buffer");
+
+       if(buffer)
+               unlink_from_buffer();
+
+       buffer = buf;
+       if(buffer)
+       {
+               prev_in_buffer = prev;
+               if(prev_in_buffer)
+               {
+                       next_in_buffer = prev_in_buffer->next_in_buffer;
+                       prev_in_buffer->next_in_buffer = this;
+               }
+       }
+
+       location_dirty = true;
+       dirty = true;
+       update_offset();
+}
+
+void Bufferable::change_buffer(Buffer *buf)
+{
+       for(Bufferable *b=this; b; b=b->next_in_buffer)
+       {
+               b->buffer = buf;
+               b->location_dirty = true;
+               b->dirty = true;
+       }
+       for(Bufferable *b=prev_in_buffer; b; b=b->prev_in_buffer)
+       {
+               b->buffer = buf;
+               b->location_dirty = true;
+               b->dirty = true;
+       }
+}
+
+unsigned Bufferable::get_required_buffer_size() const
+{
+       const Bufferable *last = this;
+       for(; last->next_in_buffer; last=last->next_in_buffer) ;
+       return last->offset+last->get_data_size();
+}
+
+Bufferable::AsyncUpdater *Bufferable::refresh_async() const
+{
+       return dirty ? new AsyncUpdater(*this) : 0;
+}
+
+void Bufferable::unlink_from_buffer()
+{
+       if(prev_in_buffer)
+               prev_in_buffer->next_in_buffer = next_in_buffer;
+       if(next_in_buffer)
+       {
+               next_in_buffer->prev_in_buffer = prev_in_buffer;
+               next_in_buffer->update_offset();
+       }
+       prev_in_buffer = 0;
+       next_in_buffer = 0;
+       buffer = 0;
+       offset = 0;
+}
+
+void Bufferable::update_offset()
+{
+       unsigned new_offset = 0;
+       if(prev_in_buffer)
+               new_offset = prev_in_buffer->offset+prev_in_buffer->get_data_size();
+
+       unsigned align = get_alignment();
+       new_offset += align-1;
+       new_offset -= new_offset%align;
+       if(new_offset!=offset)
+       {
+               offset = new_offset;
+               location_dirty = true;
+               dirty = true;
+       }
+
+       if(next_in_buffer)
+               next_in_buffer->update_offset();
+       else if(buffer && offset+get_data_size()>buffer->get_size())
+       {
+               location_dirty = true;
+               dirty = true;
+       }
+}
+
+void Bufferable::upload_data(char *target) const
+{
+       unsigned data_size = get_data_size();
+       if(location_dirty)
+       {
+               buffer->require_size(offset+data_size);
+               location_changed(buffer, offset, data_size);
+               location_dirty = false;
+       }
+
+       if(target)
+       {
+               const char *source = reinterpret_cast<const char *>(get_data_pointer());
+               copy(source, source+data_size, target);
+       }
+       else
+               buffer->sub_data(offset, data_size, get_data_pointer());
+       dirty = false;
+}
+
+
+Bufferable::AsyncUpdater::AsyncUpdater(const Bufferable &b):
+       bufferable(b)
+{
+       bufferable.buffer->require_size(bufferable.get_required_buffer_size());
+       mapped_address = reinterpret_cast<char *>(bufferable.buffer->map());
+}
+
+Bufferable::AsyncUpdater::~AsyncUpdater()
+{
+       bufferable.buffer->unmap();
+}
+
+void Bufferable::AsyncUpdater::upload_data()
+{
+       bufferable.upload_data(mapped_address+bufferable.offset);
+       // Update all bufferables in the same buffer at once
+       for(const Bufferable *b=bufferable.prev_in_buffer; b; b=b->prev_in_buffer)
+               if(b->dirty)
+                       b->upload_data(mapped_address+b->offset);
+       for(const Bufferable *b=bufferable.next_in_buffer; b; b=b->next_in_buffer)
+               if(b->dirty)
+                       b->upload_data(mapped_address+b->offset);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/bufferable.h b/source/core/bufferable.h
new file mode 100644 (file)
index 0000000..cbbde4e
--- /dev/null
@@ -0,0 +1,100 @@
+#ifndef MSP_GL_BUFFERABLE_H_
+#define MSP_GL_BUFFERABLE_H_
+
+namespace Msp {
+namespace GL {
+
+class Buffer;
+
+/**
+Base class for things that can store data in buffers.  Supports buffer sharing.
+A dirty flag is provided for derived classes.  It should be set when the data
+in the buffer is considered out of date, and is cleared by Bufferable after
+uploading fresh data to the buffer.
+*/
+class Bufferable
+{
+public:
+       class AsyncUpdater
+       {
+       private:
+               const Bufferable &bufferable;
+               char *mapped_address;
+
+       public:
+               AsyncUpdater(const Bufferable &);
+               ~AsyncUpdater();
+
+               void upload_data();
+       };
+
+private:
+       Buffer *buffer;
+       unsigned offset;
+       Bufferable *next_in_buffer;
+       Bufferable *prev_in_buffer;
+       mutable bool location_dirty;
+protected:
+       mutable bool dirty;
+
+       Bufferable();
+public:
+       virtual ~Bufferable();
+
+       /** Sets the buffer to use.  If prev is not null, it must use the same
+       buffer, and this object is inserted after it. */
+       void use_buffer(Buffer *buf, Bufferable *prev = 0);
+
+       /** Sets the buffer for the entire chain of objects. */
+       void change_buffer(Buffer *);
+
+       /** Returns the total amount of storage required by this object and others
+       in the same chain, including any alignment between objects. */
+       unsigned get_required_buffer_size() const;
+
+       /** Uploads new data into the buffer if necessary. */
+       void refresh() const { if(buffer && dirty) upload_data(0); }
+
+       /** Returns an object which can be used to upload data to the buffer using
+       mapped memory. */
+       AsyncUpdater *refresh_async() const;
+
+private:
+       void unlink_from_buffer();
+
+public:
+       /** Returns the buffer in which the data is stored. */
+       const Buffer *get_buffer() const { return buffer; }
+
+protected:
+       /** Returns the amount of data to be stored in the buffer, in bytes. */
+       virtual unsigned get_data_size() const = 0;
+
+       /** Returns a pointer to the start of data in client memory. */
+       virtual const void *get_data_pointer() const = 0;
+
+       /** Returns the alignment required for the data, in bytes.  The offset is
+       guaranteed to be a multiple of this. */
+       virtual unsigned get_alignment() const { return 1; }
+
+       /** Updates the offsets for the chain so that data from different objects
+       does not overlap.  Should be called if either data size or alignment
+       changes. */
+       void update_offset();
+
+       /** Returns the offset where the data should be uploaded. */
+       unsigned get_offset() const { return offset; }
+
+       /** Called when the target buffer or offset within it has changed. */
+       virtual void location_changed(Buffer *, unsigned, unsigned) const { }
+
+private:
+       /** Uploads data to the buffer.  Receives pointer to mapped buffer memory as
+       parameter, or null to use the buffer upload interface. */
+       void upload_data(char *) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/clipping.cpp b/source/core/clipping.cpp
new file mode 100644 (file)
index 0000000..3bdeee3
--- /dev/null
@@ -0,0 +1,85 @@
+#include <msp/gl/extensions/msp_clipping.h>
+#include "clipping.h"
+#include "clipplane.h"
+#include "matrix.h"
+#include "misc.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+unsigned Clipping::get_n_attach_points()
+{
+       static Require _req(MSP_clipping);
+       static int count = get_i(GL_MAX_CLIP_PLANES);
+       return count;
+}
+
+void Clipping::attach(unsigned i, const ClipPlane &p)
+{
+       if(i>=get_n_attach_points())
+               throw out_of_range("Clipping::attach");
+
+       if(i>=planes.size())
+               planes.resize(i+1);
+
+       planes[i] = &p;
+       if(current()==this)
+               glEnable(GL_CLIP_PLANE0+i);
+}
+
+void Clipping::detach(unsigned i)
+{
+       if(i>=planes.size())
+               return;
+
+       planes[i] = 0;
+       if(current()==this)
+               disable(GL_CLIP_PLANE0+i);
+}
+
+void Clipping::update_shader_data(ProgramData &shdata, const Matrix &view_matrix) const
+{
+       Matrix view_inverse = invert(view_matrix);
+       for(unsigned i=0; i<planes.size(); ++i)
+               if(planes[i])
+                       planes[i]->update_shader_data(shdata, view_inverse, i);
+}
+
+void Clipping::bind() const
+{
+       static Require _req(MSP_clipping);
+
+       const Clipping *old = current();
+       if(!set_current(this))
+               return;
+
+       for(unsigned i=0; i<planes.size(); ++i)
+       {
+               if(planes[i])
+                       enable(GL_CLIP_PLANE0+i);
+               else
+                       disable(GL_CLIP_PLANE0+i);
+       }
+
+       if(old)
+       {
+               for(unsigned i=planes.size(); i<old->planes.size(); ++i)
+                       disable(GL_CLIP_PLANE0+i);
+       }
+}
+
+void Clipping::unbind()
+{
+       const Clipping *old = current();
+       if(!set_current(0))
+               return;
+
+       for(unsigned i=0; i<old->planes.size(); ++i)
+               if(old->planes[i])
+                       disable(GL_CLIP_PLANE0+i);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/clipping.h b/source/core/clipping.h
new file mode 100644 (file)
index 0000000..e42c28e
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef MSP_GL_CLIPPING_H_
+#define MSP_GL_CLIPPING_H_
+
+#include <vector>
+#include "bindable.h"
+
+namespace Msp {
+namespace GL {
+
+class ClipPlane;
+class Matrix;
+class ProgramData;
+
+class Clipping: public Bindable<Clipping>
+{
+private:
+       std::vector<const ClipPlane *> planes;
+
+public:
+       static unsigned get_n_attach_points();
+
+       void attach(unsigned, const ClipPlane &);
+       void detach(unsigned);
+
+       void update_shader_data(ProgramData &, const Matrix &) const;
+
+       void bind() const;
+
+       static void unbind();
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/clipplane.cpp b/source/core/clipplane.cpp
new file mode 100644 (file)
index 0000000..4adf2bf
--- /dev/null
@@ -0,0 +1,40 @@
+#include <msp/strings/format.h>
+#include "clipplane.h"
+#include "gl.h"
+#include "matrix.h"
+#include "misc.h"
+#include "programdata.h"
+
+namespace Msp {
+namespace GL {
+
+ClipPlane::ClipPlane():
+       eq(0, 0, 0, 1)
+{ }
+
+ClipPlane::ClipPlane(const Vector4 &e):
+       eq(e)
+{ }
+
+ClipPlane::ClipPlane(const Vector3 &p, const Vector3 &d):
+       eq(compose(d, -dot(p, d)))
+{ }
+
+void ClipPlane::set_equation(const Vector4 &e)
+{
+       eq = e;
+}
+
+void ClipPlane::set_plane(const Vector3 &p, const Vector3 &d)
+{
+       Vector3 nd = normalize(d);
+       set_equation(compose(nd, -dot(p, nd)));
+}
+
+void ClipPlane::update_shader_data(ProgramData &shdata, const Matrix &view_inverse, unsigned i) const
+{
+       shdata.uniform(format("clip_planes[%d].equation", i), eq*view_inverse);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/clipplane.h b/source/core/clipplane.h
new file mode 100644 (file)
index 0000000..98c633b
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef MSP_GL_CLIP_H_
+#define MSP_GL_CLIP_H_
+
+#include "vector.h"
+
+namespace Msp {
+namespace GL {
+
+class Matrix;
+class ProgramData;
+
+class ClipPlane
+{
+private:
+       Vector4 eq;
+
+public:
+       ClipPlane();
+       ClipPlane(const Vector4 &);
+       ClipPlane(const Vector3 &, const Vector3 &);
+
+       void set_equation(const Vector4 &);
+       void set_plane(const Vector3 &, const Vector3 &);
+       void update_shader_data(ProgramData &, const Matrix &, unsigned) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/color.h b/source/core/color.h
new file mode 100644 (file)
index 0000000..e57689b
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef MSP_GL_COLOR_H_
+#define MSP_GL_COLOR_H_
+
+#include <cmath>
+
+namespace Msp {
+namespace GL {
+
+inline float to_srgb(float c)
+{ return (c<=0.0031308f ? 12.92f*c : 1.055f*std::pow(c, 1/2.4f)-0.055f); }
+
+inline float to_linear(float c)
+{ return (c<=0.04045 ? c/12.92f : std::pow((c+0.055f)/1.055f, 2.4f)); }
+
+struct Color
+{
+       float r, g, b, a;
+
+       Color(): r(1), g(1), b(1), a(1) { }
+       Color(float v): r(v), g(v), b(v), a(1) { }
+       Color(float r_, float g_, float b_): r(r_), g(g_), b(b_), a(1) { }
+       Color(float r_, float g_, float b_, float a_): r(r_), g(g_), b(b_), a(a_) { }
+       Color operator*(float f) const { return Color(r*f, g*f, b*f, a); }
+       Color operator+(const Color &c) const { return Color(r+c.r, g+c.g, b+c.b, 1-(1-a)*(1-c.a)); }
+       bool operator==(const Color &c) const { return (r==c.r && g==c.g && b==c.b && a==c.a); }
+       bool operator!=(const Color &c) const { return !operator==(c); }
+
+       Color to_srgb() const { return Color(GL::to_srgb(r), GL::to_srgb(g), GL::to_srgb(b), a); }
+       Color to_linear() const { return Color(GL::to_linear(r), GL::to_linear(g), GL::to_linear(b), a); }
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/datatype.cpp b/source/core/datatype.cpp
new file mode 100644 (file)
index 0000000..1def3ca
--- /dev/null
@@ -0,0 +1,26 @@
+#include <stdexcept>
+#include "datatype.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+unsigned get_type_size(DataType type)
+{
+       switch(type)
+       {
+       case BYTE:
+       case UNSIGNED_BYTE: return 1;
+       case SHORT:
+       case UNSIGNED_SHORT:
+       case HALF_FLOAT: return 2;
+       case INT:
+       case UNSIGNED_INT:
+       case FLOAT: return 4;
+       default: throw invalid_argument("get_type_size");
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/datatype.h b/source/core/datatype.h
new file mode 100644 (file)
index 0000000..2d783f6
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef MSP_GL_DATATYPE_H_
+#define MSP_GL_DATATYPE_H_
+
+#include "gl.h"
+#include <msp/gl/extensions/nv_half_float.h>
+
+namespace Msp {
+namespace GL {
+
+enum DataType
+{
+       BYTE           = GL_BYTE,
+       UNSIGNED_BYTE  = GL_UNSIGNED_BYTE,
+       SHORT          = GL_SHORT,
+       UNSIGNED_SHORT = GL_UNSIGNED_SHORT,
+       INT            = GL_INT,
+       UNSIGNED_INT   = GL_UNSIGNED_INT,
+       FLOAT          = GL_FLOAT,
+       HALF_FLOAT     = GL_HALF_FLOAT
+};
+
+unsigned get_type_size(DataType);
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/error.h b/source/core/error.h
new file mode 100644 (file)
index 0000000..8af3ec6
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef MSP_GL_ERROR_H_
+#define MSP_GL_ERROR_H_
+
+#include <stdexcept>
+
+namespace Msp {
+namespace GL {
+
+class unsupported_extension: public std::runtime_error
+{
+public:
+       unsupported_extension(const std::string &w): std::runtime_error(w) { }
+       virtual ~unsupported_extension() throw() { }
+};
+
+class invalid_operation: public std::logic_error
+{
+public:
+       invalid_operation(const std::string &w): std::logic_error(w) { }
+       virtual ~invalid_operation() throw() { }
+};
+
+class stack_underflow: public std::logic_error
+{
+public:
+       stack_underflow(const std::string &w): std::logic_error(w) { }
+       virtual ~stack_underflow() throw() { }
+};
+
+class incompatible_data: public std::logic_error
+{
+public:
+       incompatible_data(const std::string &w): std::logic_error(w) { }
+       virtual ~incompatible_data() throw() { }
+};
+
+class compile_error: public std::runtime_error
+{
+public:
+       compile_error(const std::string &w): std::runtime_error(w) { }
+       virtual ~compile_error() throw() { }
+};
+
+class incomplete_uniform_block: public std::runtime_error
+{
+public:
+       incomplete_uniform_block(const std::string &w): std::runtime_error(w) { }
+       virtual ~incomplete_uniform_block() throw() { }
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/extension.cpp b/source/core/extension.cpp
new file mode 100644 (file)
index 0000000..8618cc3
--- /dev/null
@@ -0,0 +1,257 @@
+#include <set>
+#include <cstdlib>
+#if defined(__ANDROID__)
+#include <EGL/egl.h>
+#elif defined(_WIN32)
+#include <windows.h>
+#elif !defined(__APPLE__)
+#define GLX_GLXEXT_PROTOTYPES
+#include <GL/glx.h>
+#endif
+#include <msp/strings/format.h>
+#include <msp/strings/utils.h>
+#include "error.h"
+#include "extension.h"
+#include "gl.h"
+
+#ifndef GL_VERSION_3_0
+#define GL_NUM_EXTENSIONS 0x821D
+#endif
+
+#ifndef GL_VERSION_3_2
+#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001
+#define GL_CONTEXT_PROFILE_MASK 0x9126
+#endif
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Version::Version()
+{
+       major = 0;
+       minor = 0;
+}
+
+Version::Version(unsigned short a, unsigned short i)
+{
+       major = a;
+       minor = i;
+}
+
+Version::Version(const string &s)
+{
+       vector<string> parts = split(s, '.');
+       major = lexical_cast<unsigned>(parts[0]);
+       minor = lexical_cast<unsigned>(parts[1]);
+}
+
+bool Version::operator>=(const Version &other) const
+{
+       return major>other.major || (major==other.major && minor>=other.minor);
+}
+
+
+Extension::Extension(const char *n, InitFunc f):
+       name(n),
+       init_func(f),
+       init_done(false),
+       support(UNSUPPORTED)
+{ }
+
+Extension::operator bool() const
+{
+       if(!init_done)
+       {
+               support = init_func();
+               init_done = true;
+       }
+
+       return support>UNSUPPORTED;
+}
+
+
+Require::Require(const Extension &ext)
+{
+       if(!ext)
+               throw unsupported_extension(ext.get_name());
+}
+
+
+bool is_supported(const string &ext)
+{
+       if(is_disabled(ext))
+               return false;
+
+       static set<string> extensions;
+       static bool init_done = false;
+
+       if(!init_done)
+       {
+               if(get_gl_api()==OPENGL && get_gl_version()>=Version(3, 0))
+               {
+                       typedef GLubyte *(APIENTRY *FPtr_glGetStringi)(GLenum, GLuint);
+                       FPtr_glGetStringi glGetStringi = reinterpret_cast<FPtr_glGetStringi>(get_proc_address("glGetStringi"));
+                       int n_extensions;
+                       glGetIntegerv(GL_NUM_EXTENSIONS, &n_extensions);
+                       for(int i=0; i<n_extensions; ++i)
+                               extensions.insert(reinterpret_cast<const char *>(glGetStringi(GL_EXTENSIONS, i)));
+               }
+               else
+               {
+                       if(const char *gl_ext = reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)))
+                       {
+                               vector<string> exts = split(gl_ext);
+                               extensions.insert(exts.begin(), exts.end());
+                       }
+               }
+
+               init_done = true;
+       }
+
+       return extensions.count(ext);
+}
+
+bool is_supported(const Version &core_version, const Version &deprecated_version)
+{
+       const Version &version = get_gl_version();
+       if(deprecated_version && version>=deprecated_version && get_gl_profile()==CORE_PROFILE)
+               return false;
+       return (version>=core_version);
+}
+
+bool is_disabled(const string &ext)
+{
+       static set<string> disabled_exts;
+       static bool init_done = false;
+
+       if(!init_done)
+       {
+               if(const char *disable_ptr = getenv("MSPGL_DISABLE_EXTENSIONS"))
+               {
+                       vector<string> disable = split(disable_ptr);
+                       disabled_exts.insert(disable.begin(), disable.end());
+               }
+
+               if(const char *renderer_ptr = reinterpret_cast<const char *>(glGetString(GL_RENDERER)))
+               {
+                       string renderer = renderer_ptr;
+                       if(renderer.find("Radeon")!=string::npos || renderer.find("AMD")!=string::npos)
+                       {
+                               // The core primitive restart feature does not work either.
+                               disabled_exts.insert("GL_MSP_primitive_restart");
+
+                               /* AMD's uniform buffer objects only work with the core version of
+                               shaders. */
+                               if(get_gl_version()<Version(2, 0))
+                                       disabled_exts.insert("GL_ARB_uniform_buffer_object");
+                       }
+               }
+
+               init_done = true;
+       }
+
+       return disabled_exts.count(ext);
+}
+
+GLApi get_gl_api()
+{
+#ifdef GL_ES_VERSION_2_0
+       return OPENGL_ES2;
+#else
+       return OPENGL;
+#endif
+}
+
+inline GLProfile _get_gl_profile()
+{
+       if(get_gl_api()==OPENGL && get_gl_version()>=Version(3, 0))
+       {
+               int mask;
+               glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask);
+               if(mask==GL_CONTEXT_CORE_PROFILE_BIT)
+                       return CORE_PROFILE;
+       }
+
+       return COMPATIBILITY_PROFILE;
+}
+
+GLProfile get_gl_profile()
+{
+       static GLProfile profile = _get_gl_profile();
+       return profile;
+}
+
+inline Version _get_gl_version()
+{
+       const char *gl_ver_ptr = reinterpret_cast<const char *>(glGetString(GL_VERSION));
+       if(!gl_ver_ptr)
+               throw runtime_error("OpenGL version not available");
+
+       string gl_ver = gl_ver_ptr;
+       if(!gl_ver.compare(0, 10, "OpenGL ES "))
+               gl_ver.erase(0, 10);
+
+       Version ver(gl_ver.substr(0, gl_ver.find(' ')));
+
+       if(const char *force_ver_ptr = getenv("MSPGL_FORCE_VERSION"))
+       {
+               Version force_ver(force_ver_ptr);
+               if(force_ver<ver)
+                       ver = force_ver;
+       }
+
+       return ver;
+}
+
+const Version &get_gl_version()
+{
+       static Version version = _get_gl_version();
+       return version;
+}
+
+inline Version _get_glsl_version()
+{
+       const char *glsl_ver_ptr = reinterpret_cast<const char *>(glGetString(GL_SHADING_LANGUAGE_VERSION));
+       if(!glsl_ver_ptr)
+               throw runtime_error("GLSL version not available");
+
+       string glsl_ver = glsl_ver_ptr;
+       if(!glsl_ver.compare(0, 18, "OpenGL ES GLSL ES "))
+               glsl_ver.erase(0, 18);
+
+       Version ver(glsl_ver.substr(0, glsl_ver.find(' ')));
+
+       if(const char *force_ver_ptr = getenv("MSPGL_FORCE_GLSL_VERSION"))
+       {
+               Version force_ver(force_ver_ptr);
+               if(force_ver<ver)
+                       ver = force_ver;
+       }
+
+       return ver;
+}
+
+const Version &get_glsl_version()
+{
+       static Version version = _get_glsl_version();
+       return version;
+}
+
+ExtFunc *get_proc_address(const string &name)
+{
+#if defined(_WIN32)
+       return reinterpret_cast<ExtFunc *>(wglGetProcAddress(name.c_str()));
+#elif defined(__APPLE__)
+       (void)name;
+       return 0;  // Not supported
+#elif defined(__ANDROID__)
+       return eglGetProcAddress(name.c_str());
+#else
+       return glXGetProcAddressARB(reinterpret_cast<const unsigned char *>(name.c_str()));
+#endif
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/extension.h b/source/core/extension.h
new file mode 100644 (file)
index 0000000..cf824e2
--- /dev/null
@@ -0,0 +1,105 @@
+#ifndef MSP_GL_EXTENSION_H_
+#define MSP_GL_EXTENSION_H_
+
+#include <string>
+
+namespace Msp {
+namespace GL {
+
+enum GLApi
+{
+       OPENGL,
+       OPENGL_ES2
+};
+
+enum GLProfile
+{
+       CORE_PROFILE,
+       COMPATIBILITY_PROFILE
+};
+
+
+struct Version
+{
+       unsigned short major;
+       unsigned short minor;
+
+       Version();
+       Version(unsigned short, unsigned short);
+       Version(const std::string &);
+
+       bool operator>=(const Version &) const;
+       bool operator<(const Version &o) const { return !(*this>=o); }
+       operator bool() const { return major || minor; }
+};
+
+
+/**
+Holds metadata about an extension.  Evaluates to true if the extension is
+supported.
+*/
+class Extension
+{
+public:
+       enum SupportLevel
+       {
+               UNSUPPORTED,
+               EXTENSION,
+               CORE
+       };
+
+       typedef SupportLevel (*InitFunc)();
+
+private:
+       const char *name;
+       InitFunc init_func;
+       mutable bool init_done;
+       mutable SupportLevel support;
+
+public:
+       Extension(const char *, InitFunc);
+
+       const char *get_name() const { return name; }
+       operator bool() const;
+};
+
+
+struct Require
+{
+       Require(const Extension &);
+};
+
+
+typedef void ExtFunc();
+
+/** Checks for extension support.  Only intended for internal use. */
+bool is_supported(const std::string &);
+
+/** Checks for OpenGL version support.  Only intended for internal use. */
+bool is_supported(const Version &, const Version & = Version());
+
+/** Indicates whether an extension has been disabled, either explicitly through
+the MSPGL_DISABLE_EXTENSIONS environment variable or implicitly as a workaround
+for a driver bug.  Only intended for internal use. */
+bool is_disabled(const std::string &);
+
+/** Returns the API for which the library was compiled. */
+GLApi get_gl_api();
+
+/** Returns the OpenGL profile for the active context. */
+GLProfile get_gl_profile();
+
+/** Returns the OpenGL version number, as reported by the implementation. */
+const Version &get_gl_version();
+
+/** Returns the GLSL version number, as reported by the implementation. */
+const Version &get_glsl_version();
+
+/** Returns the address of an extension function.  Only indended for internal
+use. */
+ExtFunc *get_proc_address(const std::string &);
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/framebuffer.cpp b/source/core/framebuffer.cpp
new file mode 100644 (file)
index 0000000..c90bb22
--- /dev/null
@@ -0,0 +1,436 @@
+#include <msp/gl/extensions/arb_draw_buffers.h>
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/ext_framebuffer_blit.h>
+#include <msp/gl/extensions/ext_framebuffer_object.h>
+#include <msp/gl/extensions/ext_texture_array.h>
+#include <msp/gl/extensions/ext_texture3d.h>
+#include <msp/gl/extensions/msp_buffer_control.h>
+#include "error.h"
+#include "framebuffer.h"
+#include "misc.h"
+#include "renderbuffer.h"
+#include "texture2d.h"
+#include "texture3d.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+void operator<<(LexicalConverter &conv, FramebufferStatus status)
+{
+       switch(status)
+       {
+       case FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
+               conv.result("incomplete attachment");
+               break;
+       case FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
+               conv.result("missing attachment");
+               break;
+       case FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
+               conv.result("mismatched attachment dimensions");
+               break;
+       case FRAMEBUFFER_INCOMPLETE_FORMATS:
+               conv.result("mismatched attachment formats");
+               break;
+       case FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
+               conv.result("missing draw buffer attachment");
+               break;
+       case FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
+               conv.result("missing read buffer attachment");
+               break;
+       case FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
+               conv.result("mismatched attachment sample counts");
+               break;
+       case FRAMEBUFFER_INCOMPLETE_LAYER_COUNT:
+               conv.result("mismatched attachment layer counts");
+               break;
+       case FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
+               conv.result("mismatched attachment layering");
+               break;
+       case FRAMEBUFFER_UNSUPPORTED:
+               conv.result("unsupported");
+               break;
+       default:
+               conv.result(lexical_cast<string, unsigned>(status, "%#x"));
+               break;
+       }
+}
+
+framebuffer_incomplete::framebuffer_incomplete(FramebufferStatus status):
+       runtime_error(lexical_cast<string>(status))
+{ }
+
+
+Framebuffer::Framebuffer(unsigned i):
+       id(i),
+       dirty(0)
+{
+       if(id)
+               throw invalid_argument("System framebuffer must have id 0");
+
+       glGetIntegerv(GL_VIEWPORT, &view.left);
+       width = view.width;
+       height = view.height;
+}
+
+Framebuffer::Framebuffer():
+       width(0),
+       height(0),
+       dirty(0)
+{
+       static Require _req(EXT_framebuffer_object);
+
+       if(ARB_direct_state_access)
+               glCreateFramebuffers(1, &id);
+       else
+               glGenFramebuffers(1, &id);
+}
+
+Framebuffer::~Framebuffer()
+{
+       if(id)
+               glDeleteFramebuffers(1, &id);
+       if(current()==this)
+               unbind();
+}
+
+void Framebuffer::update_attachment(unsigned mask) const
+{
+       if(!ARB_direct_state_access && current()!=this)
+       {
+               dirty |= mask;
+               return;
+       }
+
+       std::vector<GLenum> color_bufs;
+       color_bufs.reserve(attachments.size());
+       for(unsigned i=0; i<attachments.size(); ++i)
+       {
+               const Attachment &attch = attachments[i];
+               if(mask&(1<<i))
+               {
+                       if(attch.type==GL_RENDERBUFFER)
+                       {
+                               if(ARB_direct_state_access)
+                                       glNamedFramebufferRenderbuffer(id, attch.attachment, GL_RENDERBUFFER, attch.rbuf->get_id());
+                               else
+                                       glFramebufferRenderbuffer(GL_FRAMEBUFFER, attch.attachment, GL_RENDERBUFFER, attch.rbuf->get_id());
+                       }
+                       else if(attch.type==GL_TEXTURE_2D)
+                       {
+                               static_cast<Texture2D *>(attch.tex)->allocate(attch.level);
+                               if(ARB_direct_state_access)
+                                       glNamedFramebufferTexture(id, attch.attachment, attch.tex->get_id(), attch.level);
+                               else
+                                       glFramebufferTexture2D(GL_FRAMEBUFFER, attch.attachment, attch.type, attch.tex->get_id(), attch.level);
+                       }
+                       else if(attch.type==GL_TEXTURE_3D || attch.type==GL_TEXTURE_2D_ARRAY)
+                       {
+                               static_cast<Texture3D *>(attch.tex)->allocate(attch.level);
+                               if(ARB_direct_state_access)
+                                       glNamedFramebufferTextureLayer(id, attch.attachment, attch.tex->get_id(), attch.level, attch.layer);
+                               else if(attch.type==GL_TEXTURE_2D_ARRAY)
+                                       glFramebufferTextureLayer(GL_FRAMEBUFFER, attch.attachment, attch.tex->get_id(), attch.level, attch.layer);
+                               else
+                                       glFramebufferTexture3D(GL_FRAMEBUFFER, attch.attachment, attch.type, attch.tex->get_id(), attch.level, attch.layer);
+                       }
+                       else if(attch.type==GL_TEXTURE_CUBE_MAP)
+                       {
+                               static_cast<TextureCube *>(attch.tex)->allocate(attch.level);
+                               if(ARB_direct_state_access)
+                                       glNamedFramebufferTextureLayer(id, attch.attachment, attch.tex->get_id(), attch.level, attch.layer);
+                               else
+                                       glFramebufferTexture2D(GL_FRAMEBUFFER, attch.attachment, TextureCube::enumerate_faces(attch.layer), attch.tex->get_id(), attch.level);
+                       }
+                       else if(ARB_direct_state_access)
+                               glNamedFramebufferRenderbuffer(id, attch.attachment, 0, 0);
+                       else
+                               glFramebufferRenderbuffer(GL_FRAMEBUFFER, attch.attachment, 0, 0);
+               }
+
+               if(attch.attachment>=COLOR_ATTACHMENT0 && attch.attachment<=COLOR_ATTACHMENT3)
+                       color_bufs.push_back(attch.attachment);
+       }
+
+       if(color_bufs.size()>1)
+               static Require _req(ARB_draw_buffers);
+
+       GLenum first_buffer = (color_bufs.empty() ? GL_NONE : color_bufs.front());
+       if(ARB_direct_state_access)
+       {
+               /* ARB_direct_state_access ties the availability of these functions to
+               framebuffers themselves, so no further checks are needed. */
+               glNamedFramebufferDrawBuffers(id, color_bufs.size(), &color_bufs[0]);
+               glNamedFramebufferReadBuffer(id, first_buffer);
+       }
+       else
+       {
+               if(ARB_draw_buffers)
+                       glDrawBuffers(color_bufs.size(), &color_bufs[0]);
+               else if(MSP_buffer_control)
+                       glDrawBuffer(first_buffer);
+
+               if(MSP_buffer_control)
+                       glReadBuffer(first_buffer);
+       }
+}
+
+void Framebuffer::check_size()
+{
+       bool full_viewport = (view.left==0 && view.bottom==0 && view.width==width && view.height==height);
+       for(vector<Attachment>::iterator i=attachments.begin(); i!=attachments.end(); ++i)
+               if(i->type)
+               {
+                       if(i->type==GL_RENDERBUFFER)
+                       {
+                               width = i->rbuf->get_width();
+                               height = i->rbuf->get_height();
+                       }
+                       else if(i->type==GL_TEXTURE_2D)
+                       {
+                               Texture2D *tex = static_cast<Texture2D *>(i->tex);
+                               width = max(tex->get_width()>>i->level, 1U);
+                               height = max(tex->get_height()>>i->level, 1U);
+                       }
+                       else if(i->type==GL_TEXTURE_3D || i->type==GL_TEXTURE_2D_ARRAY)
+                       {
+                               Texture3D *tex = static_cast<Texture3D *>(i->tex);
+                               width = max(tex->get_width()>>i->level, 1U);
+                               height = max(tex->get_height()>>i->level, 1U);
+                       }
+                       else if(i->type==GL_TEXTURE_CUBE_MAP)
+                       {
+                               width = max(static_cast<TextureCube *>(i->tex)->get_size()>>i->level, 1U);
+                               height = width;
+                       }
+                       if(full_viewport)
+                               reset_viewport();
+                       break;
+               }
+}
+
+unsigned Framebuffer::get_attachment_index(FramebufferAttachment attch)
+{
+       for(unsigned i=0; i<attachments.size(); ++i)
+               if(attachments[i].attachment==attch)
+                       return i;
+       attachments.push_back(Attachment(attch));
+       return attachments.size()-1;
+}
+
+void Framebuffer::attach(FramebufferAttachment attch, Renderbuffer &rbuf)
+{
+       if(!id)
+               throw invalid_operation("Framebuffer::attach");
+
+       unsigned i = get_attachment_index(attch);
+       attachments[i].set(rbuf);
+       update_attachment(1<<i);
+       check_size();
+}
+
+void Framebuffer::attach(FramebufferAttachment attch, Texture2D &tex, unsigned level)
+{
+       if(!id)
+               throw invalid_operation("Framebuffer::attach");
+
+       unsigned i = get_attachment_index(attch);
+       attachments[i].set(tex, level, 0);
+       update_attachment(1<<i);
+       check_size();
+}
+
+void Framebuffer::attach(FramebufferAttachment attch, Texture3D &tex, unsigned layer, unsigned level)
+{
+       if(!id)
+               throw invalid_operation("Framebuffer::attach");
+
+       unsigned i = get_attachment_index(attch);
+       attachments[i].set(tex, level, layer);
+       update_attachment(1<<i);
+       check_size();
+}
+
+void Framebuffer::attach(FramebufferAttachment attch, TextureCube &tex, TextureCubeFace face, unsigned level)
+{
+       if(!id)
+               throw invalid_operation("Framebuffer::attach");
+
+       unsigned i = get_attachment_index(attch);
+       attachments[i].set(tex, level, TextureCube::get_face_index(face));
+       update_attachment(1<<i);
+       check_size();
+}
+
+void Framebuffer::detach(FramebufferAttachment attch)
+{
+       if(!id)
+               throw invalid_operation("Framebuffer::detach");
+
+       unsigned i = get_attachment_index(attch);
+       attachments[i].clear();
+       update_attachment(1<<i);
+       check_size();
+}
+
+FramebufferStatus Framebuffer::check_status() const
+{
+       if(ARB_direct_state_access)
+               return static_cast<FramebufferStatus>(glCheckNamedFramebufferStatus(id, GL_FRAMEBUFFER));
+       else
+       {
+               BindRestore _bind(this);
+               return static_cast<FramebufferStatus>(glCheckFramebufferStatus(GL_FRAMEBUFFER));
+       }
+}
+
+void Framebuffer::require_complete() const
+{
+       FramebufferStatus status = check_status();
+       if(status!=FRAMEBUFFER_COMPLETE)
+               throw framebuffer_incomplete(status);
+}
+
+void Framebuffer::viewport(int l, int b, unsigned w, unsigned h)
+{
+       view.left = l;
+       view.bottom = b;
+       view.width = w;
+       view.height = h;
+
+       if(current()==this)
+               glViewport(view.left, view.bottom, view.width, view.height);
+}
+
+void Framebuffer::reset_viewport()
+{
+       viewport(0, 0, width, height);
+}
+
+void Framebuffer::clear()
+{
+       clear(COLOR_BUFFER_BIT|DEPTH_BUFFER_BIT|STENCIL_BUFFER_BIT);
+}
+
+void Framebuffer::clear(BufferBits bits)
+{
+       BindRestore _bind(this);
+       glClear(bits);
+}
+
+void Framebuffer::blit_from(const Framebuffer &other, int sx0, int sy0, int sx1, int sy1, int dx0, int dy0, int dx1, int dy1, BufferBits bits, bool filter)
+{
+       static Require _req(EXT_framebuffer_blit);
+
+       if(ARB_direct_state_access)
+       {
+               glBlitNamedFramebuffer(other.id, id, sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1, bits, (filter ? GL_LINEAR : GL_NEAREST));
+               return;
+       }
+
+       const Framebuffer *old = current();
+       if(set_current(this))
+       {
+               glBindFramebuffer(GL_DRAW_FRAMEBUFFER, id);
+               if(dirty)
+               {
+                       update_attachment(dirty);
+                       dirty = 0;
+               }
+       }
+       if(old!=&other)
+               glBindFramebuffer(GL_READ_FRAMEBUFFER, other.id);
+
+       glBlitFramebuffer(sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1, bits, (filter ? GL_LINEAR : GL_NEAREST));
+
+       set_current(old);
+       glBindFramebuffer(GL_FRAMEBUFFER, (old ? old->id : 0));
+}
+
+void Framebuffer::blit_from(const Framebuffer &other, int sx, int sy, unsigned wd, unsigned ht, int dx, int dy, BufferBits bits)
+{
+       blit_from(other, sx, sy, sx+wd, sy+ht, dx, dy, dx+wd, dy+ht, bits, false);
+}
+
+void Framebuffer::blit_from(const Framebuffer &other, BufferBits bits, bool filter)
+{
+       blit_from(other, 0, 0, other.width, other.height, 0, 0, width, height, bits, filter);
+}
+
+void Framebuffer::bind() const
+{
+       if(id && attachments.empty())
+               throw invalid_operation("Framebuffer::bind");
+
+       if(set_current(this))
+       {
+               glBindFramebuffer(GL_FRAMEBUFFER, id);
+               if(dirty)
+               {
+                       update_attachment(dirty);
+                       dirty = 0;
+               }
+
+               if(width && height)
+                       glViewport(view.left, view.bottom, view.width, view.height);
+       }
+}
+
+const Framebuffer *Framebuffer::current()
+{
+       if(!cur_obj)
+               cur_obj = &system();
+       return cur_obj;
+}
+
+void Framebuffer::unbind()
+{
+       system().bind();
+}
+
+Framebuffer &Framebuffer::system()
+{
+       static Framebuffer sys_framebuf(0);
+       return sys_framebuf;
+}
+
+
+Framebuffer::Attachment::Attachment(FramebufferAttachment a):
+       attachment(a),
+       type(0),
+       level(0),
+       layer(0)
+{ }
+
+void Framebuffer::Attachment::set(Renderbuffer &r)
+{
+       type = GL_RENDERBUFFER;
+       rbuf = &r;
+       level = 0;
+       layer = 0;
+}
+
+void Framebuffer::Attachment::set(Texture &t, unsigned l, unsigned z)
+{
+       type = t.get_target();
+       tex = &t;
+       level = l;
+       layer = z;
+}
+
+void Framebuffer::Attachment::clear()
+{
+       type = 0;
+}
+
+
+Framebuffer::Viewport::Viewport():
+       left(0),
+       bottom(0),
+       width(0),
+       height(0)
+{ }
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/framebuffer.h b/source/core/framebuffer.h
new file mode 100644 (file)
index 0000000..3054bd6
--- /dev/null
@@ -0,0 +1,173 @@
+#ifndef MSP_GL_FRAMEBUFFER_H_
+#define MSP_GL_FRAMEBUFFER_H_
+
+#include <vector>
+#include "bindable.h"
+#include "gl.h"
+#include "texturecube.h"
+#include <msp/gl/extensions/arb_geometry_shader4.h>
+#include <msp/gl/extensions/ext_framebuffer_multisample.h>
+#include <msp/gl/extensions/ext_framebuffer_object.h>
+#include <msp/gl/extensions/nv_fbo_color_attachments.h>
+
+namespace Msp {
+namespace GL {
+
+class Renderbuffer;
+class Texture;
+class Texture2D;
+class Texture3D;
+
+enum FramebufferAttachment
+{
+       COLOR_ATTACHMENT0  = GL_COLOR_ATTACHMENT0,
+       COLOR_ATTACHMENT1  = GL_COLOR_ATTACHMENT1,
+       COLOR_ATTACHMENT2  = GL_COLOR_ATTACHMENT2,
+       COLOR_ATTACHMENT3  = GL_COLOR_ATTACHMENT3,
+       DEPTH_ATTACHMENT   = GL_DEPTH_ATTACHMENT,
+       STENCIL_ATTACHMENT = GL_STENCIL_ATTACHMENT
+};
+
+enum FramebufferStatus
+{
+       FRAMEBUFFER_INCOMPLETE_ATTACHMENT         = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
+       FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT,
+       FRAMEBUFFER_INCOMPLETE_DIMENSIONS         = GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT,
+       FRAMEBUFFER_INCOMPLETE_FORMATS            = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT,
+       FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER        = GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER,
+       FRAMEBUFFER_INCOMPLETE_READ_BUFFER        = GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER,
+       FRAMEBUFFER_INCOMPLETE_MULTISAMPLE        = GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
+       FRAMEBUFFER_INCOMPLETE_LAYER_COUNT        = GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB,
+       FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS      = GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS,
+       FRAMEBUFFER_UNSUPPORTED                   = GL_FRAMEBUFFER_UNSUPPORTED,
+       FRAMEBUFFER_COMPLETE                      = GL_FRAMEBUFFER_COMPLETE
+};
+
+enum BufferBits
+{
+       COLOR_BUFFER_BIT   = GL_COLOR_BUFFER_BIT,
+       DEPTH_BUFFER_BIT   = GL_DEPTH_BUFFER_BIT,
+       STENCIL_BUFFER_BIT = GL_STENCIL_BUFFER_BIT
+};
+
+class framebuffer_incomplete: public std::runtime_error
+{
+public:
+       framebuffer_incomplete(FramebufferStatus);
+       virtual ~framebuffer_incomplete() throw() { }
+};
+
+/**
+Framebuffer objects can be used to perform offscreen rendering.  The most
+common application is rendering to a texture, which can then be used for
+fullscreen shader effects.
+
+A framebuffer consist of a number of logical buffers, such as color and depth
+buffers.  Renderbuffers and Textures can be attached to the logical buffers.  At
+least one image must be attached for the framebuffer to be usable.
+
+Requires the GL_EXT_framebuffer_object extension.  The blit functions require
+the GL_EXT_framebuffer_blit extension.
+*/
+class Framebuffer: public Bindable<Framebuffer>
+{
+private:
+       struct Attachment
+       {
+               FramebufferAttachment attachment;
+               GLenum type;
+               union
+               {
+                       Renderbuffer *rbuf;
+                       Texture *tex;
+               };
+               unsigned level;
+               unsigned layer;
+
+               Attachment(FramebufferAttachment);
+               void set(Renderbuffer &);
+               void set(Texture &, unsigned, unsigned);
+               void clear();
+       };
+
+       struct Viewport
+       {
+               int left;
+               int bottom;
+               unsigned width;
+               unsigned height;
+
+               Viewport();
+       };
+
+       unsigned id;
+       std::vector<Attachment> attachments;
+       unsigned width;
+       unsigned height;
+       Viewport view;
+       mutable unsigned dirty;
+
+       Framebuffer(unsigned);
+public:
+       Framebuffer();
+       ~Framebuffer();
+
+       unsigned get_width() const { return width; }
+       unsigned get_height() const { return height; }
+
+private:
+       void update_attachment(unsigned) const;
+       void check_size();
+       unsigned get_attachment_index(FramebufferAttachment);
+public:
+       void attach(FramebufferAttachment attch, Renderbuffer &rbuf);
+       void attach(FramebufferAttachment attch, Texture2D &tex, unsigned level = 0);
+       void attach(FramebufferAttachment attch, Texture3D &tex, unsigned layer, unsigned level = 0);
+       void attach(FramebufferAttachment attch, TextureCube &tex, TextureCubeFace face, unsigned level = 0);
+       void detach(FramebufferAttachment attch);
+
+       /** Checks the completeness of the framebuffer.  Returns
+       FRAMEBUFFER_COMPLETE if the framebuffer is complete and can be rendered to,
+       or one of the error status codes otherwise. */
+       FramebufferStatus check_status() const;
+
+       /** Ensures that the framebuffer is complete, throwing an exception if it
+       isn't. */
+       void require_complete() const;
+
+       void viewport(int, int, unsigned, unsigned);
+       void reset_viewport();
+
+       void clear();
+       void clear(BufferBits);
+
+       /** Blits a region from another framebuffer into this one.  If the source
+       and destination regions have different dimensions, the contents will be
+       stretched.  If filter is true, linear interpolation will be used, otherwise
+       no interpolation is done. */
+       void blit_from(const Framebuffer &other, int sx0, int sy0, int sx1, int sy1,
+               int dx0, int dy0, int dx1, int dy1, BufferBits bits, bool filter);
+
+       /** Blits a region from another framebuffer into this one, retaining its
+       dimensions. */
+       void blit_from(const Framebuffer & other, int sx, int sy,
+               unsigned wd, unsigned ht, int dx, int dy, BufferBits bits);
+
+       /** Blits the entire contents of another framebuffer into this one. */
+       void blit_from(const Framebuffer &other, BufferBits bits, bool filter);
+
+       void bind() const;
+
+       static const Framebuffer *current();
+       static void unbind();
+
+       static Framebuffer &system();
+};
+
+inline BufferBits operator|(BufferBits a, BufferBits b)
+{ return static_cast<BufferBits>(static_cast<int>(a)|static_cast<int>(b)); }
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/gl.h b/source/core/gl.h
new file mode 100644 (file)
index 0000000..f27b63f
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef MSP_GL_GL_H_
+#define MSP_GL_GL_H_
+
+#ifdef __APPLE__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wkeyword-macro"
+#define extern extern __attribute__((weak_import))
+#include <OpenGL/gl.h>
+#include <OpenGL/glext.h>
+#undef extern
+#pragma clang diagnostic pop
+#elif defined(__ANDROID__)
+#include <GLES3/gl3.h>
+#include <GLES3/gl3ext.h>
+typedef double GLdouble;
+typedef long long GLint64;
+#else
+#ifdef _WIN32
+#ifndef WINAPI
+#if defined(_ARM_)
+#define WINAPI
+#else
+#define WINAPI __stdcall
+#endif
+#endif
+#ifndef APIENTRY
+#define APIENTRY WINAPI
+#endif
+#ifndef DECLSPEC_IMPORT
+#define DECLSPEC_IMPORT __declspec(dllimport)
+#endif
+#ifndef WINGDIAPI
+#define WINGDIAPI DECLSPEC_IMPORT
+#endif
+#endif
+#include <GL/gl.h>
+#include <GL/glext.h>
+#endif
+
+#ifndef APIENTRY
+#define APIENTRY
+#endif
+
+#endif
diff --git a/source/core/matrix.cpp b/source/core/matrix.cpp
new file mode 100644 (file)
index 0000000..311958a
--- /dev/null
@@ -0,0 +1,120 @@
+#include <algorithm>
+#include <cmath>
+#include <msp/geometry/affinetransformation.h>
+#include "error.h"
+#include "matrix.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Matrix::Matrix():
+       Base(Base::identity())
+{ }
+
+Matrix::Matrix(const float *m):
+       Base(m)
+{ }
+
+Matrix::Matrix(const LinAl::Matrix<float, 4, 4> &other):
+       Base(other)
+{ }
+
+Matrix &Matrix::translate(const Vector3 &t)
+{
+       return multiply(translation(t));
+}
+
+Matrix &Matrix::rotate(const Angle &a, const Vector3 &x)
+{
+       return multiply(rotation(a, x));
+}
+
+Matrix &Matrix::scale(const Vector3 &s)
+{
+       return multiply(scaling(s));
+}
+
+float Matrix::operator[](unsigned i) const
+{
+       if(i>=16)
+               throw out_of_range("Matrix::operator[]");
+       return operator()(i%4, i/4);
+}
+
+Matrix Matrix::translation(const Vector3 &t)
+{
+       return Geometry::AffineTransformation<float, 3>::translation(t).get_matrix();
+}
+
+Matrix Matrix::rotation(const Angle &a, const Vector3 &x)
+{
+       return Geometry::AffineTransformation<float, 3>::rotation(a, x).get_matrix();
+}
+
+Matrix Matrix::scaling(const Vector3 &s)
+{
+       return Geometry::AffineTransformation<float, 3>::scaling(s).get_matrix();
+}
+
+Matrix Matrix::ortho(float l, float r, float b, float t, float n, float f)
+{
+       if(l==r || b==t || n==f)
+               throw invalid_argument("Matrix::ortho");
+
+       Matrix result;
+       result(0, 0) = 2/(r-l);
+       result(1, 1) = 2/(t-b);
+       result(2, 2) = -2/(f-n);
+       result(0, 3) = -(r+l)/(r-l);
+       result(1, 3) = -(t+b)/(t-b);
+       result(2, 3) = -(f+n)/(f-n);
+       return result;
+}
+
+Matrix Matrix::ortho_centered(float w, float h)
+{
+       return ortho(-w/2, w/2, -h/2, h/2, -1, 1);
+}
+
+Matrix Matrix::ortho_bottomleft(float w, float h)
+{
+       return ortho(0, w, 0, h, -1, 1);
+}
+
+Matrix Matrix::ortho_topleft(float w, float h)
+{
+       return ortho(0, w, h, 0, -1, 1);
+}
+
+Matrix Matrix::frustum(float l, float r, float b, float t, float n, float f)
+{
+       if(l==r || b==t || n<=0 || f<=n)
+               throw invalid_argument("Matrix::frustum");
+
+       Matrix result;
+       result(0, 0) = 2*n/(r-l);
+       result(1, 1) = 2*n/(t-b);
+       result(0, 2) = (r+l)/(r-l);
+       result(1, 2) = (t+b)/(t-b);
+       result(2, 2) = -(f+n)/(f-n);
+       result(3, 2) = -1;
+       result(2, 3) = -2*f*n/(f-n);
+       result(3, 3) = 0;
+       return result;
+}
+
+Matrix Matrix::frustum_centered(float w, float h, float n, float f)
+{
+       return frustum(-w/2, w/2, -h/2, h/2, n, f);
+}
+
+Matrix Matrix::perspective(const Angle &h, float a, float n, float f)
+{
+       float hh = tan(h/2.0f)*n;
+       return frustum(-hh*a, hh*a, -hh, hh, n, f);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/matrix.h b/source/core/matrix.h
new file mode 100644 (file)
index 0000000..cb61e29
--- /dev/null
@@ -0,0 +1,71 @@
+#ifndef MSP_GL_MATRIX_H_
+#define MSP_GL_MATRIX_H_
+
+#include <vector>
+#include <msp/geometry/angle.h>
+#include <msp/linal/squarematrix.h>
+#include "gl.h"
+#include "vector.h"
+
+namespace Msp {
+namespace GL {
+
+class Matrix: public LinAl::SquareMatrix<float, 4>
+{
+private:
+       typedef LinAl::SquareMatrix<float, 4> Base;
+       typedef Geometry::Angle<float> Angle;
+
+public:
+       Matrix();
+       Matrix(const float *);
+       Matrix(const LinAl::Matrix<float, 4, 4> &);
+
+       const float *data() const { return &Base::operator()(0, 0); }
+
+       Matrix &multiply(const Matrix &m) { return operator*=(m); }
+       Matrix &translate(float x, float y, float z) { return translate(Vector3(x, y, z)); }
+       Matrix &translate(const Vector3 &);
+       Matrix &rotate(const Angle &a, float x, float y, float z) { return rotate(a, Vector3(x, y, z)); }
+       Matrix &rotate(const Angle &, const Vector3 &);
+       Matrix &rotate(float a, float x, float y, float z) { return rotate(Angle::from_radians(a), Vector3(x, y, z)); }
+       Matrix &rotate(float a, const Vector3 &x) { return rotate(Angle::from_radians(a), x); }
+       Matrix &rotate_deg(float a, float x, float y, float z) { return rotate(Angle::from_degrees(a), Vector3(x, y, z)); }
+       Matrix &rotate_deg(float a, const Vector3 & x) { return rotate(Angle::from_degrees(a), x); }
+       Matrix &scale(float s) { return scale(Vector3(s, s, s)); }
+       Matrix &scale(float x, float y, float z) { return scale(Vector3(x, y, z)); }
+       Matrix &scale(const Vector3 &);
+
+       Matrix operator*(const Matrix &m) const { return static_cast<const Base &>(*this)*static_cast<const Base &>(m); }
+       Matrix &operator*=(const Matrix &m) { Base::operator*=(m); return *this; }
+       Matrix operator*(float s) const { return static_cast<const Base &>(*this)*s; }
+       Matrix &operator*=(float s) { Base::operator*=(s); return *this; }
+       Vector4 operator*(const Vector4 &v) const { return static_cast<const Base &>(*this)*v; }
+       Vector3 operator*(const Vector3 &v) const { return ((*this)*compose(v, 1.0f)).slice<3>(0); }
+       float operator[](unsigned) const;
+
+       static Matrix translation(float x, float y, float z) { return translation(Vector3(x, y, z)); }
+       static Matrix translation(const Vector3 &);
+       static Matrix rotation(const Angle &a, float x, float y, float z) { return rotation(a, Vector3(x, y, z)); }
+       static Matrix rotation(const Angle &, const Vector3 &);
+       static Matrix rotation(float a, float x, float y, float z) { return rotation(Angle::from_radians(a), Vector3(x, y, z)); }
+       static Matrix rotation(float a, const Vector3 &x) { return rotation(Angle::from_radians(a), x); }
+       static Matrix rotation_deg(float a, float x, float y, float z) { return rotation(Angle::from_degrees(a), Vector3(x, y, z)); }
+       static Matrix rotation_deg(float a, const Vector3 &x) { return rotation(Angle::from_degrees(a), x); }
+       static Matrix scaling(float s) { return scaling(Vector3(s, s, s)); }
+       static Matrix scaling(float x, float y, float z) { return scaling(Vector3(x, y, z)); }
+       static Matrix scaling(const Vector3 &);
+
+       static Matrix ortho(float, float, float, float, float, float);
+       static Matrix ortho_centered(float, float);
+       static Matrix ortho_bottomleft(float, float);
+       static Matrix ortho_topleft(float, float);
+       static Matrix frustum(float, float, float, float, float, float);
+       static Matrix frustum_centered(float, float, float, float);
+       static Matrix perspective(const Angle &, float, float, float);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/mesh.cpp b/source/core/mesh.cpp
new file mode 100644 (file)
index 0000000..dba8a99
--- /dev/null
@@ -0,0 +1,316 @@
+#include <msp/gl/extensions/arb_vertex_array_object.h>
+#include <msp/gl/extensions/arb_vertex_buffer_object.h>
+#include <msp/gl/extensions/arb_vertex_shader.h>
+#include "buffer.h"
+#include "error.h"
+#include "mesh.h"
+#include "renderer.h"
+#include "resourcemanager.h"
+#include "vertexsetup.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Mesh::Mesh(ResourceManager *rm):
+       vertices(VERTEX3)
+{
+       init(rm);
+}
+
+Mesh::Mesh(const VertexFormat &f, ResourceManager *rm):
+       vertices(f)
+{
+       init(rm);
+}
+
+void Mesh::init(ResourceManager *rm)
+{
+       vbuf = 0;
+       ibuf = 0;
+       dirty = 0;
+       disallow_rendering = false;
+       winding = 0;
+
+       if(rm)
+               set_manager(rm);
+}
+
+Mesh::~Mesh()
+{
+       set_manager(0);
+       batches.clear();
+       delete vbuf;
+       delete ibuf;
+}
+
+void Mesh::clear()
+{
+       vertices.clear();
+       batches.clear();
+}
+
+void Mesh::check_buffers(unsigned mask)
+{
+       if(mask&VERTEX_BUFFER)
+       {
+               unsigned req_size = vertices.get_required_buffer_size();
+               if(!vbuf || (vbuf->get_size()>0 && vbuf->get_size()<req_size))
+               {
+                       delete vbuf;
+                       vbuf = new Buffer(ARRAY_BUFFER);
+                       vertices.use_buffer(vbuf);
+                       vtx_setup.set_vertex_array(vertices);
+                       dirty |= VERTEX_BUFFER;
+               }
+       }
+
+       if(mask&INDEX_BUFFER)
+       {
+               unsigned req_size = (batches.empty() ? 0 : batches.front().get_required_buffer_size());
+               if(!ibuf || (ibuf->get_size()>0 && ibuf->get_size()<req_size))
+               {
+                       delete ibuf;
+                       ibuf = new Buffer(ELEMENT_ARRAY_BUFFER);
+                       if(!batches.empty())
+                               batches.front().change_buffer(ibuf);
+                       vtx_setup.set_index_buffer(*ibuf);
+                       dirty |= INDEX_BUFFER;
+               }
+       }
+}
+
+unsigned Mesh::get_n_vertices() const
+{
+       return vertices.size();
+}
+
+float *Mesh::modify_vertex(unsigned i)
+{
+       return vertices.modify(i);
+}
+
+void Mesh::add_batch(const Batch &b)
+{
+       if(batches.empty())
+       {
+               batches.push_back(b);
+               if(ibuf)
+                       batches.back().use_buffer(ibuf);
+       }
+       else if(batches.back().can_append(b.get_type()))
+               batches.back().append(b);
+       else
+       {
+               bool reallocate = (batches.size()==batches.capacity());
+               if(reallocate)
+               {
+                       for(vector<Batch>::iterator i=batches.end(); i!=batches.begin(); )
+                               (--i)->use_buffer(0);
+               }
+
+               Batch *prev = &batches.back();
+               batches.push_back(b);
+               if(reallocate)
+               {
+                       prev = 0;
+                       for(vector<Batch>::iterator i=batches.begin(); i!=batches.end(); ++i)
+                       {
+                               i->use_buffer(ibuf, prev);
+                               prev = &*i;
+                       }
+               }
+               else
+                       batches.back().use_buffer(ibuf, prev);
+       }
+
+       check_buffers(INDEX_BUFFER);
+}
+
+void Mesh::set_winding(const WindingTest *w)
+{
+       winding = w;
+}
+
+void Mesh::draw(Renderer &renderer) const
+{
+       draw(renderer, 0, 0);
+}
+
+void Mesh::draw_instanced(Renderer &renderer, const VertexSetup &vs, unsigned count) const
+{
+       if(vs.get_vertex_array()!=&vertices)
+               throw invalid_argument("Mesh::draw_instanced");
+
+       draw(renderer, &vs, count);
+}
+
+void Mesh::draw(Renderer &renderer, const VertexSetup *vs, unsigned count) const
+{
+       if(manager)
+       {
+               manager->resource_used(*this);
+               if(disallow_rendering)
+                       return;
+       }
+
+       if(dirty)
+               resize_buffers();
+
+       renderer.set_vertex_setup(vs ? vs : &vtx_setup);
+       renderer.set_winding_test(winding);
+
+       if(!count)
+       {
+               for(vector<Batch>::const_iterator i=batches.begin(); i!=batches.end(); ++i)
+                       renderer.draw(*i);
+       }
+       else
+       {
+               for(vector<Batch>::const_iterator i=batches.begin(); i!=batches.end(); ++i)
+                       renderer.draw_instanced(*i, count);
+       }
+}
+
+void Mesh::resize_buffers() const
+{
+       if(dirty&VERTEX_BUFFER)
+               vbuf->storage(vertices.get_required_buffer_size());
+       if(dirty&INDEX_BUFFER)
+               ibuf->storage(batches.front().get_required_buffer_size());
+       dirty = 0;
+}
+
+Resource::AsyncLoader *Mesh::load(IO::Seekable &io, const Resources *)
+{
+       return new AsyncLoader(*this, io);
+}
+
+UInt64 Mesh::get_data_size() const
+{
+       UInt64 size = 0;
+       if(vbuf)
+               size += vbuf->get_size();
+       if(ibuf)
+               size += ibuf->get_size();
+       return size;
+}
+
+void Mesh::unload()
+{
+       vertices.clear();
+       vertices.use_buffer(0);
+       batches.clear();
+       delete vbuf;
+       delete ibuf;
+       vbuf = 0;
+       ibuf = 0;
+}
+
+
+Mesh::Loader::Loader(Mesh &m, bool g):
+       DataFile::ObjectLoader<Mesh>(m),
+       allow_gl_calls(g)
+{
+       add("batch",    &Loader::batch);
+       add("vertices", &Loader::vertices);
+       add("winding",  &Loader::winding);
+}
+
+void Mesh::Loader::vertices(const vector<VertexComponent> &c)
+{
+       if(c.empty())
+               throw invalid_argument("No vertex components");
+
+       VertexFormat fmt;
+       for(vector<VertexComponent>::const_iterator i=c.begin(); i!=c.end(); ++i)
+               fmt = (fmt, *i);
+       obj.vertices.reset(fmt);
+       load_sub(obj.vertices);
+       if(allow_gl_calls)
+       {
+               obj.check_buffers(VERTEX_BUFFER);
+               obj.vtx_setup.refresh();
+       }
+}
+
+void Mesh::Loader::batch(PrimitiveType p)
+{
+       Batch btc(p);
+       load_sub(btc);
+       obj.add_batch(btc);
+}
+
+void Mesh::Loader::winding(FaceWinding w)
+{
+       if(w==CLOCKWISE)
+               obj.winding = &WindingTest::clockwise();
+       else if(w==COUNTERCLOCKWISE)
+               obj.winding = &WindingTest::counterclockwise();
+}
+
+
+Mesh::AsyncLoader::AsyncLoader(Mesh &m, IO::Seekable &i):
+       mesh(m),
+       io(i),
+       vertex_updater(0),
+       index_updater(0),
+       phase(0)
+{
+       mesh.disallow_rendering = true;
+       mesh.check_buffers(VERTEX_BUFFER|INDEX_BUFFER);
+}
+
+Mesh::AsyncLoader::~AsyncLoader()
+{
+       mesh.disallow_rendering = false;
+       delete vertex_updater;
+       delete index_updater;
+}
+
+bool Mesh::AsyncLoader::needs_sync() const
+{
+       return phase%2;
+}
+
+bool Mesh::AsyncLoader::process()
+{
+       if(phase==0)
+       {
+               // TODO use correct filename
+               DataFile::Parser parser(io, "async");
+               Loader loader(mesh, false);
+               loader.load(parser);
+       }
+       else if(phase==1)
+       {
+               mesh.resize_buffers();
+               mesh.vtx_setup.refresh();
+               vertex_updater = mesh.vertices.refresh_async();
+               if(!mesh.batches.empty())
+                       index_updater = mesh.batches.front().refresh_async();
+       }
+       else if(phase==2)
+       {
+               if(vertex_updater)
+                       vertex_updater->upload_data();
+               if(index_updater)
+                       index_updater->upload_data();
+       }
+       else if(phase==3)
+       {
+               delete vertex_updater;
+               vertex_updater = 0;
+               delete index_updater;
+               index_updater = 0;
+       }
+
+       ++phase;
+       if(phase==1 && !mesh.vbuf && !mesh.ibuf)
+               phase += 3;
+       return phase>3;
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/mesh.h b/source/core/mesh.h
new file mode 100644 (file)
index 0000000..57c87b7
--- /dev/null
@@ -0,0 +1,113 @@
+#ifndef MSP_GL_MESH_H_
+#define MSP_GL_MESH_H_
+
+#include <msp/datafile/objectloader.h>
+#include "batch.h"
+#include "resource.h"
+#include "vertexarray.h"
+#include "vertexsetup.h"
+#include "windingtest.h"
+
+namespace Msp {
+namespace GL {
+
+class Buffer;
+class Renderer;
+
+/**
+Raw mesh data, consisting of a VertexArray and one or more Batches.  Though a
+Mesh can draw itself, it's usually used as part of Renderables rather than on
+its own.
+*/
+class Mesh: public Resource
+{
+       friend class MeshBuilder;
+
+public:
+       class Loader: public DataFile::ObjectLoader<Mesh>
+       {
+       private:
+               bool allow_gl_calls;
+
+       public:
+               Loader(Mesh &, bool = true);
+       private:
+               void vertices(const std::vector<VertexComponent> &);
+               void batch(PrimitiveType);
+               void winding(FaceWinding);
+       };
+
+private:
+       class AsyncLoader: public Resource::AsyncLoader
+       {
+       private:
+               Mesh &mesh;
+               IO::Seekable &io;
+               Bufferable::AsyncUpdater *vertex_updater;
+               Bufferable::AsyncUpdater *index_updater;
+               unsigned phase;
+
+       public:
+               AsyncLoader(Mesh &, IO::Seekable &);
+               ~AsyncLoader();
+
+               virtual bool needs_sync() const;
+               virtual bool process();
+       };
+
+       enum BufferMask
+       {
+               VERTEX_BUFFER = 1,
+               INDEX_BUFFER = 2
+       };
+
+       VertexArray vertices;
+       std::vector<Batch> batches;
+       Buffer *vbuf;
+       Buffer *ibuf;
+       VertexSetup vtx_setup;
+       mutable unsigned short dirty;
+       bool disallow_rendering;
+       const WindingTest *winding;
+
+public:
+       Mesh(ResourceManager * = 0);
+       Mesh(const VertexFormat &, ResourceManager * = 0);
+private:
+       void init(ResourceManager *);
+public:
+       ~Mesh();
+
+       void clear();
+private:
+       void check_buffers(unsigned);
+
+public:
+       const VertexArray &get_vertices() const { return vertices; }
+       const VertexSetup &get_vertex_setup() const { return vtx_setup; }
+       const Buffer *get_index_buffer() const { return ibuf; }
+       unsigned get_n_vertices() const;
+       float *modify_vertex(unsigned);
+
+       void add_batch(const Batch &b);
+       const std::vector<Batch> &get_batches() const { return batches; }
+
+       void set_winding(const WindingTest *);
+
+       void draw(Renderer &) const;
+       void draw_instanced(Renderer &, const VertexSetup &, unsigned) const;
+private:
+       void draw(Renderer &, const VertexSetup *, unsigned) const;
+       void resize_buffers() const;
+
+public:
+       virtual int get_load_priority() const { return 1; }
+       virtual Resource::AsyncLoader *load(IO::Seekable &, const Resources * = 0);
+       virtual UInt64 get_data_size() const;
+       virtual void unload();
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/misc.cpp b/source/core/misc.cpp
new file mode 100644 (file)
index 0000000..39810be
--- /dev/null
@@ -0,0 +1,47 @@
+#include <msp/gl/extensions/arb_shader_objects.h>
+#include "misc.h"
+
+namespace Msp {
+namespace GL {
+
+void enable(GLenum state)
+{
+       glEnable(state);
+}
+
+void disable(GLenum state)
+{
+       glDisable(state);
+}
+
+void set(GLenum state, bool value)
+{
+       if(value)
+               enable(state);
+       else
+               disable(state);
+}
+
+int get_i(GLenum state)
+{
+       int data;
+       glGetIntegerv(state, &data);
+       return data;
+}
+
+int get_shader_i(unsigned id, GLenum state)
+{
+       int data;
+       glGetShaderiv(id, state, &data);
+       return data;
+}
+
+int get_program_i(unsigned id, GLenum state)
+{
+       int data;
+       glGetProgramiv(id, state, &data);
+       return data;
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/misc.h b/source/core/misc.h
new file mode 100644 (file)
index 0000000..32adc9b
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef MSP_GL_MISC_H_
+#define MSP_GL_MISC_H_
+
+#include "gl.h"
+
+namespace Msp {
+namespace GL {
+
+void enable(GLenum);
+void disable(GLenum);
+void set(GLenum, bool);
+
+int get_i(GLenum);
+int get_shader_i(unsigned, GLenum);
+int get_program_i(unsigned, GLenum);
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/pixelformat.cpp b/source/core/pixelformat.cpp
new file mode 100644 (file)
index 0000000..87191c3
--- /dev/null
@@ -0,0 +1,389 @@
+#include <msp/gl/extensions/arb_texture_float.h>
+#include <msp/io/print.h>
+#include <msp/strings/format.h>
+#include "pixelformat.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+void operator>>(const LexicalConverter &conv, PixelComponents &comp)
+{
+       if(conv.get()=="STENCIL_INDEX")
+               comp = STENCIL_INDEX;
+       else if(conv.get()=="DEPTH_COMPONENT")
+               comp = DEPTH_COMPONENT;
+       else if(conv.get()=="RED")
+               comp = RED;
+       else if(conv.get()=="RG")
+               comp = RG;
+       else if(conv.get()=="RGB")
+               comp = RGB;
+       else if(conv.get()=="RGBA")
+               comp = RGBA;
+       else if(conv.get()=="BGR")
+               comp = BGR;
+       else if(conv.get()=="BGRA")
+               comp = BGRA;
+       else if(conv.get()=="LUMINANCE")
+               comp = LUMINANCE;
+       else if(conv.get()=="LUMINANCE_ALPHA")
+               comp = LUMINANCE_ALPHA;
+       else
+               throw lexical_error(format("conversion of '%s' to PixelFormat", conv.get()));
+}
+
+void operator>>(const LexicalConverter &conv, PixelFormat &fmt)
+{
+       if(conv.get()=="R8")
+               fmt = R8;
+       else if(conv.get()=="R16F")
+               fmt = R16F;
+       else if(conv.get()=="R32F")
+               fmt = R32F;
+       else if(conv.get()=="RG8")
+               fmt = RG8;
+       else if(conv.get()=="RG16F")
+               fmt = RG16F;
+       else if(conv.get()=="RG32F")
+               fmt = RG32F;
+       else if(conv.get()=="RGB8")
+               fmt = RGB8;
+       else if(conv.get()=="RGB16F")
+               fmt = RGB16F;
+       else if(conv.get()=="RGB32F")
+               fmt = RGB32F;
+       else if(conv.get()=="RGBA8")
+               fmt = RGBA8;
+       else if(conv.get()=="RGBA16F")
+               fmt = RGBA16F;
+       else if(conv.get()=="RGBA32F")
+               fmt = RGBA32F;
+       else if(conv.get()=="SRGB8")
+               fmt = SRGB8;
+       else if(conv.get()=="SRGB8_ALPHA8")
+               fmt = SRGB8_ALPHA8;
+       else if(conv.get()=="BGR8")
+               fmt = BGR8;
+       else if(conv.get()=="BGRA8")
+               fmt = BGRA8;
+       else if(conv.get()=="LUMINANCE8")
+               fmt = LUMINANCE8;
+       else if(conv.get()=="LUMINANCE8_ALPHA8")
+               fmt = LUMINANCE8_ALPHA8;
+       else if(conv.get()=="DEPTH_COMPONENT16")
+               fmt = DEPTH_COMPONENT16;
+       else if(conv.get()=="DEPTH_COMPONENT24")
+               fmt = DEPTH_COMPONENT24;
+       else if(conv.get()=="DEPTH_COMPONENT32")
+               fmt = DEPTH_COMPONENT32;
+       else if(conv.get()=="DEPTH_COMPONENT32F")
+               fmt = DEPTH_COMPONENT32F;
+       else
+       {
+               PixelComponents comp;
+               conv >> comp;
+               fmt = make_pixelformat(comp, (comp==DEPTH_COMPONENT ? FLOAT : UNSIGNED_BYTE));
+               IO::print(IO::cerr, "Warning: deprecated conversion of '%s' to PixelFormat\n", conv.get());
+       }
+}
+
+PixelComponents pixelformat_from_graphics(Graphics::PixelFormat pf)
+{
+       switch(pf)
+       {
+       case Graphics::LUMINANCE: return LUMINANCE;
+       case Graphics::LUMINANCE_ALPHA: return LUMINANCE_ALPHA;
+       case Graphics::RGB: return RGB;
+       case Graphics::RGBX:
+       case Graphics::RGBA: return RGBA;
+       case Graphics::BGR: return BGR;
+       case Graphics::BGRX:
+       case Graphics::BGRA: return BGRA;
+       default: throw invalid_argument("pixelformat_from_graphics");
+       }
+}
+
+PixelComponents storage_pixelformat_from_graphics(Graphics::PixelFormat pf)
+{
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+       switch(pf)
+       {
+       case Graphics::RGBX:
+       case Graphics::BGR:
+       case Graphics::BGRX: return RGB;
+       case Graphics::BGRA: return RGBA;
+       default: return pixelformat_from_graphics(pf);
+       }
+#pragma GCC diagnostic pop
+}
+
+PixelFormat pixelformat_from_image(const Graphics::Image &image)
+{
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+       PixelComponents comp = pixelformat_from_graphics(image.get_format());
+#pragma GCC diagnostic pop
+       return make_pixelformat(comp, UNSIGNED_BYTE);
+}
+
+PixelFormat make_pixelformat(PixelComponents comp, DataType type, bool srgb)
+{
+       if(srgb && type!=UNSIGNED_BYTE && comp!=RGB && comp!=RGBA && comp!=BGR && comp!=BGRA)
+               throw invalid_argument("make_pixelformat");
+
+       switch(comp)
+       {
+       case RED:
+               switch(type)
+               {
+               case UNSIGNED_BYTE: return R8;
+               case HALF_FLOAT: return R16F;
+               case FLOAT: return R32F;
+               default: throw invalid_argument("make_pixelformat");
+               }
+       case RG:
+               switch(type)
+               {
+               case UNSIGNED_BYTE: return RG8;
+               case HALF_FLOAT: return RG16F;
+               case FLOAT: return RG32F;
+               default: throw invalid_argument("make_pixelformat");
+               }
+       case RGB:
+               switch(type)
+               {
+               case UNSIGNED_BYTE: return (srgb ? SRGB8 : RGB8);
+               case HALF_FLOAT: return RGB16F;
+               case FLOAT: return RGB32F;
+               default: throw invalid_argument("make_pixelformat");
+               }
+       case RGBA:
+               switch(type)
+               {
+               case UNSIGNED_BYTE: return (srgb ? SRGB8_ALPHA8 : RGBA8);
+               case HALF_FLOAT: return RGBA16F;
+               case FLOAT: return RGBA32F;
+               default: throw invalid_argument("make_pixelformat");
+               }
+       case BGR:
+               if(type!=UNSIGNED_BYTE)
+                       throw invalid_argument("make_pixelformat");
+               return (srgb ? BGR8 : SBGR8);
+       case BGRA:
+               if(type!=UNSIGNED_BYTE)
+                       throw invalid_argument("make_pixelformat");
+               return (srgb ? BGRA8 : SBGR8_ALPHA8);
+       case LUMINANCE:
+               if(type!=UNSIGNED_BYTE)
+                       throw invalid_argument("make_pixelformat");
+               return LUMINANCE8;
+       case LUMINANCE_ALPHA:
+               if(type!=UNSIGNED_BYTE)
+                       throw invalid_argument("make_pixelformat");
+               return LUMINANCE8;
+       case STENCIL_INDEX:
+               if(type!=UNSIGNED_BYTE)
+                       throw invalid_argument("make_pixelformat");
+               return STENCIL_INDEX8;
+       case DEPTH_COMPONENT:
+               switch(type)
+               {
+               case UNSIGNED_SHORT: return DEPTH_COMPONENT16;
+               case UNSIGNED_INT: return DEPTH_COMPONENT32;
+               case FLOAT: return DEPTH_COMPONENT32F;
+               default: throw invalid_argument("make_pixelformat");
+               }
+       default:
+               throw invalid_argument("make_pixelformat");
+       }
+}
+
+PixelFormat get_base_pixelformat(PixelFormat pf)
+{
+       switch(pf)
+       {
+       case SRGB8: return RGB8;
+       case SRGB8_ALPHA8: return RGBA8;
+       default: return pf;
+       }
+}
+
+PixelComponents get_components(PixelFormat pf)
+{
+       switch(pf)
+       {
+       case R8:
+       case R16F:
+       case R32F: return RED;
+       case RG8:
+       case RG16F:
+       case RG32F: return RG;
+       case RGB8:
+       case RGB16F:
+       case RGB32F:
+       case SRGB8: return RGB;
+       case RGBA8:
+       case RGBA16F:
+       case RGBA32F:
+       case SRGB8_ALPHA8: return RGBA;
+       case BGR8:
+       case SBGR8: return BGR;
+       case BGRA8:
+       case SBGR8_ALPHA8: return BGRA;
+       case LUMINANCE8: return LUMINANCE;
+       case LUMINANCE8_ALPHA8: return LUMINANCE_ALPHA;
+       case STENCIL_INDEX8: return STENCIL_INDEX;
+       case DEPTH_COMPONENT16:
+       case DEPTH_COMPONENT24:
+       case DEPTH_COMPONENT32:
+       case DEPTH_COMPONENT32F: return DEPTH_COMPONENT;
+       default: throw invalid_argument("get_components");
+       }
+}
+
+PixelFormat get_default_sized_pixelformat(PixelComponents comp)
+{
+       DataType type = UNSIGNED_BYTE;
+       if(comp==DEPTH_COMPONENT)
+       {
+               if(get_gl_api()==OPENGL_ES2 && !ARB_depth_buffer_float)
+                       type = UNSIGNED_SHORT;
+               else
+                       type = FLOAT;
+       }
+       return make_pixelformat(comp, type);
+}
+
+PixelFormat get_srgb_pixelformat(PixelFormat pf)
+{
+       switch(pf)
+       {
+       case RGB8: return SRGB8;
+       case RGBA8: return SRGB8_ALPHA8;
+       default: return pf;
+       }
+}
+
+unsigned get_component_count(PixelComponents comp)
+{
+       switch(comp)
+       {
+       case RED:
+       case LUMINANCE:
+       case DEPTH_COMPONENT:
+       case STENCIL_INDEX:
+               return 1;
+       case RG:
+       case LUMINANCE_ALPHA:
+               return 2;
+       case RGB:
+       case BGR:
+               return 3;
+       case RGBA:
+       case BGRA:
+               return 4;
+       default:
+               throw invalid_argument("get_component_count");
+       }
+}
+
+DataType get_component_type(PixelFormat pf)
+{
+       switch(pf)
+       {
+       case R8:
+       case RG8:
+       case RGB8:
+       case RGBA8:
+       case SRGB8:
+       case SRGB8_ALPHA8:
+       case BGR8:
+       case BGRA8:
+       case SBGR8:
+       case SBGR8_ALPHA8:
+       case LUMINANCE8:
+       case LUMINANCE8_ALPHA8:
+               return UNSIGNED_BYTE;
+       case R16F:
+       case RG16F:
+       case RGB16F:
+       case RGBA16F:
+               return HALF_FLOAT;
+       case DEPTH_COMPONENT16:
+               return UNSIGNED_SHORT;
+       case R32F:
+       case RG32F:
+       case RGB32F:
+       case RGBA32F:
+       case DEPTH_COMPONENT32:
+               return UNSIGNED_INT;
+       case DEPTH_COMPONENT32F:
+               return FLOAT;
+       case DEPTH_COMPONENT24:
+               // There's no DataType value with 24-bit size
+       default:
+               throw invalid_argument("get_component_type");
+       }
+}
+
+unsigned get_pixel_size(PixelFormat pf)
+{
+       return get_component_count(pf)*get_type_size(get_component_type(pf));
+}
+
+void require_pixelformat(PixelFormat pf)
+{
+       /* TODO These checks are only accurate for textures.  On OpenGL ES some
+       formats are allowed for render buffers earlier than textures.  In particular
+       it's possible to create a 16-bit depth renderbuffer on OpenGL ES 2.0 but
+       depth textures are only available with 3.0 or the OES_depth_texture
+       extension.*/
+       switch(pf)
+       {
+       case RGB8:
+       case RGBA8:
+               { static Require _req(OES_required_internalformat); }
+               break;
+       case R8:
+       case RG8:
+               { static Require _req(ARB_texture_rg); }
+               break;
+       case R16F:
+       case R32F:
+       case RG16F:
+       case RG32F:
+               { static Require _req(ARB_texture_rg); }
+               { static Require _req(ARB_texture_float); }
+               break;
+       case RGB16F:
+       case RGB32F:
+       case RGBA16F:
+       case RGBA32F:
+               { static Require _req(ARB_texture_float); }
+               break;
+       case SRGB8:
+       case SRGB8_ALPHA8:
+               { static Require _req(EXT_texture_sRGB); }
+               break;
+       case DEPTH_COMPONENT16:
+       case DEPTH_COMPONENT24:
+       case DEPTH_COMPONENT32:
+               { static Require _req(ARB_depth_texture); }
+               { static Require _req(OES_required_internalformat); }
+               break;
+       case DEPTH_COMPONENT32F:
+               { static Require _req(ARB_depth_buffer_float); }
+               break;
+       case STENCIL_INDEX8:
+               { static Require _req(OES_texture_stencil8); }
+               break;
+       default:
+               throw invalid_argument("require_pixelformat");
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/pixelformat.h b/source/core/pixelformat.h
new file mode 100644 (file)
index 0000000..213328a
--- /dev/null
@@ -0,0 +1,99 @@
+#ifndef MSP_GL_PIXELFORMAT_H_
+#define MSP_GL_PIXELFORMAT_H_
+
+#include <msp/core/attributes.h>
+#include <msp/graphics/image.h>
+#include <msp/strings/lexicalcast.h>
+#include "gl.h"
+#include <msp/gl/extensions/arb_depth_buffer_float.h>
+#include <msp/gl/extensions/arb_depth_texture.h>
+#include <msp/gl/extensions/arb_texture_float.h>
+#include <msp/gl/extensions/arb_texture_rg.h>
+#include <msp/gl/extensions/ext_bgra.h>
+#include <msp/gl/extensions/ext_texture_srgb.h>
+#include <msp/gl/extensions/oes_required_internalformat.h>
+#include <msp/gl/extensions/oes_texture_stencil8.h>
+#include <msp/gl/extensions/msp_luminance_formats.h>
+#include "datatype.h"
+
+namespace Msp {
+namespace GL {
+
+enum PixelComponents
+{
+       RED             = GL_RED,
+       RG              = GL_RG,
+       RGB             = GL_RGB,
+       RGBA            = GL_RGBA,
+       BGR             = GL_BGR,
+       BGRA            = GL_BGRA,
+       LUMINANCE       = GL_LUMINANCE,
+       LUMINANCE_ALPHA = GL_LUMINANCE_ALPHA,
+       DEPTH_COMPONENT = GL_DEPTH_COMPONENT,
+       STENCIL_INDEX   = GL_STENCIL_INDEX
+};
+
+enum PixelFormat
+{
+       R8              = GL_R8,
+       R16F            = GL_R16F,
+       R32F            = GL_R32F,
+       RG8             = GL_RG8,
+       RG16F           = GL_RG16F,
+       RG32F           = GL_RG32F,
+       RGB8            = GL_RGB8,
+       RGB16F          = GL_RGB16F,
+       RGB32F          = GL_RGB32F,
+       RGBA8           = GL_RGBA8,
+       RGBA16F         = GL_RGBA16F,
+       RGBA32F         = GL_RGBA32F,
+       SRGB8           = GL_SRGB8,
+       SRGB8_ALPHA8    = GL_SRGB8_ALPHA8,
+       BGR8            = 200000,
+       BGRA8           = 200001,
+       SBGR8           = 200002,
+       SBGR8_ALPHA8    = 200003,
+       LUMINANCE8      = GL_LUMINANCE8,
+       LUMINANCE8_ALPHA8 = GL_LUMINANCE8_ALPHA8,
+       DEPTH_COMPONENT16 = GL_DEPTH_COMPONENT16,
+       DEPTH_COMPONENT24 = GL_DEPTH_COMPONENT24,
+       DEPTH_COMPONENT32 = GL_DEPTH_COMPONENT32,
+       DEPTH_COMPONENT32F = GL_DEPTH_COMPONENT32F,
+       STENCIL_INDEX8 = GL_STENCIL_INDEX8
+};
+
+void operator>>(const LexicalConverter &, PixelComponents &);
+void operator>>(const LexicalConverter &, PixelFormat &);
+
+DEPRECATED PixelComponents pixelformat_from_graphics(Graphics::PixelFormat);
+DEPRECATED PixelComponents storage_pixelformat_from_graphics(Graphics::PixelFormat, bool);
+PixelFormat pixelformat_from_image(const Graphics::Image &);
+
+PixelFormat make_pixelformat(PixelComponents, DataType, bool = false);
+DEPRECATED PixelFormat get_base_pixelformat(PixelFormat);
+PixelComponents get_components(PixelFormat);
+DEPRECATED PixelFormat get_default_sized_pixelformat(PixelComponents);
+DEPRECATED PixelFormat get_srgb_pixelformat(PixelFormat);
+
+unsigned get_component_count(PixelComponents);
+inline unsigned get_component_count(PixelFormat f)
+{ return get_component_count(get_components(f)); }
+
+DataType get_component_type(PixelFormat);
+inline unsigned get_component_size(PixelFormat f)
+{ return get_type_size(get_component_type(f)); }
+
+unsigned get_pixel_size(PixelFormat);
+
+void require_pixelformat(PixelFormat);
+
+DEPRECATED inline PixelFormat get_sized_pixelformat(PixelComponents c, unsigned s = 1)
+{ return make_pixelformat(c, (s==2 ? HALF_FLOAT : s==4 ? FLOAT : UNSIGNED_BYTE)); }
+
+DEPRECATED inline PixelComponents get_unsized_pixelformat(PixelFormat f)
+{ return get_components(f); }
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/pixelstore.cpp b/source/core/pixelstore.cpp
new file mode 100644 (file)
index 0000000..409f0f9
--- /dev/null
@@ -0,0 +1,86 @@
+#include <algorithm>
+#include <msp/gl/extensions/ext_texture3d.h>
+#include <msp/gl/extensions/ext_unpack_subimage.h>
+#include "gl.h"
+#include "pixelformat.h"
+#include "pixelstore.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+PixelStore::PixelStore():
+       row_length(0),
+       image_height(0),
+       skip_pixels(0),
+       skip_rows(0),
+       skip_images(0),
+       alignment(4)
+{ }
+
+PixelStore PixelStore::from_image(const Graphics::Image &img)
+{
+       PixelStore pstore;
+       unsigned stride = img.get_stride();
+       unsigned ncomp = get_component_count(pixelformat_from_image(img));
+       pstore.set_canvas_size(img.get_stride()/ncomp, 0);
+       pstore.set_alignment(min(stride&~(stride-1), 8U));
+       return pstore;
+}
+
+void PixelStore::update_parameter(int mask) const
+{
+       if(cur_obj!=this)
+               return;
+
+       if(mask&SIZE)
+       {
+               glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length);
+               if(EXT_texture3D)
+                       glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, image_height);
+       }
+       if(mask&ORIGIN)
+       {
+               glPixelStorei(GL_UNPACK_SKIP_PIXELS, skip_pixels);
+               glPixelStorei(GL_UNPACK_SKIP_ROWS, skip_rows);
+               if(EXT_texture3D)
+                       glPixelStorei(GL_UNPACK_SKIP_IMAGES, skip_images);
+       }
+       if(mask&ALIGNMENT)
+               glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
+}
+
+void PixelStore::set_canvas_size(unsigned w, unsigned h)
+{
+       static Require _req(EXT_unpack_subimage);
+       row_length = w;
+       image_height = h;
+       update_parameter(SIZE);
+}
+
+void PixelStore::set_origin(unsigned x, unsigned y, unsigned z)
+{
+       static Require _req(EXT_unpack_subimage);
+       if(z>0)
+               static Require _req3d(EXT_texture3D);
+       skip_pixels = x;
+       skip_rows = y;
+       skip_images = z;
+       update_parameter(ORIGIN);
+}
+
+void PixelStore::set_alignment(unsigned a)
+{
+       alignment = a;
+       update_parameter(ALIGNMENT);
+}
+
+void PixelStore::bind() const
+{
+       if(set_current(this))
+               update_parameter(-1);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/pixelstore.h b/source/core/pixelstore.h
new file mode 100644 (file)
index 0000000..80032c3
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef MSP_GL_PIXELSTORE_H_
+#define MSP_GL_PIXELSTORE_H_
+
+#include <msp/graphics/image.h>
+#include "bindable.h"
+
+namespace Msp {
+namespace GL {
+
+class PixelStore: public BindableWithDefault<PixelStore>
+{
+private:
+       enum ParameterMask
+       {
+               SIZE = 1,
+               ORIGIN = 2,
+               ALIGNMENT = 4
+       };
+
+       unsigned row_length;
+       unsigned image_height;
+       unsigned skip_pixels;
+       unsigned skip_rows;
+       unsigned skip_images;
+       unsigned alignment;
+
+public:
+       PixelStore();
+
+       static PixelStore from_image(const Graphics::Image &);
+
+private:
+       void update_parameter(int) const;
+
+public:
+       void set_canvas_size(unsigned, unsigned);
+       void set_origin(unsigned, unsigned, unsigned);
+       void set_alignment(unsigned);
+
+       void bind() const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/predicate.cpp b/source/core/predicate.cpp
new file mode 100644 (file)
index 0000000..a195373
--- /dev/null
@@ -0,0 +1,49 @@
+#include <msp/strings/format.h>
+#include "predicate.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+void operator>>(const LexicalConverter &conv, Predicate &pred)
+{
+       const string &str = conv.get();
+       if(str=="NEVER")
+               pred = NEVER;
+       else if(str=="ALWAYS")
+               pred = ALWAYS;
+       else if(str=="LESS")
+               pred = LESS;
+       else if(str=="LEQUAL")
+               pred = LEQUAL;
+       else if(str=="EQUAL")
+               pred = EQUAL;
+       else if(str=="GREATER")
+               pred = GREATER;
+       else if(str=="GEQUAL")
+               pred = GEQUAL;
+       else if(str=="NOTEQUAL")
+               pred = NOTEQUAL;
+       else
+               throw lexical_error(format("conversion of '%s' to Predicate", str));
+}
+
+void operator<<(LexicalConverter &conv, Predicate pred)
+{
+       switch(pred)
+       {
+       case NEVER: conv.result("NEVER"); break;
+       case ALWAYS: conv.result("ALWAYS"); break;
+       case LESS: conv.result("LESS"); break;
+       case LEQUAL: conv.result("LEQUAL"); break;
+       case EQUAL: conv.result("EQUAL"); break;
+       case GREATER: conv.result("GREATER"); break;
+       case GEQUAL: conv.result("GEQUAL"); break;
+       case NOTEQUAL: conv.result("NOTEQUAL"); break;
+       default: conv.result(format("Predicate(%#x)", static_cast<int>(pred))); break;
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/predicate.h b/source/core/predicate.h
new file mode 100644 (file)
index 0000000..13c22e8
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef MSP_GL_PREDICATE_H_
+#define MSP_GL_PREDICATE_H_
+
+#include <msp/strings/lexicalcast.h>
+#include "gl.h"
+
+namespace Msp {
+namespace GL {
+
+enum Predicate
+{
+       NEVER    = GL_NEVER,
+       ALWAYS   = GL_ALWAYS,
+       LESS     = GL_LESS,
+       LEQUAL   = GL_LEQUAL,
+       EQUAL    = GL_EQUAL,
+       GREATER  = GL_GREATER,
+       GEQUAL   = GL_GEQUAL,
+       NOTEQUAL = GL_NOTEQUAL
+};
+
+void operator>>(const LexicalConverter &, Predicate &);
+void operator<<(LexicalConverter &, Predicate);
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/primitivetype.cpp b/source/core/primitivetype.cpp
new file mode 100644 (file)
index 0000000..8137584
--- /dev/null
@@ -0,0 +1,28 @@
+#include <msp/strings/format.h>
+#include "primitivetype.h"
+
+namespace Msp {
+namespace GL {
+
+void operator>>(const LexicalConverter &conv, PrimitiveType &pt)
+{
+       if(conv.get()=="POINTS")
+               pt = POINTS;
+       else if(conv.get()=="LINES")
+               pt = LINES;
+       else if(conv.get()=="LINE_LOOP")
+               pt = LINE_LOOP;
+       else if(conv.get()=="LINE_STRIP")
+               pt = LINE_STRIP;
+       else if(conv.get()=="TRIANGLES")
+               pt = TRIANGLES;
+       else if(conv.get()=="TRIANGLE_STRIP")
+               pt = TRIANGLE_STRIP;
+       else if(conv.get()=="TRIANGLE_FAN")
+               pt = TRIANGLE_FAN;
+       else
+               throw lexical_error(format("conversion of '%s' to PrimitiveType", conv.get()));
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/primitivetype.h b/source/core/primitivetype.h
new file mode 100644 (file)
index 0000000..8e33b1d
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef MSP_GL_PRIMITIVETYPE_H_
+#define MSP_GL_PRIMITIVETYPE_H_
+
+#include <msp/strings/lexicalcast.h>
+#include "gl.h"
+
+namespace Msp {
+namespace GL {
+
+enum PrimitiveType
+{
+       POINTS         = GL_POINTS,
+       LINES          = GL_LINES,
+       LINE_STRIP     = GL_LINE_STRIP,
+       LINE_LOOP      = GL_LINE_LOOP,
+       TRIANGLES      = GL_TRIANGLES,
+       TRIANGLE_STRIP = GL_TRIANGLE_STRIP,
+       TRIANGLE_FAN   = GL_TRIANGLE_FAN
+};
+
+void operator>>(const LexicalConverter &, PrimitiveType &);
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/program.cpp b/source/core/program.cpp
new file mode 100644 (file)
index 0000000..00bca83
--- /dev/null
@@ -0,0 +1,421 @@
+#include <algorithm>
+#include <cstring>
+#include <set>
+#include <msp/core/hash.h>
+#include <msp/core/maputils.h>
+#include <msp/gl/extensions/arb_shader_objects.h>
+#include <msp/gl/extensions/arb_uniform_buffer_object.h>
+#include <msp/gl/extensions/arb_vertex_shader.h>
+#include <msp/gl/extensions/ext_gpu_shader4.h>
+#include <msp/gl/extensions/nv_non_square_matrices.h>
+#include <msp/io/print.h>
+#include <msp/strings/format.h>
+#include "buffer.h"
+#include "error.h"
+#include "misc.h"
+#include "program.h"
+#include "programcompiler.h"
+#include "resources.h"
+#include "shader.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Program::Program()
+{
+       init();
+}
+
+Program::Program(const std::string &source)
+{
+       init();
+
+       ProgramCompiler compiler;
+       if(source.find(';')==string::npos && source.size()>5 && !source.compare(source.size()-5, 5, ".glsl"))
+       {
+               if(RefPtr<IO::Seekable> io = Resources::get_builtins().open(source))
+                       compiler.compile(*io, source);
+               else
+                       throw IO::file_not_found(source);
+       }
+       else
+               compiler.compile(source);
+       compiler.add_shaders(*this);
+       link();
+}
+
+Program::Program(const string &vert, const string &frag)
+{
+       init();
+
+       attach_shader_owned(new VertexShader(vert));
+       attach_shader_owned(new FragmentShader(frag));
+       link();
+}
+
+void Program::init()
+{
+       static Require _req(ARB_shader_objects);
+
+       linked = false;
+       id = glCreateProgram();
+}
+
+Program::~Program()
+{
+       for(ShaderList::iterator i=owned_data.begin(); i!=owned_data.end(); ++i)
+               delete *i;
+       glDeleteProgram(id);
+}
+
+void Program::attach_shader(Shader &shader)
+{
+       if(find(shaders.begin(), shaders.end(), &shader)==shaders.end())
+       {
+               glAttachShader(id, shader.get_id());
+               shaders.push_back(&shader);
+       }
+}
+
+void Program::attach_shader_owned(Shader *shader)
+{
+       attach_shader(*shader);
+       if(find(owned_data.begin(), owned_data.end(), shader)==owned_data.end())
+               owned_data.push_back(shader);
+}
+
+void Program::detach_shader(Shader &shader)
+{
+       ShaderList::iterator i = remove(shaders.begin(), shaders.end(), &shader);
+       if(i!=shaders.end())
+       {
+               shaders.erase(i, shaders.end());
+               glDetachShader(id, shader.get_id());
+       }
+}
+
+void Program::bind_attribute(unsigned index, const string &name)
+{
+       static Require _req(ARB_vertex_shader);
+       glBindAttribLocation(id, index, name.c_str());
+}
+
+void Program::bind_attribute(VertexComponent comp, const string &name)
+{
+       bind_attribute(get_component_type(comp), name);
+}
+
+void Program::bind_fragment_data(unsigned index, const string &name)
+{
+       static Require _req(EXT_gpu_shader4);
+       glBindFragDataLocation(id, index, name.c_str());
+}
+
+void Program::link()
+{
+       for(ShaderList::iterator i=shaders.begin(); i!=shaders.end(); ++i)
+               if(!(*i)->is_compiled())
+                       (*i)->compile();
+
+       uniforms.clear();
+
+       glLinkProgram(id);
+       linked = get_program_i(id, GL_LINK_STATUS);
+       if(!linked)
+               throw compile_error(get_info_log());
+
+#ifdef DEBUG
+       std::string info_log = get_info_log();
+       if(!info_log.empty())
+               IO::print("Program link info log:\n%s", info_log);
+#endif
+
+       query_uniforms();
+       query_attributes();
+
+       for(UniformMap::const_iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
+               require_type(i->second.type);
+       for(AttributeMap::const_iterator i=attributes.begin(); i!=attributes.end(); ++i)
+               require_type(i->second.type);
+}
+
+void Program::require_type(GLenum t)
+{
+       switch(t)
+       {
+       case GL_FLOAT_MAT2x3:
+       case GL_FLOAT_MAT2x4:
+       case GL_FLOAT_MAT3x2:
+       case GL_FLOAT_MAT3x4:
+       case GL_FLOAT_MAT4x2:
+       case GL_FLOAT_MAT4x3:
+               { static Require _req(NV_non_square_matrices); }
+               break;
+       }
+}
+
+void Program::query_uniforms()
+{
+       unsigned count = get_program_i(id, GL_ACTIVE_UNIFORMS);
+       vector<UniformInfo *> uniforms_by_index(count);
+       for(unsigned i=0; i<count; ++i)
+       {
+               char name[128];
+               int len = 0;
+               int size;
+               GLenum type;
+               glGetActiveUniform(id, i, sizeof(name), &len, &size, &type, name);
+               if(len && strncmp(name, "gl_", 3))
+               {
+                       /* Some implementations report the first element of a uniform array,
+                       others report just the name of the array itself. */
+                       if(len>3 && !strcmp(name+len-3, "[0]"))
+                               name[len-3] = 0;
+
+                       UniformInfo &info = uniforms[name];
+                       info.block = 0;
+                       info.name = name;
+                       info.size = size;
+                       info.array_stride = 0;
+                       info.matrix_stride = 0;
+                       info.type = type;
+                       uniforms_by_index[i] = &info;
+               }
+       }
+
+       if(ARB_uniform_buffer_object)
+               query_uniform_blocks(uniforms_by_index);
+
+       UniformBlockInfo &default_block = uniform_blocks[string()];
+       default_block.data_size = 0;
+       default_block.bind_point = -1;
+
+       for(UniformMap::iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
+               if(!i->second.block)
+               {
+                       i->second.location = glGetUniformLocation(id, i->second.name.c_str());
+                       i->second.block = &default_block;
+                       default_block.uniforms.push_back(&i->second);
+               }
+
+       default_block.layout_hash = compute_layout_hash(default_block.uniforms);
+
+       string layout_descriptor;
+       for(UniformBlockMap::const_iterator i=uniform_blocks.begin(); i!=uniform_blocks.end(); ++i)
+               layout_descriptor += format("%d:%x\n", i->second.bind_point, i->second.layout_hash);
+       uniform_layout_hash = hash32(layout_descriptor);
+}
+
+void Program::query_uniform_blocks(const vector<UniformInfo *> &uniforms_by_index)
+{
+       uniform_blocks.clear();
+
+       std::set<unsigned> used_bind_points;
+       unsigned count = get_program_i(id, GL_ACTIVE_UNIFORM_BLOCKS);
+       for(unsigned i=0; i<count; ++i)
+       {
+               char name[128];
+               int len;
+               glGetActiveUniformBlockName(id, i, sizeof(name), &len, name);
+               UniformBlockInfo &info = uniform_blocks[name];
+               info.name = name;
+
+               int value;
+               glGetActiveUniformBlockiv(id, i, GL_UNIFORM_BLOCK_DATA_SIZE, &value);
+               info.data_size = value;
+
+               glGetActiveUniformBlockiv(id, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &value);
+               vector<int> indices(value);
+               glGetActiveUniformBlockiv(id, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, &indices[0]);
+               for(vector<int>::iterator j=indices.begin(); j!=indices.end(); ++j)
+               {
+                       if(!uniforms_by_index[*j])
+                               throw logic_error("Program::link");
+                       info.uniforms.push_back(uniforms_by_index[*j]);
+                       uniforms_by_index[*j]->block = &info;
+               }
+
+               vector<unsigned> indices2(indices.begin(), indices.end());
+               vector<int> values(indices.size());
+               glGetActiveUniformsiv(id, indices.size(), &indices2[0], GL_UNIFORM_OFFSET, &values[0]);
+               for(unsigned j=0; j<indices.size(); ++j)
+                       uniforms_by_index[indices[j]]->location = values[j];
+
+               indices2.clear();
+               for(vector<int>::iterator j=indices.begin(); j!=indices.end(); ++j)
+                       if(uniforms_by_index[*j]->size>1)
+                               indices2.push_back(*j);
+               if(!indices2.empty())
+               {
+                       glGetActiveUniformsiv(id, indices2.size(), &indices2[0], GL_UNIFORM_ARRAY_STRIDE, &values[0]);
+                       for(unsigned j=0; j<indices2.size(); ++j)
+                               uniforms_by_index[indices2[j]]->array_stride = values[j];
+               }
+
+               indices2.clear();
+               for(vector<int>::iterator j=indices.begin(); j!=indices.end(); ++j)
+               {
+                       GLenum t = uniforms_by_index[*j]->type;
+                       if(t==GL_FLOAT_MAT4 || t==GL_FLOAT_MAT3 || t==GL_FLOAT_MAT2 ||
+                               t==GL_FLOAT_MAT2x3 || t==GL_FLOAT_MAT2x4 || t==GL_FLOAT_MAT3x2 ||
+                               t==GL_FLOAT_MAT3x4 || t==GL_FLOAT_MAT4x2 || t==GL_FLOAT_MAT4x3)
+                               indices2.push_back(*j);
+               }
+               if(!indices2.empty())
+               {
+                       glGetActiveUniformsiv(id, indices2.size(), &indices2[0], GL_UNIFORM_MATRIX_STRIDE, &values[0]);
+                       for(unsigned j=0; j<indices2.size(); ++j)
+                               uniforms_by_index[indices2[j]]->matrix_stride = values[j];
+               }
+
+               sort(info.uniforms.begin(), info.uniforms.end(), uniform_location_compare);
+               info.layout_hash = compute_layout_hash(info.uniforms);
+               unsigned n_bindings = BufferRange::get_n_uniform_buffer_bindings();
+               info.bind_point = info.layout_hash%n_bindings;
+               while(used_bind_points.count(info.bind_point))
+                       info.bind_point = (info.bind_point+1)%n_bindings;
+               glUniformBlockBinding(id, i, info.bind_point);
+               used_bind_points.insert(info.bind_point);
+       }
+}
+
+void Program::query_attributes()
+{
+       unsigned count = get_program_i(id, GL_ACTIVE_ATTRIBUTES);
+       for(unsigned i=0; i<count; ++i)
+       {
+               char name[128];
+               int len = 0;
+               int size;
+               GLenum type;
+               glGetActiveAttrib(id, i, sizeof(name), &len, &size, &type, name);
+               if(len && strncmp(name, "gl_", 3))
+               {
+                       if(len>3 && !strcmp(name+len-3, "[0]"))
+                               name[len-3] = 0;
+
+                       AttributeInfo &info = attributes[name];
+                       info.name = name;
+                       info.location = glGetAttribLocation(id, name);
+                       info.size = size;
+                       info.type = type;
+               }
+       }
+}
+
+Program::LayoutHash Program::compute_layout_hash(const vector<const UniformInfo *> &uniforms)
+{
+       string layout_descriptor;
+       for(vector<const UniformInfo *>::const_iterator i = uniforms.begin(); i!=uniforms.end(); ++i)
+               layout_descriptor += format("%d:%s:%x:%d\n", (*i)->location, (*i)->name, (*i)->type, (*i)->size);
+       return hash32(layout_descriptor);
+}
+
+bool Program::uniform_location_compare(const UniformInfo *uni1, const UniformInfo *uni2)
+{
+       return uni1->location<uni2->location;
+}
+
+string Program::get_info_log() const
+{
+       GLsizei len = get_program_i(id, GL_INFO_LOG_LENGTH);
+       string log(len+1, 0);
+       glGetProgramInfoLog(id, len+1, &len, &log[0]);
+       log.erase(len);
+       return log;
+}
+
+const Program::UniformBlockInfo &Program::get_uniform_block_info(const string &name) const
+{
+       return get_item(uniform_blocks, name);
+}
+
+const Program::UniformInfo &Program::get_uniform_info(const string &name) const
+{
+       return get_item(uniforms, name);
+}
+
+int Program::get_uniform_location(const string &n) const
+{
+       if(n[n.size()-1]==']')
+               throw invalid_argument("Program::get_uniform_location");
+
+       UniformMap::const_iterator i = uniforms.find(n);
+       if(i==uniforms.end())
+               return -1;
+
+       return i->second.block->bind_point<0 ? i->second.location : -1;
+}
+
+const Program::AttributeInfo &Program::get_attribute_info(const string &name) const
+{
+       return get_item(attributes, name);
+}
+
+int Program::get_attribute_location(const string &n) const
+{
+       if(n[n.size()-1]==']')
+               throw invalid_argument("Program::get_attribute_location");
+
+       AttributeMap::const_iterator i = attributes.find(n);
+       return i!=attributes.end() ? i->second.location : -1;
+}
+
+void Program::bind() const
+{
+       if(!linked)
+               throw invalid_operation("Program::bind");
+
+       if(!set_current(this))
+               return;
+
+       glUseProgram(id);
+}
+
+void Program::unbind()
+{
+       if(!set_current(0))
+               return;
+
+       glUseProgram(0);
+}
+
+
+Program::Loader::Loader(Program &p):
+       DataFile::ObjectLoader<Program>(p)
+{
+       add("attribute",       &Loader::attribute);
+       add("fragment_shader", &Loader::fragment_shader);
+       add("geometry_shader", &Loader::geometry_shader);
+       add("vertex_shader",   &Loader::vertex_shader);
+}
+
+void Program::Loader::finish()
+{
+       obj.link();
+}
+
+void Program::Loader::attribute(unsigned i, const string &n)
+{
+       obj.bind_attribute(i, n);
+}
+
+void Program::Loader::fragment_shader(const string &src)
+{
+       obj.attach_shader_owned(new FragmentShader(src));
+}
+
+void Program::Loader::geometry_shader(const string &src)
+{
+       obj.attach_shader_owned(new GeometryShader(src));
+}
+
+void Program::Loader::vertex_shader(const string &src)
+{
+       obj.attach_shader_owned(new VertexShader(src));
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/program.h b/source/core/program.h
new file mode 100644 (file)
index 0000000..fc60db4
--- /dev/null
@@ -0,0 +1,136 @@
+#ifndef MSP_GL_PROGRAM_H_
+#define MSP_GL_PROGRAM_H_
+
+#include <string>
+#include <vector>
+#include <msp/datafile/objectloader.h>
+#include "bindable.h"
+#include "gl.h"
+#include "vertexformat.h"
+
+namespace Msp {
+namespace GL {
+
+class Shader;
+
+/**
+A complete shader program.  Programs can be assembled of individual Shaders or
+generated with a set of standard features.
+*/
+class Program: public Bindable<Program>
+{
+public:
+       class Loader: public DataFile::ObjectLoader<Program>
+       {
+       public:
+               Loader(Program &);
+
+       private:
+               virtual void finish();
+
+               void attribute(unsigned, const std::string &);
+               void fragment_shader(const std::string &);
+               void geometry_shader(const std::string &);
+               void vertex_shader(const std::string &);
+       };
+
+       typedef unsigned LayoutHash;
+       struct UniformBlockInfo;
+
+       struct UniformInfo
+       {
+               std::string name;
+               const UniformBlockInfo *block;
+               unsigned location;
+               unsigned size;
+               unsigned array_stride;
+               unsigned matrix_stride;
+               GLenum type;
+       };
+
+       struct UniformBlockInfo
+       {
+               std::string name;
+               unsigned data_size;
+               int bind_point;
+               std::vector<const UniformInfo *> uniforms;
+               LayoutHash layout_hash;
+       };
+
+       struct AttributeInfo
+       {
+               std::string name;
+               unsigned location;
+               unsigned size;
+               GLenum type;
+       };
+
+       typedef std::vector<Shader *> ShaderList;
+       typedef std::map<std::string, UniformInfo> UniformMap;
+       typedef std::map<std::string, UniformBlockInfo> UniformBlockMap;
+       typedef std::map<std::string, AttributeInfo> AttributeMap;
+
+private:
+       unsigned id;
+       ShaderList shaders;
+       ShaderList owned_data;
+       bool linked;
+       UniformBlockMap uniform_blocks;
+       UniformMap uniforms;
+       LayoutHash uniform_layout_hash;
+       AttributeMap attributes;
+
+public:
+       /// Constructs an empty Program with no Shaders attached.
+       Program();
+
+       /// Constructs a Program from unified source code using ProgramCompiler.
+       Program(const std::string &);
+
+       /// Constructs a Program from vertex and fragment shader source code.
+       Program(const std::string &, const std::string &);
+
+private:
+       void init();
+public:
+       virtual ~Program();
+
+       void attach_shader(Shader &shader);
+       void attach_shader_owned(Shader *shader);
+       void detach_shader(Shader &shader);
+       const ShaderList &get_attached_shaders() const { return shaders; }
+
+       void bind_attribute(unsigned, const std::string &);
+       void bind_attribute(VertexComponent, const std::string &);
+       void bind_fragment_data(unsigned, const std::string &);
+
+       void link();
+private:
+       static void require_type(GLenum);
+       void query_uniforms();
+       void query_uniform_blocks(const std::vector<UniformInfo *> &);
+       void query_attributes();
+       static LayoutHash compute_layout_hash(const std::vector<const UniformInfo *> &);
+       static bool uniform_location_compare(const UniformInfo *, const UniformInfo *);
+public:
+       bool is_linked() const { return linked; }
+       std::string get_info_log() const;
+
+       LayoutHash get_uniform_layout_hash() const { return uniform_layout_hash; }
+       const UniformBlockMap &get_uniform_blocks() const { return uniform_blocks; }
+       const UniformBlockInfo &get_uniform_block_info(const std::string &) const;
+       const UniformMap &get_uniforms() const { return uniforms; }
+       const UniformInfo &get_uniform_info(const std::string &) const;
+       int get_uniform_location(const std::string &) const;
+       const AttributeMap &get_attributes() const { return attributes; }
+       const AttributeInfo &get_attribute_info(const std::string &) const;
+       int get_attribute_location(const std::string &) const;
+
+       void bind() const;
+       static void unbind();
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/renderbuffer.cpp b/source/core/renderbuffer.cpp
new file mode 100644 (file)
index 0000000..e86a97a
--- /dev/null
@@ -0,0 +1,82 @@
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/ext_framebuffer_multisample.h>
+#include <msp/gl/extensions/ext_framebuffer_object.h>
+#include "misc.h"
+#include "renderbuffer.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Renderbuffer::Renderbuffer()
+{
+       static Require _req(EXT_framebuffer_object);
+
+       if(ARB_direct_state_access)
+               glCreateRenderbuffers(1, &id);
+       else
+               glGenRenderbuffers(1, &id);
+}
+
+Renderbuffer::~Renderbuffer()
+{
+       glDeleteRenderbuffers(1, &id);
+}
+
+void Renderbuffer::storage(PixelFormat fmt, unsigned wd, unsigned ht)
+{
+       require_pixelformat(fmt);
+       width = wd;
+       height = ht;
+       if(ARB_direct_state_access)
+               glNamedRenderbufferStorage(id, fmt, width, height);
+       else
+       {
+               BindRestore _bind(this);
+               glRenderbufferStorage(GL_RENDERBUFFER, fmt, width, height);
+       }
+}
+
+unsigned Renderbuffer::get_max_samples()
+{
+       static unsigned max_samples = (EXT_framebuffer_multisample ? get_i(GL_MAX_SAMPLES) : 0);
+       return max_samples;
+}
+
+void Renderbuffer::storage_multisample(unsigned samples, PixelFormat fmt, unsigned wd, unsigned ht)
+{
+       if(!samples)
+               return storage(fmt, wd, ht);
+
+       static Require _req(EXT_framebuffer_multisample);
+       if(samples>get_max_samples())
+               throw out_of_range("Renderbuffer::storage_multisample");
+
+       require_pixelformat(fmt);
+
+       width = wd;
+       height = ht;
+       if(ARB_direct_state_access)
+               glNamedRenderbufferStorageMultisample(id, samples, fmt, width, height);
+       else
+       {
+               BindRestore _bind(this);
+               glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, fmt, width, height);
+       }
+}
+
+void Renderbuffer::bind() const
+{
+       if(set_current(this))
+               glBindRenderbuffer(GL_RENDERBUFFER, id);
+}
+
+void Renderbuffer::unbind()
+{
+       if(set_current(0))
+               glBindRenderbuffer(GL_RENDERBUFFER, 0);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/renderbuffer.h b/source/core/renderbuffer.h
new file mode 100644 (file)
index 0000000..afc6ed2
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef MSP_GL_RENDERBUFFER_H_
+#define MSP_GL_RENDERBUFFER_H_
+
+#include "bindable.h"
+#include "pixelformat.h"
+
+namespace Msp {
+namespace GL {
+
+/**
+A Renderbuffer contains a single renderable image.  It can be attached to a
+Framebuffer to provide a logical buffer that is required to render the scene
+correctly but that is not needed as a texture later.  Renderbuffers also
+provide a capability for multisampling, which is not available in textures.
+
+Requires the GL_EXT_framebuffer_object extension.  Multisample renderbuffers
+additionally require the GL_EXT_framebuffer_multisample extension.
+*/
+class Renderbuffer: public Bindable<Renderbuffer>
+{
+private:
+       unsigned id;
+       unsigned width;
+       unsigned height;
+
+public:
+       Renderbuffer();
+       ~Renderbuffer();
+
+       unsigned get_id() const { return id; }
+       unsigned get_width() const { return width; }
+       unsigned get_height() const { return height; }
+
+       /** Allocates storage for the renderbuffer. */
+       void storage(PixelFormat fmt, unsigned wd, unsigned ht);
+
+       /** Returns the maximum supported sample count for multisampling.  If
+       multisampling is not supported, returns 0. */
+       static unsigned get_max_samples();
+
+       /** Allocates multisample storage for the renderbuffer.  All attachments in
+       a framebuffer must have the same number of samples.  To transfer the
+       contents to a texture for furter processing, use the framebuffer blit
+       functions.*/
+       void storage_multisample(unsigned samples, PixelFormat fmt, unsigned wd, unsigned ht);
+
+       void bind() const;
+
+       static void unbind();
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/sampler.cpp b/source/core/sampler.cpp
new file mode 100644 (file)
index 0000000..5375d17
--- /dev/null
@@ -0,0 +1,332 @@
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_sampler_objects.h>
+#include <msp/gl/extensions/arb_shadow.h>
+#include <msp/gl/extensions/ext_texture_filter_anisotropic.h>
+#include <msp/gl/extensions/ext_texture3d.h>
+#include <msp/strings/format.h>
+#include "error.h"
+#include "sampler.h"
+#include "texture.h"
+#include "texunit.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Sampler::Sampler():
+       owner(0)
+{
+       init();
+
+       Require _req(ARB_sampler_objects);
+       if(ARB_direct_state_access)
+               glCreateSamplers(1, &id);
+       else
+               glGenSamplers(1, &id);
+}
+
+Sampler::Sampler(const Texture &tex):
+       id(0),
+       owner(&tex)
+{
+       if(this!=&tex.get_default_sampler())
+               throw invalid_argument("Sampler::Sampler");
+
+       init();
+}
+
+void Sampler::init()
+{
+       min_filter = NEAREST_MIPMAP_LINEAR;
+       mag_filter = LINEAR;
+       max_anisotropy = 1.0f;
+       wrap_s = REPEAT;
+       wrap_t = REPEAT;
+       wrap_r = REPEAT;
+       compare = false;
+       cmp_func = LEQUAL;
+       dirty_params = 0;
+}
+
+void Sampler::update_parameter(int mask) const
+{
+       if(owner)
+       {
+               if(!owner->get_id())
+               {
+                       dirty_params |= mask;
+                       return;
+               }
+
+               if(!ARB_direct_state_access && TexUnit::current().get_texture()!=owner)
+               {
+                       TexUnit *unit = TexUnit::find_unit(owner);
+                       if(!unit)
+                       {
+                               dirty_params |= mask;
+                               return;
+                       }
+
+                       unit->bind();
+               }
+       }
+
+       if(mask&MIN_FILTER)
+               set_parameter_i(GL_TEXTURE_MIN_FILTER, min_filter);
+       if(mask&MAG_FILTER)
+               set_parameter_i(GL_TEXTURE_MAG_FILTER, mag_filter);
+       if(mask&MAX_ANISOTROPY)
+               set_parameter_f(GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy);
+       if(mask&WRAP_S)
+               set_parameter_i(GL_TEXTURE_WRAP_S, wrap_s);
+       if(mask&WRAP_T)
+               set_parameter_i(GL_TEXTURE_WRAP_T, wrap_t);
+       if(mask&WRAP_R)
+               set_parameter_i(GL_TEXTURE_WRAP_R, wrap_r);
+       if(mask&COMPARE)
+       {
+               set_parameter_i(GL_TEXTURE_COMPARE_MODE, (compare ? GL_COMPARE_R_TO_TEXTURE : GL_NONE));
+               if(compare)
+                       set_parameter_i(GL_TEXTURE_COMPARE_FUNC, cmp_func);
+       }
+}
+
+void Sampler::set_parameter_i(unsigned param, int value) const
+{
+       if(id)
+               glSamplerParameteri(id, param, value);
+       else if(ARB_direct_state_access)
+               glTextureParameteri(owner->get_id(), param, value);
+       else
+               glTexParameteri(owner->get_target(), param, value);
+}
+
+void Sampler::set_parameter_f(unsigned param, float value) const
+{
+       if(id)
+               glSamplerParameterf(id, param, value);
+       else if(ARB_direct_state_access)
+               glTextureParameterf(owner->get_id(), param, value);
+       else
+               glTexParameterf(owner->get_target(), param, value);
+}
+
+void Sampler::set_min_filter(TextureFilter f)
+{
+       min_filter = f;
+       update_parameter(MIN_FILTER);
+}
+
+void Sampler::set_mag_filter(TextureFilter f)
+{
+       mag_filter = f;
+       update_parameter(MAG_FILTER);
+}
+
+void Sampler::set_filter(TextureFilter f)
+{
+       set_min_filter(f);
+       set_mag_filter(f==NEAREST ? NEAREST : LINEAR);
+}
+
+void Sampler::set_max_anisotropy(float a)
+{
+       if(a<1.0f)
+               throw invalid_argument("Sampler::set_max_anisotropy");
+       else if(a>1.0f)
+               static Require _req(EXT_texture_filter_anisotropic);
+       max_anisotropy = a;
+       if(EXT_texture_filter_anisotropic)
+               update_parameter(MAX_ANISOTROPY);
+}
+
+void Sampler::set_wrap(TextureWrap w)
+{
+       set_wrap_s(w);
+       set_wrap_t(w);
+       if(EXT_texture3D)
+               set_wrap_r(w);
+}
+
+void Sampler::set_wrap_s(TextureWrap w)
+{
+       wrap_s = w;
+       update_parameter(WRAP_S);
+}
+
+void Sampler::set_wrap_t(TextureWrap w)
+{
+       wrap_t = w;
+       update_parameter(WRAP_T);
+}
+
+void Sampler::set_wrap_r(TextureWrap w)
+{
+       static Require _req(EXT_texture3D);
+       wrap_r = w;
+       update_parameter(WRAP_R);
+}
+
+void Sampler::disable_compare()
+{
+       compare = false;
+       update_parameter(COMPARE);
+}
+
+void Sampler::set_compare(Predicate f)
+{
+       static Require _req(ARB_shadow);
+       compare = true;
+       cmp_func = f;
+       update_parameter(COMPARE);
+}
+
+void Sampler::bind_to(unsigned i) const
+{
+       TexUnit &unit = TexUnit::get_unit(i);
+       if(owner && owner!=unit.get_texture())
+               throw invalid_operation("Sampler::bind_to");
+
+       const Sampler *cur = unit.get_sampler();
+       if(unit.set_sampler(this))
+       {
+               if(!owner || (cur && cur->id))
+                       glBindSampler(i, id);
+
+               if(dirty_params)
+               {
+                       update_parameter(dirty_params);
+                       dirty_params = 0;
+               }
+       }
+}
+
+const Sampler *Sampler::current(unsigned i)
+{
+       return TexUnit::get_unit(i).get_sampler();
+}
+
+void Sampler::unbind_from(unsigned i)
+{
+       TexUnit &unit = TexUnit::get_unit(i);
+       const Sampler *cur = unit.get_sampler();
+       if(unit.set_sampler(0) && cur->id)
+               glBindSampler(i, 0);
+}
+
+void Sampler::unload()
+{
+       if(owner && !owner->get_id())
+       {
+               if(min_filter!=NEAREST_MIPMAP_LINEAR)
+                       dirty_params |= MIN_FILTER;
+               if(mag_filter!=LINEAR)
+                       dirty_params |= MAG_FILTER;
+               if(max_anisotropy!=1.0f)
+                       dirty_params |= MAX_ANISOTROPY;
+               if(wrap_s!=REPEAT)
+                       dirty_params |= WRAP_S;
+               if(wrap_t!=REPEAT)
+                       dirty_params |= WRAP_T;
+               if(wrap_r!=REPEAT)
+                       dirty_params |= WRAP_R;
+               if(compare || cmp_func!=LEQUAL)
+                       dirty_params |= COMPARE;
+       }
+}
+
+
+Sampler::Loader::Loader(Sampler &s):
+       DataFile::ObjectLoader<Sampler>(s)
+{
+       add("filter", &Loader::filter);
+       add("mag_filter", &Loader::mag_filter);
+       add("max_anisotropy", &Loader::max_anisotropy);
+       add("min_filter", &Loader::min_filter);
+       add("wrap", &Loader::wrap);
+       add("wrap_r", &Loader::wrap_r);
+       add("wrap_s", &Loader::wrap_s);
+       add("wrap_t", &Loader::wrap_t);
+}
+
+void Sampler::Loader::filter(TextureFilter f)
+{
+       obj.set_filter(f);
+}
+
+void Sampler::Loader::mag_filter(TextureFilter f)
+{
+       obj.set_mag_filter(f);
+}
+
+void Sampler::Loader::max_anisotropy(float a)
+{
+       obj.set_max_anisotropy(a);
+}
+
+void Sampler::Loader::min_filter(TextureFilter f)
+{
+       obj.set_min_filter(f);
+}
+
+void Sampler::Loader::wrap(TextureWrap w)
+{
+       obj.set_wrap(w);
+}
+
+void Sampler::Loader::wrap_r(TextureWrap w)
+{
+       obj.set_wrap_r(w);
+}
+
+void Sampler::Loader::wrap_s(TextureWrap w)
+{
+       obj.set_wrap_s(w);
+}
+
+void Sampler::Loader::wrap_t(TextureWrap w)
+{
+       obj.set_wrap_t(w);
+}
+
+
+bool is_mipmapped(TextureFilter filter)
+{
+       return (filter==NEAREST_MIPMAP_NEAREST || filter==NEAREST_MIPMAP_LINEAR ||
+               filter==LINEAR_MIPMAP_NEAREST || filter==LINEAR_MIPMAP_LINEAR);
+}
+
+void operator>>(const LexicalConverter &c, TextureFilter &tf)
+{
+       if(c.get()=="NEAREST")
+               tf = NEAREST;
+       else if(c.get()=="LINEAR")
+               tf = LINEAR;
+       else if(c.get()=="NEAREST_MIPMAP_NEAREST")
+               tf = NEAREST_MIPMAP_NEAREST;
+       else if(c.get()=="NEAREST_MIPMAP_LINEAR")
+               tf = NEAREST_MIPMAP_LINEAR;
+       else if(c.get()=="LINEAR_MIPMAP_NEAREST")
+               tf = LINEAR_MIPMAP_NEAREST;
+       else if(c.get()=="LINEAR_MIPMAP_LINEAR")
+               tf = LINEAR_MIPMAP_LINEAR;
+       else
+               throw lexical_error(format("conversion of '%s' to TextureFilter", c.get()));
+}
+
+void operator>>(const LexicalConverter &c, TextureWrap &tw)
+{
+       if(c.get()=="REPEAT")
+               tw = REPEAT;
+       else if(c.get()=="CLAMP_TO_EDGE")
+               tw = CLAMP_TO_EDGE;
+       else if(c.get()=="MIRRORED_REPEAT")
+               tw = MIRRORED_REPEAT;
+       else
+               throw lexical_error(format("conversion of '%s' to TextureWrap", c.get()));
+}
+
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/sampler.h b/source/core/sampler.h
new file mode 100644 (file)
index 0000000..e1e9bec
--- /dev/null
@@ -0,0 +1,168 @@
+#ifndef MSP_GL_SAMPLER_H_
+#define MSP_GL_SAMPLER_H_
+
+#include <msp/datafile/objectloader.h>
+#include "gl.h"
+#include "predicate.h"
+
+namespace Msp {
+namespace GL {
+
+enum TextureFilter
+{
+       /// No filtering
+       NEAREST = GL_NEAREST,
+
+       /// Bilinear filtering
+       LINEAR = GL_LINEAR,
+
+       /// Mipmapping without filtering
+       NEAREST_MIPMAP_NEAREST = GL_NEAREST_MIPMAP_NEAREST,
+
+       /// Linear filtering between two mipmap levels
+       NEAREST_MIPMAP_LINEAR = GL_NEAREST_MIPMAP_LINEAR,
+
+       /// Bilinear filtering on the closest mipmap level
+       LINEAR_MIPMAP_NEAREST = GL_LINEAR_MIPMAP_NEAREST,
+
+       /// Trilinear filtering between two mipmap levels
+       LINEAR_MIPMAP_LINEAR = GL_LINEAR_MIPMAP_LINEAR
+};
+
+
+enum TextureWrap
+{
+       /// Tile the texture infinitely
+       REPEAT = GL_REPEAT,
+
+       /// Extend the texels at the edge of the texture to infinity
+       CLAMP_TO_EDGE = GL_CLAMP_TO_EDGE,
+
+       /// Tile the texture, with every other repetition mirrored
+       MIRRORED_REPEAT = GL_MIRRORED_REPEAT
+};
+
+class Texture;
+
+
+/**
+Samplers are used to access texture data in shaders.  To use a sampler with a
+texture, bind it to the same texture unit.  Each texture has a default sampler
+which is used if no external sampler is bound.
+
+A texture is generally rendered at a size that's either smaller or larger than
+its native size, so that the texture coordinates do not exactly correspond to
+the texels of the texture.  The kind of filtering used, if any, is determined
+by the minification and magnification filter parameters.  The default is LINEAR
+for magnification and NEAREST_MIPMAP_LINEAR for minification.
+
+If texture coordinates fall outside of the principal range of the texture,
+wrapping is applied.  The default for all directions is REPEAT.
+*/
+class Sampler
+{
+public:
+       class Loader: public DataFile::ObjectLoader<Sampler>
+       {
+       public:
+               Loader(Sampler &);
+       private:
+               void init();
+
+               void filter(TextureFilter);
+               void mag_filter(TextureFilter);
+               void max_anisotropy(float);
+               void min_filter(TextureFilter);
+               void wrap(TextureWrap);
+               void wrap_r(TextureWrap);
+               void wrap_s(TextureWrap);
+               void wrap_t(TextureWrap);
+       };
+
+private:
+       enum ParameterMask
+       {
+               MIN_FILTER = 1,
+               MAG_FILTER = 2,
+               MAX_ANISOTROPY = 4,
+               WRAP_S = 8,
+               WRAP_T = 16,
+               WRAP_R = 32,
+               COMPARE = 64
+       };
+
+       unsigned id;
+       const Texture *owner;
+       TextureFilter min_filter;
+       TextureFilter mag_filter;
+       float max_anisotropy;
+       TextureWrap wrap_s;
+       TextureWrap wrap_t;
+       TextureWrap wrap_r;
+       bool compare;
+       Predicate cmp_func;
+       mutable int dirty_params;
+
+public:
+       Sampler();
+       Sampler(const Texture &);
+private:
+       void init();
+
+       void update_parameter(int) const;
+       void set_parameter_i(unsigned, int) const;
+       void set_parameter_f(unsigned, float) const;
+
+public:
+       void set_min_filter(TextureFilter);
+       void set_mag_filter(TextureFilter);
+
+       /** Sets filter for both minification and magnification.  If a mipmapping
+       filter is specified, LINEAR is used for magnification. */
+       void set_filter(TextureFilter);
+
+       TextureFilter get_min_filter() const { return min_filter; }
+       TextureFilter get_mag_filter() const { return mag_filter; }
+
+       void set_max_anisotropy(float);
+       float get_max_anisotropy() const { return max_anisotropy; }
+
+       /** Sets the wrapping mode for all coordinates. */
+       void set_wrap(TextureWrap);
+
+       void set_wrap_s(TextureWrap);
+       void set_wrap_t(TextureWrap);
+       void set_wrap_r(TextureWrap);
+
+       /** Disables depth comparison. */
+       void disable_compare();
+
+       /** Enables depth comparison and sets the compare function.  Only has an
+       effect when used with a depth texture.  When depth comparison is enabled,
+       the third component of the texture coordinate is compared against the texel
+       value, and the result is returned as the texture sample.*/
+       void set_compare(Predicate);
+
+       bool is_compare_enabled() const { return compare; }
+       Predicate get_compare_function() const { return cmp_func; }
+
+       void bind() const { bind_to(0); }
+       void bind_to(unsigned) const;
+
+       static const Sampler *current(unsigned = 0);
+       static void unbind() { unbind_from(0); }
+       static void unbind_from(unsigned);
+
+       void unload();
+};
+
+
+bool is_mipmapped(TextureFilter);
+
+void operator>>(const LexicalConverter &, TextureFilter &);
+void operator>>(const LexicalConverter &, TextureWrap &);
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/shader.cpp b/source/core/shader.cpp
new file mode 100644 (file)
index 0000000..02dcc0d
--- /dev/null
@@ -0,0 +1,114 @@
+#include <msp/gl/extensions/arb_fragment_shader.h>
+#include <msp/gl/extensions/arb_geometry_shader4.h>
+#include <msp/gl/extensions/arb_shader_objects.h>
+#include <msp/gl/extensions/arb_vertex_shader.h>
+#include <msp/io/print.h>
+#include "error.h"
+#include "misc.h"
+#include "shader.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Shader::Shader(GLenum t)
+{
+       init(t);
+}
+
+Shader::Shader(GLenum t, const string &src)
+{
+       init(t);
+
+       source(src);
+       compile();
+}
+
+void Shader::init(GLenum t)
+{
+       static Require _req_base(ARB_shader_objects);
+       compiled = false;
+
+       if(t==GL_FRAGMENT_SHADER)
+               static Require _req(ARB_fragment_shader);
+       else if(t==GL_VERTEX_SHADER)
+               static Require _req(ARB_vertex_shader);
+       else if(t==GL_GEOMETRY_SHADER)
+               static Require _req(ARB_geometry_shader4);
+
+       id = glCreateShader(t);
+}
+
+Shader::~Shader()
+{
+       glDeleteShader(id);
+}
+
+void Shader::source(unsigned count, const char **str, const int *len)
+{
+       glShaderSource(id, count, str, len);
+}
+
+void Shader::source(const string &str)
+{
+       source(str.data(), str.size());
+}
+
+void Shader::source(const char *str, int len)
+{
+       source(1, &str, &len);
+}
+
+void Shader::compile()
+{
+       glCompileShader(id);
+       compiled = get_shader_i(id, GL_COMPILE_STATUS);
+       if(!compiled)
+               throw compile_error(get_info_log());
+
+#ifdef DEBUG
+       string info_log = get_info_log();
+       if(!info_log.empty())
+               IO::print("Shader compile info log:\n%s", info_log);
+#endif
+}
+
+string Shader::get_info_log() const
+{
+       GLsizei len = get_shader_i(id, GL_INFO_LOG_LENGTH);
+       string log(len+1, 0);
+       glGetShaderInfoLog(id, len+1, &len, &log[0]);
+       log.erase(len);
+       return log;
+}
+
+
+VertexShader::VertexShader():
+       Shader(GL_VERTEX_SHADER)
+{ }
+
+VertexShader::VertexShader(const string &src):
+       Shader(GL_VERTEX_SHADER, src)
+{ }
+
+
+FragmentShader::FragmentShader():
+       Shader(GL_FRAGMENT_SHADER)
+{ }
+
+FragmentShader::FragmentShader(const string &src):
+       Shader(GL_FRAGMENT_SHADER, src)
+{ }
+
+
+GeometryShader::GeometryShader():
+       Shader(GL_GEOMETRY_SHADER)
+{ }
+
+GeometryShader::GeometryShader(const string &src):
+       Shader(GL_GEOMETRY_SHADER, src)
+{ }
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/shader.h b/source/core/shader.h
new file mode 100644 (file)
index 0000000..bab11a7
--- /dev/null
@@ -0,0 +1,66 @@
+#ifndef MSP_GL_SHADER_H_
+#define MSP_GL_SHADER_H_
+
+#include <string>
+#include "gl.h"
+
+namespace Msp {
+namespace GL {
+
+/**
+A single shader stage.  Shaders must be attached to a Program to be used.
+
+This class can't be instantiated directly.  Use one of the VertexShader and
+FragmentShader classes to create Shaders.
+*/
+class Shader
+{
+private:
+       unsigned id;
+       bool compiled;
+
+protected:
+       Shader(GLenum t);
+       Shader(GLenum t, const std::string &);
+private:
+       void init(GLenum);
+public:
+       virtual ~Shader();
+
+       void source(unsigned count, const char **str, const int *len);
+       void source(const std::string &str);
+       void source(const char *str, int len);
+       void compile();
+       unsigned get_id() const { return id; }
+       bool is_compiled() const { return compiled; }
+       std::string get_info_log() const;
+};
+
+
+class VertexShader: public Shader
+{
+public:
+       VertexShader();
+       VertexShader(const std::string &);
+};
+
+
+class FragmentShader: public Shader
+{
+public:
+       FragmentShader();
+       FragmentShader(const std::string &);
+};
+
+
+class GeometryShader: public Shader
+{
+public:
+       GeometryShader();
+       GeometryShader(const std::string &);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/stencil.cpp b/source/core/stencil.cpp
new file mode 100644 (file)
index 0000000..9de8b1f
--- /dev/null
@@ -0,0 +1,17 @@
+#include "stencil.h"
+
+namespace Msp {
+namespace GL {
+
+void stencil_func(Predicate func, int ref, unsigned mask)
+{
+       glStencilFunc(func, ref, mask);
+}
+
+void stencil_op(StencilOp sfail, StencilOp dfail, StencilOp dpass)
+{
+       glStencilOp(sfail, dfail, dpass);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/stencil.h b/source/core/stencil.h
new file mode 100644 (file)
index 0000000..02f4ed3
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef MSP_GL_STENCIL_H_
+#define MSP_GL_STENCIL_H_
+
+#include "gl.h"
+#include "predicate.h"
+
+namespace Msp {
+namespace GL {
+
+enum
+{
+       STENCIL_TEST = GL_STENCIL_TEST
+};
+
+enum StencilOp
+{
+       KEEP      = GL_KEEP,
+       SET_ZERO  = GL_ZERO,
+       REPLACE   = GL_REPLACE,
+       INCR      = GL_INCR,
+       DECR      = GL_DECR,
+       INVERT    = GL_INVERT,
+       INCR_WRAP = GL_INCR_WRAP,
+       DECR_WRAP = GL_DECR_WRAP
+};
+
+void stencil_func(Predicate func, int ref, unsigned mask);
+void stencil_op(StencilOp sfail, StencilOp dfail, StencilOp dpass);
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/tests.cpp b/source/core/tests.cpp
new file mode 100644 (file)
index 0000000..8e24bb5
--- /dev/null
@@ -0,0 +1,73 @@
+#include "tests.h"
+
+namespace Msp {
+namespace GL {
+
+DepthTest::DepthTest():
+       write(true),
+       pred(LESS)
+{ }
+
+DepthTest::DepthTest(Predicate p, bool w):
+       write(w),
+       pred(p)
+{ }
+
+void DepthTest::bind() const
+{
+       if(set_current(this))
+       {
+               glEnable(GL_DEPTH_TEST);
+               glDepthFunc(pred);
+               glDepthMask(write);
+       }
+}
+
+const DepthTest &DepthTest::lequal()
+{
+       static DepthTest test(LEQUAL);
+       return test;
+}
+
+void DepthTest::unbind()
+{
+       if(set_current(0))
+       {
+               glDisable(GL_DEPTH_TEST);
+               // Allow glClear(GL_DEPTH_BUFFER_BIT) to work
+               glDepthMask(true);
+       }
+}
+
+
+ScissorTest::ScissorTest():
+       left(0),
+       bottom(0),
+       width(1),
+       height(1)
+{ }
+
+ScissorTest::ScissorTest(int l, int b, unsigned w, unsigned h):
+       left(l),
+       bottom(b),
+       width(w),
+       height(h)
+{ }
+
+void ScissorTest::bind() const
+{
+       if(set_current(this))
+       {
+               glEnable(GL_SCISSOR_TEST);
+               glScissor(left, bottom, width, height);
+       }
+}
+
+void ScissorTest::unbind()
+{
+       if(set_current(0))
+               glDisable(GL_SCISSOR_TEST);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/tests.h b/source/core/tests.h
new file mode 100644 (file)
index 0000000..f7d7295
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef MSP_GL_TESTS_H_
+#define MSP_GL_TESTS_H_
+
+#include "bindable.h"
+#include "gl.h"
+#include "predicate.h"
+
+namespace Msp {
+namespace GL {
+
+/**
+Tests incoming fragment depth values against the depth buffer.  If the test
+fails, the fragment is discarded.
+*/
+class DepthTest: public Bindable<DepthTest>
+{
+private:
+       bool write;
+       Predicate pred;
+
+public:
+       DepthTest();
+       DepthTest(Predicate, bool = true);
+
+       void bind() const;
+
+       static const DepthTest &lequal();
+       static void unbind();
+};
+
+
+/**
+Tests fragment coordinates against a rectangle.  Any fragments outside the
+rectangle are discarded.
+*/
+class ScissorTest: public Bindable<ScissorTest>
+{
+private:
+       int left;
+       int bottom;
+       unsigned width;
+       unsigned height;
+
+public:
+       ScissorTest();
+       ScissorTest(int, int, unsigned, unsigned);
+
+       void bind() const;
+
+       static void unbind();
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/texture.cpp b/source/core/texture.cpp
new file mode 100644 (file)
index 0000000..f6594ab
--- /dev/null
@@ -0,0 +1,410 @@
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_texture_swizzle.h>
+#include <msp/gl/extensions/ext_framebuffer_object.h>
+#include <msp/io/memory.h>
+#include "bindable.h"
+#include "error.h"
+#include "resourcemanager.h"
+#include "resources.h"
+#include "texture.h"
+#include "texunit.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+int Texture::swizzle_orders[] =
+{
+       GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA,
+       GL_RED, GL_RED, GL_RED, GL_ONE,
+       GL_RED, GL_RED, GL_RED, GL_GREEN,
+       GL_BLUE, GL_GREEN, GL_RED, GL_ALPHA
+};
+
+Texture::Texture(GLenum t, ResourceManager *m):
+       id(0),
+       target(t),
+       format(RGB8),
+       storage_fmt(RGB8),
+       swizzle(NO_SWIZZLE),
+       use_srgb_format(false),
+       auto_gen_mipmap(false),
+       default_sampler(*this)
+{
+       if(m)
+               set_manager(m);
+       else if(ARB_direct_state_access)
+               glCreateTextures(target, 1, &id);
+       else
+               glGenTextures(1, &id);
+}
+
+Texture::~Texture()
+{
+       while(TexUnit *unit = TexUnit::find_unit(this))
+               unbind_from(unit->get_index());
+
+       if(id)
+               glDeleteTextures(1, &id);
+}
+
+void Texture::set_format(PixelFormat fmt)
+{
+       PixelComponents comp = get_components(fmt);
+       PixelComponents st_comp = comp;
+       FormatSwizzle swiz = NO_SWIZZLE;
+       switch(comp)
+       {
+       case LUMINANCE:
+               st_comp = RED;
+               swiz = R_TO_LUMINANCE;
+               break;
+       case LUMINANCE_ALPHA:
+               st_comp = RG;
+               swiz = RG_TO_LUMINANCE_ALPHA;
+               break;
+       case BGR:
+               st_comp = RGB;
+               swiz = RGB_TO_BGR;
+               break;
+       case BGRA:
+               st_comp = RGBA;
+               swiz = RGB_TO_BGR;
+               break;
+       default:;
+       }
+
+       PixelFormat st_fmt = make_pixelformat(st_comp, get_component_type(fmt));
+       require_pixelformat(st_fmt);
+       if(swiz!=NO_SWIZZLE)
+               static Require _req(ARB_texture_swizzle);
+
+       format = fmt;
+       storage_fmt = st_fmt;
+       swizzle = swiz;
+}
+
+void Texture::apply_swizzle()
+{
+       if(swizzle==NO_SWIZZLE)
+               return;
+
+       if(get_gl_api()==OPENGL_ES2)
+       {
+               set_parameter_i(GL_TEXTURE_SWIZZLE_R, swizzle_orders[swizzle*4]);
+               set_parameter_i(GL_TEXTURE_SWIZZLE_G, swizzle_orders[swizzle*4+1]);
+               set_parameter_i(GL_TEXTURE_SWIZZLE_B, swizzle_orders[swizzle*4+2]);
+               set_parameter_i(GL_TEXTURE_SWIZZLE_A, swizzle_orders[swizzle*4+3]);
+       }
+       else
+       {
+               if(ARB_direct_state_access)
+                       glTextureParameteriv(id, GL_TEXTURE_SWIZZLE_RGBA, swizzle_orders+swizzle*4);
+               else
+                       glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle_orders+swizzle*4);
+       }
+}
+
+void Texture::set_parameter_i(GLenum param, int value) const
+{
+       if(ARB_direct_state_access)
+               glTextureParameteri(id, param, value);
+       else
+               glTexParameteri(target, param, value);
+}
+
+void Texture::set_min_filter(TextureFilter f)
+{
+       default_sampler.set_min_filter(f);
+}
+
+void Texture::set_mag_filter(TextureFilter f)
+{
+       default_sampler.set_mag_filter(f);
+}
+
+void Texture::set_filter(TextureFilter f)
+{
+       default_sampler.set_filter(f);
+}
+
+void Texture::set_max_anisotropy(float a)
+{
+       default_sampler.set_max_anisotropy(a);
+}
+
+void Texture::set_wrap(TextureWrap w)
+{
+       default_sampler.set_wrap(w);
+}
+
+void Texture::set_wrap_s(TextureWrap w)
+{
+       default_sampler.set_wrap_s(w);
+}
+
+void Texture::set_wrap_t(TextureWrap w)
+{
+       default_sampler.set_wrap_t(w);
+}
+
+void Texture::set_wrap_r(TextureWrap w)
+{
+       default_sampler.set_wrap_r(w);
+}
+
+bool Texture::can_generate_mipmap()
+{
+       return EXT_framebuffer_object;
+}
+
+void Texture::generate_mipmap()
+{
+       // glGenerateMipmap is defined here
+       static Require _req(EXT_framebuffer_object);
+
+       if(ARB_direct_state_access)
+               glGenerateTextureMipmap(id);
+       else
+       {
+               BindRestore _bind(this);
+               glGenerateMipmap(target);
+       }
+}
+
+void Texture::set_auto_generate_mipmap(bool gm)
+{
+       if(gm)
+               static Require _req(EXT_framebuffer_object);
+
+       auto_gen_mipmap = gm;
+}
+
+void Texture::set_compare_enabled(bool c)
+{
+       if(c)
+               default_sampler.set_compare(default_sampler.get_compare_function());
+       else
+               default_sampler.disable_compare();
+}
+
+void Texture::set_compare_func(Predicate f)
+{
+       default_sampler.set_compare(f);
+}
+
+void Texture::load_image(const string &fn, bool)
+{
+       load_image(fn, 0U);
+}
+
+void Texture::load_image(const string &fn, unsigned lv)
+{
+       Graphics::Image img;
+       img.load_file(fn);
+
+       image(img, lv);
+}
+
+void Texture::image(const Graphics::Image &img, bool)
+{
+       image(img, 0U);
+}
+
+void Texture::bind_to(unsigned i) const
+{
+       if(!id)
+       {
+               if(manager)
+                       manager->resource_used(*this);
+               if(!id)
+               {
+                       unbind_from(i);
+                       return;
+               }
+       }
+
+       TexUnit &unit = TexUnit::get_unit(i);
+       const Texture *cur = unit.get_texture();
+       if(unit.set_texture(this))
+       {
+               if(manager)
+                       manager->resource_used(*this);
+
+               if(ARB_direct_state_access)
+                       glBindTextureUnit(i, id);
+               else
+               {
+                       unit.bind();
+                       glBindTexture(target, id);
+               }
+
+               if(!unit.get_sampler() || unit.get_sampler()==&cur->default_sampler)
+                       default_sampler.bind_to(i);
+       }
+}
+
+const Texture *Texture::current(unsigned i)
+{
+       return TexUnit::get_unit(i).get_texture();
+}
+
+void Texture::unbind_from(unsigned i)
+{
+       TexUnit &unit = TexUnit::get_unit(i);
+       const Texture *cur = unit.get_texture();
+       if(unit.set_texture(0))
+       {
+               if(ARB_direct_state_access)
+                       glBindTextureUnit(i, 0);
+               else
+               {
+                       unit.bind();
+                       glBindTexture(cur->target, 0);
+               }
+
+               if(unit.get_sampler()==&cur->default_sampler)
+                       Sampler::unbind_from(i);
+       }
+}
+
+
+Texture::Loader::Loader(Texture &t):
+       DataFile::CollectionObjectLoader<Texture>(t, 0)
+{
+       init();
+}
+
+Texture::Loader::Loader(Texture &t, Collection &c):
+       DataFile::CollectionObjectLoader<Texture>(t, &c)
+{
+       init();
+}
+
+void Texture::Loader::init()
+{
+       levels = 0;
+
+       add("external_image", &Loader::external_image);
+       add("external_image_srgb", &Loader::external_image);
+       add("filter", &Loader::filter);
+       add("generate_mipmap", &Loader::generate_mipmap);
+       add("image_data", &Loader::image_data);
+       add("mag_filter", &Loader::mag_filter);
+       add("max_anisotropy", &Loader::max_anisotropy);
+       add("min_filter", &Loader::min_filter);
+       add("mipmap_levels", &Loader::mipmap_levels);
+       add("sampler",    &Loader::sampler);
+       add("wrap",       &Loader::wrap);
+       add("wrap_r",     &Loader::wrap_r);
+       add("wrap_s",     &Loader::wrap_s);
+       add("wrap_t",     &Loader::wrap_t);
+}
+
+unsigned Texture::Loader::get_levels() const
+{
+       return (is_mipmapped(obj.default_sampler.get_min_filter()) ? levels : 1);
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+void Texture::Loader::load_external_image(Graphics::Image &img, const std::string &fn)
+{
+       RefPtr<IO::Seekable> io = get_collection().open_raw(fn);
+       if(!io)
+               throw IO::file_not_found(fn);
+       img.load_io(*io);
+}
+
+void Texture::Loader::external_image(const string &fn)
+{
+       obj.use_srgb_format = false;
+       external_image_common(fn);
+}
+
+void Texture::Loader::external_image_srgb(const string &fn)
+{
+       obj.use_srgb_format = true;
+       external_image_common(fn);
+}
+
+void Texture::Loader::external_image_common(const string &fn)
+{
+       if(obj.manager)
+               obj.manager->set_resource_location(obj, get_collection(), fn);
+       else
+       {
+               Graphics::Image img;
+               load_external_image(img, fn);
+               obj.image(img, get_levels());
+       }
+}
+
+void Texture::Loader::filter(TextureFilter f)
+{
+       obj.set_filter(f);
+}
+
+void Texture::Loader::generate_mipmap(bool gm)
+{
+       obj.set_auto_generate_mipmap(gm);
+}
+
+void Texture::Loader::image_data(const string &data)
+{
+       Graphics::Image img;
+       IO::Memory mem(data.data(), data.size());
+       img.load_io(mem);
+
+       obj.image(img, get_levels());
+}
+
+void Texture::Loader::mag_filter(TextureFilter f)
+{
+       obj.set_mag_filter(f);
+}
+
+void Texture::Loader::max_anisotropy(float a)
+{
+       obj.set_max_anisotropy(a);
+}
+
+void Texture::Loader::min_filter(TextureFilter f)
+{
+       obj.set_min_filter(f);
+}
+
+void Texture::Loader::mipmap_levels(unsigned l)
+{
+       levels = l;
+}
+
+void Texture::Loader::sampler()
+{
+       load_sub(obj.default_sampler);
+}
+
+void Texture::Loader::wrap(TextureWrap w)
+{
+       obj.set_wrap(w);
+}
+
+void Texture::Loader::wrap_r(TextureWrap w)
+{
+       obj.set_wrap_r(w);
+}
+
+void Texture::Loader::wrap_s(TextureWrap w)
+{
+       obj.set_wrap_s(w);
+}
+
+void Texture::Loader::wrap_t(TextureWrap w)
+{
+       obj.set_wrap_t(w);
+}
+#pragma GCC diagnostic pop
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/texture.h b/source/core/texture.h
new file mode 100644 (file)
index 0000000..c4e7203
--- /dev/null
@@ -0,0 +1,167 @@
+#ifndef MSP_GL_TEXTURE_H_
+#define MSP_GL_TEXTURE_H_
+
+#include <msp/core/attributes.h>
+#include <msp/datafile/objectloader.h>
+#include <msp/graphics/image.h>
+#include "datatype.h"
+#include "gl.h"
+#include "pixelformat.h"
+#include "predicate.h"
+#include "sampler.h"
+#include "resource.h"
+
+namespace Msp {
+namespace GL {
+
+/**
+Base class for textures.  This class only defines operations common for all
+texture types and is not instantiable.  For specifying images for textures,
+see one of the dimensioned texture classes.
+
+A texture can consinst of a stack of images, called a mipmap.  The dimensions
+of each mipmap level are half that of the previous level.  The mipmap stack
+can be used for texture minification; see the Sampler class for details.
+*/
+class Texture: public Resource
+{
+protected:
+       class Loader: public DataFile::CollectionObjectLoader<Texture>
+       {
+       protected:
+               unsigned levels;
+
+       public:
+               Loader(Texture &);
+               Loader(Texture &, Collection &);
+       private:
+               void init();
+
+               unsigned get_levels() const;
+       protected:
+               void load_external_image(Graphics::Image &, const std::string &);
+
+       private:
+               void external_image(const std::string &);
+               void external_image_srgb(const std::string &);
+               void external_image_common(const std::string &);
+               void filter(TextureFilter);
+               void generate_mipmap(bool);
+               void image_data(const std::string &);
+               void mag_filter(TextureFilter);
+               void max_anisotropy(float);
+               void min_filter(TextureFilter);
+               void mipmap_levels(unsigned);
+               void sampler();
+               void wrap(TextureWrap);
+               void wrap_r(TextureWrap);
+               void wrap_s(TextureWrap);
+               void wrap_t(TextureWrap);
+       };
+
+       enum ParameterMask
+       {
+               FORMAT_SWIZZLE = 512
+       };
+
+       enum FormatSwizzle
+       {
+               NO_SWIZZLE,
+               R_TO_LUMINANCE,
+               RG_TO_LUMINANCE_ALPHA,
+               RGB_TO_BGR
+       };
+
+       unsigned id;
+       GLenum target;
+       PixelFormat format;
+       PixelFormat storage_fmt;
+       FormatSwizzle swizzle;
+       bool use_srgb_format;
+       bool auto_gen_mipmap;
+       Sampler default_sampler;
+
+       static int swizzle_orders[];
+
+       Texture(GLenum, ResourceManager * = 0);
+       Texture(const Texture &);
+       Texture &operator=(const Texture &);
+public:
+       ~Texture();
+
+protected:
+       void set_format(PixelFormat);
+       void apply_swizzle();
+       void set_parameter_i(GLenum, int) const;
+
+public:
+       Sampler &get_default_sampler() { return default_sampler; }
+       const Sampler &get_default_sampler() const { return default_sampler; }
+
+       DEPRECATED void set_min_filter(TextureFilter);
+       DEPRECATED void set_mag_filter(TextureFilter);
+
+       /** Sets filter for both minification and magnification.  Since mipmapping
+       is not applicable to magnification, LINEAR is used instead. */
+       DEPRECATED void set_filter(TextureFilter);
+
+       DEPRECATED void set_mipmap_levels(unsigned) { }
+
+       DEPRECATED void set_max_anisotropy(float);
+
+       /** Sets the wrapping mode for all coordinates. */
+       DEPRECATED void set_wrap(TextureWrap);
+
+       DEPRECATED void set_wrap_s(TextureWrap);
+       DEPRECATED void set_wrap_t(TextureWrap);
+       DEPRECATED void set_wrap_r(TextureWrap);
+
+       static bool can_generate_mipmap();
+
+       void generate_mipmap();
+
+       /** Sets automatic mipmap generation.  If enabled, mipmaps are generated
+       when a texture image is uploaded. */
+       void set_auto_generate_mipmap(bool);
+
+       /// Deprecated.  Use set_auto_generate_mipmap instead.
+       DEPRECATED void set_generate_mipmap(bool g) { set_auto_generate_mipmap(g); }
+
+       /** Sets depth texture comparison.  Has no effect on other formats.  When
+       comparison is enabled, the third component of the texture coordinate is
+       compared against the texel value, and the result is returned as the texture
+       sample. */
+       DEPRECATED void set_compare_enabled(bool);
+
+       /** Sets the function to use for depth comparison. */
+       DEPRECATED void set_compare_func(Predicate);
+
+       /// Loads a Graphics::Image from a file and uploads it to the texture.
+       virtual void load_image(const std::string &, unsigned = 0);
+
+       DEPRECATED void load_image(const std::string &, bool srgb);
+
+       /** Uploads an image to the texture.  If storage has not been defined, it
+       will be set to match the image.  Otherwise the image must be compatible
+       with the defined storage.  Semantics depend on the type of texture.  */
+       virtual void image(const Graphics::Image &, unsigned = 0) = 0;
+
+       DEPRECATED void image(const Graphics::Image &, bool srgb);
+
+       GLenum get_target() const { return target; }
+       unsigned get_id() const { return id; }
+
+       void bind() const { bind_to(0); }
+       void bind_to(unsigned) const;
+
+       static const Texture *current(unsigned = 0);
+       static void unbind() { unbind_from(0); }
+       static void unbind_from(unsigned);
+
+       virtual UInt64 get_data_size() const { return 0; }
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/texture1d.cpp b/source/core/texture1d.cpp
new file mode 100644 (file)
index 0000000..742857b
--- /dev/null
@@ -0,0 +1,190 @@
+#include <msp/core/raii.h>
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_texture_storage.h>
+#include <msp/gl/extensions/msp_texture1d.h>
+#include "bindable.h"
+#include "error.h"
+#include "texture1d.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Texture1D::Texture1D():
+       Texture(GL_TEXTURE_1D),
+       width(0),
+       allocated(0)
+{
+       static Require _req(MSP_texture1D);
+}
+
+void Texture1D::storage(PixelFormat fmt, unsigned wd, unsigned lv)
+{
+       if(width>0)
+               throw invalid_operation("Texture1D::storage");
+       if(wd==0)
+               throw invalid_argument("Texture1D::storage");
+
+       set_format(fmt);
+       width = wd;
+       levels = get_n_levels();
+       if(lv)
+               levels = min(levels, lv);
+}
+
+void Texture1D::allocate(unsigned level)
+{
+       if(width==0)
+               throw invalid_operation("Texture1D::allocate");
+       if(level>=levels)
+               throw invalid_argument("Texture1D::allocate");
+       if(allocated&(1<<level))
+               return;
+
+       if(ARB_texture_storage)
+       {
+               Conditional<BindRestore> _bind(!ARB_direct_state_access, this);
+               if(ARB_direct_state_access)
+                       glTextureStorage1D(id, levels, storage_fmt, width);
+               else
+                       glTexStorage1D(target, levels, storage_fmt, width);
+               apply_swizzle();
+               allocated |= (1<<levels)-1;
+       }
+       else
+               image(level, 0);
+}
+
+void Texture1D::image(unsigned level, const void *data)
+{
+       if(width==0)
+               throw invalid_operation("Texture1D::image");
+
+       unsigned w = get_level_size(level);
+
+       if(ARB_texture_storage)
+               return sub_image(level, 0, w, data);
+
+       BindRestore _bind(this);
+
+       if(!allocated)
+       {
+               glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
+               apply_swizzle();
+       }
+
+       PixelComponents comp = get_components(storage_fmt);
+       DataType type = get_component_type(storage_fmt);
+       glTexImage1D(target, level, storage_fmt, w, 0, comp, type, data);
+
+       allocated |= 1<<level;
+       if(auto_gen_mipmap && level==0)
+       {
+               generate_mipmap();
+               allocated |= (1<<levels)-1;
+       }
+}
+
+void Texture1D::image(unsigned level, PixelComponents comp, DataType type, const void *data)
+{
+       if(comp!=get_components(format) || type!=get_component_type(format))
+               throw incompatible_data("Texture1D::image");
+       image(level, data);
+}
+
+void Texture1D::sub_image(unsigned level, int x, unsigned wd, const void *data)
+{
+       if(width==0)
+               throw invalid_operation("Texture3D::image");
+
+       Conditional<BindRestore> _bind(!ARB_direct_state_access, this);
+       allocate(level);
+
+       PixelComponents comp = get_components(storage_fmt);
+       DataType type = get_component_type(storage_fmt);
+       if(ARB_direct_state_access)
+               glTextureSubImage1D(id, level, x, wd, comp, type, data);
+       else
+               glTexSubImage1D(target, level, x, wd, comp, type, data);
+
+       if(auto_gen_mipmap && level==0)
+               generate_mipmap();
+}
+
+void Texture1D::sub_image(unsigned level, int x, unsigned wd, PixelComponents comp, DataType type, const void *data)
+{
+       if(comp!=get_components(format) || type!=get_component_type(format))
+               throw incompatible_data("Texture1D::sub_image");
+       sub_image(level, x, wd, data);
+}
+
+void Texture1D::image(const Graphics::Image &img, unsigned lv)
+{
+       if(img.get_height()!=1)
+               throw incompatible_data("Texture1D::image");
+
+       unsigned w = img.get_width();
+       PixelFormat fmt = pixelformat_from_image(img);
+       if(width==0)
+               storage(make_pixelformat(get_components(fmt), get_component_type(fmt), use_srgb_format), w, lv);
+       else if(w!=width)
+               throw incompatible_data("Texture1D::image");
+
+       image(0, img.get_pixels());
+}
+
+unsigned Texture1D::get_n_levels() const
+{
+       unsigned n = 0;
+       for(unsigned s=width; s; s>>=1, ++n) ;
+       return n;
+}
+
+unsigned Texture1D::get_level_size(unsigned level) const
+{
+       return width>>level;
+}
+
+UInt64 Texture1D::get_data_size() const
+{
+       return id ? width*get_pixel_size(storage_fmt) : 0;
+}
+
+
+Texture1D::Loader::Loader(Texture1D &t):
+       DataFile::DerivedObjectLoader<Texture1D, Texture::Loader>(t)
+{
+       init();
+}
+
+Texture1D::Loader::Loader(Texture1D &t, Collection &c):
+       DataFile::DerivedObjectLoader<Texture1D, Texture::Loader>(t, c)
+{
+       init();
+}
+
+void Texture1D::Loader::init()
+{
+       add("raw_data", &Loader::raw_data);
+       add("storage", &Loader::storage);
+       add("storage", &Loader::storage_levels);
+}
+
+void Texture1D::Loader::raw_data(const string &data)
+{
+       obj.image(0, data.data());
+}
+
+void Texture1D::Loader::storage(PixelFormat fmt, unsigned w)
+{
+       obj.storage(fmt, w);
+}
+
+void Texture1D::Loader::storage_levels(PixelFormat fmt, unsigned w, unsigned l)
+{
+       obj.storage(fmt, w, l);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/texture1d.h b/source/core/texture1d.h
new file mode 100644 (file)
index 0000000..b14ff91
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef MSP_GL_TEXTURE1D_H_
+#define MSP_GL_TEXTURE1D_H_
+
+#include "texture.h"
+
+namespace Msp {
+namespace GL {
+
+class Texture1D: public Texture
+{
+public:
+       class Loader: public DataFile::DerivedObjectLoader<Texture1D, Texture::Loader>
+       {
+       public:
+               Loader(Texture1D &);
+               Loader(Texture1D &, Collection &);
+       private:
+               void init();
+
+               void raw_data(const std::string &);
+               void storage(PixelFormat, unsigned);
+               void storage_levels(PixelFormat, unsigned, unsigned);
+       };
+
+private:
+       unsigned width;
+       unsigned levels;
+       unsigned allocated;
+
+public:
+       Texture1D();
+
+       void storage(PixelFormat, unsigned, unsigned = 0);
+
+       DEPRECATED void storage(PixelComponents c, unsigned w, unsigned l = 0)
+       { storage(make_pixelformat(c, UNSIGNED_BYTE), w, l); }
+
+       void allocate(unsigned);
+       void image(unsigned, const void *);
+       DEPRECATED void image(unsigned, PixelComponents, DataType, const void *);
+       void sub_image(unsigned, int, unsigned, const void *);
+       DEPRECATED void sub_image(unsigned, int, unsigned, PixelComponents, DataType, const void *);
+       virtual void image(const Graphics::Image &, unsigned = 0);
+       using Texture::image;
+       unsigned get_width() const { return width; }
+
+private:
+       unsigned get_n_levels() const;
+       unsigned get_level_size(unsigned) const;
+
+public:
+       virtual AsyncLoader *load(IO::Seekable &, const Resources * = 0) { return 0; }
+       virtual UInt64 get_data_size() const;
+       virtual void unload() { }
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/texture2d.cpp b/source/core/texture2d.cpp
new file mode 100644 (file)
index 0000000..0e76128
--- /dev/null
@@ -0,0 +1,307 @@
+#include <msp/core/raii.h>
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_texture_storage.h>
+#include <msp/graphics/imageloader.h>
+#include "bindable.h"
+#include "buffer.h"
+#include "error.h"
+#include "pixelstore.h"
+#include "resources.h"
+#include "texture2d.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+class Texture2D::AsyncLoader: public Resource::AsyncLoader
+{
+private:
+       Texture2D &texture;
+       IO::Seekable &io;
+       Buffer pixel_buffer;
+       char *mapped_address;
+       Graphics::Image image;
+       Graphics::ImageLoader *img_loader;
+       unsigned n_bytes;
+       int phase;
+
+public:
+       AsyncLoader(Texture2D &, IO::Seekable &);
+       ~AsyncLoader();
+
+       virtual bool needs_sync() const;
+       virtual bool process();
+};
+
+
+Texture2D::Texture2D(ResourceManager *m):
+       Texture(GL_TEXTURE_2D, m),
+       width(0),
+       height(0),
+       allocated(0)
+{ }
+
+Texture2D::~Texture2D()
+{
+       set_manager(0);
+}
+
+void Texture2D::storage(PixelFormat fmt, unsigned wd, unsigned ht, unsigned lv)
+{
+       if(width>0)
+               throw invalid_operation("Texture2D::storage");
+       if(wd==0 || ht==0)
+               throw invalid_argument("Texture2D::storage");
+
+       set_format(fmt);
+       width = wd;
+       height = ht;
+       levels = get_n_levels();
+       if(lv>0)
+               levels = min(levels, lv);
+}
+
+void Texture2D::allocate(unsigned level)
+{
+       if(width==0 || height==0)
+               throw invalid_operation("Texture2D::allocate");
+       if(level>=levels)
+               throw invalid_argument("Texture2D::allocate");
+       if(allocated&(1<<level))
+               return;
+
+       if(ARB_texture_storage)
+       {
+               Conditional<BindRestore> _bind(!ARB_direct_state_access, this);
+               if(ARB_direct_state_access)
+                       glTextureStorage2D(id, levels, storage_fmt, width, height);
+               else
+                       glTexStorage2D(target, levels, storage_fmt, width, height);
+               apply_swizzle();
+               allocated |= (1<<levels)-1;
+       }
+       else
+               image(level, 0);
+}
+
+void Texture2D::image(unsigned level, const void *data)
+{
+       if(width==0 || height==0)
+               throw invalid_operation("Texture2D::image");
+
+       unsigned w = width;
+       unsigned h = height;
+       get_level_size(level, w, h);
+
+       if(ARB_texture_storage)
+               return sub_image(level, 0, 0, w, h, data);
+
+       BindRestore _bind(this);
+
+       if(!allocated)
+       {
+               glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
+               apply_swizzle();
+       }
+
+       PixelComponents comp = get_components(storage_fmt);
+       DataType type = get_component_type(storage_fmt);
+       glTexImage2D(target, level, storage_fmt, w, h, 0, comp, type, data);
+
+       allocated |= 1<<level;
+       if(auto_gen_mipmap && level==0)
+       {
+               generate_mipmap();
+               allocated |= (1<<levels)-1;
+       }
+}
+
+void Texture2D::image(unsigned level, PixelComponents comp, DataType type, const void *data)
+{
+       if(comp!=get_components(format) || type!=get_component_type(format))
+               throw incompatible_data("Texture2D::image");
+       image(level, data);
+}
+
+void Texture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, const void *data)
+{
+       if(width==0 || height==0)
+               throw invalid_operation("Texture2D::sub_image");
+
+       Conditional<BindRestore> _bind(!ARB_direct_state_access, this);
+       allocate(level);
+
+       PixelComponents comp = get_components(storage_fmt);
+       DataType type = get_component_type(storage_fmt);
+       if(ARB_direct_state_access)
+               glTextureSubImage2D(id, level, x, y, wd, ht, comp, type, data);
+       else
+               glTexSubImage2D(target, level, x, y, wd, ht, comp, type, data);
+
+       if(auto_gen_mipmap && level==0)
+               generate_mipmap();
+}
+
+void Texture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, PixelComponents comp, DataType type, const void *data)
+{
+       if(comp!=get_components(format) || type!=get_component_type(format))
+               throw incompatible_data("Texture2D::sub_image");
+       sub_image(level, x, y, wd, ht, data);
+}
+
+void Texture2D::image(const Graphics::Image &img, unsigned lv)
+{
+       image(img, lv, false);
+}
+
+void Texture2D::image(const Graphics::Image &img, unsigned lv, bool from_buffer)
+{
+       unsigned w = img.get_width();
+       unsigned h = img.get_height();
+       PixelFormat fmt = pixelformat_from_image(img);
+       if(width==0)
+               storage(make_pixelformat(get_components(fmt), get_component_type(fmt), use_srgb_format), w, h, lv);
+       else if(w!=width || h!=height || (lv && lv!=levels))
+               throw incompatible_data("Texture2D::image");
+
+       PixelStore pstore = PixelStore::from_image(img);
+       BindRestore _bind_ps(pstore);
+
+       image(0, from_buffer ? 0 : img.get_pixels());
+}
+
+unsigned Texture2D::get_n_levels() const
+{
+       unsigned n = 0;
+       for(unsigned s=max(width, height); s; s>>=1, ++n) ;
+       return n;
+}
+
+void Texture2D::get_level_size(unsigned level, unsigned &w, unsigned &h) const
+{
+       w >>= level;
+       h >>= level;
+
+       if(!w && h)
+               w = 1;
+       else if(!h && w)
+               h = 1;
+}
+
+Resource::AsyncLoader *Texture2D::load(IO::Seekable &io, const Resources *)
+{
+       AsyncLoader *ldr = new AsyncLoader(*this, io);
+       return ldr;
+}
+
+UInt64 Texture2D::get_data_size() const
+{
+       return id ? width*height*get_pixel_size(format) : 0;
+}
+
+void Texture2D::unload()
+{
+       glDeleteTextures(1, &id);
+       id = 0;
+       allocated = 0;
+       default_sampler.unload();
+}
+
+
+Texture2D::Loader::Loader(Texture2D &t):
+       DataFile::DerivedObjectLoader<Texture2D, Texture::Loader>(t)
+{
+       init();
+}
+
+Texture2D::Loader::Loader(Texture2D &t, Collection &c):
+       DataFile::DerivedObjectLoader<Texture2D, Texture::Loader>(t, c)
+{
+       init();
+}
+
+void Texture2D::Loader::init()
+{
+       add("raw_data", &Loader::raw_data);
+       add("storage", &Loader::storage);
+       add("storage", &Loader::storage_levels);
+}
+
+void Texture2D::Loader::raw_data(const string &data)
+{
+       obj.image(0, data.data());
+}
+
+void Texture2D::Loader::storage(PixelFormat fmt, unsigned w, unsigned h)
+{
+       obj.storage(fmt, w, h);
+}
+
+void Texture2D::Loader::storage_levels(PixelFormat fmt, unsigned w, unsigned h, unsigned l)
+{
+       obj.storage(fmt, w, h, l);
+}
+
+
+Texture2D::AsyncLoader::AsyncLoader(Texture2D &t, IO::Seekable &i):
+       texture(t),
+       io(i),
+       pixel_buffer(PIXEL_UNPACK_BUFFER),
+       mapped_address(0),
+       img_loader(Graphics::ImageLoader::open_io(io)),
+       phase(0)
+{ }
+
+Texture2D::AsyncLoader::~AsyncLoader()
+{
+       if(mapped_address)
+               pixel_buffer.unmap();
+       delete img_loader;
+}
+
+bool Texture2D::AsyncLoader::needs_sync() const
+{
+       return phase%2;
+}
+
+bool Texture2D::AsyncLoader::process()
+{
+       if(phase==0)
+       {
+               image.load_headers(*img_loader);
+               n_bytes = image.get_stride()*image.get_height();
+       }
+       else if(phase==1)
+       {
+               pixel_buffer.storage(n_bytes);
+               mapped_address = reinterpret_cast<char *>(pixel_buffer.map());
+       }
+       else if(phase==2)
+               image.load_into(*img_loader, mapped_address);
+       else if(phase==3)
+       {
+               Bind _bind_buf(pixel_buffer, PIXEL_UNPACK_BUFFER);
+               mapped_address = 0;
+               if(!pixel_buffer.unmap())
+               {
+                       phase = 1;
+                       return false;
+               }
+
+               if(!texture.id)
+               {
+                       if(ARB_direct_state_access)
+                               glCreateTextures(texture.target, 1, &texture.id);
+                       else
+                               glGenTextures(1, &texture.id);
+               }
+               texture.image(image, 0, true);
+       }
+
+       ++phase;
+       return phase>3;
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/texture2d.h b/source/core/texture2d.h
new file mode 100644 (file)
index 0000000..7553b6b
--- /dev/null
@@ -0,0 +1,100 @@
+#ifndef MSP_GL_TEXTURE2D_H_
+#define MSP_GL_TEXTURE2D_H_
+
+#include <string>
+#include <msp/graphics/image.h>
+#include "texture.h"
+
+namespace Msp {
+namespace GL {
+
+/**
+Two-dimensional texture.  Consists of an array of texels in the shape of a
+rectangle.  Texture coordinate have a range of [0, 1].  Coordinates outside of
+this range are subject to wrapping.  This is the most common type of texture.
+*/
+class Texture2D: public Texture
+{
+public:
+       class Loader: public Msp::DataFile::DerivedObjectLoader<Texture2D, Texture::Loader>
+       {
+       public:
+               Loader(Texture2D &);
+               Loader(Texture2D &, Collection &);
+       private:
+               void init();
+
+               void raw_data(const std::string &);
+               void storage(PixelFormat, unsigned, unsigned);
+               void storage_levels(PixelFormat, unsigned, unsigned, unsigned);
+       };
+
+private:
+       class AsyncLoader;
+
+       unsigned width;
+       unsigned height;
+       unsigned levels;
+       unsigned allocated;
+
+public:
+       Texture2D(ResourceManager * = 0);
+       virtual ~Texture2D();
+
+       /** Defines storage structure for the texture.  If lv is zero, the number
+       of mipmap levels is automatically determined from storage dimensions.
+
+       Must be called before an image can be uploaded.  Once storage is defined,
+       it can't be changed. */
+       void storage(PixelFormat fmt, unsigned wd, unsigned ht, unsigned lv = 0);
+
+       DEPRECATED void storage(PixelComponents cm, unsigned wd, unsigned ht, unsigned lv = 0)
+       { storage(make_pixelformat(cm, UNSIGNED_BYTE), wd, ht, lv); }
+
+       /** Allocates storage for the texture.  The contents are initially
+       undefined.  If storage has already been allocated, does nothing. */
+       void allocate(unsigned level);
+
+       /** Updates the contents of the entire texture.  Storage must be defined
+       beforehand.  The image data must have dimensions and format matching the
+       defined storage. */
+       virtual void image(unsigned level, const void *data);
+
+       DEPRECATED void image(unsigned level, PixelComponents fmt, DataType type, const void *data);
+
+       /** Updates a rectangular region of the texture.  Storage must be defined
+       beforehand.  The image data must be in a format mathing the defined storage
+       and the update region must be fully inside the texture. */
+       void sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, const void *data);
+
+       DEPRECATED void sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht,
+               PixelComponents fmt, DataType type, const void *data);
+
+       /** Updates the contents of the entire texture from an image.  If storage
+       has not been defined, it will be set to match the image.  Otherwise the
+       image must match the defined storage. */
+       virtual void image(const Graphics::Image &, unsigned lv = 0);
+
+       using Texture::image;
+
+private:
+       void image(const Graphics::Image &, unsigned, bool);
+
+public:
+       unsigned get_width() const { return width; }
+       unsigned get_height() const { return height; }
+
+private:
+       unsigned get_n_levels() const;
+       void get_level_size(unsigned, unsigned &, unsigned &) const;
+
+public:
+       virtual Resource::AsyncLoader *load(IO::Seekable &, const Resources * = 0);
+       virtual UInt64 get_data_size() const;
+       virtual void unload();
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/texture2darray.cpp b/source/core/texture2darray.cpp
new file mode 100644 (file)
index 0000000..1ecb4cf
--- /dev/null
@@ -0,0 +1,80 @@
+#include <msp/datafile/collection.h>
+#include <msp/gl/extensions/ext_texture_array.h>
+#include "error.h"
+#include "pixelstore.h"
+#include "texture2darray.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Texture2DArray::Texture2DArray():
+       Texture3D(GL_TEXTURE_2D_ARRAY)
+{
+       static Require _req(EXT_texture_array);
+}
+
+void Texture2DArray::layer_image(unsigned level, unsigned z, const void *data)
+{
+       unsigned w = get_width();
+       unsigned h = get_height();
+       unsigned d = get_depth();
+       get_level_size(level, w, h, d);
+
+       sub_image(level, 0, 0, z, w, h, 1, data);
+}
+
+void Texture2DArray::layer_image(unsigned level, unsigned z, PixelComponents comp, DataType type, const void *data)
+{
+       if(comp!=get_components(format) || type!=get_component_type(format))
+               throw incompatible_data("Texture2DArray::layer_image");
+       layer_image(level, z, data);
+}
+
+void Texture2DArray::layer_image(unsigned level, unsigned z, const Graphics::Image &img)
+{
+       if(!get_width())
+               throw invalid_operation("Texture2DArray::layer_image");
+
+       unsigned w = img.get_width();
+       unsigned h = img.get_height();
+       if(w!=get_width() || h!=get_height())
+               throw incompatible_data("Texture2DArray::layer_image");
+       PixelFormat fmt = pixelformat_from_image(img);
+       if(get_components(fmt)!=get_components(format) || get_component_type(fmt)!=get_component_type(format))
+               throw incompatible_data("Texture2DArray::layer_image");
+
+       PixelStore pstore = PixelStore::from_image(img);
+       BindRestore _bind_ps(pstore);
+
+       layer_image(level, z, img.get_pixels());
+}
+
+
+Texture2DArray::Loader::Loader(Texture2DArray &t):
+       DataFile::DerivedObjectLoader<Texture2DArray, Texture3D::Loader>(t)
+{
+       init();
+}
+
+Texture2DArray::Loader::Loader(Texture2DArray &t, Collection &c):
+       DataFile::DerivedObjectLoader<Texture2DArray, Texture3D::Loader>(t, c)
+{
+       init();
+}
+
+void Texture2DArray::Loader::init()
+{
+       add("external_image", &Loader::external_image);
+}
+
+void Texture2DArray::Loader::external_image(unsigned z, const string &fn)
+{
+       Graphics::Image img;
+       load_external_image(img, fn);
+       obj.layer_image(0, z, img);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/texture2darray.h b/source/core/texture2darray.h
new file mode 100644 (file)
index 0000000..bd7d03b
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef MSP_GL_TEXTURE2DARRAY_H_
+#define MSP_GL_TEXTURE2DARRAY_H_
+
+#include "texture3d.h"
+
+namespace Msp {
+namespace GL {
+
+/**
+An array of two-dimensional textures.  It's very much like a 3D texture, with
+two important differences: there's no filtering nor mipmapping along the third
+dimension.
+*/
+class Texture2DArray: public Texture3D
+{
+public:
+       class Loader: public Msp::DataFile::DerivedObjectLoader<Texture2DArray, Texture3D::Loader>
+       {
+       public:
+               Loader(Texture2DArray &);
+               Loader(Texture2DArray &, Collection &);
+       private:
+               void init();
+
+               void external_image(unsigned, const std::string &);
+       };
+
+       Texture2DArray();
+
+       void layer_image(unsigned, unsigned, const void *);
+       DEPRECATED void layer_image(unsigned, unsigned, PixelComponents, DataType, const void *);
+       void layer_image(unsigned, unsigned, const Graphics::Image &);
+
+       unsigned get_layers() const { return get_depth(); }
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/texture3d.cpp b/source/core/texture3d.cpp
new file mode 100644 (file)
index 0000000..03c58d0
--- /dev/null
@@ -0,0 +1,229 @@
+#include <cmath>
+#include <msp/core/raii.h>
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_texture_storage.h>
+#include <msp/gl/extensions/ext_texture3d.h>
+#include <msp/gl/extensions/ext_texture_array.h>
+#include <msp/graphics/image.h>
+#include "bindable.h"
+#include "error.h"
+#include "pixelstore.h"
+#include "texture3d.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Texture3D::Texture3D(GLenum t):
+       Texture(t),
+       width(0),
+       height(0),
+       depth(0),
+       allocated(0)
+{ }
+
+Texture3D::Texture3D():
+       Texture(GL_TEXTURE_3D),
+       width(0),
+       height(0),
+       depth(0),
+       allocated(0)
+{
+       static Require _req(EXT_texture3D);
+}
+
+void Texture3D::storage(PixelFormat fmt, unsigned wd, unsigned ht, unsigned dp, unsigned lv)
+{
+       if(width>0)
+               throw invalid_operation("Texture3D::storage");
+       if(wd==0 || ht==0 || dp==0)
+               throw invalid_argument("Texture3D::storage");
+
+       set_format(fmt);
+       width = wd;
+       height = ht;
+       depth = dp;
+       levels = get_n_levels();
+       if(lv>0)
+               levels = min(levels, lv);
+}
+
+void Texture3D::allocate(unsigned level)
+{
+       if(width==0 || height==0 || depth==0)
+               throw invalid_operation("Texture3D::allocate");
+       if(level>=levels)
+               throw invalid_argument("Texture3D::allocate");
+       if(allocated&(1<<level))
+               return;
+
+       if(ARB_texture_storage)
+       {
+               Conditional<BindRestore> _bind(!ARB_direct_state_access, this);
+               if(ARB_direct_state_access)
+                       glTextureStorage3D(id, levels, storage_fmt, width, height, depth);
+               else
+                       glTexStorage3D(target, levels, storage_fmt, width, height, depth);
+               apply_swizzle();
+               allocated |= (1<<levels)-1;
+       }
+       else
+               image(level, 0);
+}
+
+void Texture3D::image(unsigned level, const void *data)
+{
+       if(width==0 || height==0 || depth==0)
+               throw invalid_operation("Texture3D::image");
+
+       unsigned w = width;
+       unsigned h = height;
+       unsigned d = depth;
+       get_level_size(level, w, h, d);
+
+       if(ARB_texture_storage)
+               return sub_image(level, 0, 0, 0, w, h, d, data);
+
+       BindRestore _bind(this);
+
+       if(!allocated)
+       {
+               glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
+               apply_swizzle();
+       }
+
+       PixelComponents comp = get_components(storage_fmt);
+       DataType type = get_component_type(storage_fmt);
+       glTexImage3D(target, level, storage_fmt, width, height, depth, 0, comp, type, data);
+
+       allocated |= 1<<level;
+       if(auto_gen_mipmap && level==0)
+       {
+               generate_mipmap();
+               allocated |= (1<<levels)-1;
+       }
+}
+
+void Texture3D::image(unsigned level, PixelComponents comp, DataType type, const void *data)
+{
+       if(comp!=get_components(format) || type!=get_component_type(format))
+               throw incompatible_data("Texture3D::image");
+       image(level, data);
+}
+
+void Texture3D::sub_image(unsigned level, int x, int y, int z, unsigned wd, unsigned ht, unsigned dp, const void *data)
+{
+       if(width==0 || height==0 || depth==0)
+               throw invalid_operation("Texture3D::image");
+
+       Conditional<BindRestore> _bind(!ARB_direct_state_access, this);
+       allocate(level);
+
+       PixelComponents comp = get_components(storage_fmt);
+       DataType type = get_component_type(storage_fmt);
+       if(ARB_direct_state_access)
+               glTextureSubImage3D(id, level, x, y, z, wd, ht, dp, comp, type, data);
+       else
+               glTexSubImage3D(target, level, x, y, z, wd, ht, dp, comp, type, data);
+
+       if(auto_gen_mipmap && level==0)
+               generate_mipmap();
+}
+
+void Texture3D::sub_image(unsigned level, int x, int y, int z, unsigned wd, unsigned ht, unsigned dp, PixelComponents comp, DataType type, const void *data)
+{
+       if(comp!=get_components(format) || type!=get_component_type(format))
+               throw incompatible_data("Texture3D::sub_image");
+       sub_image(level, x, y, z, wd, ht, dp, data);
+}
+
+void Texture3D::image(const Graphics::Image &img, unsigned lv)
+{
+       unsigned w = img.get_width();
+       unsigned h = img.get_height();
+
+       if(h%w)
+               throw incompatible_data("Texture3D::load_image");
+       unsigned d = h/w;
+       h = w;
+
+       PixelFormat fmt = pixelformat_from_image(img);
+       if(width==0)
+               storage(make_pixelformat(get_components(fmt), get_component_type(fmt), use_srgb_format), w, h, d, lv);
+       else if(w!=width || h!=height || d!=depth)
+               throw incompatible_data("Texture3D::load_image");
+
+       PixelStore pstore = PixelStore::from_image(img);
+       BindRestore _bind_ps(pstore);
+
+       image(0, img.get_pixels());
+}
+
+unsigned Texture3D::get_n_levels() const
+{
+       unsigned s = max(width, height);
+       if(target!=GL_TEXTURE_2D_ARRAY)
+               s = max(s, depth);
+       unsigned n = 0;
+       for(; s; s>>=1, ++n) ;
+       return n;
+}
+
+void Texture3D::get_level_size(unsigned level, unsigned &w, unsigned &h, unsigned &d) const
+{
+       w >>= level;
+       h >>= level;
+       if(target!=GL_TEXTURE_2D_ARRAY)
+               d >>= level;
+
+       if(!w && (h || d))
+               w = 1;
+       if(!h && (w || d))
+               h = 1;
+       if(!d && (w || h))
+               d = 1;
+}
+
+UInt64 Texture3D::get_data_size() const
+{
+       return id ? width*height*depth*get_pixel_size(storage_fmt) : 0;
+}
+
+
+Texture3D::Loader::Loader(Texture3D &t):
+       DataFile::DerivedObjectLoader<Texture3D, Texture::Loader>(t)
+{
+       init();
+}
+
+Texture3D::Loader::Loader(Texture3D &t, Collection &c):
+       DataFile::DerivedObjectLoader<Texture3D, Texture::Loader>(t, c)
+{
+       init();
+}
+
+void Texture3D::Loader::init()
+{
+       add("raw_data", &Loader::raw_data);
+       add("storage", &Loader::storage);
+       add("storage", &Loader::storage_levels);
+}
+
+void Texture3D::Loader::raw_data(const string &data)
+{
+       obj.image(0, data.data());
+}
+
+void Texture3D::Loader::storage(PixelFormat fmt, unsigned w, unsigned h, unsigned d)
+{
+       obj.storage(fmt, w, h, d);
+}
+
+void Texture3D::Loader::storage_levels(PixelFormat fmt, unsigned w, unsigned h, unsigned d, unsigned l)
+{
+       obj.storage(fmt, w, h, d, l);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/texture3d.h b/source/core/texture3d.h
new file mode 100644 (file)
index 0000000..1f50e05
--- /dev/null
@@ -0,0 +1,97 @@
+#ifndef MSP_GL_TEXTURE3D_H_
+#define MSP_GL_TEXTURE3D_H_
+
+#include <string>
+#include "texture.h"
+
+namespace Msp {
+namespace GL {
+
+/**
+Three-dimensional texture.  Consists of an array of texels in the shape of a
+right cuboid.  Texture coordinates have a principal range of [0, 1].
+*/
+class Texture3D: public Texture
+{
+public:
+       class Loader: public Msp::DataFile::DerivedObjectLoader<Texture3D, Texture::Loader>
+       {
+       public:
+               Loader(Texture3D &);
+               Loader(Texture3D &, Collection &);
+       private:
+               void init();
+
+               void raw_data(const std::string &);
+               void storage(PixelFormat, unsigned, unsigned, unsigned);
+               void storage_levels(PixelFormat, unsigned, unsigned, unsigned, unsigned);
+       };
+
+private:
+       unsigned width;
+       unsigned height;
+       unsigned depth;
+       unsigned levels;
+       unsigned allocated;
+
+protected:
+       Texture3D(GLenum);
+public:
+       Texture3D();
+
+       /** Defines storage structure for the texture.  If lv is zero, the number
+       of mipmap levels is automatically determined from storage dimensions.
+
+       Must be called before an image can be uploaded.  Once storage is defined,
+       it can't be changed. */
+       void storage(PixelFormat fmt, unsigned wd, unsigned ht, unsigned dp, unsigned lv = 0);
+
+       DEPRECATED void storage(PixelComponents c, unsigned w, unsigned h, unsigned d, unsigned l = 0)
+       { storage(make_pixelformat(c, UNSIGNED_BYTE), w, h, d, l); }
+
+       /** Allocates storage for the texture.  The contents are initially
+       undefined.  If storage has already been allocated, does nothing. */
+       void allocate(unsigned level);
+
+       /** Updates the contents of the entire texture.  Storage must be defined
+       beforehand.  The image data must have dimensions and format matching the
+       defined storage. */
+       void image(unsigned level, const void *data);
+
+       DEPRECATED void image(unsigned level, PixelComponents comp, DataType type, const void *data);
+
+       /** Updates a cuboid-shaped region of the texture.  Storage must be defined
+       beforehand.  The image data must be in a format mathing the defined storage
+       and the update region must be fully inside the texture. */
+       void sub_image(unsigned level, int x, int y, int z, unsigned wd, unsigned ht, unsigned dp, const void *data);
+
+       DEPRECATED void sub_image(unsigned level,
+               int x, int y, int z, unsigned wd, unsigned ht, unsigned dp,
+               PixelComponents comp, DataType type, const void *data);
+
+       /** Updates the contents of the entire texture from an image.  If storage
+       has not been defined, it will be set to match the image.  In this case the
+       image will be treated as a stack of square layers and its height must be
+       divisible by its width.  Otherwise the image must match the defined
+       storage. */
+       virtual void image(const Graphics::Image &, unsigned = 0);
+
+       using Texture::image;
+
+       unsigned get_width() const { return width; }
+       unsigned get_height() const { return height; }
+       unsigned get_depth() const { return depth; }
+protected:
+       unsigned get_n_levels() const;
+       void get_level_size(unsigned, unsigned &, unsigned &, unsigned &) const;
+
+public:
+       virtual AsyncLoader *load(IO::Seekable &, const Resources * = 0) { return 0; }
+       virtual UInt64 get_data_size() const;
+       virtual void unload() { }
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/texturecube.cpp b/source/core/texturecube.cpp
new file mode 100644 (file)
index 0000000..6ec3662
--- /dev/null
@@ -0,0 +1,357 @@
+#include <msp/datafile/collection.h>
+#include <msp/gl/extensions/arb_texture_cube_map.h>
+#include <msp/gl/extensions/arb_texture_storage.h>
+#include <msp/io/memory.h>
+#include <msp/strings/format.h>
+#include "bindable.h"
+#include "error.h"
+#include "pixelstore.h"
+#include "texturecube.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+TextureCubeFace TextureCube::face_order[6] =
+{
+       POSITIVE_X,
+       NEGATIVE_X,
+       POSITIVE_Y,
+       NEGATIVE_Y,
+       POSITIVE_Z,
+       NEGATIVE_Z
+};
+
+Vector3 TextureCube::directions[6] =
+{
+       Vector3(1, 0, 0),
+       Vector3(-1, 0, 0),
+       Vector3(0, 1, 0),
+       Vector3(0, -1, 0),
+       Vector3(0, 0, 1),
+       Vector3(0, 0, -1)
+};
+
+unsigned TextureCube::orientations[12] =
+{
+       5, 3,
+       4, 3,
+       0, 4,
+       0, 5,
+       0, 3,
+       1, 3
+};
+
+TextureCube::TextureCube():
+       Texture(GL_TEXTURE_CUBE_MAP),
+       size(0),
+       allocated(0)
+{
+       static Require _req(ARB_texture_cube_map);
+}
+
+void TextureCube::storage(PixelFormat fmt, unsigned sz, unsigned lv)
+{
+       if(size>0)
+               throw invalid_operation("TextureCube::storage");
+       if(sz==0)
+               throw invalid_argument("TextureCube::storage");
+
+       set_format(fmt);
+       size = sz;
+       levels = get_n_levels();
+       if(lv>0)
+               levels = min(levels, lv);
+}
+
+void TextureCube::allocate(unsigned level)
+{
+       if(size==0)
+               throw invalid_operation("TextureCube::allocate");
+       if(level>=levels)
+               throw invalid_argument("TextureCube::allocate");
+       if(allocated&(64<<level))
+               return;
+
+       if(ARB_texture_storage)
+       {
+               BindRestore _bind(this);
+               glTexStorage2D(target, levels, storage_fmt, size, size);
+               apply_swizzle();
+               allocated |= (1<<levels)-1;
+       }
+       else
+       {
+               for(unsigned i=0; i<6; ++i)
+                       image(enumerate_faces(i), level, 0);
+       }
+}
+
+void TextureCube::image(TextureCubeFace face, unsigned level, const void *data)
+{
+       if(size==0)
+               throw invalid_operation("TextureCube::image");
+
+       unsigned s = get_level_size(level);
+       if(s==0)
+               throw out_of_range("TextureCube::image");
+
+       if(ARB_texture_storage)
+               return sub_image(face, level, 0, 0, s, s, data);
+
+       BindRestore _bind(this);
+
+       if(!allocated)
+       {
+               glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
+               apply_swizzle();
+       }
+
+       PixelComponents comp = get_components(storage_fmt);
+       DataType type = get_component_type(storage_fmt);
+       glTexImage2D(face, level, storage_fmt, s, s, 0, comp, type, data);
+
+       if(level==0)
+       {
+               allocated |= 1<<get_face_index(face);
+               if((allocated&63)==63)
+               {
+                       allocated |= 64;
+                       if(auto_gen_mipmap)
+                       {
+                               generate_mipmap();
+                               allocated |= (64<<levels)-1;
+                       }
+               }
+       }
+       else if(!(allocated&(64<<level)))
+       {
+               for(unsigned i=0; i<6; ++i)
+                       if(enumerate_faces(i)!=face)
+                               glTexImage2D(enumerate_faces(i), level, storage_fmt, s, s, 0, comp, type, 0);
+
+               allocated |= 64<<level;
+       }
+}
+
+void TextureCube::image(TextureCubeFace face, unsigned level, PixelComponents comp, DataType type, const void *data)
+{
+       if(comp!=get_components(format) || type!=get_component_type(format))
+               throw incompatible_data("TextureCube::image");
+       image(face, level, data);
+}
+
+void TextureCube::sub_image(TextureCubeFace face, unsigned level, int x, int y, unsigned wd, unsigned ht, const void *data)
+{
+       if(size==0)
+               throw invalid_operation("TextureCube::sub_image");
+
+       BindRestore _bind(this);
+       allocate(level);
+
+       PixelComponents comp = get_components(storage_fmt);
+       DataType type = get_component_type(storage_fmt);
+       glTexSubImage2D(face, level, x, y, wd, ht, comp, type, data);
+
+       if(auto_gen_mipmap && level==0)
+               generate_mipmap();
+}
+
+void TextureCube::sub_image(TextureCubeFace face, unsigned level, int x, int y, unsigned wd, unsigned ht, PixelComponents comp, DataType type, const void *data)
+{
+       if(comp!=get_components(format) || type!=get_component_type(format))
+               throw incompatible_data("TextureCube::subimage");
+       sub_image(face, level, x, y, wd, ht, data);
+}
+
+void TextureCube::image(TextureCubeFace face, const Graphics::Image &img)
+{
+       unsigned w = img.get_width();
+       unsigned h = img.get_height();
+       PixelFormat fmt = pixelformat_from_image(img);
+       if(size==0)
+       {
+               if(w!=h)
+                       throw incompatible_data("TextureCube::image");
+
+               storage(make_pixelformat(get_components(fmt), get_component_type(fmt), use_srgb_format), w);
+       }
+       else if(w!=size || h!=size)
+               throw incompatible_data("TextureCube::image");
+
+       PixelStore pstore = PixelStore::from_image(img);
+       BindRestore _bind_ps(pstore);
+
+       image(face, 0, img.get_pixels());
+}
+
+void TextureCube::image(TextureCubeFace face, const Graphics::Image &img, bool)
+{
+       image(face, img);
+}
+
+void TextureCube::image(const Graphics::Image &img, unsigned lv)
+{
+       unsigned w = img.get_width();
+       unsigned h = img.get_height();
+
+       if(h!=w*6)
+               throw incompatible_data("TextureCube::image");
+       h /= 6;
+
+       PixelFormat fmt = pixelformat_from_image(img);
+       if(size==0)
+               storage(make_pixelformat(get_components(fmt), get_component_type(fmt), use_srgb_format), w, lv);
+       else if(w!=size || h!=size)
+               throw incompatible_data("TextureCube::image");
+
+       PixelStore pstore = PixelStore::from_image(img);
+       BindRestore _bind_ps(pstore);
+
+       const char *pixels = reinterpret_cast<const char *>(img.get_pixels());
+       unsigned face_size = img.get_stride()*size;
+       for(unsigned i=0; i<6; ++i)
+               image(enumerate_faces(i), 0, pixels+i*face_size);
+}
+
+unsigned TextureCube::get_n_levels() const
+{
+       unsigned n = 0;
+       for(unsigned s=size; s; s>>=1, ++n) ;
+       return n;
+}
+
+unsigned TextureCube::get_level_size(unsigned level) const
+{
+       return size>>level;
+}
+
+TextureCubeFace TextureCube::enumerate_faces(unsigned i)
+{
+       if(i>=6)
+               throw out_of_range("TextureCube::enumerate_faces");
+       return face_order[i];
+}
+
+unsigned TextureCube::get_face_index(TextureCubeFace face)
+{
+       switch(face)
+       {
+       case POSITIVE_X: return 0;
+       case NEGATIVE_X: return 1;
+       case POSITIVE_Y: return 2;
+       case NEGATIVE_Y: return 3;
+       case POSITIVE_Z: return 4;
+       case NEGATIVE_Z: return 5;
+       default: throw invalid_argument("TextureCube::get_face_index");
+       }
+}
+
+const Vector3 &TextureCube::get_face_direction(TextureCubeFace face)
+{
+       return directions[get_face_index(face)];
+}
+
+const Vector3 &TextureCube::get_s_direction(TextureCubeFace face)
+{
+       return directions[orientations[get_face_index(face)*2]];
+}
+
+const Vector3 &TextureCube::get_t_direction(TextureCubeFace face)
+{
+       return directions[orientations[get_face_index(face)*2+1]];
+}
+
+Vector3 TextureCube::get_texel_direction(TextureCubeFace face, unsigned u, unsigned v)
+{
+       float s = (u+0.5f)*2.0f/size-1.0f;
+       float t = (v+0.5f)*2.0f/size-1.0f;
+       const Vector3 &fv = get_face_direction(face);
+       const Vector3 &sv = get_s_direction(face);
+       const Vector3 &tv = get_t_direction(face);
+       return fv+s*sv+t*tv;
+}
+
+UInt64 TextureCube::get_data_size() const
+{
+       return id ? size*size*6*get_pixel_size(storage_fmt) : 0;
+}
+
+
+TextureCube::Loader::Loader(TextureCube &t):
+       DataFile::DerivedObjectLoader<TextureCube, Texture::Loader>(t)
+{
+       init();
+}
+
+TextureCube::Loader::Loader(TextureCube &t, Collection &c):
+       DataFile::DerivedObjectLoader<TextureCube, Texture::Loader>(t, c)
+{
+       init();
+}
+
+void TextureCube::Loader::init()
+{
+       add("external_image", &Loader::external_image);
+       add("image_data", &Loader::image_data);
+       add("raw_data", &Loader::raw_data);
+       add("storage", &Loader::storage);
+       add("storage", &Loader::storage_levels);
+}
+
+void TextureCube::Loader::external_image(TextureCubeFace face, const string &fn)
+{
+       Graphics::Image img;
+       RefPtr<IO::Seekable> io = get_collection().open_raw(fn);
+       img.load_io(*io);
+
+       obj.image(face, img);
+}
+
+void TextureCube::Loader::image_data(TextureCubeFace face, const string &data)
+{
+       Graphics::Image img;
+       IO::Memory mem(data.data(), data.size());
+       img.load_io(mem);
+
+       obj.image(face, img);
+}
+
+void TextureCube::Loader::raw_data(TextureCubeFace face, const string &data)
+{
+       obj.image(face, 0, data.data());
+}
+
+void TextureCube::Loader::storage(PixelFormat fmt, unsigned s)
+{
+       obj.storage(fmt, s);
+}
+
+void TextureCube::Loader::storage_levels(PixelFormat fmt, unsigned s, unsigned l)
+{
+       obj.storage(fmt, s, l);
+}
+
+
+void operator>>(const LexicalConverter &conv, TextureCubeFace &face)
+{
+       const string &str = conv.get();
+       if(str=="POSITIVE_X")
+               face = POSITIVE_X;
+       else if(str=="NEGATIVE_X")
+               face = NEGATIVE_X;
+       else if(str=="POSITIVE_Y")
+               face = POSITIVE_Y;
+       else if(str=="NEGATIVE_Y")
+               face = NEGATIVE_Y;
+       else if(str=="POSITIVE_Z")
+               face = POSITIVE_Z;
+       else if(str=="NEGATIVE_Z")
+               face = NEGATIVE_Z;
+       else
+               throw lexical_error(format("conversion of '%s' to TextureCubeFace", str));
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/texturecube.h b/source/core/texturecube.h
new file mode 100644 (file)
index 0000000..ae30515
--- /dev/null
@@ -0,0 +1,138 @@
+#ifndef MSP_GL_TEXTURECUBE_H_
+#define MSP_GL_TEXTURECUBE_H_
+
+#include <msp/gl/extensions/arb_texture_cube_map.h>
+#include <msp/graphics/image.h>
+#include "texture.h"
+#include "vector.h"
+
+namespace Msp {
+namespace GL {
+
+enum TextureCubeFace
+{
+       POSITIVE_X = GL_TEXTURE_CUBE_MAP_POSITIVE_X,
+       NEGATIVE_X = GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
+       POSITIVE_Y = GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
+       NEGATIVE_Y = GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
+       POSITIVE_Z = GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
+       NEGATIVE_Z = GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
+};
+
+/**
+Cube map texture, consisting of six square faces.  All of the faces must be of
+the same size.  A cube map texture is addressed by three-dimensional texture
+coordinates, with a principal range of [-1, 1].  The face is first selected
+according to the largest coordinate, and the remaining two coordinates are used
+to sample the face image.  The images are oriented so that the cross product of
+the s and t axes will point into the cube.
+
+All faces of a cube map texture must be allocated for it to be usable.
+
+Requires OpenGL version 1.3.
+*/
+class TextureCube: public Texture
+{
+public:
+       class Loader: public Msp::DataFile::DerivedObjectLoader<TextureCube, Texture::Loader>
+       {
+       public:
+               Loader(TextureCube &);
+               Loader(TextureCube &, Collection &);
+       private:
+               void init();
+
+               void external_image(TextureCubeFace, const std::string &);
+               void image_data(TextureCubeFace, const std::string &);
+               void raw_data(TextureCubeFace, const std::string &);
+               void storage(PixelFormat, unsigned);
+               void storage_levels(PixelFormat, unsigned, unsigned);
+       };
+
+private:
+       unsigned size;
+       unsigned levels;
+       /* Lowest six bits track allocation status of faces on the base level.  Bit
+       seven is set if the entire base level is allocated. */
+       unsigned allocated;
+
+       static TextureCubeFace face_order[6];
+       static Vector3 directions[6];
+       static unsigned orientations[12];
+
+public:
+       TextureCube();
+
+       /** Defines storage structure for the texture.  If lv is zero, the number
+       of mipmap levels is automatically determined from storage dimensions.
+
+       Must be called before an image can be uploaded.  Once storage is defined,
+       it can't be changed. */
+       void storage(PixelFormat fmt, unsigned size, unsigned lv = 0);
+
+       DEPRECATED void storage(PixelComponents c, unsigned s, unsigned l = 0)
+       { storage(make_pixelformat(c, UNSIGNED_BYTE), s, l); }
+
+       /** Allocates storage for the cube faces.  The contents are initially
+       undefined.  If storage has already been allocated, does nothing. */
+       void allocate(unsigned level);
+
+       /** Updates the contents of a face.  Storage must be defined beforehand.
+       The image data must have dimensions and format matching the defined
+       storage. */
+       void image(TextureCubeFace face, unsigned level, const void *data);
+
+       DEPRECATED void image(TextureCubeFace face, unsigned level,
+               PixelComponents comp, DataType type, const void *data);
+
+       /** Updates a rectangular region of a face.  Storage must be defined
+       beforehand.  The image data must be in a format mathing the defined storage
+       and the update region must be fully inside the face. */
+       void sub_image(TextureCubeFace face, unsigned level, int x, int y, unsigned w, unsigned h, const void *data);
+
+       DEPRECATED void sub_image(TextureCubeFace face, unsigned level,
+               int x, int y, unsigned w, unsigned h,
+               PixelComponents comp, DataType type, const void *data);
+
+       void image(TextureCubeFace, const Graphics::Image &);
+
+       DEPRECATED void image(TextureCubeFace, const Graphics::Image &, bool);
+
+       virtual void image(const Graphics::Image &, unsigned = 0);
+       using Texture::image;
+
+       unsigned get_size() const { return size; }
+private:
+       unsigned get_n_levels() const;
+       unsigned get_level_size(unsigned) const;
+
+public:
+       /** Translates indices into face constants.  Valid indices are between 0
+       and 5, inclusive. */
+       static TextureCubeFace enumerate_faces(unsigned);
+
+       static unsigned get_face_index(TextureCubeFace);
+
+       /** Returns a vector pointing out of the face. */
+       static const Vector3 &get_face_direction(TextureCubeFace);
+
+       /** Returns a vector in the direction of the s axis of the face. */
+       static const Vector3 &get_s_direction(TextureCubeFace);
+
+       /** Returns a vector in the direction of the t axis of the face. */
+       static const Vector3 &get_t_direction(TextureCubeFace);
+
+       /** Returns a vector pointing to the center a texel. */
+       Vector3 get_texel_direction(TextureCubeFace, unsigned, unsigned);
+
+       virtual AsyncLoader *load(IO::Seekable &, const Resources * = 0) { return 0; }
+       virtual UInt64 get_data_size() const;
+       virtual void unload() { }
+};
+
+void operator>>(const LexicalConverter &, TextureCubeFace &);
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/texunit.cpp b/source/core/texunit.cpp
new file mode 100644 (file)
index 0000000..0659d61
--- /dev/null
@@ -0,0 +1,92 @@
+#include <stdexcept>
+#include <msp/gl/extensions/arb_multitexture.h>
+#include <msp/gl/extensions/arb_vertex_shader.h>
+#include "gl.h"
+#include "misc.h"
+#include "texture.h"
+#include "texunit.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+vector<TexUnit> TexUnit::units;
+TexUnit *TexUnit::cur_unit = 0;
+
+TexUnit::TexUnit():
+       texture(0),
+       sampler(0)
+{ }
+
+bool TexUnit::set_texture(const Texture *tex)
+{
+       bool result = (tex!=texture);
+       texture = tex;
+       return result;
+}
+
+bool TexUnit::set_sampler(const Sampler *samp)
+{
+       bool result = (samp!=sampler);
+       sampler = samp;
+       return result;
+}
+
+void TexUnit::bind()
+{
+       if(cur_unit!=this && (cur_unit || index))
+               glActiveTexture(GL_TEXTURE0+index);
+       cur_unit = this;
+}
+
+unsigned TexUnit::get_n_units()
+{
+       static int count = -1;
+       if(count<0)
+       {
+               if(ARB_vertex_shader)
+                       count = get_i(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS);
+               else if(ARB_multitexture)
+                       count = get_i(GL_MAX_TEXTURE_UNITS);
+               else
+                       count = 1;
+       }
+       return count;
+}
+
+TexUnit &TexUnit::get_unit(unsigned n)
+{
+       if(n>0)
+               static Require _req(ARB_multitexture);
+       if(n>=get_n_units())
+               throw out_of_range("TexUnit::get_unit");
+
+       if(units.size()<=n)
+       {
+               unsigned i = units.size();
+               units.resize(n+1, TexUnit());
+               for(; i<units.size(); ++i)
+                       units[i].index = i;
+       }
+
+       return units[n];
+}
+
+TexUnit &TexUnit::current()
+{
+       if(!cur_unit)
+               get_unit(0).bind();
+       return *cur_unit;
+}
+
+TexUnit *TexUnit::find_unit(const Texture *tex)
+{
+       for(vector<TexUnit>::iterator i=units.begin(); i!=units.end(); ++i)
+               if(i->texture==tex)
+                       return &*i;
+       return 0;
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/texunit.h b/source/core/texunit.h
new file mode 100644 (file)
index 0000000..6253d8d
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef MSP_GL_TEXUNIT_H_
+#define MSP_GL_TEXUNIT_H_
+
+#include <vector>
+
+namespace Msp {
+namespace GL {
+
+class Sampler;
+class Texture;
+
+/**
+Keeps track of texture unit related state.  Intended for internal use.
+*/
+class TexUnit
+{
+private:
+       unsigned index;
+       const Texture *texture;
+       const Sampler *sampler;
+
+       static std::vector<TexUnit> units;
+       static TexUnit *cur_unit;
+
+       TexUnit();
+
+public:
+       unsigned get_index() const { return index; }
+       bool set_texture(const Texture *);
+       const Texture *get_texture() const { return texture; }
+       bool set_sampler(const Sampler *);
+       const Sampler *get_sampler() const { return sampler; }
+       void bind();
+
+       static unsigned get_n_units();
+       static TexUnit &get_unit(unsigned);
+       static TexUnit &current();
+       static TexUnit *find_unit(const Texture *);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/uniform.cpp b/source/core/uniform.cpp
new file mode 100644 (file)
index 0000000..c18fc36
--- /dev/null
@@ -0,0 +1,113 @@
+#include <msp/gl/extensions/arb_shader_objects.h>
+#include <msp/gl/extensions/nv_non_square_matrices.h>
+#include "uniform.h"
+
+namespace Msp {
+namespace GL {
+
+template<>
+void UniformScalar<int>::apply(int index, unsigned size, const int *value)
+{
+       glUniform1iv(index, size, value);
+}
+
+template<>
+void UniformScalar<float>::apply(int index, unsigned size, const float *value)
+{
+       glUniform1fv(index, size, value);
+}
+
+
+template<>
+void UniformVector<int, 2>::apply(int index, unsigned size, const int *value)
+{
+       glUniform2iv(index, size, value);
+}
+
+template<>
+void UniformVector<float, 2>::apply(int index, unsigned size, const float *value)
+{
+       glUniform2fv(index, size, value);
+}
+
+template<>
+void UniformVector<int, 3>::apply(int index, unsigned size, const int *value)
+{
+       glUniform3iv(index, size, value);
+}
+
+template<>
+void UniformVector<float, 3>::apply(int index, unsigned size, const float *value)
+{
+       glUniform3fv(index, size, value);
+}
+
+template<>
+void UniformVector<int, 4>::apply(int index, unsigned size, const int *value)
+{
+       glUniform4iv(index, size, value);
+}
+
+template<>
+void UniformVector<float, 4>::apply(int index, unsigned size, const float *value)
+{
+       glUniform4fv(index, size, value);
+}
+
+
+template<>
+void UniformMatrix<float, 2, 2>::apply(int index, unsigned size, const float *value)
+{
+       glUniformMatrix2fv(index, size, false, value);
+}
+
+template<>
+void UniformMatrix<float, 2, 3>::apply(int index, unsigned size, const float *value)
+{
+       glUniformMatrix3x2fv(index, size, false, value);
+}
+
+template<>
+void UniformMatrix<float, 2, 4>::apply(int index, unsigned size, const float *value)
+{
+       glUniformMatrix4x2fv(index, size, false, value);
+}
+
+template<>
+void UniformMatrix<float, 3, 2>::apply(int index, unsigned size, const float *value)
+{
+       glUniformMatrix2x3fv(index, size, false, value);
+}
+
+template<>
+void UniformMatrix<float, 3, 3>::apply(int index, unsigned size, const float *value)
+{
+       glUniformMatrix3fv(index, size, false, value);
+}
+
+template<>
+void UniformMatrix<float, 3, 4>::apply(int index, unsigned size, const float *value)
+{
+       glUniformMatrix4x3fv(index, size, false, value);
+}
+
+template<>
+void UniformMatrix<float, 4, 2>::apply(int index, unsigned size, const float *value)
+{
+       glUniformMatrix2x4fv(index, size, false, value);
+}
+
+template<>
+void UniformMatrix<float, 4, 3>::apply(int index, unsigned size, const float *value)
+{
+       glUniformMatrix3x4fv(index, size, false, value);
+}
+
+template<>
+void UniformMatrix<float, 4, 4>::apply(int index, unsigned size, const float *value)
+{
+       glUniformMatrix4fv(index, size, false, value);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/uniform.h b/source/core/uniform.h
new file mode 100644 (file)
index 0000000..bc78bab
--- /dev/null
@@ -0,0 +1,193 @@
+#ifndef MSP_GL_UNIFORM_H_
+#define MSP_GL_UNIFORM_H_
+
+#include <algorithm>
+#include "program.h"
+
+namespace Msp {
+namespace GL {
+
+class Uniform
+{
+protected:
+       Uniform() { }
+private:
+       Uniform(const Uniform &);
+       Uniform &operator=(const Uniform &);
+public:
+       virtual ~Uniform() { }
+
+       virtual void apply(int) const = 0;
+       virtual void store(const Program::UniformInfo &, void *) const = 0;
+       virtual Uniform *clone() const = 0;
+};
+
+
+template<typename T>
+class UniformScalar: public Uniform
+{
+public:
+       typedef T BaseType;
+       typedef T Type;
+
+private:
+       Type value;
+
+public:
+       UniformScalar(Type v): value(v) { }
+
+       void set(Type v) { value = v; }
+
+       Type get() const { return value; }
+
+       virtual void apply(int index) const
+       { apply(index, 1, &value); }
+
+       static void apply(int, unsigned, const T *);
+
+       virtual void store(const Program::UniformInfo &info, void *buffer) const
+       { store(info, buffer, &value); }
+
+       static void store(const Program::UniformInfo &, void *buffer, const T *value)
+       { *reinterpret_cast<T *>(buffer) = *value; }
+
+       virtual UniformScalar *clone() const
+       { return new UniformScalar(value); }
+};
+
+typedef UniformScalar<int> Uniform1i;
+typedef UniformScalar<float> Uniform1f;
+
+
+template<typename T, unsigned vecsize>
+class UniformVector: public Uniform
+{
+public:
+       typedef T BaseType;
+       typedef T Type[vecsize];
+
+private:
+       Type value;
+
+public:
+       UniformVector(const T *vp) { set(vp); }
+
+       void set(const T *vp)
+       { std::copy(vp, vp+vecsize, value); }
+
+       BaseType get(unsigned i) const { return value[i]; }
+
+       virtual void apply(int index) const
+       { apply(index, 1, value); }
+
+       static void apply(int index, unsigned size, const T *value);
+
+       virtual void store(const Program::UniformInfo &info, void *buffer) const
+       { store(info, buffer, value); }
+
+       static void store(const Program::UniformInfo &, void *buffer, const T *value)
+       { std::copy(value, value+vecsize, reinterpret_cast<T *>(buffer)); }
+
+       virtual UniformVector *clone() const
+       { return new UniformVector(value); }
+};
+
+typedef UniformVector<int, 2> Uniform2i;
+typedef UniformVector<float, 2> Uniform2f;
+typedef UniformVector<int, 3> Uniform3i;
+typedef UniformVector<float, 3> Uniform3f;
+typedef UniformVector<int, 4> Uniform4i;
+typedef UniformVector<float, 4> Uniform4f;
+
+
+template<typename T, unsigned rows, unsigned cols>
+class UniformMatrix: public Uniform
+{
+public:
+       typedef T BaseType;
+       typedef T Type[rows*cols];
+
+private:
+       Type value;
+
+public:
+       UniformMatrix(const T *vp) { set(vp); }
+
+       void set(const T *vp)
+       { std::copy(vp, vp+rows*cols, value); }
+
+       virtual void apply(int index) const
+       { apply(index, 1, value); }
+
+       static void apply(int index, unsigned size, const T *value);
+
+       virtual void store(const Program::UniformInfo &info, void *buffer) const
+       { store(info, buffer, value); }
+
+       static void store(const Program::UniformInfo &info, void *buffer, const T *value)
+       {
+               for(unsigned i=0; i<cols; ++i)
+                       UniformVector<T, rows>::store(info, reinterpret_cast<char *>(buffer)+i*info.matrix_stride, value+i*rows);
+       }
+
+       virtual UniformMatrix *clone() const
+       { return new UniformMatrix(value); }
+};
+
+// The naming of these types follows the OpenGL convention of columns x rows
+typedef UniformMatrix<float, 2, 2> UniformMatrix2x2f;
+typedef UniformMatrix<float, 2, 3> UniformMatrix3x2f;
+typedef UniformMatrix<float, 2, 4> UniformMatrix4x2f;
+typedef UniformMatrix<float, 3, 2> UniformMatrix2x3f;
+typedef UniformMatrix<float, 3, 3> UniformMatrix3x3f;
+typedef UniformMatrix<float, 3, 4> UniformMatrix4x3f;
+typedef UniformMatrix<float, 4, 2> UniformMatrix2x4f;
+typedef UniformMatrix<float, 4, 3> UniformMatrix3x4f;
+typedef UniformMatrix<float, 4, 4> UniformMatrix4x4f;
+
+
+template<typename T>
+class UniformArray: public Uniform
+{
+private:
+       typedef typename T::BaseType BaseType;
+       enum { elemsize = sizeof(typename T::Type)/sizeof(typename T::BaseType) };
+
+       unsigned size_;
+       BaseType *values;
+
+public:
+       UniformArray(unsigned n, const BaseType *vp):
+               size_(n),
+               values(new BaseType[elemsize*size_])
+       {
+               set(vp);
+       }
+
+       ~UniformArray()
+       {
+               delete[] values;
+       }
+
+       unsigned size() const { return size_; }
+
+       void set(const BaseType *vp)
+       { std::copy(vp, vp+elemsize*size_, values); }
+
+       virtual void apply(int index) const
+       { T::apply(index, size_, values); }
+
+       virtual void store(const Program::UniformInfo &info, void *buffer) const
+       {
+               for(unsigned i=0; i<size_; ++i)
+                       T::store(info, reinterpret_cast<char *>(buffer)+i*info.array_stride, values+i*elemsize);
+       }
+
+       virtual UniformArray *clone() const
+       { return new UniformArray(size_, values); }
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/uniformblock.cpp b/source/core/uniformblock.cpp
new file mode 100644 (file)
index 0000000..cc8945c
--- /dev/null
@@ -0,0 +1,91 @@
+#include <stdexcept>
+#include <msp/gl/extensions/arb_shader_objects.h>
+#include <msp/gl/extensions/arb_uniform_buffer_object.h>
+#include "buffer.h"
+#include "color.h"
+#include "error.h"
+#include "matrix.h"
+#include "uniform.h"
+#include "uniformblock.h"
+#include "vector.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+UniformBlock::UniformBlock():
+       size(0),
+       buf_range(0)
+{
+       static Require _req(ARB_shader_objects);
+}
+
+UniformBlock::UniformBlock(unsigned s):
+       size(s),
+       buf_range(0)
+{
+       static Require _req(ARB_uniform_buffer_object);
+
+       if(!size)
+               throw invalid_argument("UniformBlock::UniformBlock");
+       data.resize(size);
+}
+
+UniformBlock::~UniformBlock()
+{
+       delete buf_range;
+}
+
+unsigned UniformBlock::get_alignment() const
+{
+       return BufferRange::get_uniform_buffer_alignment();
+}
+
+void UniformBlock::location_changed(Buffer *buf, unsigned off, unsigned) const
+{
+       delete buf_range;
+       buf_range = new BufferRange(*buf, off, size);
+}
+
+void UniformBlock::attach(int index, const Uniform &uni)
+{
+       if(size)
+               throw invalid_operation("UniformBlock::attach");
+
+       uniforms[index] = &uni;
+}
+
+void UniformBlock::attach(const Program::UniformInfo &info, const Uniform &uni)
+{
+       if(size)
+       {
+               uni.store(info, &data[info.location]);
+               dirty = true;
+       }
+       else
+               uniforms[info.location] = &uni;
+}
+
+void UniformBlock::apply(int index) const
+{
+       if((index>=0) != (size>0))
+               throw invalid_operation("UniformBlock::apply");
+
+       if(size)
+       {
+               if(!get_buffer())
+                       throw invalid_operation("UniformBlock::apply");
+
+               refresh();
+               buf_range->bind_to(UNIFORM_BUFFER, index);
+       }
+       else
+       {
+               for(map<int, const Uniform *>::const_iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
+                       i->second->apply(i->first);
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/uniformblock.h b/source/core/uniformblock.h
new file mode 100644 (file)
index 0000000..c8b8d36
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef MSP_GL_UNIFORMBLOCK_H_
+#define MSP_GL_UNIFORMBLOCK_H_
+
+#include <map>
+#include <vector>
+#include "bufferable.h"
+#include "program.h"
+#include "vector.h"
+
+namespace Msp {
+namespace GL {
+
+class BufferRange;
+class Matrix;
+class Uniform;
+struct Color;
+
+/**
+Stores uniforms with a specific layout.  Both named and default uniform blocks
+are supported.
+*/
+class UniformBlock: public Bufferable
+{
+private:
+       std::map<int, const Uniform *> uniforms;
+       unsigned size;
+       std::vector<char> data;
+       mutable BufferRange *buf_range;
+
+       UniformBlock(const UniformBlock &);
+       UniformBlock &operator=(const UniformBlock &);
+public:
+       UniformBlock();
+       UniformBlock(unsigned);
+       ~UniformBlock();
+
+private:
+       virtual unsigned get_data_size() const { return size; }
+       virtual const void *get_data_pointer() const { return &data[0]; }
+       virtual unsigned get_alignment() const;
+       virtual void location_changed(Buffer *, unsigned, unsigned) const;
+
+public:
+       void attach(int, const Uniform &);
+       void attach(const Program::UniformInfo &, const Uniform &);
+
+       void apply(int) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/vector.h b/source/core/vector.h
new file mode 100644 (file)
index 0000000..5a4ffb6
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef MSP_GL_VECTOR_H_
+#define MSP_GL_VECTOR_H_
+
+#include <msp/linal/vector.h>
+
+namespace Msp {
+namespace GL {
+
+typedef LinAl::Vector<float, 3> Vector3;
+typedef LinAl::Vector<float, 4> Vector4;
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/vertexarray.cpp b/source/core/vertexarray.cpp
new file mode 100644 (file)
index 0000000..e3aef7a
--- /dev/null
@@ -0,0 +1,103 @@
+#include <msp/gl/extensions/arb_multitexture.h>
+#include <msp/gl/extensions/arb_vertex_shader.h>
+#include "buffer.h"
+#include "error.h"
+#include "gl.h"
+#include "mesh.h"
+#include "vertexarray.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+VertexArray::VertexArray(const VertexFormat &f)
+{
+       reset(f);
+}
+
+void VertexArray::reset(const VertexFormat &f)
+{
+       clear();
+       format = f;
+       stride = get_stride(format);
+}
+
+void VertexArray::clear()
+{
+       data.clear();
+}
+
+void VertexArray::reserve(unsigned n)
+{
+       data.reserve(n*stride);
+}
+
+float *VertexArray::append()
+{
+       data.insert(data.end(), stride, 0.0f);
+       update_offset();
+       dirty = true;
+       return &*(data.end()-stride);
+}
+
+float *VertexArray::modify(unsigned i)
+{
+       dirty = true;
+       return &data[0]+i*stride;
+}
+
+unsigned VertexArray::get_data_size() const
+{
+       return data.size()*sizeof(float);
+}
+
+
+VertexArray::Loader::Loader(VertexArray &a):
+       VertexArrayBuilder(a)
+{
+       add("vertex", static_cast<void (Loader::*)(float, float)>(&Loader::vertex));
+       add("vertex", static_cast<void (Loader::*)(float, float, float)>(&Loader::vertex));
+       add("vertex", static_cast<void (Loader::*)(float, float, float, float)>(&Loader::vertex));
+       add("normal", static_cast<void (Loader::*)(float, float, float)>(&Loader::normal));
+       add("texcoord", static_cast<void (Loader::*)(float)>(&Loader::texcoord));
+       add("texcoord", static_cast<void (Loader::*)(float, float)>(&Loader::texcoord));
+       add("texcoord", static_cast<void (Loader::*)(float, float, float)>(&Loader::texcoord));
+       add("texcoord", static_cast<void (Loader::*)(float, float, float, float)>(&Loader::texcoord));
+       add("multitexcoord", static_cast<void (Loader::*)(unsigned, float)>(&Loader::multitexcoord));
+       add("multitexcoord", static_cast<void (Loader::*)(unsigned, float, float)>(&Loader::multitexcoord));
+       add("multitexcoord", static_cast<void (Loader::*)(unsigned, float, float, float)>(&Loader::multitexcoord));
+       add("multitexcoord", static_cast<void (Loader::*)(unsigned, float, float, float, float)>(&Loader::multitexcoord));
+       add("color", static_cast<void (Loader::*)(float, float, float)>(&Loader::color));
+       add("color", static_cast<void (Loader::*)(float, float, float, float)>(&Loader::color));
+       add("attrib", static_cast<void (Loader::*)(unsigned, float)>(&Loader::attrib));
+       add("attrib", static_cast<void (Loader::*)(unsigned, float, float)>(&Loader::attrib));
+       add("attrib", static_cast<void (Loader::*)(unsigned, float, float, float)>(&Loader::attrib));
+       add("attrib", static_cast<void (Loader::*)(unsigned, float, float, float, float)>(&Loader::attrib));
+       add("tangent", static_cast<void (Loader::*)(float, float, float)>(&Loader::tangent));
+       add("binormal", static_cast<void (Loader::*)(float, float, float)>(&Loader::binormal));
+
+       add("vertex2",   static_cast<void (Loader::*)(float, float)>(&Loader::vertex));
+       add("vertex3",   static_cast<void (Loader::*)(float, float, float)>(&Loader::vertex));
+       add("vertex4",   static_cast<void (Loader::*)(float, float, float, float)>(&Loader::vertex));
+       add("normal3",   static_cast<void (Loader::*)(float, float, float)>(&Loader::normal));
+       add("texcoord1", static_cast<void (Loader::*)(float)>(&Loader::texcoord));
+       add("texcoord2", static_cast<void (Loader::*)(float, float)>(&Loader::texcoord));
+       add("texcoord3", static_cast<void (Loader::*)(float, float, float)>(&Loader::texcoord));
+       add("texcoord4", static_cast<void (Loader::*)(float, float, float, float)>(&Loader::texcoord));
+       add("multitexcoord1", static_cast<void (Loader::*)(unsigned, float)>(&Loader::multitexcoord));
+       add("multitexcoord2", static_cast<void (Loader::*)(unsigned, float, float)>(&Loader::multitexcoord));
+       add("multitexcoord3", static_cast<void (Loader::*)(unsigned, float, float, float)>(&Loader::multitexcoord));
+       add("multitexcoord4", static_cast<void (Loader::*)(unsigned, float, float, float, float)>(&Loader::multitexcoord));
+       add("color3",    static_cast<void (Loader::*)(float, float, float)>(&Loader::color));
+       add("color4",    static_cast<void (Loader::*)(float, float, float, float)>(&Loader::color));
+       add("attrib1",   static_cast<void (Loader::*)(unsigned, float)>(&Loader::attrib));
+       add("attrib2",   static_cast<void (Loader::*)(unsigned, float, float)>(&Loader::attrib));
+       add("attrib3",   static_cast<void (Loader::*)(unsigned, float, float, float)>(&Loader::attrib));
+       add("attrib4",   static_cast<void (Loader::*)(unsigned, float, float, float, float)>(&Loader::attrib));
+       add("tangent3",  static_cast<void (Loader::*)(float, float, float)>(&Loader::tangent));
+       add("binormal3", static_cast<void (Loader::*)(float, float, float)>(&Loader::binormal));
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/vertexarray.h b/source/core/vertexarray.h
new file mode 100644 (file)
index 0000000..95a6ec2
--- /dev/null
@@ -0,0 +1,77 @@
+#ifndef MSP_GL_VERTEXARRAY_H_
+#define MSP_GL_VERTEXARRAY_H_
+
+#include <climits>
+#include <vector>
+#include <msp/core/refptr.h>
+#include <msp/datafile/loader.h>
+#include "bufferable.h"
+#include "datatype.h"
+#include "primitivetype.h"
+#include "vertexarraybuilder.h"
+#include "vertexformat.h"
+
+namespace Msp {
+namespace GL {
+
+class Buffer;
+
+/**
+Stores vertex data.
+
+The array's contents can be modified with the append and modify methods.  To
+obtain the location of an individual component within the vertex, use
+VertexFormat::offset.
+
+A higher-level interface for filling in vertex data is available in the
+VertexArrayBuilder class.
+*/
+class VertexArray: public Bufferable
+{
+public:
+       class Loader: public DataFile::Loader, public VertexArrayBuilder
+       {
+       public:
+               Loader(VertexArray &);
+       };
+
+private:
+       VertexFormat format;
+       std::vector<float> data;
+       unsigned stride;
+
+       VertexArray(const VertexArray &);
+       VertexArray &operator=(const VertexArray &);
+public:
+       VertexArray(const VertexFormat &);
+
+       /// Resets the VertexArray to a different format.  All data is cleared.
+       void reset(const VertexFormat &);
+
+       const VertexFormat &get_format() const { return format; }
+
+       /// Clears all vertices from the array.
+       void clear();
+
+       /// Reserve space for vertices.
+       void reserve(unsigned);
+
+       /// Append a new vertex at the end of the array and return its location.
+       float *append();
+
+       /// Returns the location of a vertex for modification.
+       float *modify(unsigned);
+private:
+       virtual unsigned get_data_size() const;
+       virtual const void *get_data_pointer() const { return &data[0]; }
+
+public:
+       unsigned size() const { return data.size()/stride; }
+       const std::vector<float> &get_data() const { return data; }
+       const float *operator[](unsigned i) const { return &data[0]+i*stride; }
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/vertexformat.cpp b/source/core/vertexformat.cpp
new file mode 100644 (file)
index 0000000..6c2e293
--- /dev/null
@@ -0,0 +1,141 @@
+#include <algorithm>
+#include <msp/strings/format.h>
+#include <msp/strings/lexicalcast.h>
+#include <msp/strings/utils.h>
+#include "error.h"
+#include "vertexformat.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+VertexFormat::VertexFormat():
+       count(0)
+{ }
+
+VertexFormat::VertexFormat(VertexComponent c):
+       count(1)
+{
+       components[0] = c;
+}
+
+VertexFormat VertexFormat::operator,(VertexComponent c) const
+{
+       if(count>=MAX_COMPONENTS)
+               throw invalid_operation("VertexFormat::operator,");
+
+       VertexFormat r = *this;
+       r.components[r.count++] = c;
+
+       return r;
+}
+
+VertexFormat VertexFormat::operator,(unsigned i) const
+{
+       if(!count)
+               throw invalid_operation("VertexFormat::operator,");
+
+       VertexFormat r = *this;
+       unsigned char *c = &r.components[r.count-1];
+       *c = make_indexed_component(static_cast<VertexComponent>(*c), i);
+
+       return r;
+}
+
+bool VertexFormat::operator==(const VertexFormat &other) const
+{
+       if(count!=other.count)
+               return false;
+       return equal(components, components+count, other.components);
+}
+
+unsigned VertexFormat::stride() const
+{
+       unsigned s = 0;
+       for(const unsigned char *i=begin(); i!=end(); ++i)
+               s += get_component_size(*i);
+       return s;
+}
+
+int VertexFormat::offset(VertexComponent comp) const
+{
+       unsigned type = get_component_type(comp);
+       unsigned size = get_component_size(comp);
+       unsigned offs = 0;
+       for(const unsigned char *i=begin(); i!=end(); ++i)
+       {
+               if(get_component_type(*i)==type)
+               {
+                       if(get_component_size(*i)>=size)
+                               return offs;
+                       else
+                               return -1;
+               }
+               else
+                       offs += get_component_size(*i);
+       }
+
+       return -1;
+}
+
+VertexComponent make_indexed_component(VertexComponent comp, unsigned index)
+{
+       if(comp>=TEXCOORD1 && comp<=TEXCOORD4)
+       {
+               if(index>=4)
+                       throw out_of_range("make_indexed_component");
+       }
+       else if(comp>=ATTRIB1 && comp<=ATTRIB4)
+       {
+               if(index>=24)
+                       throw out_of_range("make_indexed_component");
+       }
+       else
+               throw invalid_argument("make_indexed_component");
+       return static_cast<VertexComponent>(comp+index*4);
+}
+
+void operator>>(const LexicalConverter &conv, VertexComponent &c)
+{
+       const string &str = conv.get();
+       if(str.size()==7 && !str.compare(0, 6, "VERTEX") && str[6]>='2' && str[6]<='4')
+               c = static_cast<VertexComponent>(VERTEX2+(str[6]-'2'));
+       else if(str=="NORMAL3")
+               c = NORMAL3;
+       else if(str.size()==12 && !str.compare(0, 5, "COLOR") && str[5]>='3' && str[5]<='4' && !str.compare(6, 6, "_FLOAT"))
+               c = static_cast<VertexComponent>(COLOR3_FLOAT+(str[5]-'3'));
+       else if(str=="COLOR4_UBYTE")
+               c = COLOR4_UBYTE;
+       else if(str=="TANGENT3")
+               c = TANGENT3;
+       else if(str=="BINORMAL3")
+               c = BINORMAL3;
+       else if(str.size()>=9 && !str.compare(0, 8, "TEXCOORD") && str[8]>='1' && str[8]<='4')
+       {
+               if(str.size()==9)
+                       c = static_cast<VertexComponent>(TEXCOORD1+(str[8]-'1'));
+               else if(str.size()==11 && str[9]=='_' && str[10]>='0' && str[10]<='7')
+                       c = static_cast<VertexComponent>(TEXCOORD1+(str[8]-'1')+(str[10]-'0')*4);
+               else
+                       throw lexical_error(format("conversion of '%s' to VertexComponent", str));
+       }
+       else if(str.size()>=9 && !str.compare(0, 6, "ATTRIB") && str[6]>='1' && str[6]<='4' && str[7]=='_')
+       {
+               unsigned n;
+               try
+               {
+                       n = lexical_cast<unsigned>(str.substr(8));
+               }
+               catch(const lexical_error &)
+               {
+                       throw lexical_error(format("conversion of '%s' to VertexComponent", str));
+               }
+               c = static_cast<VertexComponent>(ATTRIB1+(str[6]-'1')+n*4);
+       }
+       else
+               throw lexical_error(format("conversion of '%s' to VertexComponent", str));
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/vertexformat.h b/source/core/vertexformat.h
new file mode 100644 (file)
index 0000000..3561292
--- /dev/null
@@ -0,0 +1,79 @@
+#ifndef MSP_GL_VERTEXFORMAT_H_
+#define MSP_GL_VERTEXFORMAT_H_
+
+#include <msp/strings/lexicalcast.h>
+
+namespace Msp {
+namespace GL {
+
+/** A single vertex component.  Symbolic names are provided for commonly used
+attributes.  These are aliased with with generic attributes, so be careful when
+picking your attribute indices. */
+enum VertexComponent
+{
+       VERTEX2 = 1,
+       VERTEX3,
+       VERTEX4,
+       NORMAL3 = 10,
+       COLOR4_UBYTE = 12,
+       COLOR3_FLOAT = 14,
+       COLOR4_FLOAT,
+       TANGENT3 = 18,
+       BINORMAL3 = 22,
+       TEXCOORD1 = 32,
+       TEXCOORD2,
+       TEXCOORD3,
+       TEXCOORD4,
+       ATTRIB1 = 64,
+       ATTRIB2,
+       ATTRIB3,
+       ATTRIB4
+};
+
+class VertexFormat
+{
+private:
+       enum { MAX_COMPONENTS = 15 };
+
+       unsigned char count;
+       unsigned char components[MAX_COMPONENTS];
+
+public:
+       VertexFormat();
+       VertexFormat(VertexComponent);
+
+       VertexFormat operator,(VertexComponent c) const;
+       VertexFormat operator,(unsigned i) const;
+       bool operator==(const VertexFormat &) const;
+       bool operator!=(const VertexFormat &other) const { return !(*this==other); }
+
+       bool empty() const { return !count; }
+       const unsigned char *begin() const { return components; }
+       const unsigned char *end() const { return components+count; }
+       unsigned stride() const;
+       int offset(VertexComponent) const;
+};
+
+inline VertexFormat operator,(VertexComponent c1, VertexComponent c2)
+{ return (VertexFormat(c1), c2); }
+
+inline VertexFormat operator,(VertexComponent c, unsigned i)
+{ return (VertexFormat(c), i); }
+
+VertexComponent make_indexed_component(VertexComponent, unsigned);
+
+inline unsigned get_component_type(unsigned char c)
+{ return c>>2; }
+
+inline unsigned get_component_size(unsigned char c)
+{ return (c&3)+1; }
+
+inline unsigned get_stride(const VertexFormat &f)
+{ return f.stride(); }
+
+void operator>>(const LexicalConverter &, VertexComponent &);
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/vertexsetup.cpp b/source/core/vertexsetup.cpp
new file mode 100644 (file)
index 0000000..d50406a
--- /dev/null
@@ -0,0 +1,202 @@
+#include <msp/core/raii.h>
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/gl/extensions/arb_instanced_arrays.h>
+#include <msp/gl/extensions/arb_vertex_array_object.h>
+#include <msp/gl/extensions/arb_vertex_attrib_binding.h>
+#include <msp/gl/extensions/arb_vertex_buffer_object.h>
+#include <msp/gl/extensions/arb_vertex_shader.h>
+#include "buffer.h"
+#include "error.h"
+#include "gl.h"
+#include "vertexarray.h"
+#include "vertexsetup.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+VertexSetup::VertexSetup():
+       dirty(0),
+       vertex_array(0),
+       inst_array(0),
+       index_buffer(0)
+{
+       static Require req(ARB_vertex_array_object);
+       if(ARB_direct_state_access)
+               glCreateVertexArrays(1, &id);
+       else
+               glGenVertexArrays(1, &id);
+}
+
+VertexSetup::~VertexSetup()
+{
+       if(current()==this)
+               unbind();
+       glDeleteVertexArrays(1, &id);
+}
+
+void VertexSetup::set_vertex_array(const VertexArray &a)
+{
+       if(!a.get_buffer())
+               throw invalid_argument("VertexSetup::set_vertex_array");
+
+       vertex_array = &a;
+       update(get_update_mask(VERTEX_ARRAY, vertex_format, *vertex_array));
+       vertex_format = vertex_array->get_format();
+}
+
+void VertexSetup::set_instance_array(const VertexArray *a)
+{
+       if(a)
+       {
+               if(!a->get_buffer())
+                       throw invalid_argument("VertexSetup::set_instance_array");
+
+               static Require req(ARB_instanced_arrays);
+       }
+
+       inst_array = a;
+       update(get_update_mask(INSTANCE_ARRAY, inst_format, *inst_array));
+       inst_format = inst_array->get_format();
+}
+
+void VertexSetup::set_index_buffer(const Buffer &ibuf)
+{
+       index_buffer = &ibuf;
+       update(INDEX_BUFFER);
+}
+
+void VertexSetup::refresh()
+{
+       if(vertex_array && vertex_array->get_format()!=vertex_format)
+               set_vertex_array(*vertex_array);
+
+       if(inst_array && inst_array->get_format()!=inst_format)
+               set_instance_array(inst_array);
+}
+
+unsigned VertexSetup::get_attribs(const VertexFormat &fmt)
+{
+       unsigned mask = 0;
+       for(const unsigned char *c=fmt.begin(); c!=fmt.end(); ++c)
+       {
+               unsigned t = get_component_type(*c);
+               if(t>=get_component_type(ATTRIB1))
+                       t -= get_component_type(ATTRIB1);
+               mask |= 1<<t;
+       }
+       return mask;
+}
+
+unsigned VertexSetup::get_update_mask(unsigned base, const VertexFormat &cur_fmt, const VertexArray &new_array)
+{
+       unsigned unused = get_attribs(cur_fmt)&~get_attribs(new_array.get_format());
+       return base | (unused ? UNUSED_ATTRIBS | (unused<<ATTRIB_SHIFT) : 0);
+}
+
+void VertexSetup::update(unsigned mask) const
+{
+       static bool direct = ARB_direct_state_access && ARB_vertex_attrib_binding;
+       if(!direct && current()!=this)
+       {
+               dirty |= mask;
+               return;
+       }
+
+       if(mask&UNUSED_ATTRIBS)
+       {
+               for(unsigned i=0, am=mask>>ATTRIB_SHIFT; am; ++i, am>>=1)
+                       if(am&1)
+                       {
+                               if(direct)
+                                       glDisableVertexArrayAttrib(id, i);
+                               else
+                                       glDisableVertexAttribArray(i);
+                       }
+       }
+
+       if(mask&VERTEX_ARRAY)
+               update_vertex_array(*vertex_array, 0, 0, direct);
+
+       if((mask&INSTANCE_ARRAY) && inst_array)
+               update_vertex_array(*inst_array, 1, 1, direct);
+
+       if(mask&INDEX_BUFFER)
+       {
+               if(direct)
+                       glVertexArrayElementBuffer(id, index_buffer->get_id());
+               else
+                       glBindBuffer(ELEMENT_ARRAY_BUFFER, index_buffer->get_id());
+       }
+}
+
+void VertexSetup::update_vertex_array(const VertexArray &array, unsigned binding, unsigned divisor, bool direct) const
+{
+       Conditional<Bind> bind_vbuf(!direct, array.get_buffer(), ARRAY_BUFFER);
+
+       const VertexFormat &fmt = array.get_format();
+       unsigned stride = get_stride(fmt)*sizeof(float);
+       if(direct)
+       {
+               glVertexArrayVertexBuffer(id, binding, array.get_buffer()->get_id(), 0, stride);
+               glVertexArrayBindingDivisor(id, binding, divisor);
+       }
+
+       unsigned offset = 0;
+       for(const unsigned char *c=fmt.begin(); c!=fmt.end(); ++c)
+       {
+               unsigned t = get_component_type(*c);
+               if(t>=get_component_type(ATTRIB1))
+                       t -= get_component_type(ATTRIB1);
+               unsigned sz = get_component_size(*c);
+               if(direct)
+               {
+                       if(*c==COLOR4_UBYTE)
+                               glVertexArrayAttribFormat(id, t, 4, GL_UNSIGNED_BYTE, true, offset);
+                       else
+                               glVertexArrayAttribFormat(id, t, sz, GL_FLOAT, false, offset);
+                       glVertexArrayAttribBinding(id, t, binding);
+                       glEnableVertexArrayAttrib(id, t);
+               }
+               else
+               {
+                       if(*c==COLOR4_UBYTE)
+                               glVertexAttribPointer(t, 4, GL_UNSIGNED_BYTE, true, stride, reinterpret_cast<unsigned char *>(offset));
+                       else
+                               glVertexAttribPointer(t, sz, GL_FLOAT, false, stride, reinterpret_cast<float *>(offset));
+                       if(ARB_instanced_arrays)
+                               glVertexAttribDivisor(t, divisor);
+                       glEnableVertexAttribArray(t);
+               }
+               offset += sz*sizeof(float);
+       }
+}
+
+void VertexSetup::bind() const
+{
+       if(!vertex_array || !index_buffer)
+               throw invalid_operation("VertexSetup::bind");
+
+       if(set_current(this))
+       {
+               vertex_array->refresh();
+               if(inst_array)
+                       inst_array->refresh();
+               glBindVertexArray(id);
+               if(dirty)
+               {
+                       update(dirty);
+                       dirty = 0;
+               }
+       }
+}
+
+void VertexSetup::unbind()
+{
+       if(set_current(0))
+               glBindVertexArray(0);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/vertexsetup.h b/source/core/vertexsetup.h
new file mode 100644 (file)
index 0000000..bae4c5f
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef MSP_GL_VERTEXSETUP_H_
+#define MSP_GL_VERTEXSETUP_H_
+
+#include "bindable.h"
+#include "vertexformat.h"
+
+namespace Msp {
+namespace GL {
+
+class VertexArray;
+
+/**
+Combines a VertexArray with an index buffer.  This wraps OpenGL's vertex array
+objects.  Intended for internal use.
+*/
+class VertexSetup: public Bindable<VertexSetup>
+{
+private:
+       enum ComponentMask
+       {
+               VERTEX_ARRAY = 1,
+               INSTANCE_ARRAY = 2,
+               INDEX_BUFFER = 4,
+               UNUSED_ATTRIBS = 8,
+               ATTRIB_SHIFT = 4
+       };
+
+       unsigned id;
+       mutable unsigned dirty;
+       const VertexArray *vertex_array;
+       VertexFormat vertex_format;
+       const VertexArray *inst_array;
+       VertexFormat inst_format;
+       const Buffer *index_buffer;
+
+public:
+       VertexSetup();
+       ~VertexSetup();
+
+       void set_vertex_array(const VertexArray &);
+       void set_instance_array(const VertexArray *);
+       void set_index_buffer(const Buffer &);
+       void refresh();
+       const VertexArray *get_vertex_array() const { return vertex_array; }
+       const VertexArray *get_instance_array() const { return inst_array; }
+       const Buffer *get_index_buffer() const { return index_buffer; }
+
+private:
+       static unsigned get_attribs(const VertexFormat &);
+       static unsigned get_update_mask(unsigned, const VertexFormat &, const VertexArray &);
+       void update(unsigned) const;
+       void update_vertex_array(const VertexArray &, unsigned, unsigned, bool) const;
+
+public:
+       void bind() const;
+       static void unbind();
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/core/windingtest.cpp b/source/core/windingtest.cpp
new file mode 100644 (file)
index 0000000..0b8218a
--- /dev/null
@@ -0,0 +1,61 @@
+#include <msp/strings/format.h>
+#include "windingtest.h"
+
+namespace Msp {
+namespace GL {
+
+void operator>>(const LexicalConverter &conv, FaceWinding &winding)
+{
+       if(conv.get()=="CLOCKWISE")
+               winding = CLOCKWISE;
+       else if(conv.get()=="COUNTERCLOCKWISE")
+               winding = COUNTERCLOCKWISE;
+       else
+               throw lexical_error(format("conversion of '%s' to FaceWinding", conv.get()));
+}
+
+WindingTest::WindingTest():
+       winding(COUNTERCLOCKWISE)
+{ }
+
+WindingTest::WindingTest(FaceWinding w):
+       winding(w)
+{ }
+
+void WindingTest::bind() const
+{
+       if(set_current(this))
+       {
+               glEnable(GL_CULL_FACE);
+               glFrontFace(winding);
+       }
+}
+
+void WindingTest::unbind()
+{
+       if(set_current(0))
+               glDisable(GL_CULL_FACE);
+}
+
+const WindingTest &WindingTest::get_reverse() const
+{
+       if(winding==CLOCKWISE)
+               return counterclockwise();
+       else
+               return clockwise();
+}
+
+const WindingTest &WindingTest::clockwise()
+{
+       static WindingTest test(CLOCKWISE);
+       return test;
+}
+
+const WindingTest &WindingTest::counterclockwise()
+{
+       static WindingTest test(COUNTERCLOCKWISE);
+       return test;
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/core/windingtest.h b/source/core/windingtest.h
new file mode 100644 (file)
index 0000000..ffc5255
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef MSP_GL_WINDINGTEST_H_
+#define MSP_GL_WINDINGTEST_H_
+
+#include <msp/strings/lexicalcast.h>
+#include "bindable.h"
+#include "gl.h"
+
+namespace Msp {
+namespace GL {
+
+enum FaceWinding
+{
+       CLOCKWISE = GL_CW,
+       COUNTERCLOCKWISE = GL_CCW
+};
+
+void operator>>(const LexicalConverter &, FaceWinding &);
+
+/**
+Tests the winding of polygons.  If the order of vertices on screen does not
+match the winding, the polygon is not rendered.
+*/
+class WindingTest: public Bindable<WindingTest>
+{
+private:
+       FaceWinding winding;
+
+public:
+       WindingTest();
+       WindingTest(FaceWinding);
+
+       void bind() const;
+
+       static void unbind();
+
+       const WindingTest &get_reverse() const;
+
+       static const WindingTest &clockwise();
+       static const WindingTest &counterclockwise();
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/cylinder.cpp b/source/cylinder.cpp
deleted file mode 100644 (file)
index 901a0f4..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-#define _USE_MATH_DEFINES
-#include <cmath>
-#include "cylinder.h"
-#include "primitivebuilder.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-CylinderBuilder::CylinderBuilder(float r, float l, unsigned s):
-       radius(r),
-       length(l),
-       segments(s)
-{
-       if(segments<3)
-               segments = 3;
-}
-
-void CylinderBuilder::build(PrimitiveBuilder &builder) const
-{
-       if(generate_tbn)
-               builder.binormal(0, 1, 0);
-       for(unsigned i=0; i<2; ++i)
-       {
-               float z = (i-0.5)*length;
-               builder.normal(0, 0, i*2.0-1.0);
-               builder.texcoord(0.5, 0.5);
-               if(generate_tbn)
-                       builder.tangent((i ? 1 : -1), 0, 0);
-               builder.vertex(0, 0, z);
-               for(unsigned j=0; j<segments; ++j)
-               {
-                       float a = j*M_PI*2/segments;
-                       float c = cos(a);
-                       float s = sin(a);
-                       builder.texcoord(0.5+(i ? 0.5 : -0.5)*c, 0.5+0.5*s);
-                       builder.vertex(radius*c, radius*s, z);
-               }
-       }
-
-       float u_scale = 1.0/segments;
-       float v_scale = 1;
-       adjust_texture_scale(u_scale, v_scale, radius*M_PI*2, length);
-
-       if(generate_tbn)
-               builder.binormal(0, 0, 1);
-       for(unsigned i=0; i<2; ++i)
-       {
-               float z = (i-0.5)*length;
-               for(unsigned j=0; j<=segments; ++j)
-               {
-                       float a = j*M_PI*2/segments;
-                       float c = cos(a);
-                       float s = sin(a);
-                       builder.normal(c, s, 0);
-                       builder.texcoord(j*u_scale, i*v_scale);
-                       if(generate_tbn)
-                               builder.tangent(-s, c, 0);
-                       builder.vertex(radius*c, radius*s, z);
-               }
-       }
-
-       unsigned base = 0;
-       for(unsigned i=0; i<2; ++i)
-       {
-               builder.begin(TRIANGLE_FAN);
-               builder.element(base);
-               for(unsigned j=0; j<=segments; ++j)
-                       builder.element(base+1+j%segments);
-               builder.end();
-
-               base += segments+1;
-       }
-
-       builder.begin(TRIANGLE_STRIP);
-       for(unsigned j=0; j<=segments; ++j)
-       {
-               builder.element(base+segments+1+j);
-               builder.element(base+j);
-       }
-       builder.end();
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/cylinder.h b/source/cylinder.h
deleted file mode 100644 (file)
index dee4c4f..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef MSP_GL_CYLINDER_H_
-#define MSP_GL_CYLINDER_H_
-
-#include "geometrybuilder.h"
-
-namespace Msp {
-namespace GL {
-
-class CylinderBuilder: public GeometryBuilder
-{
-private:
-       float radius;
-       float length;
-       unsigned segments;
-
-public:
-       CylinderBuilder(float, float, unsigned = 16);
-
-       using GeometryBuilder::build;
-       virtual void build(PrimitiveBuilder &) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/datatype.cpp b/source/datatype.cpp
deleted file mode 100644 (file)
index 1def3ca..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-#include <stdexcept>
-#include "datatype.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-unsigned get_type_size(DataType type)
-{
-       switch(type)
-       {
-       case BYTE:
-       case UNSIGNED_BYTE: return 1;
-       case SHORT:
-       case UNSIGNED_SHORT:
-       case HALF_FLOAT: return 2;
-       case INT:
-       case UNSIGNED_INT:
-       case FLOAT: return 4;
-       default: throw invalid_argument("get_type_size");
-       }
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/datatype.h b/source/datatype.h
deleted file mode 100644 (file)
index 2d783f6..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef MSP_GL_DATATYPE_H_
-#define MSP_GL_DATATYPE_H_
-
-#include "gl.h"
-#include <msp/gl/extensions/nv_half_float.h>
-
-namespace Msp {
-namespace GL {
-
-enum DataType
-{
-       BYTE           = GL_BYTE,
-       UNSIGNED_BYTE  = GL_UNSIGNED_BYTE,
-       SHORT          = GL_SHORT,
-       UNSIGNED_SHORT = GL_UNSIGNED_SHORT,
-       INT            = GL_INT,
-       UNSIGNED_INT   = GL_UNSIGNED_INT,
-       FLOAT          = GL_FLOAT,
-       HALF_FLOAT     = GL_HALF_FLOAT
-};
-
-unsigned get_type_size(DataType);
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/effect.cpp b/source/effect.cpp
deleted file mode 100644 (file)
index ad789af..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-#include "effect.h"
-#include "sampler.h"
-
-namespace Msp {
-namespace GL {
-
-WeakPtr<Sampler> Effect::linear_sampler;
-
-Effect::Effect(Renderable &r):
-       renderable(r)
-{
-       enabled_passes.insert(Tag());
-}
-
-void Effect::enable_for_pass(const Tag &tag)
-{
-       enabled_passes.insert(tag);
-}
-
-void Effect::disable_for_pass(const Tag &tag)
-{
-       enabled_passes.erase(tag);
-}
-
-RefPtr<Sampler> Effect::get_linear_sampler()
-{
-       RefPtr<Sampler> sampler = linear_sampler;
-       if(!sampler)
-       {
-               sampler = new Sampler;
-               sampler->set_filter(LINEAR);
-               sampler->set_wrap(CLAMP_TO_EDGE);
-               linear_sampler = sampler;
-       }
-       return sampler;
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/effect.h b/source/effect.h
deleted file mode 100644 (file)
index bc9e19e..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-#ifndef MSP_GL_EFFECT_H_
-#define MSP_GL_EFFECT_H_
-
-#include <set>
-#include <msp/core/refptr.h>
-#include "renderable.h"
-
-namespace Msp {
-namespace GL {
-
-class Sampler;
-
-/**
-Effects are used to wrap other renderables and give them additional visual
-properties.  An Effect's render method should set up the necessary state, call
-the wrapped Renderable's render method, and clean up after itself.
-*/
-class Effect: public Renderable
-{
-protected:
-       Renderable &renderable;
-       std::set<Tag> enabled_passes;
-
-private:
-       static WeakPtr<Sampler> linear_sampler;
-
-protected:
-       Effect(Renderable &);
-public:
-       virtual ~Effect() { }
-
-       void enable_for_pass(const Tag &);
-       void disable_for_pass(const Tag &);
-
-       virtual const Matrix *get_matrix() const { return renderable.get_matrix(); }
-       virtual const Geometry::BoundingSphere<float, 3> *get_bounding_sphere() const { return renderable.get_bounding_sphere(); }
-
-       virtual void setup_frame(Renderer &r) { renderable.setup_frame(r); }
-       virtual void finish_frame() { renderable.finish_frame(); }
-
-protected:
-       static RefPtr<Sampler> get_linear_sampler();
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/effects/ambientocclusion.cpp b/source/effects/ambientocclusion.cpp
new file mode 100644 (file)
index 0000000..8c1dd61
--- /dev/null
@@ -0,0 +1,142 @@
+#include <algorithm>
+#include "ambientocclusion.h"
+#include "blend.h"
+#include "camera.h"
+#include "renderer.h"
+#include "shader.h"
+#include "tests.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+AmbientOcclusion::AmbientOcclusion(unsigned w, unsigned h, float):
+       occlude_target(w, h, (RENDER_COLOR,R8)),
+       occlude_shader("ambientocclusion_occlude.glsl"),
+       combine_shader("ambientocclusion_combine.glsl"),
+       quad(get_fullscreen_quad()),
+       linear_sampler(get_linear_sampler()),
+       nearest_sampler(get_nearest_sampler())
+{
+       texturing.attach(2, occlude_target.get_target_texture(RENDER_COLOR), linear_sampler.get());
+
+       unsigned seed = 1;
+       rotate_lookup.storage(RGBA8, 4, 4, 1);
+       unsigned char data[64];
+       for(unsigned i=0; i<16; ++i)
+       {
+               Geometry::Angle<float> a = Geometry::Angle<float>::from_turns(random(seed));
+               unsigned char c = (cos(a)*0.5f+0.5f)*255;
+               unsigned char s = (sin(a)*0.5f+0.5f)*255;
+               data[i*4  ] = c;
+               data[i*4+1] = s;
+               data[i*4+2] = 255-s;
+               data[i*4+3] = ((i+i/4)%2)*255;
+       }
+       rotate_lookup.image(0, data);
+
+       texturing.attach(3, rotate_lookup, nearest_sampler.get());
+
+       shdata.uniform("source", 0);
+       shdata.uniform("depth", 1);
+       shdata.uniform("occlusion", 2);
+       shdata.uniform("rotate", 3);
+       shdata.uniform("inverse_projection", Matrix());
+
+       set_n_samples(16);
+       set_occlusion_radius(0.5f);
+       set_darkness(1.0f);
+       set_edge_depth_threshold(0.1f);
+}
+
+float AmbientOcclusion::random(unsigned &seed)
+{
+       static const unsigned modulus = (1U<<31)-1;
+       seed = (static_cast<UInt64>(seed)*48271)%modulus;  // minstd
+       return static_cast<float>(seed)/(modulus-1);
+}
+
+void AmbientOcclusion::set_n_samples(unsigned n)
+{
+       if(n<1 || n>32)
+               throw out_of_range("AmbientOcclusion::set_n_samples");
+
+       unsigned seed = 1;
+       float radius_divisor = (n-1)*(n-1);
+       Vector3 sample_points[32];
+       for(unsigned i=0; i<n; ++i)
+       {
+               Vector3 v(random(seed)-0.5f, random(seed)-0.5f, random(seed)-0.5f);
+               sample_points[i] = normalize(v)*(0.1f+0.9f*i*i/radius_divisor);
+       }
+       shdata.uniform3_array("sample_points", n, &sample_points[0].x);
+       shdata.uniform("n_samples", static_cast<int>(n));
+}
+
+void AmbientOcclusion::set_occlusion_radius(float r)
+{
+       shdata.uniform("occlusion_radius", r);
+}
+
+void AmbientOcclusion::set_darkness(float darkness)
+{
+       shdata.uniform("darkness", darkness);
+}
+
+void AmbientOcclusion::set_edge_depth_threshold(float edt)
+{
+       shdata.uniform("edge_depth_threshold", edt);
+}
+
+void AmbientOcclusion::render(Renderer &renderer, const Texture2D &color, const Texture2D &depth)
+{
+       texturing.attach(0, color, nearest_sampler.get());
+       texturing.attach(1, depth, nearest_sampler.get());
+
+       if(renderer.get_camera())
+               shdata.uniform("inverse_projection", invert(renderer.get_camera()->get_projection_matrix()));
+
+       Renderer::Push push(renderer);
+       renderer.set_texturing(&texturing);
+       renderer.set_shader_program(&occlude_shader, &shdata);
+
+       {
+               BindRestore bind_fbo(occlude_target.get_framebuffer());
+               quad->draw(renderer);
+       }
+
+       renderer.set_shader_program(&combine_shader);
+       quad->draw(renderer);
+}
+
+
+AmbientOcclusion::Template::Template():
+       n_samples(16),
+       occlusion_radius(0.5f),
+       darkness(1.0f),
+       edge_depth_threshold(0.1f)
+{ }
+
+AmbientOcclusion *AmbientOcclusion::Template::create(unsigned width, unsigned height) const
+{
+       RefPtr<AmbientOcclusion> ao = new AmbientOcclusion(width/size_divisor, height/size_divisor);
+       ao->set_n_samples(n_samples);
+       ao->set_occlusion_radius(occlusion_radius);
+       ao->set_darkness(darkness);
+       ao->set_edge_depth_threshold(edge_depth_threshold);
+       return ao.release();
+}
+
+
+AmbientOcclusion::Template::Loader::Loader(Template &t):
+       DataFile::DerivedObjectLoader<Template, PostProcessor::Template::Loader>(t)
+{
+       add("darkness", &Template::darkness);
+       add("edge_depth_threshold", &Template::edge_depth_threshold);
+       add("occlusion_radius", &Template::occlusion_radius);
+       add("samples", &Template::n_samples);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/effects/ambientocclusion.h b/source/effects/ambientocclusion.h
new file mode 100644 (file)
index 0000000..6c2c5d7
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef MSP_GL_AMBIENTOCCLUSION_H_
+#define MSP_GL_AMBIENTOCCLUSION_H_
+
+#include "framebuffer.h"
+#include "mesh.h"
+#include "postprocessor.h"
+#include "program.h"
+#include "programdata.h"
+#include "rendertarget.h"
+#include "texture2d.h"
+#include "texturing.h"
+
+namespace Msp {
+namespace GL {
+
+/**
+Implements screen-space ambient occlusion.
+
+http://en.wikipedia.org/wiki/Screen_Space_Ambient_Occlusion
+*/
+class AmbientOcclusion: public PostProcessor
+{
+public:
+       struct Template: PostProcessor::Template
+       {
+               class Loader: public DataFile::DerivedObjectLoader<Template, PostProcessor::Template::Loader>
+               {
+               public:
+                       Loader(Template &);
+               };
+
+               unsigned n_samples;
+               float occlusion_radius;
+               float darkness;
+               float edge_depth_threshold;
+
+               Template();
+
+               virtual AmbientOcclusion *create(unsigned, unsigned) const;
+       };
+
+private:
+       Texture2D rotate_lookup;
+       RenderTarget occlude_target;
+       Texturing texturing;
+       Program occlude_shader;
+       Program combine_shader;
+       mutable ProgramData shdata;
+       RefPtr<Mesh> quad;
+       RefPtr<Sampler> linear_sampler;
+       RefPtr<Sampler> nearest_sampler;
+
+public:
+       AmbientOcclusion(unsigned, unsigned, float = 1.0f);
+
+private:
+       static float random(unsigned &);
+
+public:
+       void set_n_samples(unsigned);
+       void set_occlusion_radius(float);
+       void set_edge_depth_threshold(float);
+
+       void set_darkness(float);
+
+       virtual void render(Renderer &, const Texture2D &, const Texture2D &);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/effects/bloom.cpp b/source/effects/bloom.cpp
new file mode 100644 (file)
index 0000000..ee7c76c
--- /dev/null
@@ -0,0 +1,112 @@
+#include <cmath>
+#include <msp/strings/format.h>
+#include "blend.h"
+#include "bloom.h"
+#include "misc.h"
+#include "renderer.h"
+#include "shader.h"
+#include "tests.h"
+#include "texunit.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Bloom::Bloom(unsigned w, unsigned h):
+       blur_shader("bloom_blur.glsl"),
+       combine_shader("bloom_combine.glsl"),
+       quad(get_fullscreen_quad()),
+       nearest_sampler(get_nearest_sampler()),
+       linear_sampler(get_linear_sampler())
+{
+       blur_shdata[0].uniform("delta", 1.0f/w, 0.0f);
+       blur_shdata[1].uniform("delta", 0.0f, 1.0f/h);
+
+       for(unsigned i=0; i<2; ++i)
+               target[i] = new RenderTarget(w, h, (RENDER_COLOR,RGB16F));
+
+       common_shdata.uniform("source", 0);
+       common_shdata.uniform("blurred", 1);
+
+       combine_texturing.attach(1, target[1]->get_target_texture(RENDER_COLOR), linear_sampler.get());
+
+       set_radius(2.0f);
+       set_strength(0.2f);
+}
+
+Bloom::~Bloom()
+{
+       for(unsigned i=0; i<2; ++i)
+               delete target[i];
+}
+
+void Bloom::set_radius(float r)
+{
+       if(r<=0.0f)
+               throw invalid_argument("Bloom::set_radius");
+
+       int size = min(static_cast<int>(r*3.0f), 9);
+       common_shdata.uniform("size", size);
+
+       vector<float> factors(size*2+1);
+       float sum = 0.0f;
+       r = 2*r*r;
+       for(int i=-size; i<=size; ++i)
+               sum += (factors[size+i] = exp(-i*i/r));
+       for(int i=0; i<=size*2; ++i)
+               factors[i] /= sum;
+
+       common_shdata.uniform1_array("factors", size*2+1, &factors.front());
+}
+
+void Bloom::set_strength(float s)
+{
+       if(s<0.0f || s>1.0f)
+               throw invalid_argument("Bloom::set_strength");
+       common_shdata.uniform("strength", s);
+}
+
+void Bloom::render(Renderer &renderer, const Texture2D &src, const Texture2D &)
+{
+       Renderer::Push push(renderer);
+       renderer.set_shader_program(&blur_shader, &common_shdata);
+       for(unsigned i=0; i<2; ++i)
+       {
+               BindRestore bind_fbo(target[i]->get_framebuffer());
+               Renderer::Push push2(renderer);
+               renderer.set_texture(i ? &target[0]->get_target_texture(RENDER_COLOR) : &src, nearest_sampler.get());
+               renderer.add_shader_data(blur_shdata[i]);
+               quad->draw(renderer);
+       }
+
+       combine_texturing.attach(0, src, nearest_sampler.get());
+       renderer.set_texturing(&combine_texturing);
+       renderer.set_shader_program(&combine_shader);
+       quad->draw(renderer);
+}
+
+
+Bloom::Template::Template():
+       radius(2.0f),
+       strength(0.2f)
+{ }
+
+Bloom *Bloom::Template::create(unsigned width, unsigned height) const
+{
+       RefPtr<Bloom> bloom = new Bloom(width/size_divisor, height/size_divisor);
+       bloom->set_radius(radius);
+       bloom->set_strength(strength);
+       return bloom.release();
+}
+
+
+Bloom::Template::Loader::Loader(Template &t):
+       DataFile::DerivedObjectLoader<Template, PostProcessor::Template::Loader>(t)
+{
+       add("strength", &Template::strength);
+       add("radius", &Template::radius);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/effects/bloom.h b/source/effects/bloom.h
new file mode 100644 (file)
index 0000000..6b10195
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef MSP_GL_BLOOM_H_
+#define MSP_GL_BLOOM_H_
+
+#include "framebuffer.h"
+#include "mesh.h"
+#include "postprocessor.h"
+#include "texture2d.h"
+#include "texturing.h"
+#include "program.h"
+#include "programdata.h"
+#include "rendertarget.h"
+
+namespace Msp {
+namespace GL {
+
+/**
+The Bloom post-processing effect causes very bright areas of the image to bleed
+into surrounding pixels.  Commonly used together with HDR rendering.
+
+The technique used is to gaussian blur the image and then blend the result with
+the original image.  With suitable parameters, this effect may also be used as
+a blur filter.
+*/
+class Bloom: public PostProcessor
+{
+public:
+       struct Template: public PostProcessor::Template
+       {
+               class Loader: public DataFile::DerivedObjectLoader<Template, PostProcessor::Template::Loader>
+               {
+               public:
+                       Loader(Template &);
+               };
+
+               float radius;
+               float strength;
+
+               Template();
+
+               virtual Bloom *create(unsigned, unsigned) const;
+       };
+
+private:
+       RenderTarget *target[2];
+       ProgramData common_shdata;
+       Program blur_shader;
+       ProgramData blur_shdata[2];
+       Program combine_shader;
+       Texturing combine_texturing;
+       RefPtr<Mesh> quad;
+       RefPtr<Sampler> nearest_sampler;
+       RefPtr<Sampler> linear_sampler;
+
+public:
+       Bloom(unsigned, unsigned);
+       ~Bloom();
+
+       /** Sets the Ïƒ value of the gaussian blur.  Values much larger than 4.0 are
+       likely to cause artifacts. */
+       void set_radius(float);
+
+       /** Sets the blend factor between original and blurred images.  Larger
+       values mean more blurriness. */
+       void set_strength(float);
+
+       virtual void render(Renderer &, const Texture2D &, const Texture2D &);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/effects/colorcurve.cpp b/source/effects/colorcurve.cpp
new file mode 100644 (file)
index 0000000..8811b06
--- /dev/null
@@ -0,0 +1,124 @@
+#include <cmath>
+#include "color.h"
+#include "colorcurve.h"
+#include "mesh.h"
+#include "renderer.h"
+#include "shader.h"
+#include "texture2d.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+ColorCurve::ColorCurve():
+       shprog("colorcurve.glsl"),
+       quad(get_fullscreen_quad()),
+       linear_sampler(get_linear_sampler()),
+       nearest_sampler(get_nearest_sampler())
+{
+       shdata.uniform("source", 0);
+       shdata.uniform("curve", 1);
+
+       curve.storage(LUMINANCE8, 256, 1);
+       texturing.attach(1, curve, linear_sampler.get());
+
+       set_exposure_adjust(0.0f);
+       set_brightness_response(0.4f);
+       set_linear();
+}
+
+void ColorCurve::set_exposure_adjust(float e)
+{
+       shdata.uniform("exposure", pow(2.0f, e));
+}
+
+void ColorCurve::set_brightness_response(float b)
+{
+       if(b<=0 || b>1)
+               throw invalid_argument("ColorCurve::set_brightness_response");
+       float t = (b<1 ? pow(b, 1/(1-b)) : 0.0f);
+       shdata.uniform("brightness_response", b, t, pow(t, b));
+}
+
+void ColorCurve::set_gamma(float g)
+{
+       if(g<0.1 || g>10)
+               throw invalid_argument("ColorCurve::set_gamma");
+
+       unsigned char curve_data[256];
+       for(unsigned i=0; i<256; ++i)
+               curve_data[i] = pow(i/255.0f, 1/g)*255+0.5f;
+       curve.image(0, curve_data);
+}
+
+void ColorCurve::set_srgb()
+{
+       unsigned char curve_data[256];
+       curve_data[0] = 0;
+       for(unsigned i=1; i<256; ++i)
+               curve_data[i] = to_srgb(i/255.0f)*255+0.5f;
+       curve.image(0, curve_data);
+}
+
+void ColorCurve::set_linear()
+{
+       unsigned char curve_data[256];
+       for(unsigned i=0; i<256; ++i)
+               curve_data[i] = i;
+       curve.image(0, curve_data);
+}
+
+void ColorCurve::render(Renderer &renderer, const Texture2D &color_buf, const Texture2D &)
+{
+       texturing.attach(0, color_buf, nearest_sampler.get());
+
+       Renderer::Push push(renderer);
+       renderer.set_shader_program(&shprog, &shdata);
+       renderer.set_texturing(&texturing);
+       quad->draw(renderer);
+}
+
+
+ColorCurve::Template::Template():
+       exposure_adjust(0.0f),
+       brightness_response(0.4f),
+       gamma(1.0f),
+       srgb(false)
+{ }
+
+ColorCurve *ColorCurve::Template::create(unsigned, unsigned) const
+{
+       RefPtr<ColorCurve> colorcurve = new ColorCurve;
+       colorcurve->set_exposure_adjust(exposure_adjust);
+       colorcurve->set_brightness_response(brightness_response);
+       if(srgb)
+               colorcurve->set_srgb();
+       else
+               colorcurve->set_gamma(gamma);
+       return colorcurve.release();
+}
+
+
+ColorCurve::Template::Loader::Loader(Template &t):
+       DataFile::DerivedObjectLoader<Template, PostProcessor::Template::Loader>(t)
+{
+       add("brightness_response", &Template::brightness_response);
+       add("exposure_adjust", &Template::exposure_adjust);
+       add("gamma", &Loader::gamma);
+       add("srgb", &Loader::srgb);
+}
+
+void ColorCurve::Template::Loader::gamma(float g)
+{
+       obj.gamma = g;
+       obj.srgb = false;
+}
+
+void ColorCurve::Template::Loader::srgb()
+{
+       obj.srgb = true;
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/effects/colorcurve.h b/source/effects/colorcurve.h
new file mode 100644 (file)
index 0000000..3f65b04
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef MSP_GL_COLORCURVE_H_
+#define MSP_GL_COLORCURVE_H_
+
+#include "postprocessor.h"
+#include "program.h"
+#include "programdata.h"
+#include "texture1d.h"
+#include "texturing.h"
+
+namespace Msp {
+namespace GL {
+
+/**
+Processes oversaturated colors to preserve hues.  When one color component
+exceeds 1.0, the overflow is distributed to the other components, scaling the
+color towards white.
+
+Gamma or sRGB correction can also be applied to the output.  It can be used to
+improve color reproduction by performing lighting calculations in linear color
+space and converting to sRGB for display.
+*/
+class ColorCurve: public PostProcessor
+{
+public:
+       struct Template: public PostProcessor::Template
+       {
+               class Loader: public DataFile::DerivedObjectLoader<Template, PostProcessor::Template::Loader>
+               {
+               public:
+                       Loader(Template &);
+
+               private:
+                       void gamma(float);
+                       void srgb();
+               };
+
+               float exposure_adjust;
+               float brightness_response;
+               float gamma;
+               bool srgb;
+
+               Template();
+
+               virtual ColorCurve *create(unsigned, unsigned) const;
+       };
+
+private:
+       Program shprog;
+       ProgramData shdata;
+       Texture1D curve;
+       Texturing texturing;
+       RefPtr<Mesh> quad;
+       RefPtr<Sampler> linear_sampler;
+       RefPtr<Sampler> nearest_sampler;
+
+public:
+       ColorCurve();
+
+       /** Set exposure adjustment in EV units.  Positive values brighten the
+       image, negative values darken it.  Zero is neutral. */
+       void set_exposure_adjust(float);
+
+       /** Sets the exponent of the */
+       void set_brightness_response(float);
+
+       /** Sets the gamma value used for mapping output colors.  Allowed range is
+       from 0.1 to 10. */
+       void set_gamma(float);
+
+       /** Sets output mapping to sRGB.  This is almost, but not exactly equivalent
+       to set_gamma(2.2). */
+       void set_srgb();
+
+       /// Sets output mapping to linear.  This is equivalent to set_gamma(1).
+       void set_linear();
+
+       virtual void render(Renderer &, const Texture2D &, const Texture2D &);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/effects/effect.cpp b/source/effects/effect.cpp
new file mode 100644 (file)
index 0000000..ad789af
--- /dev/null
@@ -0,0 +1,39 @@
+#include "effect.h"
+#include "sampler.h"
+
+namespace Msp {
+namespace GL {
+
+WeakPtr<Sampler> Effect::linear_sampler;
+
+Effect::Effect(Renderable &r):
+       renderable(r)
+{
+       enabled_passes.insert(Tag());
+}
+
+void Effect::enable_for_pass(const Tag &tag)
+{
+       enabled_passes.insert(tag);
+}
+
+void Effect::disable_for_pass(const Tag &tag)
+{
+       enabled_passes.erase(tag);
+}
+
+RefPtr<Sampler> Effect::get_linear_sampler()
+{
+       RefPtr<Sampler> sampler = linear_sampler;
+       if(!sampler)
+       {
+               sampler = new Sampler;
+               sampler->set_filter(LINEAR);
+               sampler->set_wrap(CLAMP_TO_EDGE);
+               linear_sampler = sampler;
+       }
+       return sampler;
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/effects/effect.h b/source/effects/effect.h
new file mode 100644 (file)
index 0000000..bc9e19e
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef MSP_GL_EFFECT_H_
+#define MSP_GL_EFFECT_H_
+
+#include <set>
+#include <msp/core/refptr.h>
+#include "renderable.h"
+
+namespace Msp {
+namespace GL {
+
+class Sampler;
+
+/**
+Effects are used to wrap other renderables and give them additional visual
+properties.  An Effect's render method should set up the necessary state, call
+the wrapped Renderable's render method, and clean up after itself.
+*/
+class Effect: public Renderable
+{
+protected:
+       Renderable &renderable;
+       std::set<Tag> enabled_passes;
+
+private:
+       static WeakPtr<Sampler> linear_sampler;
+
+protected:
+       Effect(Renderable &);
+public:
+       virtual ~Effect() { }
+
+       void enable_for_pass(const Tag &);
+       void disable_for_pass(const Tag &);
+
+       virtual const Matrix *get_matrix() const { return renderable.get_matrix(); }
+       virtual const Geometry::BoundingSphere<float, 3> *get_bounding_sphere() const { return renderable.get_bounding_sphere(); }
+
+       virtual void setup_frame(Renderer &r) { renderable.setup_frame(r); }
+       virtual void finish_frame() { renderable.finish_frame(); }
+
+protected:
+       static RefPtr<Sampler> get_linear_sampler();
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/effects/environmentmap.cpp b/source/effects/environmentmap.cpp
new file mode 100644 (file)
index 0000000..3902eb8
--- /dev/null
@@ -0,0 +1,121 @@
+#include <algorithm>
+#include <cmath>
+#include "environmentmap.h"
+#include "renderer.h"
+#include "texunit.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+EnvironmentMap::EnvironmentMap(unsigned s, Renderable &r, Renderable &e):
+       Effect(r),
+       size(s),
+       environment(e),
+       sampler(get_linear_sampler()),
+       rendered(false),
+       update_interval(1),
+       update_delay(0)
+{
+       env_tex.storage(RGB8, size, 1);
+       depth_buf.storage(DEPTH_COMPONENT32F, size, size);
+       for(unsigned i=0; i<6; ++i)
+       {
+               fbo[i].attach(COLOR_ATTACHMENT0, env_tex, TextureCube::enumerate_faces(i), 0);
+               fbo[i].attach(DEPTH_ATTACHMENT, depth_buf);
+               fbo[i].require_complete();
+       }
+
+       camera.set_field_of_view(Geometry::Angle<float>::right());
+       camera.set_aspect_ratio(1);
+       camera.set_depth_clip(0.1, 100);
+}
+
+void EnvironmentMap::set_depth_clip(float n, float f)
+{
+       camera.set_depth_clip(n, f);
+}
+
+void EnvironmentMap::set_update_interval(unsigned i)
+{
+       update_interval = i;
+       update_delay = min(update_delay, update_interval-1);
+}
+
+void EnvironmentMap::queue_update()
+{
+       update_delay = 0;
+}
+
+void EnvironmentMap::setup_frame(Renderer &renderer)
+{
+       if(rendered)
+               return;
+
+       rendered = true;
+       renderable.setup_frame(renderer);
+
+       if(update_delay)
+       {
+               if(update_interval)
+                       --update_delay;
+               return;
+       }
+       update_delay = update_interval-1;
+       environment.setup_frame(renderer);
+
+       const Matrix *matrix = renderable.get_matrix();
+       if(!matrix)
+               return;
+
+       Renderer::Push push(renderer);
+       Renderer::Exclude exclude1(renderer, renderable);
+       Renderer::Exclude exclude2(renderer, *this);
+
+       camera.set_position(matrix->column(3).slice<3>(0));
+
+       BindRestore bind_fbo(fbo[0]);
+       for(unsigned i=0; i<6; ++i)
+       {
+               TextureCubeFace face = TextureCube::enumerate_faces(i);
+               fbo[i].bind();
+               fbo[i].clear();
+               camera.set_look_direction(TextureCube::get_face_direction(face));
+               camera.set_up_direction(TextureCube::get_t_direction(face));
+               renderer.set_camera(camera);
+               renderer.render(environment);
+       }
+}
+
+void EnvironmentMap::finish_frame()
+{
+       if(rendered)
+       {
+               rendered = false;
+               renderable.finish_frame();
+               environment.finish_frame();
+       }
+}
+
+void EnvironmentMap::render(Renderer &renderer, const Tag &tag) const
+{
+       if(!enabled_passes.count(tag))
+               return renderer.render(renderable, tag);
+
+       Renderer::Push _push_rend(renderer);
+
+       unsigned unit = renderer.allocate_effect_texunit();
+       shdata.uniform("environment", static_cast<int>(unit));
+       Bind _bind_sampler(*sampler, unit);
+       Bind _bind_env(env_tex, unit);
+
+       const Matrix &camera_matrix = renderer.get_camera()->get_object_matrix();
+       shdata.uniform("env_eye_matrix", camera_matrix.block<3, 3>(0, 0));
+
+       renderer.add_shader_data(shdata);
+       renderer.render(renderable, tag);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/effects/environmentmap.h b/source/effects/environmentmap.h
new file mode 100644 (file)
index 0000000..c54968d
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef MSP_GL_ENVIRONMENTMAP_H_
+#define MSP_GL_ENVIRONMENTMAP_H_
+
+#include "camera.h"
+#include "effect.h"
+#include "framebuffer.h"
+#include "matrix.h"
+#include "programdata.h"
+#include "renderbuffer.h"
+#include "texturecube.h"
+#include "vector.h"
+
+namespace Msp {
+namespace GL {
+
+class Renderable;
+
+/**
+Creates a cube map texture of the surroundings of the renderable.  This texture
+can then be used to implement effects such as reflections or refractions.
+
+If the EnvironmentMap is used in a Pipeline, it's worth noting that the cube
+map will be prepared outside of any rendering pass.  It's recommended to use
+another Pipeline to define which passes should be used to render the
+environment.
+*/
+class EnvironmentMap: public Effect
+{
+private:
+       unsigned size;
+       Renderable &environment;
+       TextureCube env_tex;
+       Renderbuffer depth_buf;
+       Framebuffer fbo[6];
+       RefPtr<Sampler> sampler;
+       Camera camera;
+       mutable ProgramData shdata;
+       bool rendered;
+       unsigned update_interval;
+       unsigned update_delay;
+
+public:
+       EnvironmentMap(unsigned size, Renderable &rend, Renderable &env);
+
+       void set_depth_clip(float, float);
+       void set_update_interval(unsigned);
+       void queue_update();
+
+       virtual void setup_frame(Renderer &);
+       virtual void finish_frame();
+
+       virtual void render(Renderer &, const Tag & = Tag()) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/effects/postprocessor.cpp b/source/effects/postprocessor.cpp
new file mode 100644 (file)
index 0000000..6af069e
--- /dev/null
@@ -0,0 +1,76 @@
+#include "mesh.h"
+#include "meshbuilder.h"
+#include "postprocessor.h"
+#include "sampler.h"
+#include "shader.h"
+
+namespace Msp {
+namespace GL {
+
+WeakPtr<Mesh> PostProcessor::fullscreen_quad;
+WeakPtr<Sampler> PostProcessor::nearest_sampler;
+WeakPtr<Sampler> PostProcessor::linear_sampler;
+
+void PostProcessor::render(Renderer &, const Texture2D &color, const Texture2D &depth)
+{
+       render(color, depth);
+}
+
+RefPtr<Mesh> PostProcessor::get_fullscreen_quad()
+{
+       RefPtr<Mesh> mesh = fullscreen_quad;
+       if(!mesh)
+       {
+               mesh = new Mesh(VERTEX2);
+               MeshBuilder builder(*mesh);
+               builder.begin(TRIANGLE_STRIP);
+               builder.vertex(-1, 1);
+               builder.vertex(-1, -1);
+               builder.vertex(1, 1);
+               builder.vertex(1, -1);
+               builder.end();
+               fullscreen_quad = mesh;
+       }
+       return mesh;
+}
+
+RefPtr<Sampler> PostProcessor::get_nearest_sampler()
+{
+       RefPtr<Sampler> sampler = nearest_sampler;
+       if(!sampler)
+       {
+               sampler = new Sampler;
+               sampler->set_filter(NEAREST);
+               sampler->set_wrap(CLAMP_TO_EDGE);
+               nearest_sampler = sampler;
+       }
+       return sampler;
+}
+
+RefPtr<Sampler> PostProcessor::get_linear_sampler()
+{
+       RefPtr<Sampler> sampler = linear_sampler;
+       if(!sampler)
+       {
+               sampler = new Sampler;
+               sampler->set_filter(LINEAR);
+               sampler->set_wrap(CLAMP_TO_EDGE);
+               linear_sampler = sampler;
+       }
+       return sampler;
+}
+
+
+PostProcessor::Template::Template():
+       size_divisor(1)
+{ }
+
+
+PostProcessor::Template::Loader::Loader(Template &t):
+       DataFile::ObjectLoader<Template>(t)
+{
+       add("size_divisor", &Template::size_divisor);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/effects/postprocessor.h b/source/effects/postprocessor.h
new file mode 100644 (file)
index 0000000..c107d88
--- /dev/null
@@ -0,0 +1,66 @@
+#ifndef MSP_GL_POSTPROCESSOR_H_
+#define MSP_GL_POSTPROCESSOR_H_
+
+#include <msp/datafile/objectloader.h>
+
+namespace Msp {
+namespace GL {
+
+class Mesh;
+class Renderer;
+class Sampler;
+class Shader;
+class Texture2D;
+
+/**
+Base class for post-processing effects.  Post-processors receive the contents
+of the entire framebuffer as a texture and render it back, altering it in the
+process.
+*/
+class PostProcessor
+{
+public:
+       struct Template
+       {
+               class Loader: public Msp::DataFile::ObjectLoader<Template>
+               {
+               public:
+                       Loader(Template &);
+               };
+
+               unsigned size_divisor;
+
+               Template();
+               virtual ~Template() { }
+
+               virtual PostProcessor *create(unsigned, unsigned) const = 0;
+       };
+
+private:
+       static WeakPtr<Mesh> fullscreen_quad;
+       static WeakPtr<Sampler> nearest_sampler;
+       static WeakPtr<Sampler> linear_sampler;
+
+protected:
+       PostProcessor() { }
+public:
+       virtual ~PostProcessor() { }
+
+       /// Renders the effect.
+       virtual void render(const Texture2D &, const Texture2D &) { }
+
+       virtual void render(Renderer &, const Texture2D &, const Texture2D &);
+
+protected:
+       /** Returns a mesh consisting of a single quad, covering the entire screen.
+       The vertices are in normalized device coordinates. */
+       static RefPtr<Mesh> get_fullscreen_quad();
+
+       static RefPtr<Sampler> get_nearest_sampler();
+       static RefPtr<Sampler> get_linear_sampler();
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/effects/shadowmap.cpp b/source/effects/shadowmap.cpp
new file mode 100644 (file)
index 0000000..eaf6e13
--- /dev/null
@@ -0,0 +1,126 @@
+#include <cmath>
+#include <cstdlib>
+#include "camera.h"
+#include "light.h"
+#include "renderer.h"
+#include "scene.h"
+#include "shadowmap.h"
+#include "tests.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+WeakPtr<Sampler> ShadowMap::shadow_sampler;
+
+ShadowMap::ShadowMap(unsigned s, Renderable &r, const Light &l):
+       Effect(r),
+       size(s),
+       light(l),
+       radius(1),
+       depth_bias(4),
+       rendered(false)
+{
+       sampler = shadow_sampler;
+       if(!sampler)
+       {
+               sampler = new Sampler;
+               sampler->set_filter(LINEAR);
+               sampler->set_compare(LEQUAL);
+               sampler->set_wrap(CLAMP_TO_EDGE);
+               shadow_sampler = sampler;
+       }
+       depth_buf.storage(DEPTH_COMPONENT32F, size, size, 1);
+       fbo.attach(DEPTH_ATTACHMENT, depth_buf, 0);
+       fbo.require_complete();
+
+       set_darkness(0.7);
+}
+
+void ShadowMap::set_target(const Vector3 &t, float r)
+{
+       target = t;
+       radius = r;
+}
+
+void ShadowMap::set_darkness(float d)
+{
+       if(d<0.0f || d>1.0f)
+               throw invalid_argument("ShadowMap::set_darkness");
+
+       shdata.uniform("shadow_darkness", d);
+}
+
+void ShadowMap::set_depth_bias(float b)
+{
+       if(b<0.0f)
+               throw invalid_argument("ShadowMap::set_depth_bias");
+
+       depth_bias = b;
+}
+
+void ShadowMap::setup_frame(Renderer &renderer)
+{
+       if(rendered)
+               return;
+
+       rendered = true;
+       renderable.setup_frame(renderer);
+
+       Camera camera;
+       camera.set_object_matrix(*light.get_matrix());
+       camera.set_position(target);
+       // TODO support point and spot lights with a frustum projection.
+       // Omnidirectional lights also need a cube shadow map.
+       camera.set_orthographic(radius*2, radius*2);
+       camera.set_depth_clip(-radius, radius);
+
+       shadow_matrix = camera.get_object_matrix();
+       shadow_matrix.scale(radius*2, radius*2, -radius*2);
+       shadow_matrix.translate(-0.5, -0.5, depth_bias/size-0.5);
+       shadow_matrix.invert();
+
+       BindRestore bind_fbo(fbo);
+       Bind bind_depth(DepthTest::lequal());
+       fbo.clear(DEPTH_BUFFER_BIT);
+
+       Renderer::Push push(renderer);
+       renderer.set_camera(camera);
+
+       renderer.render(renderable, "shadow");
+}
+
+void ShadowMap::finish_frame()
+{
+       renderable.finish_frame();
+       rendered = false;
+}
+
+void ShadowMap::render(Renderer &renderer, const Tag &tag) const
+{
+       if(!enabled_passes.count(tag))
+               return renderer.render(renderable, tag);
+
+       Renderer::Push _push_rend(renderer);
+
+       unsigned unit = renderer.allocate_effect_texunit();
+       int iunit = unit;
+       shdata.uniform("shadow_map", iunit);
+
+       Bind _bind_sampler(*sampler, unit);
+       Bind _bind_depth(depth_buf, unit);
+
+       if(const Camera *camera = renderer.get_camera())
+               /* Multiply by camera's object matrix to form a matrix that transforms
+               from eye space to shadow space. */
+               shdata.uniform("shd_eye_matrix", shadow_matrix*camera->get_object_matrix());
+       else
+               shdata.uniform("shd_eye_matrix", shadow_matrix);
+
+       renderer.add_shader_data(shdata);
+       renderer.render(renderable, tag);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/effects/shadowmap.h b/source/effects/shadowmap.h
new file mode 100644 (file)
index 0000000..e142fc8
--- /dev/null
@@ -0,0 +1,70 @@
+#ifndef MSP_GL_SHADOWMAP_H_
+#define MSP_GL_SHADOWMAP_H_
+
+#include "effect.h"
+#include "framebuffer.h"
+#include "programdata.h"
+#include "texture2d.h"
+#include "vector.h"
+
+namespace Msp {
+namespace GL {
+
+class Light;
+class Scene;
+
+/**
+Creates shadows on a Scene through a shadow map texture.  In the preparation
+phase, the scene is rendered to a depth texture from the point of view of the
+lightsource.  This texture is then used in the rendering phase together with
+texture coordinate generation to determine whether each fragment is lit.
+*/
+class ShadowMap: public Effect
+{
+private:
+       unsigned size;
+       const Light &light;
+       Framebuffer fbo;
+       Matrix shadow_matrix;
+       Texture2D depth_buf;
+       RefPtr<Sampler> sampler;
+       Vector3 target;
+       float radius;
+       float depth_bias;
+       mutable ProgramData shdata;
+       bool rendered;
+
+       static WeakPtr<Sampler> shadow_sampler;
+
+public:
+       ShadowMap(unsigned, Renderable &, const Light &);
+
+       /** Sets the ShadowMap target point and radius.  The transformation matrix is
+       computed so that a sphere with the specified parameters will be completely
+       covered by the ShadowMap. */
+       void set_target(const Vector3 &, float);
+
+       /** Sets the darkness of shadows.  Must be in the range between 0.0 and 1.0,
+       inclusive.  Only usable with shaders, and provided through the
+       shadow_darkness uniform. */
+       void set_darkness(float);
+
+       /** Sets a distance beyond objects from which the shadow starts.  Expressed
+       in pixel-sized units.  Must be positive; values less than 1.0 are not
+       recommended.  Larger values produce less depth artifacts, but may prevent
+       thin objects from casting shadows on nearby sufraces. */
+       void set_depth_bias(float);
+
+       const Texture2D &get_depth_texture() const { return depth_buf; }
+       const Matrix &get_shadow_matrix() const { return shadow_matrix; }
+
+       virtual void setup_frame(Renderer &);
+       virtual void finish_frame();
+
+       virtual void render(Renderer &, const Tag & = Tag()) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/environmentmap.cpp b/source/environmentmap.cpp
deleted file mode 100644 (file)
index 3902eb8..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-#include <algorithm>
-#include <cmath>
-#include "environmentmap.h"
-#include "renderer.h"
-#include "texunit.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-EnvironmentMap::EnvironmentMap(unsigned s, Renderable &r, Renderable &e):
-       Effect(r),
-       size(s),
-       environment(e),
-       sampler(get_linear_sampler()),
-       rendered(false),
-       update_interval(1),
-       update_delay(0)
-{
-       env_tex.storage(RGB8, size, 1);
-       depth_buf.storage(DEPTH_COMPONENT32F, size, size);
-       for(unsigned i=0; i<6; ++i)
-       {
-               fbo[i].attach(COLOR_ATTACHMENT0, env_tex, TextureCube::enumerate_faces(i), 0);
-               fbo[i].attach(DEPTH_ATTACHMENT, depth_buf);
-               fbo[i].require_complete();
-       }
-
-       camera.set_field_of_view(Geometry::Angle<float>::right());
-       camera.set_aspect_ratio(1);
-       camera.set_depth_clip(0.1, 100);
-}
-
-void EnvironmentMap::set_depth_clip(float n, float f)
-{
-       camera.set_depth_clip(n, f);
-}
-
-void EnvironmentMap::set_update_interval(unsigned i)
-{
-       update_interval = i;
-       update_delay = min(update_delay, update_interval-1);
-}
-
-void EnvironmentMap::queue_update()
-{
-       update_delay = 0;
-}
-
-void EnvironmentMap::setup_frame(Renderer &renderer)
-{
-       if(rendered)
-               return;
-
-       rendered = true;
-       renderable.setup_frame(renderer);
-
-       if(update_delay)
-       {
-               if(update_interval)
-                       --update_delay;
-               return;
-       }
-       update_delay = update_interval-1;
-       environment.setup_frame(renderer);
-
-       const Matrix *matrix = renderable.get_matrix();
-       if(!matrix)
-               return;
-
-       Renderer::Push push(renderer);
-       Renderer::Exclude exclude1(renderer, renderable);
-       Renderer::Exclude exclude2(renderer, *this);
-
-       camera.set_position(matrix->column(3).slice<3>(0));
-
-       BindRestore bind_fbo(fbo[0]);
-       for(unsigned i=0; i<6; ++i)
-       {
-               TextureCubeFace face = TextureCube::enumerate_faces(i);
-               fbo[i].bind();
-               fbo[i].clear();
-               camera.set_look_direction(TextureCube::get_face_direction(face));
-               camera.set_up_direction(TextureCube::get_t_direction(face));
-               renderer.set_camera(camera);
-               renderer.render(environment);
-       }
-}
-
-void EnvironmentMap::finish_frame()
-{
-       if(rendered)
-       {
-               rendered = false;
-               renderable.finish_frame();
-               environment.finish_frame();
-       }
-}
-
-void EnvironmentMap::render(Renderer &renderer, const Tag &tag) const
-{
-       if(!enabled_passes.count(tag))
-               return renderer.render(renderable, tag);
-
-       Renderer::Push _push_rend(renderer);
-
-       unsigned unit = renderer.allocate_effect_texunit();
-       shdata.uniform("environment", static_cast<int>(unit));
-       Bind _bind_sampler(*sampler, unit);
-       Bind _bind_env(env_tex, unit);
-
-       const Matrix &camera_matrix = renderer.get_camera()->get_object_matrix();
-       shdata.uniform("env_eye_matrix", camera_matrix.block<3, 3>(0, 0));
-
-       renderer.add_shader_data(shdata);
-       renderer.render(renderable, tag);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/environmentmap.h b/source/environmentmap.h
deleted file mode 100644 (file)
index c54968d..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-#ifndef MSP_GL_ENVIRONMENTMAP_H_
-#define MSP_GL_ENVIRONMENTMAP_H_
-
-#include "camera.h"
-#include "effect.h"
-#include "framebuffer.h"
-#include "matrix.h"
-#include "programdata.h"
-#include "renderbuffer.h"
-#include "texturecube.h"
-#include "vector.h"
-
-namespace Msp {
-namespace GL {
-
-class Renderable;
-
-/**
-Creates a cube map texture of the surroundings of the renderable.  This texture
-can then be used to implement effects such as reflections or refractions.
-
-If the EnvironmentMap is used in a Pipeline, it's worth noting that the cube
-map will be prepared outside of any rendering pass.  It's recommended to use
-another Pipeline to define which passes should be used to render the
-environment.
-*/
-class EnvironmentMap: public Effect
-{
-private:
-       unsigned size;
-       Renderable &environment;
-       TextureCube env_tex;
-       Renderbuffer depth_buf;
-       Framebuffer fbo[6];
-       RefPtr<Sampler> sampler;
-       Camera camera;
-       mutable ProgramData shdata;
-       bool rendered;
-       unsigned update_interval;
-       unsigned update_delay;
-
-public:
-       EnvironmentMap(unsigned size, Renderable &rend, Renderable &env);
-
-       void set_depth_clip(float, float);
-       void set_update_interval(unsigned);
-       void queue_update();
-
-       virtual void setup_frame(Renderer &);
-       virtual void finish_frame();
-
-       virtual void render(Renderer &, const Tag & = Tag()) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/error.h b/source/error.h
deleted file mode 100644 (file)
index 8af3ec6..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef MSP_GL_ERROR_H_
-#define MSP_GL_ERROR_H_
-
-#include <stdexcept>
-
-namespace Msp {
-namespace GL {
-
-class unsupported_extension: public std::runtime_error
-{
-public:
-       unsupported_extension(const std::string &w): std::runtime_error(w) { }
-       virtual ~unsupported_extension() throw() { }
-};
-
-class invalid_operation: public std::logic_error
-{
-public:
-       invalid_operation(const std::string &w): std::logic_error(w) { }
-       virtual ~invalid_operation() throw() { }
-};
-
-class stack_underflow: public std::logic_error
-{
-public:
-       stack_underflow(const std::string &w): std::logic_error(w) { }
-       virtual ~stack_underflow() throw() { }
-};
-
-class incompatible_data: public std::logic_error
-{
-public:
-       incompatible_data(const std::string &w): std::logic_error(w) { }
-       virtual ~incompatible_data() throw() { }
-};
-
-class compile_error: public std::runtime_error
-{
-public:
-       compile_error(const std::string &w): std::runtime_error(w) { }
-       virtual ~compile_error() throw() { }
-};
-
-class incomplete_uniform_block: public std::runtime_error
-{
-public:
-       incomplete_uniform_block(const std::string &w): std::runtime_error(w) { }
-       virtual ~incomplete_uniform_block() throw() { }
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/extension.cpp b/source/extension.cpp
deleted file mode 100644 (file)
index 8618cc3..0000000
+++ /dev/null
@@ -1,257 +0,0 @@
-#include <set>
-#include <cstdlib>
-#if defined(__ANDROID__)
-#include <EGL/egl.h>
-#elif defined(_WIN32)
-#include <windows.h>
-#elif !defined(__APPLE__)
-#define GLX_GLXEXT_PROTOTYPES
-#include <GL/glx.h>
-#endif
-#include <msp/strings/format.h>
-#include <msp/strings/utils.h>
-#include "error.h"
-#include "extension.h"
-#include "gl.h"
-
-#ifndef GL_VERSION_3_0
-#define GL_NUM_EXTENSIONS 0x821D
-#endif
-
-#ifndef GL_VERSION_3_2
-#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001
-#define GL_CONTEXT_PROFILE_MASK 0x9126
-#endif
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Version::Version()
-{
-       major = 0;
-       minor = 0;
-}
-
-Version::Version(unsigned short a, unsigned short i)
-{
-       major = a;
-       minor = i;
-}
-
-Version::Version(const string &s)
-{
-       vector<string> parts = split(s, '.');
-       major = lexical_cast<unsigned>(parts[0]);
-       minor = lexical_cast<unsigned>(parts[1]);
-}
-
-bool Version::operator>=(const Version &other) const
-{
-       return major>other.major || (major==other.major && minor>=other.minor);
-}
-
-
-Extension::Extension(const char *n, InitFunc f):
-       name(n),
-       init_func(f),
-       init_done(false),
-       support(UNSUPPORTED)
-{ }
-
-Extension::operator bool() const
-{
-       if(!init_done)
-       {
-               support = init_func();
-               init_done = true;
-       }
-
-       return support>UNSUPPORTED;
-}
-
-
-Require::Require(const Extension &ext)
-{
-       if(!ext)
-               throw unsupported_extension(ext.get_name());
-}
-
-
-bool is_supported(const string &ext)
-{
-       if(is_disabled(ext))
-               return false;
-
-       static set<string> extensions;
-       static bool init_done = false;
-
-       if(!init_done)
-       {
-               if(get_gl_api()==OPENGL && get_gl_version()>=Version(3, 0))
-               {
-                       typedef GLubyte *(APIENTRY *FPtr_glGetStringi)(GLenum, GLuint);
-                       FPtr_glGetStringi glGetStringi = reinterpret_cast<FPtr_glGetStringi>(get_proc_address("glGetStringi"));
-                       int n_extensions;
-                       glGetIntegerv(GL_NUM_EXTENSIONS, &n_extensions);
-                       for(int i=0; i<n_extensions; ++i)
-                               extensions.insert(reinterpret_cast<const char *>(glGetStringi(GL_EXTENSIONS, i)));
-               }
-               else
-               {
-                       if(const char *gl_ext = reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)))
-                       {
-                               vector<string> exts = split(gl_ext);
-                               extensions.insert(exts.begin(), exts.end());
-                       }
-               }
-
-               init_done = true;
-       }
-
-       return extensions.count(ext);
-}
-
-bool is_supported(const Version &core_version, const Version &deprecated_version)
-{
-       const Version &version = get_gl_version();
-       if(deprecated_version && version>=deprecated_version && get_gl_profile()==CORE_PROFILE)
-               return false;
-       return (version>=core_version);
-}
-
-bool is_disabled(const string &ext)
-{
-       static set<string> disabled_exts;
-       static bool init_done = false;
-
-       if(!init_done)
-       {
-               if(const char *disable_ptr = getenv("MSPGL_DISABLE_EXTENSIONS"))
-               {
-                       vector<string> disable = split(disable_ptr);
-                       disabled_exts.insert(disable.begin(), disable.end());
-               }
-
-               if(const char *renderer_ptr = reinterpret_cast<const char *>(glGetString(GL_RENDERER)))
-               {
-                       string renderer = renderer_ptr;
-                       if(renderer.find("Radeon")!=string::npos || renderer.find("AMD")!=string::npos)
-                       {
-                               // The core primitive restart feature does not work either.
-                               disabled_exts.insert("GL_MSP_primitive_restart");
-
-                               /* AMD's uniform buffer objects only work with the core version of
-                               shaders. */
-                               if(get_gl_version()<Version(2, 0))
-                                       disabled_exts.insert("GL_ARB_uniform_buffer_object");
-                       }
-               }
-
-               init_done = true;
-       }
-
-       return disabled_exts.count(ext);
-}
-
-GLApi get_gl_api()
-{
-#ifdef GL_ES_VERSION_2_0
-       return OPENGL_ES2;
-#else
-       return OPENGL;
-#endif
-}
-
-inline GLProfile _get_gl_profile()
-{
-       if(get_gl_api()==OPENGL && get_gl_version()>=Version(3, 0))
-       {
-               int mask;
-               glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask);
-               if(mask==GL_CONTEXT_CORE_PROFILE_BIT)
-                       return CORE_PROFILE;
-       }
-
-       return COMPATIBILITY_PROFILE;
-}
-
-GLProfile get_gl_profile()
-{
-       static GLProfile profile = _get_gl_profile();
-       return profile;
-}
-
-inline Version _get_gl_version()
-{
-       const char *gl_ver_ptr = reinterpret_cast<const char *>(glGetString(GL_VERSION));
-       if(!gl_ver_ptr)
-               throw runtime_error("OpenGL version not available");
-
-       string gl_ver = gl_ver_ptr;
-       if(!gl_ver.compare(0, 10, "OpenGL ES "))
-               gl_ver.erase(0, 10);
-
-       Version ver(gl_ver.substr(0, gl_ver.find(' ')));
-
-       if(const char *force_ver_ptr = getenv("MSPGL_FORCE_VERSION"))
-       {
-               Version force_ver(force_ver_ptr);
-               if(force_ver<ver)
-                       ver = force_ver;
-       }
-
-       return ver;
-}
-
-const Version &get_gl_version()
-{
-       static Version version = _get_gl_version();
-       return version;
-}
-
-inline Version _get_glsl_version()
-{
-       const char *glsl_ver_ptr = reinterpret_cast<const char *>(glGetString(GL_SHADING_LANGUAGE_VERSION));
-       if(!glsl_ver_ptr)
-               throw runtime_error("GLSL version not available");
-
-       string glsl_ver = glsl_ver_ptr;
-       if(!glsl_ver.compare(0, 18, "OpenGL ES GLSL ES "))
-               glsl_ver.erase(0, 18);
-
-       Version ver(glsl_ver.substr(0, glsl_ver.find(' ')));
-
-       if(const char *force_ver_ptr = getenv("MSPGL_FORCE_GLSL_VERSION"))
-       {
-               Version force_ver(force_ver_ptr);
-               if(force_ver<ver)
-                       ver = force_ver;
-       }
-
-       return ver;
-}
-
-const Version &get_glsl_version()
-{
-       static Version version = _get_glsl_version();
-       return version;
-}
-
-ExtFunc *get_proc_address(const string &name)
-{
-#if defined(_WIN32)
-       return reinterpret_cast<ExtFunc *>(wglGetProcAddress(name.c_str()));
-#elif defined(__APPLE__)
-       (void)name;
-       return 0;  // Not supported
-#elif defined(__ANDROID__)
-       return eglGetProcAddress(name.c_str());
-#else
-       return glXGetProcAddressARB(reinterpret_cast<const unsigned char *>(name.c_str()));
-#endif
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/extension.h b/source/extension.h
deleted file mode 100644 (file)
index cf824e2..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-#ifndef MSP_GL_EXTENSION_H_
-#define MSP_GL_EXTENSION_H_
-
-#include <string>
-
-namespace Msp {
-namespace GL {
-
-enum GLApi
-{
-       OPENGL,
-       OPENGL_ES2
-};
-
-enum GLProfile
-{
-       CORE_PROFILE,
-       COMPATIBILITY_PROFILE
-};
-
-
-struct Version
-{
-       unsigned short major;
-       unsigned short minor;
-
-       Version();
-       Version(unsigned short, unsigned short);
-       Version(const std::string &);
-
-       bool operator>=(const Version &) const;
-       bool operator<(const Version &o) const { return !(*this>=o); }
-       operator bool() const { return major || minor; }
-};
-
-
-/**
-Holds metadata about an extension.  Evaluates to true if the extension is
-supported.
-*/
-class Extension
-{
-public:
-       enum SupportLevel
-       {
-               UNSUPPORTED,
-               EXTENSION,
-               CORE
-       };
-
-       typedef SupportLevel (*InitFunc)();
-
-private:
-       const char *name;
-       InitFunc init_func;
-       mutable bool init_done;
-       mutable SupportLevel support;
-
-public:
-       Extension(const char *, InitFunc);
-
-       const char *get_name() const { return name; }
-       operator bool() const;
-};
-
-
-struct Require
-{
-       Require(const Extension &);
-};
-
-
-typedef void ExtFunc();
-
-/** Checks for extension support.  Only intended for internal use. */
-bool is_supported(const std::string &);
-
-/** Checks for OpenGL version support.  Only intended for internal use. */
-bool is_supported(const Version &, const Version & = Version());
-
-/** Indicates whether an extension has been disabled, either explicitly through
-the MSPGL_DISABLE_EXTENSIONS environment variable or implicitly as a workaround
-for a driver bug.  Only intended for internal use. */
-bool is_disabled(const std::string &);
-
-/** Returns the API for which the library was compiled. */
-GLApi get_gl_api();
-
-/** Returns the OpenGL profile for the active context. */
-GLProfile get_gl_profile();
-
-/** Returns the OpenGL version number, as reported by the implementation. */
-const Version &get_gl_version();
-
-/** Returns the GLSL version number, as reported by the implementation. */
-const Version &get_glsl_version();
-
-/** Returns the address of an extension function.  Only indended for internal
-use. */
-ExtFunc *get_proc_address(const std::string &);
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/font.cpp b/source/font.cpp
deleted file mode 100644 (file)
index 5990c57..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-#include <msp/core/maputils.h>
-#include <msp/datafile/collection.h>
-#include "bindable.h"
-#include "gl.h"
-#include "font.h"
-#include "primitivebuilder.h"
-#include "texture2d.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Font::Font():
-       native_size(1),
-       ascent(1),
-       descent(0),
-       cap_height(1),
-       x_height(0.5)
-{ }
-
-// Avoid synthesizing ~RefPtr in files including font.h
-Font::~Font()
-{ }
-
-void Font::set_texture(const Texture2D &t)
-{
-       texture = &t;
-       texture.keep();
-}
-
-const Texture2D &Font::get_texture() const
-{
-       if(!texture)
-               throw logic_error("No texture");
-       return *texture;
-}
-
-void Font::add_glyph(const Glyph &g)
-{
-       insert_unique(glyphs, g.code, g);
-}
-
-void Font::set_kerning(unsigned l, unsigned r, float d)
-{
-       kerning[CodePair(l, r)] = d;
-}
-
-float Font::get_string_width(const string &str, StringCodec::Decoder &dec) const
-{
-       float x = 0;
-
-       unsigned prev = 0;
-       for(string::const_iterator i=str.begin(); i!=str.end();)
-       {
-               unsigned c = dec.decode_char(str, i);
-               if(prev)
-                       x += get_glyph_advance(prev, c);
-               prev = c;
-       }
-       x += get_glyph_advance(prev);
-
-       return x;
-}
-
-void Font::build_string(const string &str, StringCodec::Decoder &dec, PrimitiveBuilder &bld) const
-{
-       VertexBuilder::PushMatrix push_mtx(bld);
-
-       unsigned prev = 0;
-       unsigned next = 0;
-       for(string::const_iterator i=str.begin(); (next || i!=str.end());)
-       {
-               unsigned c = (next ? next : dec.decode_char(str, i));
-               next = (i!=str.end() ? dec.decode_char(str, i) : 0);
-
-               if(unsigned lig = get_ligature(c, next))
-               {
-                       c = lig;
-                       next = 0;
-               }
-
-               GlyphMap::const_iterator j = glyphs.find(c);
-               if(j==glyphs.end())
-                       continue;
-
-               if(prev)
-                       bld.transform(Matrix::translation(get_glyph_advance(prev, c), 0, 0));
-
-               create_glyph_quad(j->second, bld);
-               prev = c;
-       }
-}
-
-void Font::create_glyph_quad(const Glyph &glyph, PrimitiveBuilder &bld) const
-{
-       bld.begin(TRIANGLE_STRIP);
-       bld.texcoord(glyph.x1, glyph.y2);
-       bld.vertex(glyph.off_x, glyph.off_y+glyph.h);
-       bld.texcoord(glyph.x1, glyph.y1);
-       bld.vertex(glyph.off_x, glyph.off_y);
-       bld.texcoord(glyph.x2, glyph.y2);
-       bld.vertex(glyph.off_x+glyph.w, glyph.off_y+glyph.h);
-       bld.texcoord(glyph.x2, glyph.y1);
-       bld.vertex(glyph.off_x+glyph.w, glyph.off_y);
-       bld.end();
-}
-
-float Font::get_glyph_advance(unsigned code, unsigned next) const
-{
-       GlyphMap::const_iterator i = glyphs.find(code);
-       if(i==glyphs.end())
-               return 0;
-
-       float advance = i->second.advance;
-
-       if(next)
-       {
-               KerningMap::const_iterator j = kerning.find(CodePair(code, next));
-               if(j!=kerning.end())
-                       advance += j->second;
-       }
-
-       return advance;
-}
-
-unsigned Font::get_ligature(unsigned code, unsigned next) const
-{
-       LigatureMap::const_iterator i = ligatures.find(CodePair(code, next));
-       return (i!=ligatures.end() ? i->second : 0);
-}
-
-
-Font::Glyph::Glyph():
-       code(0),
-       x1(0),
-       y1(0),
-       x2(1),
-       y2(1),
-       w(1),
-       h(1),
-       off_x(0),
-       off_y(0),
-       advance(1)
-{ }
-
-
-Font::Loader::Loader(Font &f):
-       DataFile::CollectionObjectLoader<Font>(f, 0)
-{
-       init();
-}
-
-Font::Loader::Loader(Font &f, Collection &c):
-       DataFile::CollectionObjectLoader<Font>(f, &c)
-{
-       init();
-}
-
-void Font::Loader::init()
-{
-       add("native_size", &Font::native_size);
-       add("ascent",      &Font::ascent);
-       add("cap_height",  &Font::cap_height);
-       add("descent",     &Font::descent);
-       add("texture",     &Loader::texture);
-       add("texture",     &Loader::texture_ref);
-       add("glyph",       &Loader::glyph);
-       add("kerning",     &Loader::kerning);
-       add("ligature",    &Loader::ligature);
-       add("x_height",    &Font::x_height);
-}
-
-void Font::Loader::glyph(unsigned c)
-{
-       Glyph gl;
-       gl.code = c;
-       load_sub(gl);
-       obj.glyphs.insert(GlyphMap::value_type(c, gl));
-}
-
-void Font::Loader::kerning(unsigned l, unsigned r, float d)
-{
-       obj.kerning[CodePair(l, r)] = d;
-}
-
-void Font::Loader::ligature(unsigned l, unsigned r, unsigned g)
-{
-       obj.ligatures[CodePair(l, r)] = g;
-}
-
-void Font::Loader::texture()
-{
-       RefPtr<Texture2D> tex = new Texture2D;
-       load_sub(*tex);
-       obj.texture = tex;
-}
-
-void Font::Loader::texture_ref(const string &name)
-{
-       obj.texture = &get_collection().get<Texture2D>(name);
-       obj.texture.keep();
-}
-
-
-Font::Glyph::Loader::Loader(Glyph &g):
-       DataFile::ObjectLoader<Glyph>(g)
-{
-       add("texcoords", &Loader::texcoords);
-       add("size",      &Glyph::w,     &Glyph::h);
-       add("offset",    &Glyph::off_x, &Glyph::off_y);
-       add("advance",   &Glyph::advance);
-}
-
-void Font::Glyph::Loader::texcoords(float x1_, float y1_, float x2_, float y2_)
-{
-       obj.x1 = x1_;
-       obj.y1 = y1_;
-       obj.x2 = x2_;
-       obj.y2 = y2_;
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/font.h b/source/font.h
deleted file mode 100644 (file)
index 9f040dc..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-#ifndef MSP_GL_FONT_H_
-#define MSP_GL_FONT_H_
-
-#include <map>
-#include <string>
-#include <msp/datafile/objectloader.h>
-#include <msp/stringcodec/utf8.h>
-#include "vertexarray.h"
-
-namespace Msp {
-namespace GL {
-
-class PrimitiveBuilder;
-class Texture2D;
-
-/**
-Stores a set of glyphs and creates strings out of them.
-*/
-class Font
-{
-public:
-       class Loader: public DataFile::CollectionObjectLoader<Font>
-       {
-       public:
-               Loader(Font &);
-               Loader(Font &, Collection &);
-
-       private:
-               void init();
-               void glyph(unsigned);
-               void kerning(unsigned, unsigned, float);
-               void ligature(unsigned, unsigned, unsigned);
-               void texture();
-               void texture_ref(const std::string &);
-       };
-
-       struct Glyph
-       {
-               class Loader: public Msp::DataFile::ObjectLoader<Glyph>
-               {
-               public:
-                       Loader(Glyph &);
-               private:
-                       void texcoords(float, float, float, float);
-               };
-
-               unsigned code;
-               float x1, y1;
-               float x2, y2;
-               float w, h;
-               float off_x, off_y;
-               float advance;
-
-               Glyph();
-       };
-
-private:
-       typedef std::map<unsigned, Glyph> GlyphMap;
-       typedef std::pair<unsigned, unsigned> CodePair;
-       typedef std::map<CodePair, float> KerningMap;
-       typedef std::map<CodePair, unsigned> LigatureMap;
-
-       RefPtr<const Texture2D> texture;
-       float native_size;
-       float ascent;
-       float descent;
-       float cap_height;
-       float x_height;
-       GlyphMap glyphs;
-       KerningMap kerning;
-       LigatureMap ligatures;
-
-public:
-       Font();
-       ~Font();
-
-       void set_texture(const Texture2D &);
-       const Texture2D &get_texture() const;
-
-       /** Adds a glyph to the font.  There must not be an existing glyph with the
-       same code. */
-       void add_glyph(const Glyph &);
-       void set_kerning(unsigned, unsigned, float);
-
-       /** Returns the size used to generate the font texture.  This serves as a
-       hint for obtaining the best quality when rendering strings. */
-       float get_native_size() const { return native_size; }
-
-       float get_ascent() const { return ascent; }
-       float get_descent() const { return descent; }
-       float get_cap_height() const { return cap_height; }
-       float get_x_height() const { return x_height; }
-
-       /** Returns the width of a string, in multiples of the font size.  Scale the
-       result according to the size used in rendering. */
-       float get_string_width(const std::string &, StringCodec::Decoder &) const;
-
-       template<class C>
-       float get_string_width(const std::string &str) const
-       {
-               typename C::Decoder dec;
-               return get_string_width(str, dec);
-       }
-
-       float get_string_width(const std::string &str) const
-       { return get_string_width<StringCodec::Utf8>(str); }
-
-       /** Builds the primitives for a string.  Two-dimensional vertex and texture
-       coordinates are generated.  Size 1.0 is used for building; set up the
-       builder's matrix before the call.  The texture is not bound, to avoid
-       unnecessary bindings when creating meshes. */
-       void build_string(const std::string &, StringCodec::Decoder &, PrimitiveBuilder &) const;
-
-       template<class C>
-       void build_string(const std::string &str, PrimitiveBuilder &pb) const
-       {
-               typename C::Decoder dec;
-               build_string(str, dec, pb);
-       }
-
-       void build_string(const std::string &str, PrimitiveBuilder &pb) const
-       { return build_string<StringCodec::Utf8>(str, pb); }
-
-private:
-       void create_glyph_quad(const Glyph &, PrimitiveBuilder &) const;
-       float get_glyph_advance(unsigned, unsigned = 0) const;
-       unsigned get_ligature(unsigned, unsigned) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/framebuffer.cpp b/source/framebuffer.cpp
deleted file mode 100644 (file)
index c90bb22..0000000
+++ /dev/null
@@ -1,436 +0,0 @@
-#include <msp/gl/extensions/arb_draw_buffers.h>
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include <msp/gl/extensions/ext_framebuffer_blit.h>
-#include <msp/gl/extensions/ext_framebuffer_object.h>
-#include <msp/gl/extensions/ext_texture_array.h>
-#include <msp/gl/extensions/ext_texture3d.h>
-#include <msp/gl/extensions/msp_buffer_control.h>
-#include "error.h"
-#include "framebuffer.h"
-#include "misc.h"
-#include "renderbuffer.h"
-#include "texture2d.h"
-#include "texture3d.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-void operator<<(LexicalConverter &conv, FramebufferStatus status)
-{
-       switch(status)
-       {
-       case FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
-               conv.result("incomplete attachment");
-               break;
-       case FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
-               conv.result("missing attachment");
-               break;
-       case FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
-               conv.result("mismatched attachment dimensions");
-               break;
-       case FRAMEBUFFER_INCOMPLETE_FORMATS:
-               conv.result("mismatched attachment formats");
-               break;
-       case FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
-               conv.result("missing draw buffer attachment");
-               break;
-       case FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
-               conv.result("missing read buffer attachment");
-               break;
-       case FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
-               conv.result("mismatched attachment sample counts");
-               break;
-       case FRAMEBUFFER_INCOMPLETE_LAYER_COUNT:
-               conv.result("mismatched attachment layer counts");
-               break;
-       case FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
-               conv.result("mismatched attachment layering");
-               break;
-       case FRAMEBUFFER_UNSUPPORTED:
-               conv.result("unsupported");
-               break;
-       default:
-               conv.result(lexical_cast<string, unsigned>(status, "%#x"));
-               break;
-       }
-}
-
-framebuffer_incomplete::framebuffer_incomplete(FramebufferStatus status):
-       runtime_error(lexical_cast<string>(status))
-{ }
-
-
-Framebuffer::Framebuffer(unsigned i):
-       id(i),
-       dirty(0)
-{
-       if(id)
-               throw invalid_argument("System framebuffer must have id 0");
-
-       glGetIntegerv(GL_VIEWPORT, &view.left);
-       width = view.width;
-       height = view.height;
-}
-
-Framebuffer::Framebuffer():
-       width(0),
-       height(0),
-       dirty(0)
-{
-       static Require _req(EXT_framebuffer_object);
-
-       if(ARB_direct_state_access)
-               glCreateFramebuffers(1, &id);
-       else
-               glGenFramebuffers(1, &id);
-}
-
-Framebuffer::~Framebuffer()
-{
-       if(id)
-               glDeleteFramebuffers(1, &id);
-       if(current()==this)
-               unbind();
-}
-
-void Framebuffer::update_attachment(unsigned mask) const
-{
-       if(!ARB_direct_state_access && current()!=this)
-       {
-               dirty |= mask;
-               return;
-       }
-
-       std::vector<GLenum> color_bufs;
-       color_bufs.reserve(attachments.size());
-       for(unsigned i=0; i<attachments.size(); ++i)
-       {
-               const Attachment &attch = attachments[i];
-               if(mask&(1<<i))
-               {
-                       if(attch.type==GL_RENDERBUFFER)
-                       {
-                               if(ARB_direct_state_access)
-                                       glNamedFramebufferRenderbuffer(id, attch.attachment, GL_RENDERBUFFER, attch.rbuf->get_id());
-                               else
-                                       glFramebufferRenderbuffer(GL_FRAMEBUFFER, attch.attachment, GL_RENDERBUFFER, attch.rbuf->get_id());
-                       }
-                       else if(attch.type==GL_TEXTURE_2D)
-                       {
-                               static_cast<Texture2D *>(attch.tex)->allocate(attch.level);
-                               if(ARB_direct_state_access)
-                                       glNamedFramebufferTexture(id, attch.attachment, attch.tex->get_id(), attch.level);
-                               else
-                                       glFramebufferTexture2D(GL_FRAMEBUFFER, attch.attachment, attch.type, attch.tex->get_id(), attch.level);
-                       }
-                       else if(attch.type==GL_TEXTURE_3D || attch.type==GL_TEXTURE_2D_ARRAY)
-                       {
-                               static_cast<Texture3D *>(attch.tex)->allocate(attch.level);
-                               if(ARB_direct_state_access)
-                                       glNamedFramebufferTextureLayer(id, attch.attachment, attch.tex->get_id(), attch.level, attch.layer);
-                               else if(attch.type==GL_TEXTURE_2D_ARRAY)
-                                       glFramebufferTextureLayer(GL_FRAMEBUFFER, attch.attachment, attch.tex->get_id(), attch.level, attch.layer);
-                               else
-                                       glFramebufferTexture3D(GL_FRAMEBUFFER, attch.attachment, attch.type, attch.tex->get_id(), attch.level, attch.layer);
-                       }
-                       else if(attch.type==GL_TEXTURE_CUBE_MAP)
-                       {
-                               static_cast<TextureCube *>(attch.tex)->allocate(attch.level);
-                               if(ARB_direct_state_access)
-                                       glNamedFramebufferTextureLayer(id, attch.attachment, attch.tex->get_id(), attch.level, attch.layer);
-                               else
-                                       glFramebufferTexture2D(GL_FRAMEBUFFER, attch.attachment, TextureCube::enumerate_faces(attch.layer), attch.tex->get_id(), attch.level);
-                       }
-                       else if(ARB_direct_state_access)
-                               glNamedFramebufferRenderbuffer(id, attch.attachment, 0, 0);
-                       else
-                               glFramebufferRenderbuffer(GL_FRAMEBUFFER, attch.attachment, 0, 0);
-               }
-
-               if(attch.attachment>=COLOR_ATTACHMENT0 && attch.attachment<=COLOR_ATTACHMENT3)
-                       color_bufs.push_back(attch.attachment);
-       }
-
-       if(color_bufs.size()>1)
-               static Require _req(ARB_draw_buffers);
-
-       GLenum first_buffer = (color_bufs.empty() ? GL_NONE : color_bufs.front());
-       if(ARB_direct_state_access)
-       {
-               /* ARB_direct_state_access ties the availability of these functions to
-               framebuffers themselves, so no further checks are needed. */
-               glNamedFramebufferDrawBuffers(id, color_bufs.size(), &color_bufs[0]);
-               glNamedFramebufferReadBuffer(id, first_buffer);
-       }
-       else
-       {
-               if(ARB_draw_buffers)
-                       glDrawBuffers(color_bufs.size(), &color_bufs[0]);
-               else if(MSP_buffer_control)
-                       glDrawBuffer(first_buffer);
-
-               if(MSP_buffer_control)
-                       glReadBuffer(first_buffer);
-       }
-}
-
-void Framebuffer::check_size()
-{
-       bool full_viewport = (view.left==0 && view.bottom==0 && view.width==width && view.height==height);
-       for(vector<Attachment>::iterator i=attachments.begin(); i!=attachments.end(); ++i)
-               if(i->type)
-               {
-                       if(i->type==GL_RENDERBUFFER)
-                       {
-                               width = i->rbuf->get_width();
-                               height = i->rbuf->get_height();
-                       }
-                       else if(i->type==GL_TEXTURE_2D)
-                       {
-                               Texture2D *tex = static_cast<Texture2D *>(i->tex);
-                               width = max(tex->get_width()>>i->level, 1U);
-                               height = max(tex->get_height()>>i->level, 1U);
-                       }
-                       else if(i->type==GL_TEXTURE_3D || i->type==GL_TEXTURE_2D_ARRAY)
-                       {
-                               Texture3D *tex = static_cast<Texture3D *>(i->tex);
-                               width = max(tex->get_width()>>i->level, 1U);
-                               height = max(tex->get_height()>>i->level, 1U);
-                       }
-                       else if(i->type==GL_TEXTURE_CUBE_MAP)
-                       {
-                               width = max(static_cast<TextureCube *>(i->tex)->get_size()>>i->level, 1U);
-                               height = width;
-                       }
-                       if(full_viewport)
-                               reset_viewport();
-                       break;
-               }
-}
-
-unsigned Framebuffer::get_attachment_index(FramebufferAttachment attch)
-{
-       for(unsigned i=0; i<attachments.size(); ++i)
-               if(attachments[i].attachment==attch)
-                       return i;
-       attachments.push_back(Attachment(attch));
-       return attachments.size()-1;
-}
-
-void Framebuffer::attach(FramebufferAttachment attch, Renderbuffer &rbuf)
-{
-       if(!id)
-               throw invalid_operation("Framebuffer::attach");
-
-       unsigned i = get_attachment_index(attch);
-       attachments[i].set(rbuf);
-       update_attachment(1<<i);
-       check_size();
-}
-
-void Framebuffer::attach(FramebufferAttachment attch, Texture2D &tex, unsigned level)
-{
-       if(!id)
-               throw invalid_operation("Framebuffer::attach");
-
-       unsigned i = get_attachment_index(attch);
-       attachments[i].set(tex, level, 0);
-       update_attachment(1<<i);
-       check_size();
-}
-
-void Framebuffer::attach(FramebufferAttachment attch, Texture3D &tex, unsigned layer, unsigned level)
-{
-       if(!id)
-               throw invalid_operation("Framebuffer::attach");
-
-       unsigned i = get_attachment_index(attch);
-       attachments[i].set(tex, level, layer);
-       update_attachment(1<<i);
-       check_size();
-}
-
-void Framebuffer::attach(FramebufferAttachment attch, TextureCube &tex, TextureCubeFace face, unsigned level)
-{
-       if(!id)
-               throw invalid_operation("Framebuffer::attach");
-
-       unsigned i = get_attachment_index(attch);
-       attachments[i].set(tex, level, TextureCube::get_face_index(face));
-       update_attachment(1<<i);
-       check_size();
-}
-
-void Framebuffer::detach(FramebufferAttachment attch)
-{
-       if(!id)
-               throw invalid_operation("Framebuffer::detach");
-
-       unsigned i = get_attachment_index(attch);
-       attachments[i].clear();
-       update_attachment(1<<i);
-       check_size();
-}
-
-FramebufferStatus Framebuffer::check_status() const
-{
-       if(ARB_direct_state_access)
-               return static_cast<FramebufferStatus>(glCheckNamedFramebufferStatus(id, GL_FRAMEBUFFER));
-       else
-       {
-               BindRestore _bind(this);
-               return static_cast<FramebufferStatus>(glCheckFramebufferStatus(GL_FRAMEBUFFER));
-       }
-}
-
-void Framebuffer::require_complete() const
-{
-       FramebufferStatus status = check_status();
-       if(status!=FRAMEBUFFER_COMPLETE)
-               throw framebuffer_incomplete(status);
-}
-
-void Framebuffer::viewport(int l, int b, unsigned w, unsigned h)
-{
-       view.left = l;
-       view.bottom = b;
-       view.width = w;
-       view.height = h;
-
-       if(current()==this)
-               glViewport(view.left, view.bottom, view.width, view.height);
-}
-
-void Framebuffer::reset_viewport()
-{
-       viewport(0, 0, width, height);
-}
-
-void Framebuffer::clear()
-{
-       clear(COLOR_BUFFER_BIT|DEPTH_BUFFER_BIT|STENCIL_BUFFER_BIT);
-}
-
-void Framebuffer::clear(BufferBits bits)
-{
-       BindRestore _bind(this);
-       glClear(bits);
-}
-
-void Framebuffer::blit_from(const Framebuffer &other, int sx0, int sy0, int sx1, int sy1, int dx0, int dy0, int dx1, int dy1, BufferBits bits, bool filter)
-{
-       static Require _req(EXT_framebuffer_blit);
-
-       if(ARB_direct_state_access)
-       {
-               glBlitNamedFramebuffer(other.id, id, sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1, bits, (filter ? GL_LINEAR : GL_NEAREST));
-               return;
-       }
-
-       const Framebuffer *old = current();
-       if(set_current(this))
-       {
-               glBindFramebuffer(GL_DRAW_FRAMEBUFFER, id);
-               if(dirty)
-               {
-                       update_attachment(dirty);
-                       dirty = 0;
-               }
-       }
-       if(old!=&other)
-               glBindFramebuffer(GL_READ_FRAMEBUFFER, other.id);
-
-       glBlitFramebuffer(sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1, bits, (filter ? GL_LINEAR : GL_NEAREST));
-
-       set_current(old);
-       glBindFramebuffer(GL_FRAMEBUFFER, (old ? old->id : 0));
-}
-
-void Framebuffer::blit_from(const Framebuffer &other, int sx, int sy, unsigned wd, unsigned ht, int dx, int dy, BufferBits bits)
-{
-       blit_from(other, sx, sy, sx+wd, sy+ht, dx, dy, dx+wd, dy+ht, bits, false);
-}
-
-void Framebuffer::blit_from(const Framebuffer &other, BufferBits bits, bool filter)
-{
-       blit_from(other, 0, 0, other.width, other.height, 0, 0, width, height, bits, filter);
-}
-
-void Framebuffer::bind() const
-{
-       if(id && attachments.empty())
-               throw invalid_operation("Framebuffer::bind");
-
-       if(set_current(this))
-       {
-               glBindFramebuffer(GL_FRAMEBUFFER, id);
-               if(dirty)
-               {
-                       update_attachment(dirty);
-                       dirty = 0;
-               }
-
-               if(width && height)
-                       glViewport(view.left, view.bottom, view.width, view.height);
-       }
-}
-
-const Framebuffer *Framebuffer::current()
-{
-       if(!cur_obj)
-               cur_obj = &system();
-       return cur_obj;
-}
-
-void Framebuffer::unbind()
-{
-       system().bind();
-}
-
-Framebuffer &Framebuffer::system()
-{
-       static Framebuffer sys_framebuf(0);
-       return sys_framebuf;
-}
-
-
-Framebuffer::Attachment::Attachment(FramebufferAttachment a):
-       attachment(a),
-       type(0),
-       level(0),
-       layer(0)
-{ }
-
-void Framebuffer::Attachment::set(Renderbuffer &r)
-{
-       type = GL_RENDERBUFFER;
-       rbuf = &r;
-       level = 0;
-       layer = 0;
-}
-
-void Framebuffer::Attachment::set(Texture &t, unsigned l, unsigned z)
-{
-       type = t.get_target();
-       tex = &t;
-       level = l;
-       layer = z;
-}
-
-void Framebuffer::Attachment::clear()
-{
-       type = 0;
-}
-
-
-Framebuffer::Viewport::Viewport():
-       left(0),
-       bottom(0),
-       width(0),
-       height(0)
-{ }
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/framebuffer.h b/source/framebuffer.h
deleted file mode 100644 (file)
index 3054bd6..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-#ifndef MSP_GL_FRAMEBUFFER_H_
-#define MSP_GL_FRAMEBUFFER_H_
-
-#include <vector>
-#include "bindable.h"
-#include "gl.h"
-#include "texturecube.h"
-#include <msp/gl/extensions/arb_geometry_shader4.h>
-#include <msp/gl/extensions/ext_framebuffer_multisample.h>
-#include <msp/gl/extensions/ext_framebuffer_object.h>
-#include <msp/gl/extensions/nv_fbo_color_attachments.h>
-
-namespace Msp {
-namespace GL {
-
-class Renderbuffer;
-class Texture;
-class Texture2D;
-class Texture3D;
-
-enum FramebufferAttachment
-{
-       COLOR_ATTACHMENT0  = GL_COLOR_ATTACHMENT0,
-       COLOR_ATTACHMENT1  = GL_COLOR_ATTACHMENT1,
-       COLOR_ATTACHMENT2  = GL_COLOR_ATTACHMENT2,
-       COLOR_ATTACHMENT3  = GL_COLOR_ATTACHMENT3,
-       DEPTH_ATTACHMENT   = GL_DEPTH_ATTACHMENT,
-       STENCIL_ATTACHMENT = GL_STENCIL_ATTACHMENT
-};
-
-enum FramebufferStatus
-{
-       FRAMEBUFFER_INCOMPLETE_ATTACHMENT         = GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
-       FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT,
-       FRAMEBUFFER_INCOMPLETE_DIMENSIONS         = GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT,
-       FRAMEBUFFER_INCOMPLETE_FORMATS            = GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT,
-       FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER        = GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER,
-       FRAMEBUFFER_INCOMPLETE_READ_BUFFER        = GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER,
-       FRAMEBUFFER_INCOMPLETE_MULTISAMPLE        = GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
-       FRAMEBUFFER_INCOMPLETE_LAYER_COUNT        = GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB,
-       FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS      = GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS,
-       FRAMEBUFFER_UNSUPPORTED                   = GL_FRAMEBUFFER_UNSUPPORTED,
-       FRAMEBUFFER_COMPLETE                      = GL_FRAMEBUFFER_COMPLETE
-};
-
-enum BufferBits
-{
-       COLOR_BUFFER_BIT   = GL_COLOR_BUFFER_BIT,
-       DEPTH_BUFFER_BIT   = GL_DEPTH_BUFFER_BIT,
-       STENCIL_BUFFER_BIT = GL_STENCIL_BUFFER_BIT
-};
-
-class framebuffer_incomplete: public std::runtime_error
-{
-public:
-       framebuffer_incomplete(FramebufferStatus);
-       virtual ~framebuffer_incomplete() throw() { }
-};
-
-/**
-Framebuffer objects can be used to perform offscreen rendering.  The most
-common application is rendering to a texture, which can then be used for
-fullscreen shader effects.
-
-A framebuffer consist of a number of logical buffers, such as color and depth
-buffers.  Renderbuffers and Textures can be attached to the logical buffers.  At
-least one image must be attached for the framebuffer to be usable.
-
-Requires the GL_EXT_framebuffer_object extension.  The blit functions require
-the GL_EXT_framebuffer_blit extension.
-*/
-class Framebuffer: public Bindable<Framebuffer>
-{
-private:
-       struct Attachment
-       {
-               FramebufferAttachment attachment;
-               GLenum type;
-               union
-               {
-                       Renderbuffer *rbuf;
-                       Texture *tex;
-               };
-               unsigned level;
-               unsigned layer;
-
-               Attachment(FramebufferAttachment);
-               void set(Renderbuffer &);
-               void set(Texture &, unsigned, unsigned);
-               void clear();
-       };
-
-       struct Viewport
-       {
-               int left;
-               int bottom;
-               unsigned width;
-               unsigned height;
-
-               Viewport();
-       };
-
-       unsigned id;
-       std::vector<Attachment> attachments;
-       unsigned width;
-       unsigned height;
-       Viewport view;
-       mutable unsigned dirty;
-
-       Framebuffer(unsigned);
-public:
-       Framebuffer();
-       ~Framebuffer();
-
-       unsigned get_width() const { return width; }
-       unsigned get_height() const { return height; }
-
-private:
-       void update_attachment(unsigned) const;
-       void check_size();
-       unsigned get_attachment_index(FramebufferAttachment);
-public:
-       void attach(FramebufferAttachment attch, Renderbuffer &rbuf);
-       void attach(FramebufferAttachment attch, Texture2D &tex, unsigned level = 0);
-       void attach(FramebufferAttachment attch, Texture3D &tex, unsigned layer, unsigned level = 0);
-       void attach(FramebufferAttachment attch, TextureCube &tex, TextureCubeFace face, unsigned level = 0);
-       void detach(FramebufferAttachment attch);
-
-       /** Checks the completeness of the framebuffer.  Returns
-       FRAMEBUFFER_COMPLETE if the framebuffer is complete and can be rendered to,
-       or one of the error status codes otherwise. */
-       FramebufferStatus check_status() const;
-
-       /** Ensures that the framebuffer is complete, throwing an exception if it
-       isn't. */
-       void require_complete() const;
-
-       void viewport(int, int, unsigned, unsigned);
-       void reset_viewport();
-
-       void clear();
-       void clear(BufferBits);
-
-       /** Blits a region from another framebuffer into this one.  If the source
-       and destination regions have different dimensions, the contents will be
-       stretched.  If filter is true, linear interpolation will be used, otherwise
-       no interpolation is done. */
-       void blit_from(const Framebuffer &other, int sx0, int sy0, int sx1, int sy1,
-               int dx0, int dy0, int dx1, int dy1, BufferBits bits, bool filter);
-
-       /** Blits a region from another framebuffer into this one, retaining its
-       dimensions. */
-       void blit_from(const Framebuffer & other, int sx, int sy,
-               unsigned wd, unsigned ht, int dx, int dy, BufferBits bits);
-
-       /** Blits the entire contents of another framebuffer into this one. */
-       void blit_from(const Framebuffer &other, BufferBits bits, bool filter);
-
-       void bind() const;
-
-       static const Framebuffer *current();
-       static void unbind();
-
-       static Framebuffer &system();
-};
-
-inline BufferBits operator|(BufferBits a, BufferBits b)
-{ return static_cast<BufferBits>(static_cast<int>(a)|static_cast<int>(b)); }
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/geometrybuilder.cpp b/source/geometrybuilder.cpp
deleted file mode 100644 (file)
index 729b40f..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-#include "geometrybuilder.h"
-#include "meshbuilder.h"
-
-namespace Msp {
-namespace GL {
-
-GeometryBuilder::GeometryBuilder():
-       generate_tbn(false),
-       tex_fit(STRETCH)
-{ }
-
-GeometryBuilder &GeometryBuilder::tbn(bool t)
-{
-       generate_tbn = t;
-       return *this;
-}
-
-GeometryBuilder &GeometryBuilder::texture_fit(TextureFit tf)
-{
-       tex_fit = tf;
-       return *this;
-}
-
-void GeometryBuilder::adjust_texture_scale(float &u_scale, float &v_scale, float width, float height) const
-{
-       if(tex_fit!=STRETCH)
-       {
-               if((width<height)==(tex_fit==CROP))
-                       u_scale *= width/height;
-               else
-                       v_scale *= height/width;
-       }
-}
-
-void GeometryBuilder::build(Mesh &mesh) const
-{
-       MeshBuilder builder(mesh);
-       builder.auto_offset();
-       build(builder);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/geometrybuilder.h b/source/geometrybuilder.h
deleted file mode 100644 (file)
index 40bcd13..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-#ifndef MSP_GL_GEOMETRYBUILDER_H_
-#define MSP_GL_GEOMETRYBUILDER_H_
-
-namespace Msp {
-namespace GL {
-
-class Mesh;
-class PrimitiveBuilder;
-
-class GeometryBuilder
-{
-public:
-       enum TextureFit
-       {
-               STRETCH,
-               CROP,
-               WRAP
-       };
-
-protected:
-       bool generate_tbn;
-       TextureFit tex_fit;
-
-       GeometryBuilder();
-
-public:
-       GeometryBuilder &tbn(bool = true);
-       GeometryBuilder &texture_fit(TextureFit);
-protected:
-       void adjust_texture_scale(float &, float &, float, float) const;
-
-public:
-       virtual void build(PrimitiveBuilder &) const = 0;
-       void build(Mesh &) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/gl.h b/source/gl.h
deleted file mode 100644 (file)
index f27b63f..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef MSP_GL_GL_H_
-#define MSP_GL_GL_H_
-
-#ifdef __APPLE__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wkeyword-macro"
-#define extern extern __attribute__((weak_import))
-#include <OpenGL/gl.h>
-#include <OpenGL/glext.h>
-#undef extern
-#pragma clang diagnostic pop
-#elif defined(__ANDROID__)
-#include <GLES3/gl3.h>
-#include <GLES3/gl3ext.h>
-typedef double GLdouble;
-typedef long long GLint64;
-#else
-#ifdef _WIN32
-#ifndef WINAPI
-#if defined(_ARM_)
-#define WINAPI
-#else
-#define WINAPI __stdcall
-#endif
-#endif
-#ifndef APIENTRY
-#define APIENTRY WINAPI
-#endif
-#ifndef DECLSPEC_IMPORT
-#define DECLSPEC_IMPORT __declspec(dllimport)
-#endif
-#ifndef WINGDIAPI
-#define WINGDIAPI DECLSPEC_IMPORT
-#endif
-#endif
-#include <GL/gl.h>
-#include <GL/glext.h>
-#endif
-
-#ifndef APIENTRY
-#define APIENTRY
-#endif
-
-#endif
diff --git a/source/glsl/programcompiler.cpp b/source/glsl/programcompiler.cpp
new file mode 100644 (file)
index 0000000..d021ab4
--- /dev/null
@@ -0,0 +1,2096 @@
+#include <msp/core/algorithm.h>
+#include <msp/core/raii.h>
+#include <msp/gl/extensions/arb_explicit_attrib_location.h>
+#include <msp/gl/extensions/arb_gpu_shader5.h>
+#include <msp/gl/extensions/arb_uniform_buffer_object.h>
+#include <msp/gl/extensions/ext_gpu_shader4.h>
+#include <msp/gl/extensions/ext_texture_array.h>
+#include <msp/strings/format.h>
+#include <msp/strings/regex.h>
+#include <msp/strings/utils.h>
+#include "error.h"
+#include "program.h"
+#include "programcompiler.h"
+#include "resources.h"
+#include "shader.h"
+
+#undef interface
+
+using namespace std;
+
+namespace {
+
+const char builtins_src[] =
+       "#pragma MSP stage(vertex)\n"
+       "out gl_PerVertex {\n"
+       "  vec4 gl_Position;\n"
+       "  float gl_ClipDistance[];\n"
+       "};\n"
+       "#pragma MSP stage(geometry)\n"
+       "in gl_PerVertex {\n"
+       "  vec4 gl_Position;\n"
+       "  float gl_ClipDistance[];\n"
+       "} gl_in[];\n"
+       "out gl_PerVertex {\n"
+       "  vec4 gl_Position;\n"
+       "  float gl_ClipDistance[];\n"
+       "};\n";
+
+}
+
+namespace Msp {
+namespace GL {
+
+using namespace ProgramSyntax;
+
+ProgramCompiler::ProgramCompiler():
+       resources(0),
+       module(0)
+{ }
+
+ProgramCompiler::~ProgramCompiler()
+{
+       delete module;
+}
+
+void ProgramCompiler::compile(const string &source, const string &src_name)
+{
+       resources = 0;
+       delete module;
+       module = new Module();
+       ProgramParser parser;
+       imported_names.push_back(src_name);
+       append_module(parser.parse(source, src_name, 1));
+       process();
+}
+
+void ProgramCompiler::compile(IO::Base &io, Resources *res, const string &src_name)
+{
+       resources = res;
+       delete module;
+       module = new Module();
+       ProgramParser parser;
+       imported_names.push_back(src_name);
+       append_module(parser.parse(io, src_name, 1));
+       process();
+}
+
+void ProgramCompiler::compile(IO::Base &io, const string &src_name)
+{
+       compile(io, 0, src_name);
+}
+
+void ProgramCompiler::add_shaders(Program &program)
+{
+       if(!module)
+               throw invalid_operation("ProgramCompiler::add_shaders");
+
+       try
+       {
+               for(list<Stage>::iterator i=module->stages.begin(); i!=module->stages.end(); ++i)
+               {
+                       if(i->type==VERTEX)
+                       {
+                               program.attach_shader_owned(new VertexShader(apply<Formatter>(*i)));
+                               for(map<string, unsigned>::iterator j=i->locations.begin(); j!=i->locations.end(); ++j)
+                                       program.bind_attribute(j->second, j->first);
+                       }
+                       else if(i->type==GEOMETRY)
+                               program.attach_shader_owned(new GeometryShader(apply<Formatter>(*i)));
+                       else if(i->type==FRAGMENT)
+                       {
+                               program.attach_shader_owned(new FragmentShader(apply<Formatter>(*i)));
+                               if(EXT_gpu_shader4)
+                               {
+                                       for(map<string, unsigned>::iterator j=i->locations.begin(); j!=i->locations.end(); ++j)
+                                               program.bind_fragment_data(j->second, j->first);
+                               }
+                       }
+               }
+       }
+       catch(const compile_error &e)
+       {
+               static const Regex r_message("^(([0-9]+)\\(([0-9]+)\\) :|ERROR: ([0-9]+):([0-9]+):) (.*)$");
+               vector<string> lines = split(e.what(), '\n');
+               string translated;
+               for(vector<string>::const_iterator i=lines.begin(); i!=lines.end(); ++i)
+               {
+                       RegMatch m = r_message.match(*i);
+                       if(m)
+                       {
+                               unsigned index = 0;
+                               unsigned line = 0;
+                               if(m[2])
+                               {
+                                       index = lexical_cast<unsigned>(m[2].str);
+                                       line = lexical_cast<unsigned>(m[3].str);
+                               }
+                               else if(m[4])
+                               {
+                                       index = lexical_cast<unsigned>(m[4].str);
+                                       line = lexical_cast<unsigned>(m[5].str);
+                               }
+                               const char *src = "<unknown>";
+                               if(index==0)
+                                       src = "<generated>";
+                               else if(index-1<imported_names.size())
+                                       src = imported_names[index-1].c_str();
+                               translated += format("%s:%d: %s", src, line, m[6].str);
+                       }
+                       else
+                               translated += *i;
+                       translated += '\n';
+               }
+
+               throw compile_error(translated);
+       }
+}
+
+Module *ProgramCompiler::create_builtins_module()
+{
+       ProgramParser parser;
+       Module *module = new Module(parser.parse(builtins_src, "<builtin>"));
+       for(list<Stage>::iterator i=module->stages.begin(); i!=module->stages.end(); ++i)
+       {
+               VariableResolver resolver;
+               i->content.visit(resolver);
+               for(map<string, VariableDeclaration *>::iterator j=i->content.variables.begin(); j!=i->content.variables.end(); ++j)
+                       j->second->linked_declaration = j->second;
+       }
+       return module;
+}
+
+Module &ProgramCompiler::get_builtins_module()
+{
+       static RefPtr<Module> builtins_module = create_builtins_module();
+       return *builtins_module;
+}
+
+Stage *ProgramCompiler::get_builtins(StageType type)
+{
+       Module &module = get_builtins_module();
+       for(list<Stage>::iterator i=module.stages.begin(); i!=module.stages.end(); ++i)
+               if(i->type==type)
+                       return &*i;
+       return 0;
+}
+
+void ProgramCompiler::append_module(ProgramSyntax::Module &mod)
+{
+       vector<Import *> imports = apply<NodeGatherer<Import> >(mod.shared);
+       for(vector<Import *>::iterator i=imports.begin(); i!=imports.end(); ++i)
+               import((*i)->module);
+       apply<NodeRemover>(mod.shared, set<Node *>(imports.begin(), imports.end()));
+
+       append_stage(mod.shared);
+       for(list<Stage>::iterator i=mod.stages.begin(); i!=mod.stages.end(); ++i)
+               append_stage(*i);
+}
+
+void ProgramCompiler::append_stage(Stage &stage)
+{
+       Stage *target = 0;
+       if(stage.type==SHARED)
+               target = &module->shared;
+       else
+       {
+               list<Stage>::iterator i;
+               for(i=module->stages.begin(); (i!=module->stages.end() && i->type<stage.type); ++i) ;
+               if(i==module->stages.end() || i->type>stage.type)
+               {
+                       list<Stage>::iterator j = module->stages.insert(i, stage.type);
+                       if(i!=module->stages.end())
+                               i->previous = &*j;
+                       i = j;
+                       if(i!=module->stages.begin())
+                               i->previous = &*--j;
+               }
+
+               target = &*i;
+       }
+
+       if(stage.required_version>target->required_version)
+               target->required_version = stage.required_version;
+       for(NodeList<Statement>::iterator i=stage.content.body.begin(); i!=stage.content.body.end(); ++i)
+               target->content.body.push_back(*i);
+       apply<DeclarationCombiner>(*target);
+}
+
+void ProgramCompiler::process()
+{
+       for(list<Stage>::iterator i=module->stages.begin(); i!=module->stages.end(); ++i)
+               generate(*i);
+       for(list<Stage>::iterator i=module->stages.begin(); i!=module->stages.end(); )
+       {
+               if(optimize(*i))
+                       i = module->stages.begin();
+               else
+                       ++i;
+       }
+       for(list<Stage>::iterator i=module->stages.begin(); i!=module->stages.end(); ++i)
+               finalize(*i);
+}
+
+void ProgramCompiler::import(const string &name)
+{
+       string fn = name+".glsl";
+       if(find(imported_names, fn)!=imported_names.end())
+               return;
+       imported_names.push_back(fn);
+
+       RefPtr<IO::Seekable> io = (resources ? resources->open_raw(fn) : Resources::get_builtins().open(fn));
+       if(!io)
+               throw runtime_error(format("module %s not found", name));
+       ProgramParser import_parser;
+       append_module(import_parser.parse(*io, fn, imported_names.size()));
+}
+
+void ProgramCompiler::generate(Stage &stage)
+{
+       if(module->shared.required_version>stage.required_version)
+               stage.required_version = module->shared.required_version;
+       inject_block(stage.content, module->shared.content);
+
+       apply<DeclarationReorderer>(stage);
+       apply<FunctionResolver>(stage);
+       apply<VariableResolver>(stage);
+       apply<InterfaceGenerator>(stage);
+       apply<VariableResolver>(stage);
+       apply<DeclarationReorderer>(stage);
+       apply<FunctionResolver>(stage);
+       apply<LegacyConverter>(stage);
+}
+
+bool ProgramCompiler::optimize(Stage &stage)
+{
+       apply<ConstantConditionEliminator>(stage);
+
+       set<FunctionDeclaration *> inlineable = apply<InlineableFunctionLocator>(stage);
+       apply<FunctionInliner>(stage, inlineable);
+
+       set<Node *> unused = apply<UnusedVariableLocator>(stage);
+       set<Node *> unused2 = apply<UnusedFunctionLocator>(stage);
+       unused.insert(unused2.begin(), unused2.end());
+       apply<NodeRemover>(stage, unused);
+
+       return !unused.empty();
+}
+
+void ProgramCompiler::finalize(Stage &stage)
+{
+       if(get_gl_api()==OPENGL_ES2)
+               apply<DefaultPrecisionGenerator>(stage);
+       else
+               apply<PrecisionRemover>(stage);
+}
+
+void ProgramCompiler::inject_block(Block &target, const Block &source)
+{
+       NodeList<Statement>::iterator insert_point = target.body.begin();
+       for(NodeList<Statement>::const_iterator i=source.body.begin(); i!=source.body.end(); ++i)
+               target.body.insert(insert_point, (*i)->clone());
+}
+
+template<typename T>
+typename T::ResultType ProgramCompiler::apply(Stage &stage)
+{
+       T visitor;
+       visitor.apply(stage);
+       return visitor.get_result();
+}
+
+template<typename T, typename A>
+typename T::ResultType ProgramCompiler::apply(Stage &stage, const A &arg)
+{
+       T visitor(arg);
+       visitor.apply(stage);
+       return visitor.get_result();
+}
+
+
+ProgramCompiler::Visitor::Visitor():
+       stage(0)
+{ }
+
+void ProgramCompiler::Visitor::apply(Stage &s)
+{
+       SetForScope<Stage *> set(stage, &s);
+       stage->content.visit(*this);
+}
+
+
+ProgramCompiler::BlockModifier::BlockModifier():
+       remove_node(false)
+{ }
+
+void ProgramCompiler::BlockModifier::flatten_block(Block &block)
+{
+       insert_nodes.insert(insert_nodes.end(), block.body.begin(), block.body.end());
+       remove_node = true;
+}
+
+void ProgramCompiler::BlockModifier::apply_and_increment(Block &block, NodeList<Statement>::iterator &i)
+{
+       block.body.insert(i, insert_nodes.begin(), insert_nodes.end());
+       insert_nodes.clear();
+
+       if(remove_node)
+               block.body.erase(i++);
+       else
+               ++i;
+       remove_node = false;
+}
+
+void ProgramCompiler::BlockModifier::visit(Block &block)
+{
+       for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); )
+       {
+               (*i)->visit(*this);
+               apply_and_increment(block, i);
+       }
+}
+
+
+ProgramCompiler::Formatter::Formatter():
+       source_index(0),
+       source_line(1),
+       indent(0),
+       parameter_list(false)
+{ }
+
+void ProgramCompiler::Formatter::apply(ProgramSyntax::Stage &s)
+{
+       GLApi api = get_gl_api();
+       const Version &ver = s.required_version;
+
+       if(ver)
+       {
+               append(format("#version %d%02d", ver.major, ver.minor));
+               if(api==OPENGL_ES2 && ver>=Version(3, 0))
+                       append(" es");
+               formatted += '\n';
+       }
+
+       for(vector<const Extension *>::const_iterator i=s.required_extensions.begin(); i!=s.required_extensions.end(); ++i)
+               append(format("#extension %s: require\n", (*i)->get_name()));
+       if(!s.required_extensions.empty())
+               formatted += '\n';
+
+       Visitor::apply(s);
+}
+
+void ProgramCompiler::Formatter::append(const string &text)
+{
+       formatted += text;
+       for(string::const_iterator i=text.begin(); i!=text.end(); ++i)
+               if(*i=='\n')
+                       ++source_line;
+}
+
+void ProgramCompiler::Formatter::append(char c)
+{
+       formatted += c;
+       if(c=='\n')
+               ++source_line;
+}
+
+void ProgramCompiler::Formatter::set_source(unsigned index, unsigned line)
+{
+       if(index!=source_index || (index && line!=source_line))
+       {
+               if(index==source_index && line==source_line+1)
+                       formatted += '\n';
+               else
+               {
+                       unsigned l = line;
+                       if(stage->required_version<Version(3, 30))
+                               --l;
+                       formatted += format("#line %d %d\n", l, index);
+               }
+       }
+       source_index = index;
+       source_line = line;
+}
+
+void ProgramCompiler::Formatter::visit(Literal &literal)
+{
+       append(literal.token);
+}
+
+void ProgramCompiler::Formatter::visit(ParenthesizedExpression &parexpr)
+{
+       append('(');
+       parexpr.expression->visit(*this);
+       append(')');
+}
+
+void ProgramCompiler::Formatter::visit(VariableReference &var)
+{
+       append(var.name);
+}
+
+void ProgramCompiler::Formatter::visit(MemberAccess &memacc)
+{
+       memacc.left->visit(*this);
+       append(format(".%s", memacc.member));
+}
+
+void ProgramCompiler::Formatter::visit(UnaryExpression &unary)
+{
+       if(unary.prefix)
+               append(unary.oper);
+       unary.expression->visit(*this);
+       if(!unary.prefix)
+               append(unary.oper);
+}
+
+void ProgramCompiler::Formatter::visit(BinaryExpression &binary)
+{
+       binary.left->visit(*this);
+       append(binary.oper);
+       binary.right->visit(*this);
+       append(binary.after);
+}
+
+void ProgramCompiler::Formatter::visit(Assignment &assign)
+{
+       assign.left->visit(*this);
+       append(format(" %s ", assign.oper));
+       assign.right->visit(*this);
+}
+
+void ProgramCompiler::Formatter::visit(FunctionCall &call)
+{
+       append(format("%s(", call.name));
+       for(NodeArray<Expression>::iterator i=call.arguments.begin(); i!=call.arguments.end(); ++i)
+       {
+               if(i!=call.arguments.begin())
+                       append(", ");
+               (*i)->visit(*this);
+       }
+       append(')');
+}
+
+void ProgramCompiler::Formatter::visit(ExpressionStatement &expr)
+{
+       expr.expression->visit(*this);
+       append(';');
+}
+
+void ProgramCompiler::Formatter::visit(Block &block)
+{
+       unsigned brace_indent = indent;
+       bool use_braces = (block.use_braces || (indent && block.body.size()!=1));
+       if(use_braces)
+               append(format("%s{\n", string(brace_indent*2, ' ')));
+
+       SetForScope<unsigned> set(indent, indent+(indent>0 || use_braces));
+       string spaces(indent*2, ' ');
+       for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
+       {
+               if(i!=block.body.begin())
+                       append('\n');
+               set_source((*i)->source, (*i)->line);
+               append(spaces);
+               (*i)->visit(*this);
+       }
+
+       if(use_braces)
+               append(format("\n%s}", string(brace_indent*2, ' ')));
+}
+
+void ProgramCompiler::Formatter::visit(Import &import)
+{
+       append(format("import %s;", import.module));
+}
+
+void ProgramCompiler::Formatter::visit(Precision &prec)
+{
+       append(format("precision %s %s;", prec.precision, prec.type));
+}
+
+void ProgramCompiler::Formatter::visit(Layout &layout)
+{
+       append("layout(");
+       for(vector<Layout::Qualifier>::const_iterator i=layout.qualifiers.begin(); i!=layout.qualifiers.end(); ++i)
+       {
+               if(i!=layout.qualifiers.begin())
+                       append(", ");
+               append(i->identifier);
+               if(!i->value.empty())
+                       append(format("=%s", i->value));
+       }
+       append(')');
+}
+
+void ProgramCompiler::Formatter::visit(InterfaceLayout &layout)
+{
+       layout.layout.visit(*this);
+       append(format(" %s;", layout.interface));
+}
+
+void ProgramCompiler::Formatter::visit(StructDeclaration &strct)
+{
+       append(format("struct %s\n", strct.name));
+       strct.members.visit(*this);
+       append(';');
+}
+
+void ProgramCompiler::Formatter::visit(VariableDeclaration &var)
+{
+       if(var.layout)
+       {
+               var.layout->visit(*this);
+               append(' ');
+       }
+       if(var.constant)
+               append("const ");
+       if(!var.interpolation.empty())
+               append(format("%s ", var.interpolation));
+       if(!var.sampling.empty())
+               append(format("%s ", var.sampling));
+       if(!var.interface.empty() && var.interface!=block_interface)
+       {
+               string interface = var.interface;
+               if(stage->required_version<Version(1, 30))
+               {
+                       if(stage->type==VERTEX && var.interface=="in")
+                               interface = "attribute";
+                       else if((stage->type==VERTEX && var.interface=="out") || (stage->type==FRAGMENT && var.interface=="in"))
+                               interface = "varying";
+               }
+               append(format("%s ", interface));
+       }
+       if(!var.precision.empty())
+               append(format("%s ", var.precision));
+       append(format("%s %s", var.type, var.name));
+       if(var.array)
+       {
+               append('[');
+               if(var.array_size)
+                       var.array_size->visit(*this);
+               append(']');
+       }
+       if(var.init_expression)
+       {
+               append(" = ");
+               var.init_expression->visit(*this);
+       }
+       if(!parameter_list)
+               append(';');
+}
+
+void ProgramCompiler::Formatter::visit(InterfaceBlock &iface)
+{
+       SetForScope<string> set(block_interface, iface.interface);
+       append(format("%s %s\n", iface.interface, iface.name));
+       iface.members.visit(*this);
+       append(';');
+}
+
+void ProgramCompiler::Formatter::visit(FunctionDeclaration &func)
+{
+       append(format("%s %s(", func.return_type, func.name));
+       for(NodeArray<VariableDeclaration>::iterator i=func.parameters.begin(); i!=func.parameters.end(); ++i)
+       {
+               if(i!=func.parameters.begin())
+                       append(", ");
+               SetFlag set(parameter_list);
+               (*i)->visit(*this);
+       }
+       append(')');
+       if(func.definition==&func)
+       {
+               append('\n');
+               func.body.visit(*this);
+       }
+       else
+               append(';');
+}
+
+void ProgramCompiler::Formatter::visit(Conditional &cond)
+{
+       append("if(");
+       cond.condition->visit(*this);
+       append(")\n");
+
+       cond.body.visit(*this);
+       if(!cond.else_body.body.empty())
+       {
+               Conditional *else_cond = dynamic_cast<Conditional *>(cond.else_body.body.front().get());
+               if(cond.else_body.body.size()==1 && else_cond)
+               {
+                       append('\n');
+                       set_source(else_cond->source, else_cond->line);
+                       append(format("%selse ", string(indent*2, ' ')));
+                       else_cond->visit(*this);
+               }
+               else
+               {
+                       append(format("\n%selse\n", string(indent*2, ' ')));
+                       cond.else_body.visit(*this);
+               }
+       }
+}
+
+void ProgramCompiler::Formatter::visit(Iteration &iter)
+{
+       if(!iter.init_statement && iter.condition && !iter.loop_expression)
+       {
+               append("while(");
+               iter.condition->visit(*this);
+               append(')');
+       }
+       else
+       {
+               append("for(");
+               if(iter.init_statement)
+                       iter.init_statement->visit(*this);
+               else
+                       append(';');
+               if(iter.condition)
+               {
+                       append(' ');
+                       iter.condition->visit(*this);
+               }
+               append(';');
+               if(iter.loop_expression)
+               {
+                       append(' ');
+                       iter.loop_expression->visit(*this);
+               }
+               append(')');
+       }
+
+       if(iter.body.body.empty())
+               append(" { }");
+       else
+       {
+               append('\n');
+               iter.body.visit(*this);
+       }
+}
+
+void ProgramCompiler::Formatter::visit(Return &ret)
+{
+       append("return");
+       if(ret.expression)
+       {
+               append(' ');
+               ret.expression->visit(*this);
+       }
+       append(';');
+}
+
+void ProgramCompiler::Formatter::visit(Jump &jump)
+{
+       append(jump.keyword);
+       append(';');
+}
+
+
+ProgramCompiler::DeclarationCombiner::DeclarationCombiner():
+       toplevel(true)
+{ }
+
+void ProgramCompiler::DeclarationCombiner::visit(Block &block)
+{
+       if(!toplevel)
+               return;
+
+       SetForScope<bool> set(toplevel, false);
+       BlockModifier::visit(block);
+}
+
+void ProgramCompiler::DeclarationCombiner::visit(FunctionDeclaration &func)
+{
+       vector<FunctionDeclaration *> &decls = functions[func.name];
+       if(func.definition)
+       {
+               for(vector<FunctionDeclaration *>::iterator i=decls.begin(); i!=decls.end(); ++i)
+               {
+                       (*i)->definition = func.definition;
+                       (*i)->body.body.clear();
+               }
+       }
+       decls.push_back(&func);
+}
+
+void ProgramCompiler::DeclarationCombiner::visit(VariableDeclaration &var)
+{
+       VariableDeclaration *&ptr = variables[var.name];
+       if(ptr)
+       {
+               ptr->type = var.type;
+               if(var.init_expression)
+                       ptr->init_expression = var.init_expression;
+               if(var.layout)
+               {
+                       if(ptr->layout)
+                       {
+                               for(vector<Layout::Qualifier>::iterator i=var.layout->qualifiers.begin(); i!=var.layout->qualifiers.end(); ++i)
+                               {
+                                       bool found = false;
+                                       for(vector<Layout::Qualifier>::iterator j=ptr->layout->qualifiers.begin(); (!found && j!=ptr->layout->qualifiers.end()); ++j)
+                                               if(j->identifier==i->identifier)
+                                               {
+                                                       j->value = i->value;
+                                                       found = true;
+                                               }
+
+                                       if(!found)
+                                               ptr->layout->qualifiers.push_back(*i);
+                               }
+                       }
+                       else
+                               ptr->layout = var.layout;
+               }
+               remove_node = true;
+       }
+       else
+               ptr = &var;
+}
+
+
+ProgramCompiler::VariableResolver::VariableResolver():
+       anonymous(false),
+       record_target(false),
+       assignment_target(0),
+       self_referencing(false)
+{ }
+
+void ProgramCompiler::VariableResolver::apply(Stage &s)
+{
+       SetForScope<Stage *> set(stage, &s);
+       Stage *builtins = get_builtins(stage->type);
+       if(builtins)
+               blocks.push_back(&builtins->content);
+       stage->content.visit(*this);
+       if(builtins)
+               blocks.pop_back();
+}
+
+void ProgramCompiler::VariableResolver::visit(Block &block)
+{
+       blocks.push_back(&block);
+       block.variables.clear();
+       TraversingVisitor::visit(block);
+       blocks.pop_back();
+}
+
+void ProgramCompiler::VariableResolver::visit(VariableReference &var)
+{
+       var.declaration = 0;
+       type = 0;
+       for(vector<Block *>::iterator i=blocks.end(); i!=blocks.begin(); )
+       {
+               --i;
+               map<string, VariableDeclaration *>::iterator j = (*i)->variables.find(var.name);
+               if(j!=(*i)->variables.end())
+               {
+                       var.declaration = j->second;
+                       type = j->second->type_declaration;
+                       break;
+               }
+       }
+
+       if(record_target)
+       {
+               if(assignment_target)
+               {
+                       record_target = false;
+                       assignment_target = 0;
+               }
+               else
+                       assignment_target = var.declaration;
+       }
+       else if(var.declaration && var.declaration==assignment_target)
+               self_referencing = true;
+}
+
+void ProgramCompiler::VariableResolver::visit(MemberAccess &memacc)
+{
+       type = 0;
+       TraversingVisitor::visit(memacc);
+       memacc.declaration = 0;
+       if(type)
+       {
+               map<string, VariableDeclaration *>::iterator i = type->members.variables.find(memacc.member);
+               if(i!=type->members.variables.end())
+               {
+                       memacc.declaration = i->second;
+                       type = i->second->type_declaration;
+               }
+               else
+                       type = 0;
+       }
+}
+
+void ProgramCompiler::VariableResolver::visit(BinaryExpression &binary)
+{
+       if(binary.oper=="[")
+       {
+               {
+                       SetForScope<bool> set(record_target, false);
+                       binary.right->visit(*this);
+               }
+               type = 0;
+               binary.left->visit(*this);
+       }
+       else
+       {
+               TraversingVisitor::visit(binary);
+               type = 0;
+       }
+}
+
+void ProgramCompiler::VariableResolver::visit(Assignment &assign)
+{
+       {
+               SetFlag set(record_target);
+               assignment_target = 0;
+               assign.left->visit(*this);
+       }
+
+       self_referencing = false;
+       assign.right->visit(*this);
+
+       assign.self_referencing = (self_referencing || assign.oper!="=");
+       assign.target_declaration = assignment_target;
+}
+
+void ProgramCompiler::VariableResolver::visit(StructDeclaration &strct)
+{
+       TraversingVisitor::visit(strct);
+       blocks.back()->types[strct.name] = &strct;
+}
+
+void ProgramCompiler::VariableResolver::visit(VariableDeclaration &var)
+{
+       for(vector<Block *>::iterator i=blocks.end(); i!=blocks.begin(); )
+       {
+               --i;
+               map<string, StructDeclaration *>::iterator j = (*i)->types.find(var.type);
+               if(j!=(*i)->types.end())
+                       var.type_declaration = j->second;
+       }
+
+       if(!block_interface.empty() && var.interface.empty())
+               var.interface = block_interface;
+
+       TraversingVisitor::visit(var);
+       blocks.back()->variables[var.name] = &var;
+       if(anonymous && blocks.size()>1)
+               blocks[blocks.size()-2]->variables[var.name] = &var;
+}
+
+void ProgramCompiler::VariableResolver::visit(InterfaceBlock &iface)
+{
+       SetFlag set(anonymous);
+       SetForScope<string> set2(block_interface, iface.interface);
+       TraversingVisitor::visit(iface);
+}
+
+
+void ProgramCompiler::FunctionResolver::visit(FunctionCall &call)
+{
+       map<string, vector<FunctionDeclaration *> >::iterator i = functions.find(call.name);
+       if(i!=functions.end())
+               call.declaration = i->second.back();
+
+       TraversingVisitor::visit(call);
+}
+
+void ProgramCompiler::FunctionResolver::visit(FunctionDeclaration &func)
+{
+       vector<FunctionDeclaration *> &decls = functions[func.name];
+       if(func.definition)
+       {
+               for(vector<FunctionDeclaration *>::iterator i=decls.begin(); i!=decls.end(); ++i)
+                       (*i)->definition = func.definition;
+               decls.clear();
+               decls.push_back(&func);
+       }
+       else if(!decls.empty() && decls.back()->definition)
+               func.definition = decls.back()->definition;
+       else
+               decls.push_back(&func);
+
+       TraversingVisitor::visit(func);
+}
+
+
+ProgramCompiler::InterfaceGenerator::InterfaceGenerator():
+       scope_level(0)
+{ }
+
+string ProgramCompiler::InterfaceGenerator::get_out_prefix(StageType type)
+{
+       if(type==VERTEX)
+               return "_vs_out_";
+       else if(type==GEOMETRY)
+               return "_gs_out_";
+       else
+               return string();
+}
+
+void ProgramCompiler::InterfaceGenerator::apply(Stage &s)
+{
+       SetForScope<Stage *> set(stage, &s);
+       if(stage->previous)
+               in_prefix = get_out_prefix(stage->previous->type);
+       out_prefix = get_out_prefix(stage->type);
+       stage->content.visit(*this);
+}
+
+void ProgramCompiler::InterfaceGenerator::visit(Block &block)
+{
+       SetForScope<unsigned> set(scope_level, scope_level+1);
+       for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); )
+       {
+               (*i)->visit(*this);
+
+               if(scope_level==1)
+               {
+                       for(map<string, RefPtr<VariableDeclaration> >::iterator j=iface_declarations.begin(); j!=iface_declarations.end(); ++j)
+                       {
+                               NodeList<Statement>::iterator k = block.body.insert(i, j->second);
+                               (*k)->visit(*this);
+                       }
+                       iface_declarations.clear();
+               }
+
+               apply_and_increment(block, i);
+       }
+}
+
+string ProgramCompiler::InterfaceGenerator::change_prefix(const string &name, const string &prefix) const
+{
+       unsigned offset = (name.compare(0, in_prefix.size(), in_prefix) ? 0 : in_prefix.size());
+       return prefix+name.substr(offset);
+}
+
+bool ProgramCompiler::InterfaceGenerator::generate_interface(VariableDeclaration &var, const string &iface, const string &name)
+{
+       const map<string, VariableDeclaration *> &stage_vars = (iface=="in" ? stage->in_variables : stage->out_variables);
+       if(stage_vars.count(name) || iface_declarations.count(name))
+               return false;
+
+       VariableDeclaration* iface_var = new VariableDeclaration;
+       iface_var->sampling = var.sampling;
+       iface_var->interface = iface;
+       iface_var->type = var.type;
+       iface_var->type_declaration = var.type_declaration;
+       iface_var->name = name;
+       if(stage->type==GEOMETRY)
+               iface_var->array = ((var.array && var.interface!="in") || iface=="in");
+       else
+               iface_var->array = var.array;
+       if(iface_var->array)
+               iface_var->array_size = var.array_size;
+       if(iface=="in")
+               iface_var->linked_declaration = &var;
+       iface_declarations[name] = iface_var;
+
+       return true;
+}
+
+ExpressionStatement &ProgramCompiler::InterfaceGenerator::insert_assignment(const string &left, ProgramSyntax::Expression *right)
+{
+       Assignment *assign = new Assignment;
+       VariableReference *ref = new VariableReference;
+       ref->name = left;
+       assign->left = ref;
+       assign->oper = "=";
+       assign->right = right;
+
+       ExpressionStatement *stmt = new ExpressionStatement;
+       stmt->expression = assign;
+       stmt->visit(*this);
+       insert_nodes.push_back(stmt);
+
+       return *stmt;
+}
+
+void ProgramCompiler::InterfaceGenerator::visit(VariableReference &var)
+{
+       if(var.declaration || !stage->previous)
+               return;
+       if(iface_declarations.count(var.name))
+               return;
+
+       const map<string, VariableDeclaration *> &prev_out = stage->previous->out_variables;
+       map<string, VariableDeclaration *>::const_iterator i = prev_out.find(var.name);
+       if(i==prev_out.end())
+               i = prev_out.find(in_prefix+var.name);
+       if(i!=prev_out.end())
+       {
+               generate_interface(*i->second, "in", i->second->name);
+               var.name = i->second->name;
+       }
+}
+
+void ProgramCompiler::InterfaceGenerator::visit(VariableDeclaration &var)
+{
+       if(var.interface=="out")
+       {
+               if(scope_level==1)
+                       stage->out_variables[var.name] = &var;
+               else if(generate_interface(var, "out", change_prefix(var.name, string())))
+               {
+                       remove_node = true;
+                       if(var.init_expression)
+                       {
+                               ExpressionStatement &stmt = insert_assignment(var.name, var.init_expression->clone());
+                               stmt.source = var.source;
+                               stmt.line = var.line;
+                               return;
+                       }
+               }
+       }
+       else if(var.interface=="in")
+       {
+               stage->in_variables[var.name] = &var;
+               if(var.linked_declaration)
+                       var.linked_declaration->linked_declaration = &var;
+               else if(stage->previous)
+               {
+                       const map<string, VariableDeclaration *> &prev_out = stage->previous->out_variables;
+                       map<string, VariableDeclaration *>::const_iterator i = prev_out.find(var.name);
+                       if(i!=prev_out.end())
+                       {
+                               var.linked_declaration = i->second;
+                               i->second->linked_declaration = &var;
+                       }
+               }
+       }
+
+       TraversingVisitor::visit(var);
+}
+
+void ProgramCompiler::InterfaceGenerator::visit(Passthrough &pass)
+{
+       vector<VariableDeclaration *> pass_vars;
+
+       for(map<string, VariableDeclaration *>::const_iterator i=stage->in_variables.begin(); i!=stage->in_variables.end(); ++i)
+               pass_vars.push_back(i->second);
+       for(map<string, RefPtr<VariableDeclaration> >::const_iterator i=iface_declarations.begin(); i!=iface_declarations.end(); ++i)
+               if(i->second->interface=="in")
+                       pass_vars.push_back(i->second.get());
+
+       if(stage->previous)
+       {
+               const map<string, VariableDeclaration *> &prev_out = stage->previous->out_variables;
+               for(map<string, VariableDeclaration *>::const_iterator i=prev_out.begin(); i!=prev_out.end(); ++i)
+               {
+                       bool linked = false;
+                       for(vector<VariableDeclaration *>::const_iterator j=pass_vars.begin(); (!linked && j!=pass_vars.end()); ++j)
+                               linked = ((*j)->linked_declaration==i->second);
+
+                       if(!linked && generate_interface(*i->second, "in", i->second->name))
+                               pass_vars.push_back(i->second);
+               }
+       }
+
+       if(stage->type==GEOMETRY)
+       {
+               VariableReference *ref = new VariableReference;
+               ref->name = "gl_in";
+
+               BinaryExpression *subscript = new BinaryExpression;
+               subscript->left = ref;
+               subscript->oper = "[";
+               subscript->right = pass.subscript;
+               subscript->after = "]";
+
+               MemberAccess *memacc = new MemberAccess;
+               memacc->left = subscript;
+               memacc->member = "gl_Position";
+
+               insert_assignment("gl_Position", memacc);
+       }
+
+       for(vector<VariableDeclaration *>::const_iterator i=pass_vars.begin(); i!=pass_vars.end(); ++i)
+       {
+               string out_name = change_prefix((*i)->name, out_prefix);
+               generate_interface(**i, "out", out_name);
+
+               VariableReference *ref = new VariableReference;
+               ref->name = (*i)->name;
+               if(pass.subscript)
+               {
+                       BinaryExpression *subscript = new BinaryExpression;
+                       subscript->left = ref;
+                       subscript->oper = "[";
+                       subscript->right = pass.subscript;
+                       subscript->after = "]";
+                       insert_assignment(out_name, subscript);
+               }
+               else
+                       insert_assignment(out_name, ref);
+       }
+
+       remove_node = true;
+}
+
+
+ProgramCompiler::DeclarationReorderer::DeclarationReorderer():
+       scope_level(0),
+       kind(NO_DECLARATION)
+{ }
+
+void ProgramCompiler::DeclarationReorderer::visit(FunctionCall &call)
+{
+       FunctionDeclaration *def = call.declaration;
+       if(def)
+               def = def->definition;
+       if(def && !ordered_funcs.count(def))
+               needed_funcs.insert(def);
+}
+
+void ProgramCompiler::DeclarationReorderer::visit(Block &block)
+{
+       SetForScope<unsigned> set(scope_level, scope_level+1);
+       if(scope_level>1)
+               return Visitor::visit(block);
+
+       NodeList<Statement>::iterator struct_insert_point = block.body.end();
+       NodeList<Statement>::iterator variable_insert_point = block.body.end();
+       NodeList<Statement>::iterator function_insert_point = block.body.end();
+       unsigned unordered_func_count = 0;
+       bool ordered_any_funcs = false;
+
+       for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); )
+       {
+               kind = NO_DECLARATION;
+               (*i)->visit(*this);
+
+               bool moved = false;
+               if(kind==STRUCT && struct_insert_point!=block.body.end())
+               {
+                       block.body.insert(struct_insert_point, *i);
+                       moved = true;
+               }
+               else if(kind>STRUCT && struct_insert_point==block.body.end())
+                       struct_insert_point = i;
+
+               if(kind==VARIABLE && variable_insert_point!=block.body.end())
+               {
+                       block.body.insert(variable_insert_point, *i);
+                       moved = true;
+               }
+               else if(kind>VARIABLE && variable_insert_point==block.body.end())
+                       variable_insert_point = i;
+
+               if(kind==FUNCTION)
+               {
+                       if(function_insert_point==block.body.end())
+                               function_insert_point = i;
+
+                       if(needed_funcs.empty())
+                       {
+                               ordered_funcs.insert(i->get());
+                               if(i!=function_insert_point)
+                               {
+                                       block.body.insert(function_insert_point, *i);
+                                       moved = true;
+                               }
+                               else
+                                       ++function_insert_point;
+                               ordered_any_funcs = true;
+                       }
+                       else
+                               ++unordered_func_count;
+               }
+
+               if(moved)
+               {
+                       if(function_insert_point==i)
+                               ++function_insert_point;
+                       block.body.erase(i++);
+               }
+               else
+                       ++i;
+
+               if(i==block.body.end() && unordered_func_count)
+               {
+                       if(!ordered_any_funcs)
+                               // A subset of the remaining functions forms a recursive loop
+                               /* TODO pick a function and move it up, adding any necessary
+                               declarations */
+                               break;
+
+                       i = function_insert_point;
+                       unordered_func_count = 0;
+               }
+       }
+}
+
+void ProgramCompiler::DeclarationReorderer::visit(ProgramSyntax::VariableDeclaration &var)
+{
+       Visitor::visit(var);
+       kind = VARIABLE;
+}
+
+void ProgramCompiler::DeclarationReorderer::visit(FunctionDeclaration &func)
+{
+       needed_funcs.clear();
+       func.body.visit(*this);
+       needed_funcs.erase(&func);
+       kind = FUNCTION;
+}
+
+
+ProgramCompiler::InlineableFunctionLocator::InlineableFunctionLocator():
+       in_function(0)
+{ }
+
+void ProgramCompiler::InlineableFunctionLocator::visit(FunctionCall &call)
+{
+       FunctionDeclaration *def = call.declaration;
+       if(def && def->definition!=def)
+               def = def->definition;
+
+       if(def)
+       {
+               unsigned &count = refcounts[def];
+               ++count;
+               if(count>1 || def==in_function)
+                       inlineable.erase(def);
+       }
+
+       TraversingVisitor::visit(call);
+}
+
+void ProgramCompiler::InlineableFunctionLocator::visit(FunctionDeclaration &func)
+{
+       unsigned &count = refcounts[func.definition];
+       if(!count && func.parameters.empty())
+               inlineable.insert(func.definition);
+
+       SetForScope<FunctionDeclaration *> set(in_function, &func);
+       TraversingVisitor::visit(func);
+}
+
+
+ProgramCompiler::FunctionInliner::FunctionInliner():
+       extract_result(0)
+{ }
+
+ProgramCompiler::FunctionInliner::FunctionInliner(const set<FunctionDeclaration *> &in):
+       inlineable(in),
+       extract_result(0)
+{ }
+
+void ProgramCompiler::FunctionInliner::visit_and_inline(RefPtr<Expression> &ptr)
+{
+       inline_result = 0;
+       ptr->visit(*this);
+       if(inline_result)
+               ptr = inline_result;
+}
+
+void ProgramCompiler::FunctionInliner::visit(Block &block)
+{
+       if(extract_result)
+               --extract_result;
+
+       for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
+       {
+               (*i)->visit(*this);
+               if(extract_result)
+                       --extract_result;
+       }
+}
+
+void ProgramCompiler::FunctionInliner::visit(UnaryExpression &unary)
+{
+       visit_and_inline(unary.expression);
+       inline_result = 0;
+}
+
+void ProgramCompiler::FunctionInliner::visit(BinaryExpression &binary)
+{
+       visit_and_inline(binary.left);
+       visit_and_inline(binary.right);
+       inline_result = 0;
+}
+
+void ProgramCompiler::FunctionInliner::visit(MemberAccess &memacc)
+{
+       visit_and_inline(memacc.left);
+       inline_result = 0;
+}
+
+void ProgramCompiler::FunctionInliner::visit(FunctionCall &call)
+{
+       for(NodeArray<Expression>::iterator i=call.arguments.begin(); i!=call.arguments.end(); ++i)
+               visit_and_inline(*i);
+
+       FunctionDeclaration *def = call.declaration;
+       if(def && def->definition!=def)
+               def = def->definition;
+
+       if(def && inlineable.count(def))
+       {
+               extract_result = 2;
+               def->visit(*this);
+       }
+       else
+               inline_result = 0;
+}
+
+void ProgramCompiler::FunctionInliner::visit(VariableDeclaration &var)
+{
+       if(var.init_expression)
+               visit_and_inline(var.init_expression);
+       inline_result = 0;
+}
+
+void ProgramCompiler::FunctionInliner::visit(Return &ret)
+{
+       TraversingVisitor::visit(ret);
+
+       if(extract_result)
+               inline_result = ret.expression->clone();
+}
+
+
+ProgramCompiler::ExpressionEvaluator::ExpressionEvaluator():
+       variable_values(0),
+       result(0.0f),
+       result_valid(false)
+{ }
+
+ProgramCompiler::ExpressionEvaluator::ExpressionEvaluator(const ValueMap &v):
+       variable_values(&v),
+       result(0.0f),
+       result_valid(false)
+{ }
+
+void ProgramCompiler::ExpressionEvaluator::visit(Literal &literal)
+{
+       if(literal.token=="true")
+               result = 1.0f;
+       else if(literal.token=="false")
+               result = 0.0f;
+       else
+               result = lexical_cast<float>(literal.token);
+       result_valid = true;
+}
+
+void ProgramCompiler::ExpressionEvaluator::visit(ParenthesizedExpression &parexp)
+{
+       parexp.expression->visit(*this);
+}
+
+void ProgramCompiler::ExpressionEvaluator::visit(VariableReference &var)
+{
+       if(!var.declaration)
+               return;
+
+       if(variable_values)
+       {
+               ValueMap::const_iterator i = variable_values->find(var.declaration);
+               if(i!=variable_values->end())
+                       i->second->visit(*this);
+       }
+       else if(var.declaration->init_expression)
+               var.declaration->init_expression->visit(*this);
+}
+
+void ProgramCompiler::ExpressionEvaluator::visit(UnaryExpression &unary)
+{
+       result_valid = false;
+       unary.expression->visit(*this);
+       if(!result_valid)
+               return;
+
+       if(unary.oper=="!")
+               result = !result;
+       else
+               result_valid = false;
+}
+
+void ProgramCompiler::ExpressionEvaluator::visit(BinaryExpression &binary)
+{
+       result_valid = false;
+       binary.left->visit(*this);
+       if(!result_valid)
+               return;
+
+       float left_result = result;
+       result_valid = false;
+       binary.right->visit(*this);
+       if(!result_valid)
+               return;
+
+       if(binary.oper=="<")
+               result = (left_result<result);
+       else if(binary.oper=="<=")
+               result = (left_result<=result);
+       else if(binary.oper==">")
+               result = (left_result>result);
+       else if(binary.oper==">=")
+               result = (left_result>=result);
+       else if(binary.oper=="==")
+               result = (left_result==result);
+       else if(binary.oper=="!=")
+               result = (left_result!=result);
+       else if(binary.oper=="&&")
+               result = (left_result && result);
+       else if(binary.oper=="||")
+               result = (left_result || result);
+       else
+               result_valid = false;
+}
+
+
+ProgramCompiler::ConstantConditionEliminator::ConstantConditionEliminator():
+       scope_level(0),
+       record_only(false)
+{ }
+
+void ProgramCompiler::ConstantConditionEliminator::visit(Block &block)
+{
+       SetForScope<unsigned> set(scope_level, scope_level+1);
+       BlockModifier::visit(block);
+
+       for(map<string, VariableDeclaration *>::const_iterator i=block.variables.begin(); i!=block.variables.end(); ++i)
+               variable_values.erase(i->second);
+}
+
+void ProgramCompiler::ConstantConditionEliminator::visit(UnaryExpression &unary)
+{
+       if(VariableReference *var = dynamic_cast<VariableReference *>(unary.expression.get()))
+               if(unary.oper=="++" || unary.oper=="--")
+                       variable_values.erase(var->declaration);
+}
+
+void ProgramCompiler::ConstantConditionEliminator::visit(Assignment &assign)
+{
+       variable_values.erase(assign.target_declaration);
+}
+
+void ProgramCompiler::ConstantConditionEliminator::visit(VariableDeclaration &var)
+{
+       if(var.constant || scope_level>1)
+               variable_values[&var] = var.init_expression.get();
+}
+
+void ProgramCompiler::ConstantConditionEliminator::visit(Conditional &cond)
+{
+       if(!record_only)
+       {
+               ExpressionEvaluator eval(variable_values);
+               cond.condition->visit(eval);
+               if(eval.result_valid)
+               {
+                       flatten_block(eval.result ? cond.body : cond.else_body);
+                       return;
+               }
+       }
+
+       TraversingVisitor::visit(cond);
+}
+
+void ProgramCompiler::ConstantConditionEliminator::visit(Iteration &iter)
+{
+       if(!record_only)
+       {
+               if(iter.condition)
+               {
+                       /* If the loop condition is always false on the first iteration, the
+                       entire loop can be removed */
+                       if(iter.init_statement)
+                               iter.init_statement->visit(*this);
+                       ExpressionEvaluator eval(variable_values);
+                       iter.condition->visit(eval);
+                       if(eval.result_valid && !eval.result)
+                       {
+                               remove_node = true;
+                               return;
+                       }
+               }
+
+               /* Record all assignments that occur inside the loop body so those
+               variables won't be considered as constant */
+               SetFlag set_record(record_only);
+               TraversingVisitor::visit(iter);
+       }
+
+       TraversingVisitor::visit(iter);
+
+       if(VariableDeclaration *init_decl = dynamic_cast<VariableDeclaration *>(iter.init_statement.get()))
+               variable_values.erase(init_decl);
+}
+
+
+ProgramCompiler::UnusedVariableLocator::UnusedVariableLocator():
+       aggregate(0),
+       assignment(0),
+       assignment_target(false),
+       assign_to_subscript(false),
+       global_scope(true)
+{ }
+
+void ProgramCompiler::UnusedVariableLocator::apply(Stage &s)
+{
+       variables.push_back(BlockVariableMap());
+       Visitor::apply(s);
+       BlockVariableMap &global_variables = variables.back();
+       for(BlockVariableMap::iterator i=global_variables.begin(); i!=global_variables.end(); ++i)
+       {
+               if(i->first->interface=="out" && (s.type==FRAGMENT || i->first->linked_declaration || !i->first->name.compare(0, 3, "gl_")))
+                       continue;
+               if(!i->second.referenced)
+               {
+                       unused_nodes.insert(i->first);
+                       clear_assignments(i->second, true);
+               }
+       }
+       variables.pop_back();
+}
+
+void ProgramCompiler::UnusedVariableLocator::visit(VariableReference &var)
+{
+       map<VariableDeclaration *, Node *>::iterator i = aggregates.find(var.declaration);
+       if(i!=aggregates.end())
+               unused_nodes.erase(i->second);
+
+       if(var.declaration && !assignment_target)
+       {
+               VariableInfo &var_info = variables.back()[var.declaration];
+               var_info.assignments.clear();
+               var_info.referenced = true;
+       }
+}
+
+void ProgramCompiler::UnusedVariableLocator::visit(MemberAccess &memacc)
+{
+       TraversingVisitor::visit(memacc);
+       unused_nodes.erase(memacc.declaration);
+}
+
+void ProgramCompiler::UnusedVariableLocator::visit(BinaryExpression &binary)
+{
+       if(binary.oper=="[")
+       {
+               if(assignment_target)
+                       assign_to_subscript = true;
+               binary.left->visit(*this);
+               SetForScope<bool> set(assignment_target, false);
+               binary.right->visit(*this);
+       }
+       else
+               TraversingVisitor::visit(binary);
+}
+
+void ProgramCompiler::UnusedVariableLocator::visit(Assignment &assign)
+{
+       {
+               assign_to_subscript = false;
+               SetForScope<bool> set(assignment_target, !assign.self_referencing);
+               assign.left->visit(*this);
+       }
+       assign.right->visit(*this);
+       assignment = &assign;
+}
+
+void ProgramCompiler::UnusedVariableLocator::record_assignment(VariableDeclaration &var, Node &node, bool chained)
+{
+       VariableInfo &var_info = variables.back()[&var];
+       if(!chained)
+               clear_assignments(var_info, true);
+       var_info.assignments.push_back(&node);
+       var_info.conditionally_assigned = false;
+}
+
+void ProgramCompiler::UnusedVariableLocator::clear_assignments(VariableInfo &var_info, bool mark_unused)
+{
+       if(mark_unused)
+       {
+               for(vector<Node *>::iterator i=var_info.assignments.begin(); i!=var_info.assignments.end(); ++i)
+                       unused_nodes.insert(*i);
+       }
+       var_info.assignments.clear();
+}
+
+void ProgramCompiler::UnusedVariableLocator::visit(ExpressionStatement &expr)
+{
+       assignment = 0;
+       TraversingVisitor::visit(expr);
+       if(assignment && assignment->target_declaration)
+               record_assignment(*assignment->target_declaration, expr, (assignment->self_referencing || assign_to_subscript));
+}
+
+void ProgramCompiler::UnusedVariableLocator::visit(StructDeclaration &strct)
+{
+       SetForScope<Node *> set(aggregate, &strct);
+       unused_nodes.insert(&strct);
+       TraversingVisitor::visit(strct);
+}
+
+void ProgramCompiler::UnusedVariableLocator::visit(VariableDeclaration &var)
+{
+       if(aggregate)
+               aggregates[&var] = aggregate;
+       else
+       {
+               variables.back()[&var].local = true;
+               if(var.init_expression)
+                       record_assignment(var, *var.init_expression, false);
+       }
+       unused_nodes.erase(var.type_declaration);
+       TraversingVisitor::visit(var);
+}
+
+void ProgramCompiler::UnusedVariableLocator::visit(InterfaceBlock &iface)
+{
+       SetForScope<Node *> set(aggregate, &iface);
+       unused_nodes.insert(&iface);
+       TraversingVisitor::visit(iface);
+}
+
+void ProgramCompiler::UnusedVariableLocator::visit(FunctionDeclaration &func)
+{
+       variables.push_back(BlockVariableMap());
+
+       {
+               SetForScope<bool> set(global_scope, false);
+               for(NodeArray<VariableDeclaration>::iterator i=func.parameters.begin(); i!=func.parameters.end(); ++i)
+                       (*i)->visit(*this);
+               func.body.visit(*this);
+       }
+
+       BlockVariableMap &block_variables = variables.back();
+       for(BlockVariableMap::iterator i=block_variables.begin(); i!=block_variables.end(); ++i)
+               i->second.conditionally_assigned = true;
+       for(NodeArray<VariableDeclaration>::iterator i=func.parameters.begin(); i!=func.parameters.end(); ++i)
+               block_variables[i->get()].referenced = true;
+       merge_down_variables();
+}
+
+void ProgramCompiler::UnusedVariableLocator::merge_down_variables()
+{
+       BlockVariableMap &parent_variables = variables[variables.size()-2];
+       BlockVariableMap &block_variables = variables.back();
+       for(BlockVariableMap::iterator i=block_variables.begin(); i!=block_variables.end(); ++i)
+       {
+               if(i->second.local)
+               {
+                       if(!i->second.referenced)
+                               unused_nodes.insert(i->first);
+                       clear_assignments(i->second, i->first->interface!="out");
+                       continue;
+               }
+
+               BlockVariableMap::iterator j = parent_variables.find(i->first);
+               if(j==parent_variables.end())
+                       parent_variables.insert(*i);
+               else
+               {
+                       if(i->second.referenced || !i->second.conditionally_assigned)
+                               clear_assignments(j->second, !i->second.referenced);
+                       j->second.conditionally_assigned = i->second.conditionally_assigned;
+                       j->second.referenced |= i->second.referenced;
+                       j->second.assignments.insert(j->second.assignments.end(), i->second.assignments.begin(), i->second.assignments.end());
+               }
+       }
+       variables.pop_back();
+}
+
+void ProgramCompiler::UnusedVariableLocator::visit(Conditional &cond)
+{
+       cond.condition->visit(*this);
+       variables.push_back(BlockVariableMap());
+       cond.body.visit(*this);
+
+       BlockVariableMap if_variables;
+       swap(variables.back(), if_variables);
+       cond.else_body.visit(*this);
+
+       BlockVariableMap &else_variables = variables.back();
+       for(BlockVariableMap::iterator i=else_variables.begin(); i!=else_variables.end(); ++i)
+       {
+               BlockVariableMap::iterator j = if_variables.find(i->first);
+               if(j!=if_variables.end())
+               {
+                       i->second.assignments.insert(i->second.assignments.end(), j->second.assignments.begin(), j->second.assignments.end());
+                       i->second.conditionally_assigned |= j->second.conditionally_assigned;
+                       if_variables.erase(j);
+               }
+               else
+                       i->second.conditionally_assigned = true;
+       }
+
+       for(BlockVariableMap::iterator i=if_variables.begin(); i!=if_variables.end(); ++i)
+       {
+               i->second.conditionally_assigned = true;
+               else_variables.insert(*i);
+       }
+
+       merge_down_variables();
+}
+
+void ProgramCompiler::UnusedVariableLocator::visit(Iteration &iter)
+{
+       variables.push_back(BlockVariableMap());
+       TraversingVisitor::visit(iter);
+
+       BlockVariableMap &block_variables = variables.back();
+       for(BlockVariableMap::iterator i=block_variables.begin(); i!=block_variables.end(); ++i)
+               if(!i->second.local && i->second.referenced)
+                       i->second.assignments.clear();
+
+       merge_down_variables();
+}
+
+
+ProgramCompiler::UnusedVariableLocator::VariableInfo::VariableInfo():
+       local(false),
+       conditionally_assigned(false),
+       referenced(false)
+{ }
+
+
+void ProgramCompiler::UnusedFunctionLocator::visit(FunctionCall &call)
+{
+       TraversingVisitor::visit(call);
+
+       unused_nodes.erase(call.declaration);
+       if(call.declaration && call.declaration->definition!=call.declaration)
+               used_definitions.insert(call.declaration->definition);
+}
+
+void ProgramCompiler::UnusedFunctionLocator::visit(FunctionDeclaration &func)
+{
+       TraversingVisitor::visit(func);
+
+       if((func.name!="main" || func.body.body.empty()) && !used_definitions.count(&func))
+               unused_nodes.insert(&func);
+}
+
+
+ProgramCompiler::NodeRemover::NodeRemover(const set<Node *> &r):
+       to_remove(r)
+{ }
+
+void ProgramCompiler::NodeRemover::visit(Block &block)
+{
+       for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); )
+       {
+               (*i)->visit(*this);
+               if(to_remove.count(i->get()))
+                       block.body.erase(i++);
+               else
+                       ++i;
+       }
+}
+
+void ProgramCompiler::NodeRemover::visit(VariableDeclaration &var)
+{
+       if(to_remove.count(&var))
+       {
+               stage->in_variables.erase(var.name);
+               stage->out_variables.erase(var.name);
+               stage->locations.erase(var.name);
+               if(var.linked_declaration)
+                       var.linked_declaration->linked_declaration = 0;
+       }
+       else if(var.init_expression && to_remove.count(var.init_expression.get()))
+               var.init_expression = 0;
+}
+
+void ProgramCompiler::NodeRemover::visit(Iteration &iter)
+{
+       if(to_remove.count(iter.init_statement.get()))
+               iter.init_statement = 0;
+       TraversingVisitor::visit(iter);
+}
+
+
+void ProgramCompiler::PrecisionRemover::visit(Precision &)
+{
+       remove_node = true;
+}
+
+void ProgramCompiler::PrecisionRemover::visit(VariableDeclaration &var)
+{
+       var.precision.clear();
+}
+
+
+ProgramCompiler::DefaultPrecisionGenerator::DefaultPrecisionGenerator():
+       toplevel(true)
+{ }
+
+void ProgramCompiler::DefaultPrecisionGenerator::visit(Block &block)
+{
+       if(toplevel)
+       {
+               SetForScope<bool> set(toplevel, false);
+               BlockModifier::visit(block);
+       }
+       else
+               Visitor::visit(block);
+}
+
+void ProgramCompiler::DefaultPrecisionGenerator::visit(Precision &prec)
+{
+       have_default.insert(prec.type);
+}
+
+void ProgramCompiler::DefaultPrecisionGenerator::visit(VariableDeclaration &var)
+{
+       if(var.type_declaration)
+               return;
+
+       string type = var.type;
+       if(!type.compare(0, 3, "vec") || !type.compare(0, 3, "mat"))
+               type = "float";
+       else if(!type.compare(0, 3, "ivec") || type=="uint")
+               type = "int";
+
+       if(!have_default.count(type))
+       {
+               Precision *prec = new Precision;
+               if(!type.compare(0, 7, "sampler"))
+                       prec->precision = "lowp";
+               else if(stage->type==FRAGMENT)
+                       prec->precision = "mediump";
+               else
+                       prec->precision = "highp";
+               prec->type = type;
+               insert_nodes.push_back(prec);
+
+               have_default.insert(type);
+       }
+}
+
+
+ProgramCompiler::LegacyConverter::LegacyConverter():
+       target_api(get_gl_api()),
+       target_version(get_glsl_version()),
+       frag_out(0)
+{ }
+
+ProgramCompiler::LegacyConverter::LegacyConverter(const Version &v):
+       target_api(get_gl_api()),
+       target_version(v),
+       frag_out(0)
+{ }
+
+bool ProgramCompiler::LegacyConverter::check_version(const Version &feature_version) const
+{
+       if(target_version<feature_version)
+               return false;
+       else if(stage->required_version<feature_version)
+               stage->required_version = feature_version;
+
+       return true;
+}
+
+bool ProgramCompiler::LegacyConverter::check_extension(const Extension &extension) const
+{
+       if(!extension)
+               return false;
+
+       vector<const Extension *>::iterator i = find(stage->required_extensions, &extension);
+       if(i==stage->required_extensions.end())
+               stage->required_extensions.push_back(&extension);
+
+       return true;
+}
+
+bool ProgramCompiler::LegacyConverter::supports_unified_interface_syntax() const
+{
+       if(target_api==OPENGL_ES2)
+               return check_version(Version(3, 0));
+       else
+               return check_version(Version(1, 30));
+}
+
+void ProgramCompiler::LegacyConverter::visit(VariableReference &var)
+{
+       if(var.declaration==frag_out && !supports_unified_interface_syntax())
+       {
+               var.name = "gl_FragColor";
+               var.declaration = 0;
+               type = "vec4";
+       }
+       else if(var.declaration)
+               type = var.declaration->type;
+       else
+               type = string();
+}
+
+void ProgramCompiler::LegacyConverter::visit(Assignment &assign)
+{
+       TraversingVisitor::visit(assign);
+       if(assign.target_declaration==frag_out && !supports_unified_interface_syntax())
+               assign.target_declaration = 0;
+}
+
+bool ProgramCompiler::LegacyConverter::supports_unified_sampling_functions() const
+{
+       if(target_api==OPENGL_ES2)
+               return check_version(Version(3, 0));
+       else
+               return check_version(Version(1, 30));
+}
+
+void ProgramCompiler::LegacyConverter::visit(FunctionCall &call)
+{
+       if(call.name=="texture" && !call.declaration && !supports_unified_sampling_functions())
+       {
+               NodeArray<Expression>::iterator i = call.arguments.begin();
+               if(i!=call.arguments.end())
+               {
+                       (*i)->visit(*this);
+                       if(type=="sampler1D")
+                               call.name = "texture1D";
+                       else if(type=="sampler2D")
+                               call.name = "texture2D";
+                       else if(type=="sampler3D")
+                               call.name = "texture3D";
+                       else if(type=="samplerCube")
+                               call.name = "textureCube";
+                       else if(type=="sampler1DShadow")
+                               call.name = "shadow1D";
+                       else if(type=="sampler2DShadow")
+                               call.name = "shadow2D";
+                       else if(type=="sampler1DArray")
+                       {
+                               check_extension(EXT_texture_array);
+                               call.name = "texture1DArray";
+                       }
+                       else if(type=="sampler2DArray")
+                       {
+                               check_extension(EXT_texture_array);
+                               call.name = "texture2DArray";
+                       }
+                       else if(type=="sampler1DArrayShadow")
+                       {
+                               check_extension(EXT_texture_array);
+                               call.name = "shadow1DArray";
+                       }
+                       else if(type=="sampler2DArrayShadow")
+                       {
+                               check_extension(EXT_texture_array);
+                               call.name = "shadow2DArray";
+                       }
+
+                       for(; i!=call.arguments.end(); ++i)
+                               (*i)->visit(*this);
+               }
+       }
+       else
+               TraversingVisitor::visit(call);
+}
+
+bool ProgramCompiler::LegacyConverter::supports_interface_layouts() const
+{
+       if(target_api==OPENGL_ES2)
+               return check_version(Version(3, 0));
+       else if(check_version(Version(3, 30)))
+               return true;
+       else
+               return check_extension(ARB_explicit_attrib_location);
+}
+
+bool ProgramCompiler::LegacyConverter::supports_centroid_sampling() const
+{
+       if(target_api==OPENGL_ES2)
+               return check_version(Version(3, 0));
+       else if(check_version(Version(1, 20)))
+               return true;
+       else
+               return check_extension(EXT_gpu_shader4);
+}
+
+bool ProgramCompiler::LegacyConverter::supports_sample_sampling() const
+{
+       if(target_api==OPENGL_ES2)
+               return check_version(Version(3, 20));
+       else if(check_version(Version(4, 0)))
+               return true;
+       else
+               return check_extension(ARB_gpu_shader5);
+}
+
+void ProgramCompiler::LegacyConverter::visit(VariableDeclaration &var)
+{
+       if(var.layout && !supports_interface_layouts())
+       {
+               vector<Layout::Qualifier>::iterator i;
+               for(i=var.layout->qualifiers.begin(); (i!=var.layout->qualifiers.end() && i->identifier!="location"); ++i) ;
+               if(i!=var.layout->qualifiers.end())
+               {
+                       unsigned location = lexical_cast<unsigned>(i->value);
+                       if(stage->type==VERTEX && var.interface=="in")
+                       {
+                               stage->locations[var.name] = location;
+                               var.layout->qualifiers.erase(i);
+                       }
+                       else if(stage->type==FRAGMENT && var.interface=="out")
+                       {
+                               if(location!=0)
+                                       static Require _req(EXT_gpu_shader4);
+                               stage->locations[var.name] = location;
+                               var.layout->qualifiers.erase(i);
+                       }
+
+                       if(var.layout->qualifiers.empty())
+                               var.layout = 0;
+               }
+       }
+
+       if(var.sampling=="centroid")
+       {
+               if(!supports_centroid_sampling())
+                       var.sampling = string();
+       }
+       else if(var.sampling=="sample")
+       {
+               if(!supports_sample_sampling())
+                       var.sampling = string();
+       }
+
+       if((var.interface=="in" || var.interface=="out") && !supports_unified_interface_syntax())
+       {
+               if(stage->type==FRAGMENT && var.interface=="out")
+               {
+                       frag_out = &var;
+                       remove_node = true;
+               }
+       }
+
+       TraversingVisitor::visit(var);
+}
+
+bool ProgramCompiler::LegacyConverter::supports_interface_blocks(const string &iface) const
+{
+       if(target_api==OPENGL_ES2)
+       {
+               if(iface=="uniform")
+                       return check_version(Version(3, 0));
+               else
+                       return check_version(Version(3, 20));
+       }
+       else if(check_version(Version(1, 50)))
+               return true;
+       else if(iface=="uniform")
+               return check_extension(ARB_uniform_buffer_object);
+       else
+               return false;
+}
+
+void ProgramCompiler::LegacyConverter::visit(InterfaceBlock &iface)
+{
+       if(!supports_interface_blocks(iface.interface))
+               flatten_block(iface.members);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/glsl/programcompiler.h b/source/glsl/programcompiler.h
new file mode 100644 (file)
index 0000000..0bff354
--- /dev/null
@@ -0,0 +1,416 @@
+#ifndef MSP_GL_PROGRAMCOMPILER_H_
+#define MSP_GL_PROGRAMCOMPILER_H_
+
+#include <set>
+#include "programparser.h"
+#include "programsyntax.h"
+
+namespace Msp {
+namespace GL {
+
+class Program;
+class Resources;
+
+class ProgramCompiler
+{
+private:
+       struct Visitor: ProgramSyntax::TraversingVisitor
+       {
+               typedef void ResultType;
+
+               ProgramSyntax::Stage *stage;
+
+               Visitor();
+
+               virtual void apply(ProgramSyntax::Stage &);
+               void get_result() const { }
+       };
+
+       struct BlockModifier: Visitor
+       {
+               bool remove_node;
+               std::vector<RefPtr<ProgramSyntax::Statement> > insert_nodes;
+
+               BlockModifier();
+
+               void flatten_block(ProgramSyntax::Block &);
+               void apply_and_increment(ProgramSyntax::Block &, ProgramSyntax::NodeList<ProgramSyntax::Statement>::iterator &);
+               using Visitor::visit;
+               virtual void visit(ProgramSyntax::Block &);
+       };
+
+       struct Formatter: Visitor
+       {
+               typedef std::string ResultType;
+
+               std::string formatted;
+               unsigned source_index;
+               unsigned source_line;
+               unsigned indent;
+               bool parameter_list;
+               std::string block_interface;
+
+               Formatter();
+
+               virtual void apply(ProgramSyntax::Stage &);
+               const std::string &get_result() const { return formatted; }
+               using Visitor::visit;
+               void append(const std::string &);
+               void append(char);
+               void set_source(unsigned, unsigned);
+               virtual void visit(ProgramSyntax::Block &);
+               virtual void visit(ProgramSyntax::Literal &);
+               virtual void visit(ProgramSyntax::ParenthesizedExpression &);
+               virtual void visit(ProgramSyntax::VariableReference &);
+               virtual void visit(ProgramSyntax::MemberAccess &);
+               virtual void visit(ProgramSyntax::UnaryExpression &);
+               virtual void visit(ProgramSyntax::BinaryExpression &);
+               virtual void visit(ProgramSyntax::Assignment &);
+               virtual void visit(ProgramSyntax::FunctionCall &);
+               virtual void visit(ProgramSyntax::ExpressionStatement &);
+               virtual void visit(ProgramSyntax::Import &);
+               virtual void visit(ProgramSyntax::Precision &);
+               virtual void visit(ProgramSyntax::Layout &);
+               virtual void visit(ProgramSyntax::InterfaceLayout &);
+               virtual void visit(ProgramSyntax::StructDeclaration &);
+               virtual void visit(ProgramSyntax::VariableDeclaration &);
+               virtual void visit(ProgramSyntax::InterfaceBlock &);
+               virtual void visit(ProgramSyntax::FunctionDeclaration &);
+               virtual void visit(ProgramSyntax::Conditional &);
+               virtual void visit(ProgramSyntax::Iteration &);
+               virtual void visit(ProgramSyntax::Return &);
+               virtual void visit(ProgramSyntax::Jump &);
+       };
+
+       template<typename T>
+       struct NodeGatherer: Visitor
+       {
+               typedef std::vector<T *> ResultType;
+
+               std::vector<T *> nodes;
+
+               const ResultType &get_result() const { return nodes; }
+               using Visitor::visit;
+               virtual void visit(T &n) { nodes.push_back(&n); }
+       };
+
+       struct DeclarationCombiner: BlockModifier
+       {
+               bool toplevel;
+               std::map<std::string, std::vector<ProgramSyntax::FunctionDeclaration *> > functions;
+               std::map<std::string, ProgramSyntax::VariableDeclaration *> variables;
+
+               DeclarationCombiner();
+
+               using Visitor::visit;
+               virtual void visit(ProgramSyntax::Block &);
+               virtual void visit(ProgramSyntax::FunctionDeclaration &);
+               virtual void visit(ProgramSyntax::VariableDeclaration &);
+       };
+
+       struct VariableResolver: Visitor
+       {
+               std::vector<ProgramSyntax::Block *> blocks;
+               ProgramSyntax::StructDeclaration *type;
+               bool anonymous;
+               std::string block_interface;
+               bool record_target;
+               ProgramSyntax::VariableDeclaration *assignment_target;
+               bool self_referencing;
+
+               VariableResolver();
+
+               virtual void apply(ProgramSyntax::Stage &);
+               using Visitor::visit;
+               virtual void visit(ProgramSyntax::Block &);
+               virtual void visit(ProgramSyntax::VariableReference &);
+               virtual void visit(ProgramSyntax::MemberAccess &);
+               virtual void visit(ProgramSyntax::BinaryExpression &);
+               virtual void visit(ProgramSyntax::Assignment &);
+               virtual void visit(ProgramSyntax::StructDeclaration &);
+               virtual void visit(ProgramSyntax::VariableDeclaration &);
+               virtual void visit(ProgramSyntax::InterfaceBlock &);
+       };
+
+       struct FunctionResolver: Visitor
+       {
+               std::map<std::string, std::vector<ProgramSyntax::FunctionDeclaration *> > functions;
+
+               using Visitor::visit;
+               virtual void visit(ProgramSyntax::FunctionCall &);
+               virtual void visit(ProgramSyntax::FunctionDeclaration &);
+       };
+
+       struct InterfaceGenerator: BlockModifier
+       {
+               std::string in_prefix;
+               std::string out_prefix;
+               unsigned scope_level;
+               std::map<std::string, RefPtr<ProgramSyntax::VariableDeclaration> > iface_declarations;
+
+               InterfaceGenerator();
+
+               static std::string get_out_prefix(ProgramSyntax::StageType);
+               virtual void apply(ProgramSyntax::Stage &);
+               using Visitor::visit;
+               virtual void visit(ProgramSyntax::Block &);
+               std::string change_prefix(const std::string &, const std::string &) const;
+               bool generate_interface(ProgramSyntax::VariableDeclaration &, const std::string &, const std::string &);
+               ProgramSyntax::ExpressionStatement &insert_assignment(const std::string &, ProgramSyntax::Expression *);
+               virtual void visit(ProgramSyntax::VariableReference &);
+               virtual void visit(ProgramSyntax::VariableDeclaration &);
+               virtual void visit(ProgramSyntax::Passthrough &);
+       };
+
+       struct DeclarationReorderer: Visitor
+       {
+               enum DeclarationKind
+               {
+                       NO_DECLARATION,
+                       LAYOUT,
+                       STRUCT,
+                       VARIABLE,
+                       FUNCTION
+               };
+
+               unsigned scope_level;
+               DeclarationKind kind;
+               std::set<ProgramSyntax::Node *> ordered_funcs;
+               std::set<ProgramSyntax::Node *> needed_funcs;
+
+               DeclarationReorderer();
+
+               using Visitor::visit;
+               virtual void visit(ProgramSyntax::Block &);
+               virtual void visit(ProgramSyntax::FunctionCall &);
+               virtual void visit(ProgramSyntax::InterfaceLayout &) { kind = LAYOUT; }
+               virtual void visit(ProgramSyntax::StructDeclaration &) { kind = STRUCT; }
+               virtual void visit(ProgramSyntax::VariableDeclaration &);
+               virtual void visit(ProgramSyntax::InterfaceBlock &) { kind = VARIABLE; }
+               virtual void visit(ProgramSyntax::FunctionDeclaration &);
+       };
+
+       struct InlineableFunctionLocator: Visitor
+       {
+               typedef std::set<ProgramSyntax::FunctionDeclaration *> ResultType;
+
+               std::map<ProgramSyntax::FunctionDeclaration *, unsigned> refcounts;
+               std::set<ProgramSyntax::FunctionDeclaration *> inlineable;
+               ProgramSyntax::FunctionDeclaration *in_function;
+
+               InlineableFunctionLocator();
+
+               const ResultType &get_result() const { return inlineable; }
+               using Visitor::visit;
+               virtual void visit(ProgramSyntax::FunctionCall &);
+               virtual void visit(ProgramSyntax::FunctionDeclaration &);
+       };
+
+       struct FunctionInliner: Visitor
+       {
+               std::set<ProgramSyntax::FunctionDeclaration *> inlineable;
+               unsigned extract_result;
+               RefPtr<ProgramSyntax::Expression> inline_result;
+
+               FunctionInliner();
+               FunctionInliner(const std::set<ProgramSyntax::FunctionDeclaration *> &);
+
+               void visit_and_inline(RefPtr<ProgramSyntax::Expression> &);
+               using Visitor::visit;
+               virtual void visit(ProgramSyntax::Block &);
+               virtual void visit(ProgramSyntax::UnaryExpression &);
+               virtual void visit(ProgramSyntax::BinaryExpression &);
+               virtual void visit(ProgramSyntax::MemberAccess &);
+               virtual void visit(ProgramSyntax::FunctionCall &);
+               virtual void visit(ProgramSyntax::VariableDeclaration &);
+               virtual void visit(ProgramSyntax::Return &);
+       };
+
+       struct ExpressionEvaluator: ProgramSyntax::NodeVisitor
+       {
+               typedef std::map<ProgramSyntax::VariableDeclaration *, ProgramSyntax::Expression *> ValueMap;
+
+               const ValueMap *variable_values;
+               float result;
+               bool result_valid;
+
+               ExpressionEvaluator();
+               ExpressionEvaluator(const ValueMap &);
+
+               using ProgramSyntax::NodeVisitor::visit;
+               virtual void visit(ProgramSyntax::Literal &);
+               virtual void visit(ProgramSyntax::ParenthesizedExpression &);
+               virtual void visit(ProgramSyntax::VariableReference &);
+               virtual void visit(ProgramSyntax::UnaryExpression &);
+               virtual void visit(ProgramSyntax::BinaryExpression &);
+       };
+
+       struct ConstantConditionEliminator: BlockModifier
+       {
+               unsigned scope_level;
+               bool record_only;
+               ExpressionEvaluator::ValueMap variable_values;
+
+               ConstantConditionEliminator();
+
+               using Visitor::visit;
+               virtual void visit(ProgramSyntax::Block &);
+               virtual void visit(ProgramSyntax::UnaryExpression &);
+               virtual void visit(ProgramSyntax::Assignment &);
+               virtual void visit(ProgramSyntax::VariableDeclaration &);
+               virtual void visit(ProgramSyntax::Conditional &);
+               virtual void visit(ProgramSyntax::Iteration &);
+       };
+
+       struct UnusedVariableLocator: Visitor
+       {
+               struct VariableInfo
+               {
+                       bool local;
+                       std::vector<ProgramSyntax::Node *> assignments;
+                       bool conditionally_assigned;
+                       bool referenced;
+
+                       VariableInfo();
+               };
+
+               typedef std::set<ProgramSyntax::Node *> ResultType;
+               typedef std::map<ProgramSyntax::VariableDeclaration *, VariableInfo> BlockVariableMap;
+
+               std::set<ProgramSyntax::Node *> unused_nodes;
+               std::map<ProgramSyntax::VariableDeclaration *, ProgramSyntax::Node *> aggregates;
+               ProgramSyntax::Node *aggregate;
+               std::vector<BlockVariableMap> variables;
+               ProgramSyntax::Assignment *assignment;
+               bool assignment_target;
+               bool assign_to_subscript;
+               bool global_scope;
+
+               UnusedVariableLocator();
+
+               virtual void apply(ProgramSyntax::Stage &);
+               const ResultType &get_result() const { return unused_nodes; }
+               using Visitor::visit;
+               virtual void visit(ProgramSyntax::VariableReference &);
+               virtual void visit(ProgramSyntax::MemberAccess &);
+               virtual void visit(ProgramSyntax::BinaryExpression &);
+               virtual void visit(ProgramSyntax::Assignment &);
+               void record_assignment(ProgramSyntax::VariableDeclaration &, ProgramSyntax::Node &, bool);
+               void clear_assignments(VariableInfo &, bool);
+               virtual void visit(ProgramSyntax::ExpressionStatement &);
+               virtual void visit(ProgramSyntax::StructDeclaration &);
+               virtual void visit(ProgramSyntax::VariableDeclaration &);
+               virtual void visit(ProgramSyntax::InterfaceBlock &);
+               virtual void visit(ProgramSyntax::FunctionDeclaration &);
+               void merge_down_variables();
+               virtual void visit(ProgramSyntax::Conditional &);
+               virtual void visit(ProgramSyntax::Iteration &);
+       };
+
+       struct UnusedFunctionLocator: Visitor
+       {
+               typedef std::set<ProgramSyntax::Node *> ResultType;
+
+               std::set<ProgramSyntax::Node *> unused_nodes;
+               std::set<ProgramSyntax::FunctionDeclaration *> used_definitions;
+
+               const ResultType &get_result() const { return unused_nodes; }
+               using Visitor::visit;
+               virtual void visit(ProgramSyntax::FunctionCall &);
+               virtual void visit(ProgramSyntax::FunctionDeclaration &);
+       };
+
+       struct NodeRemover: Visitor
+       {
+               std::set<ProgramSyntax::Node *> to_remove;
+
+               NodeRemover() { }
+               NodeRemover(const std::set<ProgramSyntax::Node *> &);
+
+               using Visitor::visit;
+               virtual void visit(ProgramSyntax::Block &);
+               virtual void visit(ProgramSyntax::VariableDeclaration &);
+               virtual void visit(ProgramSyntax::Iteration &);
+       };
+
+       struct PrecisionRemover: BlockModifier
+       {
+               using Visitor::visit;
+               virtual void visit(ProgramSyntax::Precision &);
+               virtual void visit(ProgramSyntax::VariableDeclaration &);
+       };
+
+       struct DefaultPrecisionGenerator: BlockModifier
+       {
+               bool toplevel;
+               std::set<std::string> have_default;
+
+               DefaultPrecisionGenerator();
+
+               using Visitor::visit;
+               virtual void visit(ProgramSyntax::Block &);
+               virtual void visit(ProgramSyntax::Precision &);
+               virtual void visit(ProgramSyntax::VariableDeclaration &);
+       };
+
+       struct LegacyConverter: BlockModifier
+       {
+               GLApi target_api;
+               Version target_version;
+               std::string type;
+               ProgramSyntax::VariableDeclaration *frag_out;
+
+               LegacyConverter();
+               LegacyConverter(const Version &);
+
+               bool check_version(const Version &) const;
+               bool check_extension(const Extension &) const;
+               using Visitor::visit;
+               bool supports_unified_interface_syntax() const;
+               virtual void visit(ProgramSyntax::VariableReference &);
+               virtual void visit(ProgramSyntax::Assignment &);
+               bool supports_unified_sampling_functions() const;
+               virtual void visit(ProgramSyntax::FunctionCall &);
+               bool supports_interface_layouts() const;
+               bool supports_centroid_sampling() const;
+               bool supports_sample_sampling() const;
+               virtual void visit(ProgramSyntax::VariableDeclaration &);
+               bool supports_interface_blocks(const std::string &) const;
+               virtual void visit(ProgramSyntax::InterfaceBlock &);
+       };
+
+       Resources *resources;
+       ProgramSyntax::Module *module;
+       std::vector<std::string> imported_names;
+
+public:
+       ProgramCompiler();
+       ~ProgramCompiler();
+
+       void compile(const std::string &, const std::string & = "<string>");
+       void compile(IO::Base &, Resources * = 0, const std::string & = "<file>");
+       void compile(IO::Base &, const std::string &);
+       void add_shaders(Program &);
+
+private:
+       static ProgramSyntax::Module *create_builtins_module();
+       static ProgramSyntax::Module &get_builtins_module();
+       static ProgramSyntax::Stage *get_builtins(ProgramSyntax::StageType);
+       void append_module(ProgramSyntax::Module &);
+       void append_stage(ProgramSyntax::Stage &);
+       void process();
+       void import(const std::string &);
+       void generate(ProgramSyntax::Stage &);
+       bool optimize(ProgramSyntax::Stage &);
+       void finalize(ProgramSyntax::Stage &);
+       static void inject_block(ProgramSyntax::Block &, const ProgramSyntax::Block &);
+       template<typename T>
+       static typename T::ResultType apply(ProgramSyntax::Stage &);
+       template<typename T, typename A>
+       static typename T::ResultType apply(ProgramSyntax::Stage &, const A &);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/glsl/programparser.cpp b/source/glsl/programparser.cpp
new file mode 100644 (file)
index 0000000..83a2879
--- /dev/null
@@ -0,0 +1,962 @@
+#include <msp/core/raii.h>
+#include <msp/strings/format.h>
+#include <msp/strings/regex.h>
+#include "programparser.h"
+
+#undef interface
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+using namespace ProgramSyntax;
+
+ProgramParser::Operator ProgramParser::operators[] =
+{
+       { "[", 2, BINARY, LEFT_TO_RIGHT },
+       { "(", 2, BINARY, LEFT_TO_RIGHT },
+       { ".", 2, BINARY, LEFT_TO_RIGHT },
+       { "++", 2, POSTFIX, LEFT_TO_RIGHT },
+       { "--", 2, POSTFIX, LEFT_TO_RIGHT },
+       { "++", 3, PREFIX, RIGHT_TO_LEFT },
+       { "--", 3, PREFIX, RIGHT_TO_LEFT },
+       { "+", 3, PREFIX, RIGHT_TO_LEFT },
+       { "-", 3, PREFIX, RIGHT_TO_LEFT },
+       { "~", 3, PREFIX, RIGHT_TO_LEFT },
+       { "!", 3, PREFIX, RIGHT_TO_LEFT },
+       { "*", 4, BINARY, LEFT_TO_RIGHT },
+       { "/", 4, BINARY, LEFT_TO_RIGHT },
+       { "%", 4, BINARY, LEFT_TO_RIGHT },
+       { "+", 5, BINARY, LEFT_TO_RIGHT },
+       { "-", 5, BINARY, LEFT_TO_RIGHT },
+       { "<<", 6, BINARY, LEFT_TO_RIGHT },
+       { ">>", 6, BINARY, LEFT_TO_RIGHT },
+       { "<", 7, BINARY, LEFT_TO_RIGHT },
+       { ">", 7, BINARY, LEFT_TO_RIGHT },
+       { "<=", 7, BINARY, LEFT_TO_RIGHT },
+       { ">=", 7, BINARY, LEFT_TO_RIGHT },
+       { "==", 8, BINARY, LEFT_TO_RIGHT },
+       { "!=", 8, BINARY, LEFT_TO_RIGHT },
+       { "&", 9, BINARY, LEFT_TO_RIGHT },
+       { "^", 10, BINARY, LEFT_TO_RIGHT },
+       { "|", 11, BINARY, LEFT_TO_RIGHT },
+       { "&&", 12, BINARY, LEFT_TO_RIGHT },
+       { "^^", 13, BINARY, LEFT_TO_RIGHT },
+       { "||", 14, BINARY, LEFT_TO_RIGHT },
+       { "?", 15, BINARY, RIGHT_TO_LEFT },
+       { ":", 15, BINARY, RIGHT_TO_LEFT },
+       { "=", 16, BINARY, RIGHT_TO_LEFT },
+       { "+=", 16, BINARY, RIGHT_TO_LEFT },
+       { "-=", 16, BINARY, RIGHT_TO_LEFT },
+       { "*=", 16, BINARY, RIGHT_TO_LEFT },
+       { "/=", 16, BINARY, RIGHT_TO_LEFT },
+       { "%=", 16, BINARY, RIGHT_TO_LEFT },
+       { "<<=", 16, BINARY, RIGHT_TO_LEFT },
+       { ">>=", 16, BINARY, RIGHT_TO_LEFT },
+       { "&=", 16, BINARY, RIGHT_TO_LEFT },
+       { "^=", 16, BINARY, RIGHT_TO_LEFT },
+       { "|=", 16, BINARY, RIGHT_TO_LEFT },
+       { ",", 17, BINARY, LEFT_TO_RIGHT },
+       { { 0 }, 18, NO_OPERATOR, LEFT_TO_RIGHT }
+};
+
+ProgramParser::ProgramParser():
+       module(0)
+{ }
+
+ProgramParser::~ProgramParser()
+{
+       delete module;
+}
+
+Module &ProgramParser::parse(const string &s, const string &n, unsigned i)
+{
+       source = s;
+       source_name = n;
+       source_index = i;
+       parse_source();
+       return *module;
+}
+
+Module &ProgramParser::parse(IO::Base &io, const string &n, unsigned i)
+{
+       source = string();
+       source_name = n;
+       source_index = i;
+       while(!io.eof())
+       {
+               char buffer[4096];
+               unsigned len = io.read(buffer, sizeof(buffer));
+               source.append(buffer, len);
+       }
+       parse_source();
+       return *module;
+}
+
+void ProgramParser::parse_source()
+{
+       while(1)
+       {
+               string::size_type slashes = source.find("//////");
+               if(slashes==string::npos)
+                       break;
+
+               string::size_type newline = source.find('\n', slashes);
+               string pragma = format("#pragma MSP stage(%s)", source.substr(slashes+6, newline-slashes-6));
+               source.replace(slashes, newline-slashes, pragma);
+       }
+
+       delete module;
+       module = new Module;
+       cur_stage = &module->shared;
+       iter = source.begin();
+       source_end = source.end();
+       current_line = 1;
+       allow_preprocess = true;
+       while(RefPtr<Statement> statement = parse_global_declaration())
+               cur_stage->content.body.push_back(statement);
+}
+
+string ProgramParser::format_error(const std::string &message)
+{
+       string location = format("%s:%d: ", source_name, current_line);
+       return location+message;
+}
+
+string ProgramParser::format_syntax_error(const std::string &expected)
+{
+       return format_error(format("Syntax error at '%s': expected %s", last_token, expected));
+}
+
+const string &ProgramParser::peek_token(unsigned index)
+{
+       while(next_tokens.size()<=index)
+               next_tokens.push_back(parse_token_());
+       return (last_token = next_tokens[index]);
+}
+
+const string &ProgramParser::parse_token()
+{
+       if(!next_tokens.empty())
+       {
+               last_token = next_tokens.front();
+               next_tokens.pop_front();
+               return last_token;
+       }
+
+       return (last_token = parse_token_());
+}
+
+string ProgramParser::parse_token_()
+{
+       while(1)
+       {
+               skip_comment_and_whitespace();
+               if(iter==source_end)
+                       return string();
+               else if(allow_preprocess && *iter=='#')
+               {
+                       allow_preprocess = false;
+                       SetForScope<deque<string> > clear_tokens(next_tokens, deque<string>());
+                       preprocess();
+               }
+               else if(isalpha(*iter) || *iter=='_')
+                       return parse_identifier();
+               else if(isdigit(*iter))
+                       return parse_number();
+               else
+                       return parse_other();
+       }
+}
+
+string ProgramParser::parse_identifier()
+{
+       string ident;
+       while(iter!=source_end)
+       {
+               if(isalnum(*iter) || *iter=='_')
+                       ident += *iter++;
+               else
+                       break;
+       }
+
+       return ident;
+}
+
+string ProgramParser::parse_number()
+{
+       bool accept_sign = false;
+       string number;
+       while(iter!=source_end)
+       {
+               if(isdigit(*iter) || *iter=='.')
+                       number += *iter++;
+               else if(*iter=='e' || *iter=='E')
+               {
+                       number += *iter++;
+                       accept_sign = true;
+               }
+               else if(accept_sign && (*iter=='+' || *iter=='-'))
+                       number += *iter++;
+               else
+                       break;
+       }
+
+       return number;
+}
+
+string ProgramParser::parse_other()
+{
+       if(iter==source_end)
+               return string();
+
+       string token(1, *iter++);
+       for(unsigned i=1; (i<3 && iter!=source_end); ++i)
+       {
+               bool matched = false;
+               for(const Operator *j=operators; (!matched && j->type); ++j)
+               {
+                       matched = (j->token[i]==*iter);
+                       for(unsigned k=0; (matched && k<i && j->token[k]); ++k)
+                               matched = (j->token[k]==token[k]);
+               }
+
+               if(!matched)
+                       break;
+
+               token += *iter++;
+       }
+
+       return token;
+}
+
+void ProgramParser::skip_comment_and_whitespace()
+{
+       unsigned comment = 0;
+       while(iter!=source_end)
+       {
+               if(comment==0)
+               {
+                       if(*iter=='/')
+                               comment = 1;
+                       else if(!isspace(*iter))
+                               break;
+               }
+               else if(comment==1)
+               {
+                       if(*iter=='/')
+                               comment = 2;
+                       else if(*iter=='*')
+                               comment = 3;
+                       else
+                       {
+                               comment = 0;
+                               --iter;
+                               break;
+                       }
+               }
+               else if(comment==2)
+               {
+                       if(*iter=='\n')
+                               comment = 0;
+               }
+               else if(comment==3 && *iter=='*')
+                       comment = 4;
+               else if(comment==4)
+               {
+                       if(*iter=='/')
+                               comment = 0;
+                       else if(*iter!='*')
+                               comment = 3;
+               }
+
+               if(*iter=='\n')
+               {
+                       ++current_line;
+                       allow_preprocess = (comment<3);
+               }
+
+               ++iter;
+       }
+}
+
+void ProgramParser::expect(const string &token)
+{
+       string parsed = parse_token();
+       if(parsed!=token)
+               throw runtime_error(format_syntax_error(format("'%s'", token)));
+}
+
+string ProgramParser::expect_type()
+{
+       string token = parse_token();
+       if(!is_type(token))
+               throw runtime_error(format_syntax_error("a type"));
+       return token;
+}
+
+string ProgramParser::expect_identifier()
+{
+       string token = parse_token();
+       if(!is_identifier(token))
+               throw runtime_error(format_syntax_error("an identifier"));
+       return token;
+}
+
+bool ProgramParser::check(const string &token)
+{
+       bool result = (peek_token()==token);
+       if(result)
+               parse_token();
+       return result;
+}
+
+bool ProgramParser::is_interface_qualifier(const string &token)
+{
+       return (token=="uniform" || token=="in" || token=="out");
+}
+
+bool ProgramParser::is_sampling_qualifier(const string &token)
+{
+       return (token=="centroid" || token=="sample");
+}
+
+bool ProgramParser::is_interpolation_qualifier(const string &token)
+{
+       return (token=="smooth" || token=="flat" || token=="noperspective");
+}
+
+bool ProgramParser::is_precision_qualifier(const string &token)
+{
+       return (token=="highp" || token=="mediump" || token=="lowp");
+}
+
+bool ProgramParser::is_qualifier(const string &token)
+{
+       return (token=="const" ||
+               is_interface_qualifier(token) ||
+               is_sampling_qualifier(token) ||
+               is_interpolation_qualifier(token) ||
+               is_precision_qualifier(token));
+}
+
+bool ProgramParser::is_builtin_type(const string &token)
+{
+       static Regex re("^(void|float|int|bool|[ib]?vec[234]|mat[234](x[234])?|sampler((1D|2D|Cube)(Array)?(Shadow)?|3D))$");
+       return re.match(token);
+}
+
+bool ProgramParser::is_type(const string &token)
+{
+       return is_builtin_type(token) || declared_types.count(token);
+}
+
+bool ProgramParser::is_identifier(const string &token)
+{
+       static Regex re("^[a-zA-Z_][a-zA-Z0-9_]*$");
+       return re.match(token);
+}
+
+void ProgramParser::preprocess()
+{
+       expect("#");
+
+       string::const_iterator line_end = iter;
+       for(; (line_end!=source_end && *line_end!='\n'); ++line_end) ;
+       SetForScope<string::const_iterator> stop_at_line_end(source_end, line_end);
+
+       string token = peek_token();
+       if(token=="pragma")
+               preprocess_pragma();
+       else if(token=="version")
+               preprocess_version();
+       else if(token=="define" || token=="undef" || token=="if" || token=="ifdef" || token=="ifndef" || token=="else" ||
+               token=="elif" || token=="endif" || token=="error" || token=="extension" || token=="line")
+               throw runtime_error(format_error(format("Unsupported preprocessor directive '%s'", token)));
+       else if(!token.empty())
+               throw runtime_error(format_syntax_error("a preprocessor directive"));
+
+       iter = line_end;
+}
+
+void ProgramParser::preprocess_version()
+{
+       expect("version");
+       string token = parse_token();
+       unsigned version = lexical_cast<unsigned>(token);
+       cur_stage->required_version = Version(version/100, version%100);
+
+       token = parse_token();
+       if(!token.empty())
+               throw runtime_error(format_syntax_error("end of line"));
+}
+
+void ProgramParser::preprocess_pragma()
+{
+       expect("pragma");
+       string token = parse_token();
+       if(token=="MSP")
+               preprocess_pragma_msp();
+}
+
+void ProgramParser::preprocess_pragma_msp()
+{
+       string token = peek_token();
+       if(token=="stage")
+               preprocess_stage();
+       else
+               throw runtime_error(format_error(format("Unrecognized MSP pragma '%s'", token)));
+
+       token = parse_token();
+       if(!token.empty())
+               throw runtime_error(format_syntax_error("end of line"));
+}
+
+void ProgramParser::preprocess_stage()
+{
+       if(!allow_stage_change)
+               throw runtime_error(format_error("Changing stage not allowed here"));
+
+       expect("stage");
+       expect("(");
+       string token = expect_identifier();
+       StageType stage = SHARED;
+       if(token=="vertex")
+               stage = VERTEX;
+       else if(token=="geometry")
+               stage = GEOMETRY;
+       else if(token=="fragment")
+               stage = FRAGMENT;
+       else
+               throw runtime_error(format_syntax_error("stage identifier"));
+       expect(")");
+
+       if(stage<=cur_stage->type)
+               throw runtime_error(format_error(format("Stage '%s' not allowed here", token)));
+
+       module->stages.push_back(stage);
+
+       if(cur_stage->type!=SHARED)
+               module->stages.back().previous = cur_stage;
+       cur_stage = &module->stages.back();
+}
+
+RefPtr<Statement> ProgramParser::parse_global_declaration()
+{
+       allow_stage_change = true;
+       string token = peek_token();
+       allow_stage_change = false;
+
+       if(token=="import")
+               return parse_import();
+       else if(token=="precision")
+               return parse_precision();
+       else if(token=="layout")
+       {
+               RefPtr<Layout> layout = parse_layout();
+               token = peek_token();
+               if(is_interface_qualifier(token) && peek_token(1)==";")
+               {
+                       RefPtr<InterfaceLayout> iface_lo = new InterfaceLayout;
+                       iface_lo->source = source_index;
+                       iface_lo->line = current_line;
+                       iface_lo->layout.qualifiers = layout->qualifiers;
+                       iface_lo->interface = parse_token();
+                       expect(";");
+                       return iface_lo;
+               }
+               else
+               {
+                       RefPtr<VariableDeclaration> var = parse_variable_declaration();
+                       var->layout = layout;
+                       return var;
+               }
+       }
+       else if(token=="struct")
+               return parse_struct_declaration();
+       else if(is_interface_qualifier(token))
+       {
+               string next = peek_token(1);
+               if(is_type(next) || is_qualifier(next))
+                       return parse_variable_declaration();
+               else
+                       return parse_interface_block();
+       }
+       else if(is_qualifier(token))
+               return parse_variable_declaration();
+       else if(is_type(token))
+       {
+               if(peek_token(2)=="(")
+                       return parse_function_declaration();
+               else
+                       return parse_variable_declaration();
+       }
+       else if(token.empty())
+               return 0;
+       else
+               throw runtime_error(format_syntax_error("a global declaration"));
+}
+
+RefPtr<Statement> ProgramParser::parse_statement()
+{
+       string token = peek_token();
+       if(token=="if")
+               return parse_conditional();
+       else if(token=="for")
+               return parse_for();
+       else if(token=="while")
+               return parse_while();
+       else if(token=="passthrough")
+               return parse_passthrough();
+       else if(token=="return")
+               return parse_return();
+       else if(token=="break" || token=="continue" || token=="discard")
+       {
+               RefPtr<Jump> jump = new Jump;
+               jump->source = source_index;
+               jump->line = current_line;
+               jump->keyword = parse_token();
+               expect(";");
+
+               return jump;
+       }
+       else if(is_qualifier(token) || is_type(token))
+               return parse_variable_declaration();
+       else if(!token.empty())
+       {
+               RefPtr<ExpressionStatement> expr = new ExpressionStatement;
+               expr->source = source_index;
+               expr->line = current_line;
+               expr->expression = parse_expression();
+               expect(";");
+
+               return expr;
+       }
+       else
+               throw runtime_error(format_syntax_error("a statement"));
+}
+
+RefPtr<Import> ProgramParser::parse_import()
+{
+       if(cur_stage->type!=SHARED)
+               throw runtime_error(format_error("Imports are only allowed in the shared section"));
+
+       expect("import");
+       RefPtr<Import> import = new Import;
+       import->source = source_index;
+       import->line = current_line;
+       import->module = expect_identifier();
+       expect(";");
+       return import;
+}
+
+RefPtr<Precision> ProgramParser::parse_precision()
+{
+       expect("precision");
+       RefPtr<Precision> precision = new Precision;
+       precision->source = source_index;
+       precision->line = current_line;
+
+       precision->precision = parse_token();
+       if(!is_precision_qualifier(precision->precision))
+               throw runtime_error(format_syntax_error("a precision qualifier"));
+
+       precision->type = parse_token();
+       // Not entirely accurate; only float, int and sampler types are allowed
+       if(!is_builtin_type(precision->type))
+               throw runtime_error(format_syntax_error("a builtin type"));
+
+       expect(";");
+
+       return precision;
+}
+
+RefPtr<Layout> ProgramParser::parse_layout()
+{
+       expect("layout");
+       expect("(");
+       RefPtr<Layout> layout = new Layout;
+       while(1)
+       {
+               string token = parse_token();
+               if(token==")")
+                       throw runtime_error(format_syntax_error("a layout qualifier name"));
+
+               layout->qualifiers.push_back(Layout::Qualifier());
+               Layout::Qualifier &qual = layout->qualifiers.back();
+               qual.identifier = token;
+
+               if(check("="))
+                       qual.value = parse_token();
+
+               if(peek_token()==")")
+                       break;
+
+               expect(",");
+       }
+       expect(")");
+
+       return layout;
+}
+
+void ProgramParser::parse_block(Block &block, bool require_braces)
+{
+       bool have_braces = (require_braces || peek_token()=="{");
+       if(have_braces)
+               expect("{");
+
+       if(have_braces)
+       {
+               while(peek_token()!="}")
+                       block.body.push_back(parse_statement());
+       }
+       else
+               block.body.push_back(parse_statement());
+
+       block.use_braces = (require_braces || block.body.size()!=1);
+
+       if(have_braces)
+               expect("}");
+}
+
+RefPtr<Expression> ProgramParser::parse_expression(unsigned precedence)
+{
+       RefPtr<Expression> left;
+       VariableReference *left_var = 0;
+       while(1)
+       {
+               string token = peek_token();
+
+               const Operator *oper = 0;
+               for(Operator *i=operators; (!oper && i->type); ++i)
+                       if(token==i->token && (!left || i->type!=PREFIX) && (left || i->type!=POSTFIX))
+                               oper = i;
+
+               if(token==";" || token==")" || token=="]" || token=="," || (oper && precedence && oper->precedence>=precedence))
+               {
+                       if(left)
+                               return left;
+                       else
+                               throw runtime_error(format_syntax_error("an expression"));
+               }
+               else if(left)
+               {
+                       if(token=="(")
+                       {
+                               if(!left_var)
+                                       throw runtime_error(format_error("Syntax error before '(': function name must be an identifier"));
+                               left = parse_function_call(*left_var);
+                       }
+                       else if(token==".")
+                       {
+                               RefPtr<MemberAccess> memacc = new MemberAccess;
+                               memacc->left = left;
+                               parse_token();
+                               memacc->member = expect_identifier();
+                               left = memacc;
+                       }
+                       else if(oper && oper->type==POSTFIX)
+                       {
+                               RefPtr<UnaryExpression> unary = new UnaryExpression;
+                               unary->oper = parse_token();
+                               unary->prefix = false;
+                               unary->expression = left;
+                               left = unary;
+                       }
+                       else if(oper && oper->type==BINARY)
+                               left = parse_binary(left, oper);
+                       else
+                               throw runtime_error(format_syntax_error("an operator"));
+                       left_var = 0;
+               }
+               else
+               {
+                       if(token=="(")
+                       {
+                               parse_token();
+                               RefPtr<ParenthesizedExpression> parexpr = new ParenthesizedExpression;
+                               parexpr->expression = parse_expression();
+                               expect(")");
+                               left = parexpr;
+                       }
+                       else if(isdigit(token[0]) || token=="true" || token=="false")
+                       {
+                               RefPtr<Literal> literal = new Literal;
+                               literal->token = parse_token();
+                               left = literal;
+                       }
+                       else if(is_identifier(token))
+                       {
+                               RefPtr<VariableReference> var = new VariableReference;
+                               var->name = expect_identifier();
+                               left = var;
+                               left_var = var.get();
+                       }
+                       else if(oper && oper->type==PREFIX)
+                       {
+                               RefPtr<UnaryExpression> unary = new UnaryExpression;
+                               unary->oper = parse_token();
+                               unary->prefix = true;
+                               unary->expression = parse_expression(oper->precedence);
+                               left = unary;
+                       }
+                       else
+                               throw runtime_error(format_syntax_error("an expression"));
+               }
+       }
+}
+
+RefPtr<BinaryExpression> ProgramParser::parse_binary(const RefPtr<Expression> &left, const Operator *oper)
+{
+       RefPtr<BinaryExpression> binary = (oper->precedence==16 ? new Assignment : new BinaryExpression);
+       binary->left = left;
+       binary->oper = parse_token();
+       if(binary->oper=="[")
+       {
+               binary->right = parse_expression();
+               expect("]");
+               binary->after = "]";
+       }
+       else
+               binary->right = parse_expression(oper->precedence+(oper->assoc==RIGHT_TO_LEFT));
+       return binary;
+}
+
+RefPtr<FunctionCall> ProgramParser::parse_function_call(const VariableReference &var)
+{
+       RefPtr<FunctionCall> call = new FunctionCall;
+       call->name = var.name;
+       call->constructor = is_type(call->name);
+       expect("(");
+       while(peek_token()!=")")
+       {
+               if(!call->arguments.empty())
+                       expect(",");
+               call->arguments.push_back(parse_expression());
+       }
+       expect(")");
+       return call;
+}
+
+RefPtr<StructDeclaration> ProgramParser::parse_struct_declaration()
+{
+       expect("struct");
+       RefPtr<StructDeclaration> strct = new StructDeclaration;
+       strct->source = source_index;
+       strct->line = current_line;
+
+       strct->name = expect_identifier();
+       parse_block(strct->members, true);
+       expect(";");
+
+       declared_types.insert(strct->name);
+       return strct;
+}
+
+RefPtr<VariableDeclaration> ProgramParser::parse_variable_declaration()
+{
+       RefPtr<VariableDeclaration> var = new VariableDeclaration;
+       var->source = source_index;
+       var->line = current_line;
+
+       string token = peek_token();
+       while(is_qualifier(token))
+       {
+               parse_token();
+               if(is_interface_qualifier(token))
+                       var->interface = token;
+               else if(is_sampling_qualifier(token))
+                       var->sampling = token;
+               else if(is_interpolation_qualifier(token))
+                       var->interpolation = token;
+               else if(is_precision_qualifier(token))
+                       var->precision = token;
+               else if(token=="const")
+                       var->constant = true;
+               token = peek_token();
+       }
+
+       var->type = expect_type();
+       var->name = expect_identifier();
+
+       if(check("["))
+       {
+               var->array = true;
+               if(!check("]"))
+               {
+                       var->array_size = parse_expression();
+                       expect("]");
+               }
+       }
+
+       if(check("="))
+               var->init_expression = parse_expression();
+
+       expect(";");
+       return var;
+}
+
+RefPtr<FunctionDeclaration> ProgramParser::parse_function_declaration()
+{
+       RefPtr<FunctionDeclaration> func = new FunctionDeclaration;
+       func->source = source_index;
+       func->line = current_line;
+
+       func->return_type = expect_type();
+       func->name = expect_identifier();
+       expect("(");
+       while(peek_token()!=")")
+       {
+               if(!func->parameters.empty())
+                       expect(",");
+
+               RefPtr<VariableDeclaration> var = new VariableDeclaration;
+               string token = peek_token();
+               if(token=="in" || token=="out" || token=="inout")
+                       var->interface = parse_token();
+               var->type = expect_type();
+               var->name = expect_identifier();
+               func->parameters.push_back(var);
+       }
+       expect(")");
+
+       string token = peek_token();
+       if(token=="{")
+       {
+               func->definition = func.get();
+               parse_block(func->body, true);
+       }
+       else if(token==";")
+               parse_token();
+       else
+               throw runtime_error(format_syntax_error("'{' or ';'"));
+
+       return func;
+}
+
+RefPtr<InterfaceBlock> ProgramParser::parse_interface_block()
+{
+       RefPtr<InterfaceBlock> iface = new InterfaceBlock;
+       iface->source = source_index;
+       iface->line = current_line;
+
+       iface->interface = parse_token();
+       if(!is_interface_qualifier(iface->interface))
+               throw runtime_error(format_syntax_error("an interface qualifier"));
+
+       iface->name = expect_identifier();
+       parse_block(iface->members, true);
+       if(!check(";"))
+       {
+               iface->instance_name = expect_identifier();
+               if(check("["))
+               {
+                       iface->array = true;
+                       expect("]");
+               }
+               expect(";");
+       }
+
+       return iface;
+}
+
+RefPtr<Conditional> ProgramParser::parse_conditional()
+{
+       expect("if");
+       RefPtr<Conditional> cond = new Conditional;
+       cond->source = source_index;
+       cond->line = current_line;
+       expect("(");
+       cond->condition = parse_expression();
+       expect(")");
+
+       parse_block(cond->body, false);
+
+       string token = peek_token();
+       if(token=="else")
+       {
+               parse_token();
+               parse_block(cond->else_body, false);
+       }
+
+       return cond;
+}
+
+RefPtr<Iteration> ProgramParser::parse_for()
+{
+       expect("for");
+       RefPtr<Iteration> loop = new Iteration;
+       loop->source = source_index;
+       loop->line = current_line;
+       expect("(");
+       string token = peek_token();
+       if(is_type(token))
+               loop->init_statement = parse_statement();
+       else
+       {
+               if(token!=";")
+               {
+                       RefPtr<ExpressionStatement> expr = new ExpressionStatement;
+                       expr->expression = parse_expression();
+                       loop->init_statement = expr;
+               }
+               expect(";");
+       }
+       if(peek_token()!=";")
+               loop->condition = parse_expression();
+       expect(";");
+       if(peek_token()!=")")
+               loop->loop_expression = parse_expression();
+       expect(")");
+
+       parse_block(loop->body, false);
+
+       return loop;
+}
+
+RefPtr<Iteration> ProgramParser::parse_while()
+{
+       expect("while");
+       RefPtr<Iteration> loop = new Iteration;
+       loop->source = source_index;
+       loop->line = current_line;
+       expect("(");
+       loop->condition = parse_expression();
+       expect(")");
+
+       parse_block(loop->body, false);
+
+       return loop;
+}
+
+RefPtr<Passthrough> ProgramParser::parse_passthrough()
+{
+       expect("passthrough");
+       RefPtr<Passthrough> pass = new Passthrough;
+       pass->source = source_index;
+       pass->line = current_line;
+       if(cur_stage->type==GEOMETRY)
+       {
+               expect("[");
+               pass->subscript = parse_expression();
+               expect("]");
+       }
+       expect(";");
+       return pass;
+}
+
+RefPtr<Return> ProgramParser::parse_return()
+{
+       expect("return");
+       RefPtr<Return> ret = new Return;
+       ret->source = source_index;
+       ret->line = current_line;
+       if(peek_token()!=";")
+               ret->expression = parse_expression();
+       expect(";");
+       return ret;
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/glsl/programparser.h b/source/glsl/programparser.h
new file mode 100644 (file)
index 0000000..e366047
--- /dev/null
@@ -0,0 +1,118 @@
+#ifndef MSP_GL_PROGRAMPARSER_H_
+#define MSP_GL_PROGRAMPARSER_H_
+
+#include <deque>
+#include <map>
+#include <set>
+#include <string>
+#include <msp/io/base.h>
+#include "programsyntax.h"
+
+namespace Msp {
+namespace GL {
+
+class ProgramParser
+{
+private:
+       enum OperatorType
+       {
+               NO_OPERATOR,
+               BINARY,
+               PREFIX,
+               POSTFIX
+       };
+
+       enum Associativity
+       {
+               LEFT_TO_RIGHT,
+               RIGHT_TO_LEFT
+       };
+
+       struct Operator
+       {
+               const char token[4];
+               unsigned precedence;
+               OperatorType type;
+               Associativity assoc;
+       };
+
+       std::string source;
+       std::string source_name;
+       unsigned source_index;
+       unsigned current_line;
+       std::string::const_iterator iter;
+       std::string::const_iterator source_end;
+       bool allow_preprocess;
+       bool allow_stage_change;
+       std::string last_token;
+       std::deque<std::string> next_tokens;
+       ProgramSyntax::Module *module;
+       ProgramSyntax::Stage *cur_stage;
+       std::set<std::string> declared_types;
+
+       static Operator operators[];
+
+public:
+       ProgramParser();
+       ~ProgramParser();
+
+       ProgramSyntax::Module &parse(const std::string &, const std::string &, unsigned = 0);
+       ProgramSyntax::Module &parse(IO::Base &, const std::string &, unsigned = 0);
+
+private:
+       void parse_source();
+
+       std::string format_error(const std::string &);
+       std::string format_syntax_error(const std::string &);
+
+       const std::string &peek_token(unsigned = 0);
+       const std::string &parse_token();
+       std::string parse_token_();
+       std::string parse_identifier();
+       std::string parse_number();
+       std::string parse_other();
+       void skip_comment_and_whitespace();
+       void expect(const std::string &);
+       std::string expect_type();
+       std::string expect_identifier();
+       bool check(const std::string &);
+
+       static bool is_interface_qualifier(const std::string &);
+       static bool is_sampling_qualifier(const std::string &);
+       static bool is_interpolation_qualifier(const std::string &);
+       static bool is_precision_qualifier(const std::string &);
+       static bool is_qualifier(const std::string &);
+       static bool is_builtin_type(const std::string &);
+       bool is_type(const std::string &);
+       bool is_identifier(const std::string &);
+
+       void preprocess();
+       void preprocess_version();
+       void preprocess_pragma();
+       void preprocess_pragma_msp();
+       void preprocess_stage();
+
+       RefPtr<ProgramSyntax::Statement> parse_global_declaration();
+       RefPtr<ProgramSyntax::Statement> parse_statement();
+       RefPtr<ProgramSyntax::Import> parse_import();
+       RefPtr<ProgramSyntax::Precision> parse_precision();
+       RefPtr<ProgramSyntax::Layout> parse_layout();
+       void parse_block(ProgramSyntax::Block &, bool);
+       RefPtr<ProgramSyntax::Expression> parse_expression(unsigned = 0);
+       RefPtr<ProgramSyntax::BinaryExpression> parse_binary(const RefPtr<ProgramSyntax::Expression> &, const Operator *);
+       RefPtr<ProgramSyntax::FunctionCall> parse_function_call(const ProgramSyntax::VariableReference &);
+       RefPtr<ProgramSyntax::StructDeclaration> parse_struct_declaration();
+       RefPtr<ProgramSyntax::VariableDeclaration> parse_variable_declaration();
+       RefPtr<ProgramSyntax::FunctionDeclaration> parse_function_declaration();
+       RefPtr<ProgramSyntax::InterfaceBlock> parse_interface_block();
+       RefPtr<ProgramSyntax::Conditional> parse_conditional();
+       RefPtr<ProgramSyntax::Iteration> parse_for();
+       RefPtr<ProgramSyntax::Iteration> parse_while();
+       RefPtr<ProgramSyntax::Passthrough> parse_passthrough();
+       RefPtr<ProgramSyntax::Return> parse_return();
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/glsl/programsyntax.cpp b/source/glsl/programsyntax.cpp
new file mode 100644 (file)
index 0000000..6dea029
--- /dev/null
@@ -0,0 +1,333 @@
+#include "programsyntax.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+namespace ProgramSyntax {
+
+template<typename C>
+NodeContainer<C>::NodeContainer(const NodeContainer &c):
+       C(c)
+{
+       for(typename C::iterator i=this->begin(); i!=this->end(); ++i)
+               *i = (*i)->clone();
+}
+
+
+Statement::Statement():
+       source(0),
+       line(1)
+{ }
+
+
+Block::Block():
+       use_braces(false)
+{ }
+
+void Block::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+void Literal::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+void ParenthesizedExpression::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+VariableReference::VariableReference():
+       declaration(0)
+{ }
+
+void VariableReference::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+void MemberAccess::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+UnaryExpression::UnaryExpression():
+       prefix(true)
+{ }
+
+void UnaryExpression::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+void BinaryExpression::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+Assignment::Assignment():
+       self_referencing(false),
+       target_declaration(0)
+{ }
+
+void Assignment::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+FunctionCall::FunctionCall():
+       declaration(0),
+       constructor(false)
+{ }
+
+void FunctionCall::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+void ExpressionStatement::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+void Import::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+void Precision::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+void Layout::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+void InterfaceLayout::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+StructDeclaration::StructDeclaration()
+{
+       members.use_braces = true;
+}
+
+void StructDeclaration::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+VariableDeclaration::VariableDeclaration():
+       constant(false),
+       type_declaration(0),
+       array(false),
+       linked_declaration(0)
+{ }
+
+void VariableDeclaration::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+InterfaceBlock::InterfaceBlock():
+       array(false)
+{
+       members.use_braces = true;
+}
+
+void InterfaceBlock::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+FunctionDeclaration::FunctionDeclaration():
+       definition(0)
+{ }
+
+FunctionDeclaration::FunctionDeclaration(const FunctionDeclaration &other):
+       return_type(other.return_type),
+       name(other.name),
+       parameters(other.parameters),
+       definition(other.definition==&other ? this : other.definition),
+       body(other.body)
+{ }
+
+void FunctionDeclaration::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+void Conditional::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+void Iteration::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+void Passthrough::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+void Return::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+void Jump::visit(NodeVisitor &visitor)
+{
+       visitor.visit(*this);
+}
+
+
+void NodeVisitor::visit(Assignment &assign)
+{
+       visit(static_cast<BinaryExpression &>(assign));
+}
+
+
+void TraversingVisitor::visit(Block &block)
+{
+       for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
+               (*i)->visit(*this);
+}
+
+void TraversingVisitor::visit(ParenthesizedExpression &parexpr)
+{
+       parexpr.expression->visit(*this);
+}
+
+void TraversingVisitor::visit(MemberAccess &memacc)
+{
+       memacc.left->visit(*this);
+}
+
+void TraversingVisitor::visit(UnaryExpression &unary)
+{
+       unary.expression->visit(*this);
+}
+
+void TraversingVisitor::visit(BinaryExpression &binary)
+{
+       binary.left->visit(*this);
+       binary.right->visit(*this);
+}
+
+void TraversingVisitor::visit(FunctionCall &call)
+{
+       for(NodeArray<Expression>::iterator i=call.arguments.begin(); i!=call.arguments.end(); ++i)
+               (*i)->visit(*this);
+}
+
+void TraversingVisitor::visit(ExpressionStatement &expr)
+{
+       expr.expression->visit(*this);
+}
+
+void TraversingVisitor::visit(InterfaceLayout &layout)
+{
+       layout.layout.visit(*this);
+}
+
+void TraversingVisitor::visit(StructDeclaration &strct)
+{
+       strct.members.visit(*this);
+}
+
+void TraversingVisitor::visit(VariableDeclaration &var)
+{
+       if(var.layout)
+               var.layout->visit(*this);
+       if(var.init_expression)
+               var.init_expression->visit(*this);
+       if(var.array_size)
+               var.array_size->visit(*this);
+}
+
+void TraversingVisitor::visit(InterfaceBlock &iface)
+{
+       iface.members.visit(*this);
+}
+
+void TraversingVisitor::visit(FunctionDeclaration &func)
+{
+       for(NodeArray<VariableDeclaration>::iterator i=func.parameters.begin(); i!=func.parameters.end(); ++i)
+               (*i)->visit(*this);
+       func.body.visit(*this);
+}
+
+void TraversingVisitor::visit(Conditional &cond)
+{
+       cond.condition->visit(*this);
+       cond.body.visit(*this);
+       cond.else_body.visit(*this);
+}
+
+void TraversingVisitor::visit(Iteration &iter)
+{
+       if(iter.init_statement)
+               iter.init_statement->visit(*this);
+       if(iter.condition)
+               iter.condition->visit(*this);
+       if(iter.loop_expression)
+               iter.loop_expression->visit(*this);
+       iter.body.visit(*this);
+}
+
+void TraversingVisitor::visit(Passthrough &pass)
+{
+       if(pass.subscript)
+               pass.subscript->visit(*this);
+}
+
+void TraversingVisitor::visit(Return &ret)
+{
+       if(ret.expression)
+               ret.expression->visit(*this);
+}
+
+
+Stage::Stage(StageType t):
+       type(t),
+       previous(0)
+{ }
+
+
+Module::Module():
+       shared(SHARED)
+{ }
+
+} // namespace ProgramSyntax
+} // namespace GL
+} // namespace Msp
diff --git a/source/glsl/programsyntax.h b/source/glsl/programsyntax.h
new file mode 100644 (file)
index 0000000..e7b5111
--- /dev/null
@@ -0,0 +1,425 @@
+#ifndef MSP_GL_PROGRAMSYNTAX_H_
+#define MSP_GL_PROGRAMSYNTAX_H_
+
+#include <list>
+#include <map>
+#include <string>
+#include <vector>
+#include <msp/core/refptr.h>
+#include "extension.h"
+#include "uniform.h"
+
+#pragma push_macro("interface")
+#undef interface
+
+namespace Msp {
+namespace GL {
+namespace ProgramSyntax {
+
+struct NodeVisitor;
+
+struct Node
+{
+protected:
+       Node() { }
+       Node(const Node &) { }
+private:
+       Node &operator=(const Node &);
+public:
+       virtual ~Node() { }
+
+       virtual Node *clone() const = 0;
+       virtual void visit(NodeVisitor &) = 0;
+};
+
+template<typename T>
+class NodePtr: public RefPtr<T>
+{
+public:
+       NodePtr() { }
+       NodePtr(T *p): RefPtr<T>(p) { }
+       NodePtr(const NodePtr &p): RefPtr<T>(p ? p->clone() : 0) { }
+       NodePtr &operator=(const NodePtr &p) { RefPtr<T>::operator=(p); return *this; }
+
+       template<typename U>
+       NodePtr(const RefPtr<U> &p): RefPtr<T>(p) { }
+
+       template<typename U>
+       NodePtr(const NodePtr<U> &p): RefPtr<T>(p ? p->clone() : 0) { }
+};
+
+template<typename C>
+class NodeContainer: public C
+{
+public:
+       NodeContainer() { }
+       NodeContainer(const NodeContainer &);
+};
+
+template<typename T>
+class NodeList: public NodeContainer<std::list<RefPtr<T> > >
+{ };
+
+template<typename T>
+class NodeArray: public NodeContainer<std::vector<RefPtr<T> > >
+{ };
+
+struct StructDeclaration;
+struct VariableDeclaration;
+struct FunctionDeclaration;
+
+struct Statement: Node
+{
+       unsigned source;
+       unsigned line;
+
+       Statement();
+
+       virtual Statement *clone() const = 0;
+};
+
+struct Block: Node
+{
+       NodeList<Statement> body;
+       bool use_braces;
+       std::map<std::string, StructDeclaration *> types;
+       std::map<std::string, VariableDeclaration *> variables;
+
+       Block();
+
+       virtual Block *clone() const { return new Block(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct Expression: Node
+{
+       virtual Expression *clone() const = 0;
+};
+
+struct Literal: Expression
+{
+       std::string token;
+
+       virtual Literal *clone() const { return new Literal(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct ParenthesizedExpression: Expression
+{
+       NodePtr<Expression> expression;
+
+       virtual ParenthesizedExpression *clone() const { return new ParenthesizedExpression(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct VariableReference: Expression
+{
+       std::string name;
+       VariableDeclaration *declaration;
+
+       VariableReference();
+
+       virtual VariableReference *clone() const { return new VariableReference(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct MemberAccess: Expression
+{
+       NodePtr<Expression> left;
+       std::string member;
+       VariableDeclaration *declaration;
+
+       virtual MemberAccess *clone() const { return new MemberAccess(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct UnaryExpression: Expression
+{
+       std::string oper;
+       NodePtr<Expression> expression;
+       bool prefix;
+
+       UnaryExpression();
+
+       virtual UnaryExpression *clone() const { return new UnaryExpression(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct BinaryExpression: Expression
+{
+       NodePtr<Expression> left;
+       std::string oper;
+       NodePtr<Expression> right;
+       std::string after;
+
+       virtual BinaryExpression *clone() const { return new BinaryExpression(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct Assignment: BinaryExpression
+{
+       bool self_referencing;
+       VariableDeclaration *target_declaration;
+
+       Assignment();
+
+       virtual Assignment *clone() const { return new Assignment(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct FunctionCall: Expression
+{
+       std::string name;
+       FunctionDeclaration *declaration;
+       bool constructor;
+       NodeArray<Expression> arguments;
+
+       FunctionCall();
+
+       virtual FunctionCall *clone() const { return new FunctionCall(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct ExpressionStatement: Statement
+{
+       NodePtr<Expression> expression;
+
+       virtual ExpressionStatement *clone() const { return new ExpressionStatement(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct Import: Statement
+{
+       std::string module;
+
+       virtual Import *clone() const { return new Import(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct Precision: Statement
+{
+       std::string precision;
+       std::string type;
+
+       virtual Precision *clone() const { return new Precision(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct Layout: Node
+{
+       struct Qualifier
+       {
+               // TODO the standard calls this name, not identifier
+               std::string identifier;
+               std::string value;
+       };
+
+       std::vector<Qualifier> qualifiers;
+
+       virtual Layout *clone() const { return new Layout(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct InterfaceLayout: Statement
+{
+       std::string interface;
+       Layout layout;
+
+       virtual InterfaceLayout *clone() const { return new InterfaceLayout(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct StructDeclaration: Statement
+{
+       std::string name;
+       Block members;
+
+       StructDeclaration();
+
+       virtual StructDeclaration *clone() const { return new StructDeclaration(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct VariableDeclaration: Statement
+{
+       bool constant;
+       std::string sampling;
+       std::string interpolation;
+       std::string interface;
+       std::string precision;
+       std::string type;
+       StructDeclaration *type_declaration;
+       std::string name;
+       bool array;
+       NodePtr<Expression> array_size;
+       NodePtr<Expression> init_expression;
+       VariableDeclaration *linked_declaration;
+       NodePtr<Layout> layout;
+
+       VariableDeclaration();
+
+       virtual VariableDeclaration *clone() const { return new VariableDeclaration(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct InterfaceBlock: Statement
+{
+       std::string interface;
+       std::string name;
+       Block members;
+       std::string instance_name;
+       bool array;
+
+       InterfaceBlock();
+
+       virtual InterfaceBlock *clone() const { return new InterfaceBlock(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct FunctionDeclaration: Statement
+{
+       std::string return_type;
+       std::string name;
+       NodeArray<VariableDeclaration> parameters;
+       FunctionDeclaration *definition;
+       Block body;
+
+       FunctionDeclaration();
+       FunctionDeclaration(const FunctionDeclaration &);
+
+       virtual FunctionDeclaration *clone() const { return new FunctionDeclaration(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct Conditional: Statement
+{
+       NodePtr<Expression> condition;
+       Block body;
+       Block else_body;
+
+       virtual Conditional *clone() const { return new Conditional(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct Iteration: Statement
+{
+       NodePtr<Node> init_statement;
+       NodePtr<Expression> condition;
+       NodePtr<Expression> loop_expression;
+       Block body;
+
+       virtual Iteration *clone() const { return new Iteration(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct Passthrough: Statement
+{
+       NodePtr<Expression> subscript;
+
+       virtual Passthrough *clone() const { return new Passthrough(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct Return: Statement
+{
+       NodePtr<Expression> expression;
+
+       virtual Return *clone() const { return new Return(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct Jump: Statement
+{
+       std::string keyword;
+
+       virtual Jump *clone() const { return new Jump(*this); }
+       virtual void visit(NodeVisitor &);
+};
+
+struct NodeVisitor
+{
+       virtual ~NodeVisitor() { }
+
+       virtual void visit(Block &) { }
+       virtual void visit(Literal &) { }
+       virtual void visit(ParenthesizedExpression &) { }
+       virtual void visit(VariableReference &) { }
+       virtual void visit(MemberAccess &) { }
+       virtual void visit(UnaryExpression &) { }
+       virtual void visit(BinaryExpression &) { }
+       virtual void visit(Assignment &);
+       virtual void visit(FunctionCall &) { }
+       virtual void visit(ExpressionStatement &) { }
+       virtual void visit(Import &) { }
+       virtual void visit(Precision &) { }
+       virtual void visit(Layout &) { }
+       virtual void visit(InterfaceLayout &) { }
+       virtual void visit(StructDeclaration &) { }
+       virtual void visit(VariableDeclaration &) { }
+       virtual void visit(InterfaceBlock &) { }
+       virtual void visit(FunctionDeclaration &) { }
+       virtual void visit(Conditional &) { }
+       virtual void visit(Iteration &) { }
+       virtual void visit(Passthrough &) { }
+       virtual void visit(Return &) { }
+       virtual void visit(Jump &) { }
+};
+
+struct TraversingVisitor: NodeVisitor
+{
+       using NodeVisitor::visit;
+       virtual void visit(Block &);
+       virtual void visit(ParenthesizedExpression &);
+       virtual void visit(MemberAccess &);
+       virtual void visit(UnaryExpression &);
+       virtual void visit(BinaryExpression &);
+       virtual void visit(FunctionCall &);
+       virtual void visit(ExpressionStatement &);
+       virtual void visit(InterfaceLayout &);
+       virtual void visit(StructDeclaration &);
+       virtual void visit(VariableDeclaration &);
+       virtual void visit(InterfaceBlock &);
+       virtual void visit(FunctionDeclaration &);
+       virtual void visit(Conditional &);
+       virtual void visit(Iteration &);
+       virtual void visit(Passthrough &);
+       virtual void visit(Return &);
+};
+
+enum StageType
+{
+       SHARED,
+       VERTEX,
+       GEOMETRY,
+       FRAGMENT
+};
+
+struct Stage
+{
+       StageType type;
+       Stage *previous;
+       ProgramSyntax::Block content;
+       std::map<std::string, VariableDeclaration *> in_variables;
+       std::map<std::string, VariableDeclaration *> out_variables;
+       std::map<std::string, unsigned> locations;
+       Version required_version;
+       std::vector<const Extension *> required_extensions;
+
+       Stage(StageType);
+};
+
+struct Module
+{
+       Stage shared;
+       std::list<Stage> stages;
+
+       Module();
+};
+
+} // namespace ProgramSyntax
+} // namespace GL
+} // namespace Msp
+
+#pragma pop_macro("interface")
+
+#endif
diff --git a/source/grid.cpp b/source/grid.cpp
deleted file mode 100644 (file)
index 3f4d5e2..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-#include <cmath>
-#include "grid.h"
-#include "primitivebuilder.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-GridBuilder::GridBuilder(float w, float h, unsigned u, unsigned v):
-       origin(-w/2, -h/2, 0),
-       side1(w, 0, 0),
-       side2(0, h, 0),
-       norm(0, 0, 1),
-       binorm(0, 1, 0),
-       u_div(u),
-       v_div(v)
-{
-       init(false);
-}
-
-GridBuilder::GridBuilder(const Vector3 &o, const Vector3 &s, unsigned u, unsigned v):
-       origin(o),
-       u_div(u),
-       v_div(v)
-{
-       if(abs(s.z)<abs(s.x) && abs(s.z)<abs(s.y))
-       {
-               side1 = Vector3(s.x, 0, 0);
-               side2 = Vector3(0, s.y, 0);
-       }
-       else if(abs(s.y)<abs(s.x))
-       {
-               side1 = Vector3(0, 0, s.z);
-               side2 = Vector3(s.x, 0, 0);
-       }
-       else
-       {
-               side1 = Vector3(0, s.y, 0);
-               side2 = Vector3(0, 0, s.z);
-       }
-       init(true);
-}
-
-GridBuilder::GridBuilder(const Vector3 &o, const Vector3 &s1, const Vector3 &s2, unsigned u, unsigned v):
-       origin(o),
-       side1(s1),
-       side2(s2),
-       u_div(u),
-       v_div(v)
-{
-       init(true);
-}
-
-void GridBuilder::init(bool compute_normals)
-{
-       if(u_div<1)
-               u_div = 1;
-       if(v_div<1)
-               v_div = 1;
-
-       if(compute_normals)
-       {
-               norm = normalize(cross(side1, side2));
-               binorm = normalize(cross(norm, side1));
-       }
-}
-
-void GridBuilder::build(PrimitiveBuilder &builder) const
-{
-       float l1_sq = dot(side1, side1);
-       float l2 = dot(side2, binorm);
-       float u_scale = 1/l1_sq;
-       float v_scale = 1/l2;
-       adjust_texture_scale(u_scale, v_scale, sqrt(l1_sq), l2);
-
-       builder.normal(norm.x, norm.y, norm.z);
-       if(generate_tbn)
-       {
-               builder.tangent(side1);
-               builder.binormal(binorm);
-       }
-
-       for(unsigned j=0; j<=v_div; ++j)
-       {
-               Vector3 row = side2*(j*1.0f/v_div);
-               float v = dot(row, binorm)*v_scale;
-               for(unsigned i=0; i<=u_div; ++i)
-               {
-                       Vector3 p = row+side1*(i*1.0f/u_div);
-                       float u = dot(p, side1)*u_scale;
-                       builder.texcoord(u, v);
-                       builder.vertex(origin+p);
-               }
-       }
-
-       for(unsigned j=0; j<v_div; ++j)
-       {
-               builder.begin(TRIANGLE_STRIP);
-               for(unsigned i=0; i<=u_div; ++i)
-               {
-                       builder.element((j+1)*(u_div+1)+i);
-                       builder.element(j*(u_div+1)+i);
-               }
-               builder.end();
-       }
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/grid.h b/source/grid.h
deleted file mode 100644 (file)
index ed22ce7..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef MSP_GL_GRID_H_
-#define MSP_GL_GRID_H_
-
-#include "geometrybuilder.h"
-#include "vector.h"
-
-namespace Msp {
-namespace GL {
-
-class GridBuilder: public GeometryBuilder
-{
-private:
-       Vector3 origin;
-       Vector3 side1;
-       Vector3 side2;
-       Vector3 norm;
-       Vector3 binorm;
-       unsigned u_div;
-       unsigned v_div;
-
-public:
-       GridBuilder(float, float, unsigned = 4, unsigned = 4);
-       GridBuilder(const Vector3 &, const Vector3 &, unsigned = 4, unsigned = 4);
-       GridBuilder(const Vector3 &, const Vector3 &, const Vector3 &, unsigned = 4, unsigned = 4);
-private:
-       void init(bool);
-
-public:
-       using GeometryBuilder::build;
-       virtual void build(PrimitiveBuilder &) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/instancearray.cpp b/source/instancearray.cpp
deleted file mode 100644 (file)
index abd9fcd..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-#include <msp/core/algorithm.h>
-#include <msp/core/maputils.h>
-#include <msp/gl/extensions/arb_draw_instanced.h>
-#include <msp/gl/extensions/arb_instanced_arrays.h>
-#include <msp/gl/extensions/arb_vertex_array_object.h>
-#include <msp/gl/extensions/arb_vertex_shader.h>
-#include "buffer.h"
-#include "camera.h"
-#include "instancearray.h"
-#include "mesh.h"
-#include "object.h"
-#include "objectinstance.h"
-#include "renderer.h"
-#include "technique.h"
-#include "vertexsetup.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-InstanceArray::InstanceArray(const Object &o):
-       object(o),
-       instance_data(0),
-       instance_buffer(0),
-       vtx_setup(0),
-       matrix_location(-1),
-       matrix_offset(0)
-{
-       const Technique *tech = object.get_technique();
-       const Technique::PassMap &passes = tech->get_passes();
-       for(Technique::PassMap::const_iterator i=passes.begin(); i!=passes.end(); ++i)
-       {
-               const Program *shprog = i->second.get_shader_program();
-               if(!shprog)
-                       throw invalid_argument("InstanceArray::InstanceArray");
-
-               int loc = shprog->get_attribute_location("instance_transform");
-               if(matrix_location<0)
-                       matrix_location = loc;
-               else if(loc!=matrix_location)
-                       throw invalid_argument("InstanceArray::InstanceArray");
-       }
-
-       if(ARB_vertex_array_object && ARB_instanced_arrays && ARB_draw_instanced)
-       {
-               instance_data = new VertexArray((ATTRIB4,matrix_location, ATTRIB4,matrix_location+1, ATTRIB4,matrix_location+2));
-               const VertexFormat &fmt = instance_data->get_format();
-               matrix_offset = fmt.offset(make_indexed_component(ATTRIB4, matrix_location));
-
-               instance_buffer = new Buffer(ARRAY_BUFFER);
-               instance_data->use_buffer(instance_buffer);
-
-               vtx_setup = new VertexSetup;
-               vtx_setup->set_vertex_array(object.get_mesh()->get_vertices());
-               vtx_setup->set_index_buffer(*object.get_mesh()->get_index_buffer());
-               vtx_setup->set_instance_array(instance_data);
-       }
-       else
-               static Require req(ARB_vertex_shader);
-}
-
-InstanceArray::~InstanceArray()
-{
-       for(std::vector<ObjectInstance *>::iterator i=instances.begin(); i!=instances.end(); ++i)
-               delete *i;
-       delete vtx_setup;
-       delete instance_data;
-       delete instance_buffer;
-}
-
-void InstanceArray::append(ObjectInstance *inst)
-{
-       instances.push_back(inst);
-       if(instance_data)
-       {
-               if(instance_data->size()<instances.size())
-               {
-                       instance_data->append();
-                       unsigned req_size = instance_data->get_required_buffer_size();
-                       if(instance_buffer->get_size()>0 && instance_buffer->get_size()<req_size)
-                       {
-                               delete instance_buffer;
-                               instance_buffer = new Buffer(ARRAY_BUFFER);
-                               instance_data->use_buffer(instance_buffer);
-                       }
-               }
-               update_instance_matrix(instances.size()-1);
-       }
-}
-
-void InstanceArray::remove(ObjectInstance &inst)
-{
-       vector<ObjectInstance *>::iterator i = find(instances, &inst);
-       if(i==instances.end())
-               throw key_error(&inst);
-
-       delete *i;
-       *i = instances.back();
-       instances.pop_back();
-}
-
-void InstanceArray::update_instance_matrix(unsigned index)
-{
-       if(!instance_data)
-               return;
-
-       const Matrix &m = *instances[index]->get_matrix();
-
-       float *d = instance_data->modify(instances.size()-1);
-       for(unsigned i=0; i<12; ++i)
-               d[matrix_offset+i] = m(i/4, i%4);
-}
-
-void InstanceArray::render(Renderer &renderer, const Tag &tag) const
-{
-       if(instances.empty())
-               return;
-
-       if(instance_data)
-       {
-               const Technique *tech = object.get_technique();
-               if(!tech)
-                       throw logic_error("no technique");
-               const RenderPass *pass = tech->find_pass(tag);
-               if(!pass)
-                       return;
-
-               const Mesh *mesh = object.get_mesh();
-               mesh->get_vertices().refresh();
-               if(instance_buffer->get_size()==0)
-                       instance_buffer->storage(instance_data->get_required_buffer_size());
-               instance_data->refresh();
-
-               Renderer::Push push(renderer);
-               pass->apply(renderer);
-               mesh->draw_instanced(renderer, *vtx_setup, instances.size());
-       }
-       else
-       {
-               for(vector<ObjectInstance *>::const_iterator i=instances.begin(); i!=instances.end(); ++i)
-               {
-                       const Matrix &m = *(*i)->get_matrix();
-                       for(unsigned j=0; j<3; ++j)
-                               glVertexAttrib4f(matrix_location+j, m(j, 0), m(j, 1), m(j, 2), m(j, 3));
-                       (*i)->render(renderer, tag);
-               }
-       }
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/instancearray.h b/source/instancearray.h
deleted file mode 100644 (file)
index 627051f..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-#ifndef MSP_GL_INSTANCEARRAY_H_
-#define MSP_GL_INSTANCEARRAY_H_
-
-#include <vector>
-#include "programdata.h"
-#include "renderable.h"
-
-namespace Msp {
-namespace GL {
-
-class Buffer;
-class Object;
-class ObjectInstance;
-class VertexArray;
-class VertexSetup;
-
-/**
-Renders multiple instances of an Object in an efficient manner.  If instanced
-rendering is supported, only one draw call per Batch needs to be issued.
-
-Changing the Mesh of the Object while an InstanceArray exists is not supported.
-*/
-class InstanceArray: public Renderable
-{
-public:
-       template<typename T>
-       class Instance: public T
-       {
-       private:
-               InstanceArray &array;
-               unsigned index;
-
-       public:
-               Instance(const Object &o, InstanceArray &a, unsigned i): T(o), array(a), index(i) { }
-
-               virtual void set_matrix(const Matrix &);
-       };
-
-private:
-       const Object &object;
-       std::vector<ObjectInstance *> instances;
-       VertexArray *instance_data;
-       Buffer *instance_buffer;
-       VertexSetup *vtx_setup;
-       int matrix_location;
-       unsigned matrix_offset;
-
-public:
-       InstanceArray(const Object &);
-       ~InstanceArray();
-
-       void set_matrix_attribute(const std::string &);
-
-       template<typename T = ObjectInstance>
-       T &append();
-private:
-       void append(ObjectInstance *);
-       void update_instance_matrix(unsigned);
-public:
-       void remove(ObjectInstance &);
-
-       virtual void render(Renderer &, const Tag &) const;
-};
-
-template<typename T>
-T &InstanceArray::append()
-{
-       Instance<T> *inst = new Instance<T>(object, *this, instances.size());
-       append(inst);
-       return *inst;
-}
-
-template<typename T>
-void InstanceArray::Instance<T>::set_matrix(const Matrix &m)
-{
-       T::set_matrix(m);
-       array.update_instance_matrix(index);
-}
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/instancescene.cpp b/source/instancescene.cpp
deleted file mode 100644 (file)
index b533bae..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-#include "object.h"
-#include "objectinstance.h"
-#include "instancescene.h"
-#include "renderer.h"
-
-namespace Msp {
-namespace GL {
-
-void InstanceScene::add(Renderable &r)
-{
-       renderables[r.get_instance_key()].insert(&r);
-}
-
-void InstanceScene::remove(Renderable &r)
-{
-       InstanceMap::iterator i = renderables.find(r.get_instance_key());
-       if(i!=renderables.end())
-       {
-               i->second.erase(&r);
-               if(i->second.empty())
-                       renderables.erase(i);
-       }
-}
-
-void InstanceScene::setup_frame(Renderer &renderer)
-{
-       for(InstanceMap::const_iterator i=renderables.begin(); i!=renderables.end(); ++i)
-               for(RenderableSet::const_iterator j=i->second.begin(); j!=i->second.end(); ++j)
-                       (*j)->setup_frame(renderer);
-}
-
-void InstanceScene::finish_frame()
-{
-       for(InstanceMap::const_iterator i=renderables.begin(); i!=renderables.end(); ++i)
-               for(RenderableSet::const_iterator j=i->second.begin(); j!=i->second.end(); ++j)
-                       (*j)->finish_frame();
-}
-
-void InstanceScene::render(Renderer &renderer, const Tag &tag) const
-{
-       if(setup_frustum(renderer))
-       {
-               for(InstanceMap::const_iterator i=renderables.begin(); i!=renderables.end(); ++i)
-                       for(RenderableSet::const_iterator j=i->second.begin(); j!=i->second.end(); ++j)
-                               if(!frustum_cull(**j))
-                                       renderer.render(**j, tag);
-       }
-       else
-       {
-               for(InstanceMap::const_iterator i=renderables.begin(); i!=renderables.end(); ++i)
-                       for(RenderableSet::const_iterator j=i->second.begin(); j!=i->second.end(); ++j)
-                               renderer.render(**j, tag);
-       }
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/instancescene.h b/source/instancescene.h
deleted file mode 100644 (file)
index a5ac2cd..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifndef MSP_GL_INSTANCESCENE_H_
-#define MSP_GL_INSTANCESCENE_H_
-
-#include <map>
-#include <set>
-#include <msp/core/inttypes.h>
-#include "scene.h"
-
-namespace Msp {
-namespace GL {
-
-/**
-A Scene optimized for rendering instanced Renderables, such as ObjectInstances.
-All Renderables with the same instance key are rendered consecutively; within
-the same key rendering order is unspecified.
-*/
-class InstanceScene: public Scene
-{
-private:
-       typedef std::set<Renderable *> RenderableSet;
-       typedef std::map<IntPtr, RenderableSet> InstanceMap;
-
-       InstanceMap renderables;
-
-public:
-       virtual void add(Renderable &);
-       virtual void remove(Renderable &);
-
-       virtual void setup_frame(Renderer &);
-       virtual void finish_frame();
-
-       virtual void render(Renderer &, const Tag &tag = Tag()) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/keyframe.cpp b/source/keyframe.cpp
deleted file mode 100644 (file)
index f610de2..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-#include <msp/datafile/collection.h>
-#include "keyframe.h"
-#include "pose.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-// Avoid synthesizing RefPtr c'tor and d'tor in files including keyframe.h
-KeyFrame::KeyFrame()
-{ }
-
-KeyFrame::~KeyFrame()
-{ }
-
-void KeyFrame::set_transform(const Transform &t)
-{
-       transform = t;
-}
-
-void KeyFrame::set_matrix(const Matrix &m)
-{
-       transform = Transform::from_matrix(m);
-}
-
-void KeyFrame::set_uniform(const string &n, const AnimatedUniform &u)
-{
-       uniforms.erase(n);
-       uniforms.insert(UniformMap::value_type(n, u));
-}
-
-void KeyFrame::set_pose(const Pose &p)
-{
-       pose = &p;
-       pose.keep();
-}
-
-
-KeyFrame::AnimatedUniform::AnimatedUniform(unsigned s, float v0, float v1, float v2, float v3):
-       size(s)
-{
-       values[0] = v0;
-       values[1] = v1;
-       values[2] = v2;
-       values[3] = v3;
-}
-
-
-KeyFrame::Loader::Loader(KeyFrame &k):
-       DataFile::CollectionObjectLoader<KeyFrame>(k, 0)
-{
-       init();
-}
-
-KeyFrame::Loader::Loader(KeyFrame &k, Collection &c):
-       DataFile::CollectionObjectLoader<KeyFrame>(k, &c)
-{
-       init();
-}
-
-void KeyFrame::Loader::init()
-{
-       add("pose", &Loader::pose);
-       add("pose", &Loader::pose_inline);
-       add("transform", &Loader::transform);
-       add("uniforms", &Loader::uniforms);
-
-       // Deprecated; use the transform statement instead
-       add("position", &Loader::position);
-       add("rotation", &Loader::rotation);
-       add("scaling", &Loader::scaling_uniform);
-       add("scaling", &Loader::scaling);
-}
-
-void KeyFrame::Loader::pose(const string &n)
-{
-       obj.pose = &get_collection().get<Pose>(n);
-       obj.pose.keep();
-}
-
-void KeyFrame::Loader::pose_inline()
-{
-       RefPtr<Pose> p = new Pose;
-       load_sub(*p, get_collection());
-       obj.pose = p;
-}
-
-void KeyFrame::Loader::position(float x, float y, float z)
-{
-       obj.transform.set_position(Vector3(x, y, z));
-}
-
-void KeyFrame::Loader::rotation(float a, float x, float y, float z)
-{
-       obj.transform.set_rotation(Transform::Angle::from_degrees(a), Vector3(x, y, z));
-}
-
-void KeyFrame::Loader::scaling_uniform(float s)
-{
-       obj.transform.set_scale(s);
-}
-
-void KeyFrame::Loader::scaling(float x, float y, float z)
-{
-       obj.transform.set_scale(Vector3(x, y, z));
-}
-
-void KeyFrame::Loader::transform()
-{
-       load_sub(obj.transform);
-}
-
-void KeyFrame::Loader::uniforms()
-{
-       UniformsLoader ldr(obj);
-       load_sub_with(ldr);
-}
-
-
-KeyFrame::UniformsLoader::UniformsLoader(KeyFrame &k):
-       DataFile::ObjectLoader<KeyFrame>(k)
-{
-       add("uniform", &UniformsLoader::uniform1f);
-       add("uniform", &UniformsLoader::uniform2f);
-       add("uniform", &UniformsLoader::uniform3f);
-       add("uniform", &UniformsLoader::uniform4f);
-
-       // Deprecated
-       add("uniform1f", &UniformsLoader::uniform1f);
-       add("uniform2f", &UniformsLoader::uniform2f);
-       add("uniform3f", &UniformsLoader::uniform3f);
-       add("uniform4f", &UniformsLoader::uniform4f);
-}
-
-void KeyFrame::UniformsLoader::uniform1f(const string &n, float v)
-{
-       obj.uniforms.insert(UniformMap::value_type(n, AnimatedUniform(1, v)));
-}
-
-void KeyFrame::UniformsLoader::uniform2f(const string &n, float v0, float v1)
-{
-       obj.uniforms.insert(UniformMap::value_type(n, AnimatedUniform(2, v0, v1)));
-}
-
-void KeyFrame::UniformsLoader::uniform3f(const string &n, float v0, float v1, float v2)
-{
-       obj.uniforms.insert(UniformMap::value_type(n, AnimatedUniform(3, v0, v1, v2)));
-}
-
-void KeyFrame::UniformsLoader::uniform4f(const string &n, float v0, float v1, float v2, float v3)
-{
-       obj.uniforms.insert(UniformMap::value_type(n, AnimatedUniform(4, v0, v1, v2, v3)));
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/keyframe.h b/source/keyframe.h
deleted file mode 100644 (file)
index 5b748ad..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-#ifndef MSP_GL_KEYFRAME_H_
-#define MSP_GL_KEYFRAME_H_
-
-#include <msp/core/refptr.h>
-#include <msp/datafile/objectloader.h>
-#include "matrix.h"
-#include "transform.h"
-
-namespace Msp {
-namespace GL {
-
-class Pose;
-
-/**
-Keyframes are used to encapsulate object state for animation.
-*/
-class KeyFrame
-{
-public:
-       class Loader: public DataFile::CollectionObjectLoader<KeyFrame>
-       {
-       public:
-               Loader(KeyFrame &);
-               Loader(KeyFrame &, Collection &);
-       private:
-               void init();
-
-               void pose(const std::string &);
-               void pose_inline();
-               void position(float, float, float);
-               void rotation(float, float, float, float);
-               void scaling_uniform(float);
-               void scaling(float, float, float);
-               void transform();
-               void uniforms();
-       };
-
-       class UniformsLoader: public DataFile::ObjectLoader<KeyFrame>
-       {
-       public:
-               UniformsLoader(KeyFrame &);
-
-       private:
-               void uniform1f(const std::string &, float);
-               void uniform2f(const std::string &, float, float);
-               void uniform3f(const std::string &, float, float, float);
-               void uniform4f(const std::string &, float, float, float, float);
-       };
-
-       struct AnimatedUniform
-       {
-               unsigned size;
-               float values[4];
-
-               AnimatedUniform(unsigned, float, float = 0.0f, float = 0.0f, float = 0.0f);
-       };
-
-       typedef std::map<std::string, AnimatedUniform> UniformMap;
-
-private:
-       Transform transform;
-       UniformMap uniforms;
-       RefPtr<const Pose> pose;
-
-public:
-       KeyFrame();
-       ~KeyFrame();
-
-       void set_transform(const Transform &);
-       void set_matrix(const Matrix &);
-       void set_uniform(const std::string &, const AnimatedUniform &);
-       void set_pose(const Pose &);
-       const Transform &get_transform() const { return transform; }
-       Matrix get_matrix() const { return transform.to_matrix(); }
-       const UniformMap &get_uniforms() const { return uniforms; }
-       const Pose *get_pose() const { return pose.get(); }
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/light.cpp b/source/light.cpp
deleted file mode 100644 (file)
index e910411..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-#include <stdexcept>
-#include <msp/strings/format.h>
-#include "light.h"
-#include "matrix.h"
-#include "misc.h"
-#include "programdata.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Light::Light():
-       diffuse(1),
-       specular(1),
-       position(0, 0, 1, 0),
-       spot_dir(0, 0, -1),
-       spot_exp(0),
-       spot_cutoff(Geometry::Angle<float>::straight())
-{
-       attenuation[0] = 1;
-       attenuation[1] = 0;
-       attenuation[2] = 0;
-}
-
-void Light::update_matrix()
-{
-       Vector3 up_dir;
-       if(20*abs(direction.z)>abs(direction.x)+abs(direction.y))
-               up_dir.y = 1;
-       else
-               up_dir.z = 1;
-       Vector3 right_dir = normalize(cross(direction, up_dir));
-
-       Vector4 columns[4];
-       columns[0] = compose(right_dir, 0.0f);
-       columns[1] = compose(cross(right_dir, direction), 0.0f);
-       columns[2] = compose(-direction, 0.0f);
-       columns[3] = position;
-       matrix = Matrix::from_columns(columns);
-}
-
-void Light::set_diffuse(const Color &c)
-{
-       diffuse = c;
-}
-
-void Light::set_specular(const Color &c)
-{
-       specular = c;
-}
-
-void Light::set_matrix(const Matrix &m)
-{
-       Placeable::set_matrix(m);
-       position = matrix.column(3);
-       spot_dir = normalize(-matrix.column(2).slice<3>(0));
-       direction = (position.w ? spot_dir : normalize(-position.slice<3>(0)));
-       update_matrix();
-}
-
-void Light::set_position(const Vector4 &p)
-{
-       position = p;
-       if(!position.w)
-               direction = normalize(-position.slice<3>(0));
-       update_matrix();
-}
-
-void Light::set_spot_direction(const Vector3 &d)
-{
-       spot_dir = normalize(d);
-       if(position.w)
-               direction = spot_dir;
-       update_matrix();
-}
-
-void Light::set_spot_exponent(float e)
-{
-       if(e<0)
-               throw invalid_argument("Light::set_spot_exponent");
-
-       spot_exp = e;
-}
-
-void Light::set_spot_cutoff(const Geometry::Angle<float> &c)
-{
-       if(c<Geometry::Angle<float>::zero() || (c>Geometry::Angle<float>::right() && c!=Geometry::Angle<float>::straight()))
-               throw invalid_argument("Light::set_spot_cutoff");
-
-       spot_cutoff = c;
-}
-
-void Light::disable_spot_cutoff()
-{
-       set_spot_cutoff(Geometry::Angle<float>::straight());
-}
-
-void Light::set_attenuation(float c, float l, float q)
-{
-       attenuation[0] = c;
-       attenuation[1] = l;
-       attenuation[2] = q;
-}
-
-void Light::update_shader_data(ProgramData &shdata, const Matrix &view_matrix, unsigned i) const
-{
-       string base = format("light_sources[%d]", i);
-       shdata.uniform(base+".position", view_matrix*position);
-       shdata.uniform(base+".diffuse", diffuse);
-       shdata.uniform(base+".specular", specular);
-}
-
-
-Light::Loader::Loader(Light &l):
-       DataFile::ObjectLoader<Light>(l)
-{
-       add("attenuation", &Loader::attenuation);
-       add("diffuse", &Loader::diffuse);
-       add("position", &Loader::position);
-       add("specular", &Loader::specular);
-       add("spot_direction", &Loader::spot_direction);
-       add("spot_exponent", &Loader::spot_exponent);
-       add("spot_cutoff", &Loader::spot_cutoff);
-}
-
-void Light::Loader::attenuation(float c, float l, float q)
-{
-       obj.set_attenuation(c, l, q);
-}
-
-void Light::Loader::diffuse(float r, float g, float b)
-{
-       obj.set_diffuse(Color(r, g, b));
-}
-
-void Light::Loader::position(float x, float y, float z, float w)
-{
-       obj.set_position(Vector4(x, y, z, w));
-}
-
-void Light::Loader::specular(float r, float g, float b)
-{
-       obj.set_specular(Color(r, g, b));
-}
-
-void Light::Loader::spot_direction(float x, float y, float z)
-{
-       obj.set_spot_direction(Vector3(x, y, z));
-}
-
-void Light::Loader::spot_exponent(float e)
-{
-       obj.set_spot_exponent(e);
-}
-
-void Light::Loader::spot_cutoff(float c)
-{
-       obj.set_spot_cutoff(Geometry::Angle<float>::from_degrees(c));
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/light.h b/source/light.h
deleted file mode 100644 (file)
index e94f3c5..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-#ifndef MSP_GL_LIGHT_H_
-#define MSP_GL_LIGHT_H_
-
-#include <vector>
-#include <msp/datafile/objectloader.h>
-#include "color.h"
-#include "placeable.h"
-
-namespace Msp {
-namespace GL {
-
-class Matrix;
-class ProgramData;
-
-/**
-Stores properties of a single light source.  Lights can be directional, point
-lights or spotlights.  No explicit type parameter is provided; rather the
-other parameters determine what kind of light it is.  If the fourth component
-of position is zero, it's a directional light.  Otherwise, if the spot cutoff
-is not 180 degrees, it's a spotlight.  Otherwise it's an omnidirectional point
-light.
-
-Lights are usually grouped with a Lighting object, which can be used in a
-Pipeline::Pass.
-
-Lights do not cast shadows by themselves.  See ShadowMap for that.
-*/
-class Light: public Placeable
-{
-public:
-       class Loader: public DataFile::ObjectLoader<Light>
-       {
-       public:
-               Loader(Light &);
-
-       private:
-               void attenuation(float, float, float);
-               void diffuse(float, float, float);
-               void position(float, float, float, float);
-               void specular(float, float, float);
-               void spot_direction(float, float, float);
-               void spot_exponent(float);
-               void spot_cutoff(float);
-       };
-
-private:
-       Color diffuse;
-       Color specular;
-       Vector4 position;
-       Vector3 spot_dir;
-       Vector3 direction;
-       float spot_exp;
-       Geometry::Angle<float> spot_cutoff;
-       float attenuation[3];
-
-public:
-       Light();
-
-private:
-       void update_matrix();
-
-public:
-       /** Sets the diffuse (direction-independent) color of the Light.  Provided
-       to shaders with the name light_sources[i].diffuse. */
-       void set_diffuse(const Color &c);
-
-       /** Sets the specular (direction-dependent) color of the Light.  Provided to
-       shaders with the name light_sources[i].specular. */
-       void set_specular(const Color &c);
-
-       const Color &get_diffuse() const { return diffuse; }
-       const Color &get_specular() const { return specular; }
-
-       /** Sets the postion and orientation of the Light from a matrix.  Negative Z
-       axis is used as the spot direction, other axes are ignored. */
-       virtual void set_matrix(const Matrix &);
-
-       /** Sets the position of the Light.  For a directional light, set the xyz
-       components to a vector pointing towards the light and the w component to 0. */
-       void set_position(const Vector4 &);
-
-       const Vector4 &get_position() const { return position; }
-
-       /** Sets the direction of a spotlight.  Has no effect if spotlight cutoff is
-       not set. */
-       void set_spot_direction(const Vector3 &);
-
-       /** Sets the angular falloff exponent of the spotlight.  Must be >= 0. */
-       void set_spot_exponent(float);
-
-       /** Sets the cutoff angle of a spotlight.  Beyond this angle from its axis
-       the spotlight provides no illumination.  Must be between 0 and 90 degrees,
-       or exactly 180 degrees to indicate a non-spotlight. */
-       void set_spot_cutoff(const Geometry::Angle<float> &);
-
-       /** Disables spotlight, reverting to an omnidirectional point light.
-       Equivalent to setting the spot cutoff to 180 degrees. */
-       void disable_spot_cutoff();
-
-       const Vector3 &get_spot_direction() const { return spot_dir; }
-       float get_spot_exponent() const { return spot_exp; }
-       const Geometry::Angle<float> &get_spot_cutoff() const { return spot_cutoff; }
-       void set_attenuation(float, float, float);
-       const float *get_attenuation() const { return attenuation; }
-
-       /** Updates a ProgramData object with the uniforms for the Light.  A view
-       matrix and light source index must be passed in. */
-       void update_shader_data(ProgramData &, const Matrix &, unsigned) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/lighting.cpp b/source/lighting.cpp
deleted file mode 100644 (file)
index eeb4f30..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-#include <stdexcept>
-#include <cmath>
-#include "error.h"
-#include "light.h"
-#include "lighting.h"
-#include "matrix.h"
-#include "misc.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Lighting::Lighting():
-       ambient(0.2),
-       zenith_direction(0, 0, 1),
-       horizon_angle(Geometry::Angle<float>::zero()),
-       fog_color(0.0f, 0.0f, 0.0f, 0.0f),
-       fog_density(0.0f)
-{ }
-
-Lighting::~Lighting()
-{
-       for(vector<Light *>::iterator i=owned_data.begin(); i!=owned_data.end(); ++i)
-               delete *i;
-}
-
-void Lighting::set_ambient(const Color &a)
-{
-       ambient = a;
-}
-
-void Lighting::set_sky_color(const Color &s)
-{
-       sky_color = s;
-}
-
-void Lighting::set_zenith_direction(const Vector3 &d)
-{
-       zenith_direction = d;
-}
-
-void Lighting::set_horizon_angle(const Geometry::Angle<float> &a)
-{
-       horizon_angle = a;
-}
-
-void Lighting::set_fog_color(const Color &c)
-{
-       fog_color = c;
-}
-
-void Lighting::set_fog_density(float d)
-{
-       if(d<0)
-               throw invalid_argument("Lighting::set_fog_density");
-
-       fog_density = d;
-}
-
-void Lighting::set_fog_half_distance(float d)
-{
-       set_fog_density(-log(pow(0.5, 1.0/d)));
-}
-
-void Lighting::attach(unsigned i, const Light &l)
-{
-       if(i>=lights.size())
-               lights.resize(i+1);
-
-       lights[i] = &l;
-}
-
-void Lighting::detach(unsigned i)
-{
-       if(i>=lights.size())
-               return;
-
-       lights[i] = 0;
-}
-
-const Light *Lighting::get_attached_light(unsigned i) const
-{
-       return i<lights.size() ? lights[i] : 0;
-}
-
-void Lighting::update_shader_data(ProgramData &shdata, const Matrix &view_matrix) const
-{
-       shdata.uniform("ambient_color", ambient);
-       shdata.uniform("sky_color", sky_color);
-       shdata.uniform("eye_zenith_dir", view_matrix.block<3, 3>(0, 0)*zenith_direction);
-       shdata.uniform("horizon_limit", horizon_angle.radians());
-       shdata.uniform("fog_color", fog_color);
-       shdata.uniform("fog_density", fog_density);
-
-       // For backwards compatibility
-       shdata.uniform("eye_sky_dir", view_matrix.block<3, 3>(0, 0)*zenith_direction);
-
-       for(unsigned i=0; i<lights.size(); ++i)
-               if(lights[i])
-                       lights[i]->update_shader_data(shdata, view_matrix, i);
-}
-
-
-Lighting::Loader::Loader(Lighting &l):
-       DataFile::ObjectLoader<Lighting>(l)
-{
-       add("ambient", &Loader::ambient);
-       add("fog_color", &Loader::fog_color);
-       add("fog_density", &Loader::fog_density);
-       add("fog_half_distance", &Loader::fog_half_distance);
-       add("horizon_angle", &Loader::horizon_angle);
-       add("light", &Loader::light);
-       add("sky_color", &Loader::sky_color);
-       add("zenith_direction", &Loader::zenith_direction);
-}
-
-void Lighting::Loader::ambient(float r, float g, float b)
-{
-       obj.ambient = Color(r, g, b);
-}
-
-void Lighting::Loader::fog_color(float r, float g, float b)
-{
-       obj.set_fog_color(Color(r, g, b));
-}
-
-void Lighting::Loader::fog_density(float d)
-{
-       obj.set_fog_density(d);
-}
-
-void Lighting::Loader::fog_half_distance(float d)
-{
-       obj.set_fog_half_distance(d);
-}
-
-void Lighting::Loader::horizon_angle(float a)
-{
-       obj.set_horizon_angle(Geometry::Angle<float>::from_degrees(a));
-}
-
-void Lighting::Loader::light(unsigned i)
-{
-       RefPtr<Light> lgt = new Light;
-       load_sub(*lgt);
-       obj.attach(i, *lgt);
-       obj.owned_data.push_back(lgt.release());
-}
-
-void Lighting::Loader::sky_color(float r, float g, float b)
-{
-       obj.set_sky_color(Color(r, g, b));
-}
-
-void Lighting::Loader::zenith_direction(float x, float y, float z)
-{
-       obj.set_zenith_direction(Vector3(x, y, z));
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/lighting.h b/source/lighting.h
deleted file mode 100644 (file)
index ca214fc..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-#ifndef MSP_GL_LIGHTING_H_
-#define MSP_GL_LIGHTING_H_
-
-#include <vector>
-#include <msp/geometry/angle.h>
-#include "color.h"
-#include "gl.h"
-#include "programdata.h"
-
-namespace Msp {
-namespace GL {
-
-class Light;
-
-/**
-Encapsulates global lighting parameters and any number of individual light
-sources.
-*/
-class Lighting
-{
-public:
-       class Loader: public DataFile::ObjectLoader<Lighting>
-       {
-       public:
-               Loader(Lighting &);
-
-       private:
-               void ambient(float, float, float);
-               void fog_color(float, float, float);
-               void fog_density(float);
-               void fog_half_distance(float);
-               void horizon_angle(float);
-               void light(unsigned);
-               void sky_color(float, float, float);
-               void zenith_direction(float, float, float);
-       };
-
-private:
-       Color ambient;
-       Color sky_color;
-       Vector3 zenith_direction;
-       Geometry::Angle<float> horizon_angle;
-       Color fog_color;
-       float fog_density;
-       std::vector<const Light *> lights;
-       std::vector<Light *> owned_data;
-
-public:
-       Lighting();
-       ~Lighting();
-
-       /** Sets the ambient lighting color.  Affects all surfaces in the scene. */
-       void set_ambient(const Color &);
-
-       const Color &get_ambient() const { return ambient; }
-
-       /** Sets the color of the sky at zenith.  Has no effect without shaders. */
-       void set_sky_color(const Color &);
-
-       /** Sets the direction of the zenith.  Defaults to positive Z axis.  Has no
-       effect without shaders. */
-       void set_zenith_direction(const Vector3 &);
-
-       /** Sets the angle where skylight cuts off, counted from the true horizon.
-       Has no effect without shaders. */
-       void set_horizon_angle(const Geometry::Angle<float> &);
-
-       /** Sets the fog color, which is blended into distant surfaces. */
-       void set_fog_color(const Color &);
-
-       /** Sets the density of the fog.  Zero means no fog. */
-       void set_fog_density(float);
-
-       /** Sets the density of the fog so that the blending factor at the given
-       distance is 50%. */
-       void set_fog_half_distance(float);
-
-       /** Attaches a light source. */
-       void attach(unsigned, const Light &);
-
-       /** Detaches a light source. */
-       void detach(unsigned);
-
-       /** Returns an attached light.  If no light is attached at that index, null
-       is returned. */
-       const Light *get_attached_light(unsigned) const;
-
-       /** Updates a ProgramData object with the uniforms for the Lighting,
-       including all attached light sources.  A view matrix must be passed in. */
-       void update_shader_data(ProgramData &, const Matrix &) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/material.cpp b/source/material.cpp
deleted file mode 100644 (file)
index 810ecd1..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-#include <msp/core/hash.h>
-#include <msp/strings/format.h>
-#include "basicmaterial.h"
-#include "gl.h"
-#include "pbrmaterial.h"
-#include "resources.h"
-#include "texturing.h"
-#include "uniform.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Program *Material::create_compatible_shader() const
-{
-       return new Program(create_program_source());
-}
-
-const Program *Material::create_compatible_shader(DataFile::Collection &coll) const
-{
-       string source = create_program_source();
-       string name = format("_material_%016x.glsl", hash64(source));
-       Program *shprog = coll.find<Program>(name);
-       if(shprog)
-               return shprog;
-
-       shprog = new Program(create_program_source());
-       try
-       {
-               coll.add(name, shprog);
-       }
-       catch(...)
-       {
-               delete shprog;
-               throw;
-       }
-
-       return shprog;
-}
-
-void Material::attach_texture_to(const Texture *tex, Texturing &texturing, ProgramData &tex_shdata, const string &name) const
-{
-       if(!tex)
-               return;
-
-       int unit = -1;
-
-       if(const Uniform *uni = tex_shdata.find_uniform(name))
-               if(const Uniform1i *uni_int = dynamic_cast<const Uniform1i *>(uni))
-                       unit = uni_int->get();
-
-       if(unit<0)
-               unit = texturing.find_free_unit(name);
-       if(unit<0)
-               throw runtime_error("no free texunit");
-
-       texturing.attach(unit, *tex, sampler);
-       tex_shdata.uniform(name, unit);
-}
-
-Material::MaterialRegistry &Material::get_material_registry()
-{
-       static MaterialRegistry registry;
-       static bool initialized = false;
-       if(!initialized)
-       {
-               initialized = true;
-               registry.register_type<BasicMaterial>("basic");
-               registry.register_type<PbrMaterial>("pbr");
-       }
-       return registry;
-}
-
-
-Material::Loader::Loader(Material &m):
-       CollectionObjectLoader(m, 0)
-{ }
-
-Material::Loader::Loader(Material &m, Collection &c):
-       CollectionObjectLoader(m, &c)
-{ }
-
-void Material::Loader::init_actions()
-{
-       add("sampler", &Loader::sampler);
-}
-
-void Material::Loader::sampler(const std::string &name)
-{
-       obj.sampler = &get_collection().get<Sampler>(name);
-}
-
-
-DataFile::Loader::ActionMap Material::GenericLoader::shared_actions;
-
-Material::GenericLoader::GenericLoader(DataFile::Collection *c):
-       coll(c)
-{
-       set_actions(shared_actions);
-}
-
-void Material::GenericLoader::init_actions()
-{
-       get_material_registry().add_all(*this);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/material.h b/source/material.h
deleted file mode 100644 (file)
index 571f4cb..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-#ifndef MSP_GL_MATERIAL_H_
-#define MSP_GL_MATERIAL_H_
-
-#include <msp/datafile/collection.h>
-#include <msp/datafile/loadabletyperegistry.h>
-#include <msp/datafile/objectloader.h>
-#include "color.h"
-#include "programdata.h"
-#include "texture.h"
-
-namespace Msp {
-namespace GL {
-
-class Texturing;
-
-class Material
-{
-private:
-       class Loader: public DataFile::CollectionObjectLoader<Material>
-       {
-       protected:
-               Loader(Material &);
-               Loader(Material &, Collection &);
-
-               virtual void init_actions();
-
-       private:
-               void sampler(const std::string &);
-       };
-
-protected:
-       template<typename T>
-       struct Property
-       {
-               T value;
-               const Texture *texture;
-
-               Property(): value(T()), texture(0) { }
-       };
-
-       template<typename C>
-       class PropertyLoader: public DataFile::DerivedObjectLoader<Material, Loader>
-       {
-       protected:
-               PropertyLoader(C &m): DerivedObjectLoader<Material, Loader>(m) { }
-               PropertyLoader(C &m, Collection &c): DerivedObjectLoader<Material, Loader>(m, c) { }
-
-               void add_property(const std::string &, void (C::*)(float), void (C::*)(const Texture *));
-               void add_property(const std::string &, void (C::*)(const Color &), void (C::*)(const Texture *), bool);
-               void add_property(const std::string &, void (C::*)(const Texture *));
-
-               void property_value_scalar(void (C::*)(float), float);
-               void property_value_rgb(void (C::*)(const Color &), float, float, float);
-               void property_value_rgba(void (C::*)(const Color &), float, float, float, float);
-               void property_value_srgb(void (C::*)(const Color &), float, float, float);
-               void property_value_srgb_alpha(void (C::*)(const Color &), float, float, float, float);
-               void property_texture(void (C::*)(const Texture *), const std::string &);
-       };
-
-public:
-       class GenericLoader: public DataFile::Loader
-       {
-       private:
-               template<typename T>
-               struct AddType
-               {
-                       static void add(GenericLoader &ldr, const std::string &kw) { ldr.add(kw, &GenericLoader::typed_material<T>); }
-               };
-
-               DataFile::Collection *coll;
-               RefPtr<Material> material;
-
-               static ActionMap shared_actions;
-
-       public:
-               GenericLoader(DataFile::Collection * = 0);
-
-               Material *get_material() { return material.release(); }
-       private:
-               virtual void init_actions();
-
-               template<typename T>
-               void typed_material();
-
-               friend class Material;
-       };
-
-private:
-       typedef DataFile::LoadableTypeRegistry<GenericLoader, GenericLoader::AddType> MaterialRegistry;
-
-protected:
-       const Sampler *sampler;
-       ProgramData shdata;
-
-       Material(): sampler(0) { }
-public:
-       virtual ~Material() { }
-
-       virtual Program *create_compatible_shader() const;
-       virtual const Program *create_compatible_shader(DataFile::Collection &) const;
-protected:
-       virtual std::string create_program_source() const = 0;
-
-public:
-       /** Returns the uniforms for the material. */
-       const ProgramData &get_shader_data() const { return shdata; }
-
-protected:
-       void attach_texture_to(const Texture *, Texturing &, ProgramData &, const std::string &) const;
-public:
-       virtual void attach_textures_to(Texturing &, ProgramData &) const = 0;
-
-       template<typename T>
-       static void register_type(const std::string &);
-private:
-       static MaterialRegistry &get_material_registry();
-};
-
-template<typename T>
-void Material::register_type(const std::string &kw)
-{
-       get_material_registry().register_type<T>(kw);
-}
-
-
-template<typename C>
-void Material::PropertyLoader<C>::add_property(const std::string &kw, void (C::*set_value)(float), void (C::*set_texture)(const Texture *))
-{
-       add(kw, &PropertyLoader<C>::property_value_scalar, set_value);
-       add(kw+"_map", &PropertyLoader<C>::property_texture, set_texture);
-}
-
-template<typename C>
-void Material::PropertyLoader<C>::add_property(const std::string &kw, void (C::*set_value)(const Color &), void (C::*set_texture)(const Texture *), bool allow_alpha)
-{
-       add(kw, &PropertyLoader<C>::property_value_rgb, set_value);
-       add(kw+"_srgb", &PropertyLoader<C>::property_value_srgb, set_value);
-       if(allow_alpha)
-       {
-               add(kw, &PropertyLoader<C>::property_value_rgba, set_value);
-               add(kw+"_srgb", &PropertyLoader<C>::property_value_srgb_alpha, set_value);
-       }
-       add(kw+"_map", &PropertyLoader<C>::property_texture, set_texture);
-}
-
-template<typename C>
-void Material::PropertyLoader<C>::add_property(const std::string &kw, void (C::*set_texture)(const Texture *))
-{
-       add(kw+"_map", &PropertyLoader<C>::property_texture, set_texture);
-}
-
-template<typename C>
-void Material::PropertyLoader<C>::property_value_scalar(void (C::*set_value)(float), float value)
-{
-       (static_cast<C &>(obj).*set_value)(value);
-}
-
-template<typename C>
-void Material::PropertyLoader<C>::property_value_rgb(void (C::*set_value)(const Color &), float r, float g, float b)
-{
-       (static_cast<C &>(obj).*set_value)(Color(r, g, b));
-}
-
-template<typename C>
-void Material::PropertyLoader<C>::property_value_rgba(void (C::*set_value)(const Color &), float r, float g, float b, float a)
-{
-       (static_cast<C &>(obj).*set_value)(Color(r, g, b, a));
-}
-
-template<typename C>
-void Material::PropertyLoader<C>::property_value_srgb(void (C::*set_value)(const Color &), float r, float g, float b)
-{
-       (static_cast<C &>(obj).*set_value)(Color(r, g, b).to_linear());
-}
-
-template<typename C>
-void Material::PropertyLoader<C>::property_value_srgb_alpha(void (C::*set_value)(const Color &), float r, float g, float b, float a)
-{
-       (static_cast<C &>(obj).*set_value)(Color(r, g, b, a).to_linear());
-}
-
-template<typename C>
-void Material::PropertyLoader<C>::property_texture(void (C::*set_texture)(const Texture *), const std::string &name)
-{
-       /* The static_cast around get_collection is needed because otherwise Android
-       SDK's g++ 4.9 fails to parse get<Texture> as a template function call */
-       (static_cast<C &>(obj).*set_texture)(&static_cast<Collection &>(get_collection()).get<Texture>(name));
-}
-
-
-template<typename T>
-void Material::GenericLoader::typed_material()
-{
-       if(material)
-               throw std::logic_error("Material was already loaded");
-       RefPtr<T> mat = new T;
-       if(coll)
-               load_sub(*mat, *coll);
-       else
-               load_sub(*mat);
-       material = mat;
-}
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/materials/basicmaterial.cpp b/source/materials/basicmaterial.cpp
new file mode 100644 (file)
index 0000000..362fc9d
--- /dev/null
@@ -0,0 +1,153 @@
+#include "basicmaterial.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+BasicMaterial::BasicMaterial():
+       receive_shadows(false)
+{
+       set_diffuse(Color(1.0f));
+       set_specular(Color(0.0f));
+       set_emission(Color(0.0f));
+       set_shininess(50.0f);
+       set_reflectivity(0.0f);
+}
+
+string BasicMaterial::create_program_source() const
+{
+       string source = "import phong;\n";
+       if(diffuse.texture)
+               source += "const bool use_diffuse_map = true;\n";
+       if(specular.texture || specular.value.r || specular.value.g || specular.value.b)
+       {
+               source += "const bool use_specular = true;\n";
+               if(specular.texture)
+                       source += "const bool use_specular_map = true;\n";
+               if(shininess.texture)
+                       source += "const bool use_shininess_map = true;\n";
+       }
+       if(normal.texture)
+               source += "const bool use_normal_map = true;\n";
+       if(emission.texture || emission.value.r || emission.value.g || emission.value.b)
+       {
+               source += "const bool use_emission = true;\n";
+               if(emission.texture)
+                       source += "const bool use_emission_map = true;\n";
+       }
+       if(reflectivity.value || reflectivity.texture)
+       {
+               source += "const bool use_reflectivity = true;\n";
+               if (reflectivity.texture)
+                       source += "const bool use_reflectivity_map = true;\n";
+       }
+       if(receive_shadows)
+               source += "const bool use_shadow_map = true;\n";
+       return source;
+}
+
+void BasicMaterial::attach_textures_to(Texturing &texturing, ProgramData &tex_shdata) const
+{
+       attach_texture_to(diffuse.texture, texturing, tex_shdata, "diffuse_map");
+       attach_texture_to(specular.texture, texturing, tex_shdata, "specular_map");
+       attach_texture_to(normal.texture, texturing, tex_shdata, "normal_map");
+       attach_texture_to(emission.texture, texturing, tex_shdata, "emission_map");
+       attach_texture_to(shininess.texture, texturing, tex_shdata, "shininess_map");
+       attach_texture_to(reflectivity.texture, texturing, tex_shdata, "reflectivity_map");
+}
+
+void BasicMaterial::set_diffuse(const Color &color)
+{
+       diffuse.value = color;
+       shdata.uniform("basic_material.diffuse", color);
+}
+
+void BasicMaterial::set_diffuse_map(const Texture *tex)
+{
+       diffuse.texture = tex;
+}
+
+void BasicMaterial::set_specular(const Color &color)
+{
+       specular.value = color;
+       shdata.uniform("basic_material.specular", color);
+}
+
+void BasicMaterial::set_specular_map(const Texture *tex)
+{
+       specular.texture = tex;
+}
+
+void BasicMaterial::set_normal_map(const Texture *tex)
+{
+       normal.texture = tex;
+}
+
+void BasicMaterial::set_emission(const Color &color)
+{
+       emission.value = color;
+       shdata.uniform("basic_material.emission", color);
+}
+
+void BasicMaterial::set_emission_map(const Texture *tex)
+{
+       emission.texture = tex;
+}
+
+void BasicMaterial::set_shininess(float value)
+{
+       shininess.value = value;
+       shdata.uniform("basic_material.shininess", value);
+}
+
+void BasicMaterial::set_shininess_map(const Texture *tex)
+{
+       shininess.texture = tex;
+}
+
+void BasicMaterial::set_reflectivity(float value)
+{
+       reflectivity.value = value;
+       shdata.uniform("basic_material.reflectivity", value);
+}
+
+void BasicMaterial::set_reflectivity_map(const Texture *tex)
+{
+       reflectivity.texture = tex;
+}
+
+void BasicMaterial::set_receive_shadows(bool s)
+{
+       receive_shadows = s;
+}
+
+
+DataFile::Loader::ActionMap BasicMaterial::Loader::shared_actions;
+
+BasicMaterial::Loader::Loader(BasicMaterial &m):
+       DerivedObjectLoader<BasicMaterial, Material::PropertyLoader<BasicMaterial> >(m)
+{
+       set_actions(shared_actions);
+}
+
+BasicMaterial::Loader::Loader(BasicMaterial &m, Collection &c):
+       DerivedObjectLoader<BasicMaterial, Material::PropertyLoader<BasicMaterial> >(m, c)
+{
+       set_actions(shared_actions);
+}
+
+void BasicMaterial::Loader::init_actions()
+{
+       Material::PropertyLoader<BasicMaterial>::init_actions();
+       add_property("diffuse", &BasicMaterial::set_diffuse, &BasicMaterial::set_diffuse_map, true);
+       add_property("specular", &BasicMaterial::set_specular, &BasicMaterial::set_specular_map, false);
+       add_property("normal", &BasicMaterial::set_normal_map);
+       add_property("emission", &BasicMaterial::set_emission, &BasicMaterial::set_emission_map, false);
+       add_property("shininess", &BasicMaterial::set_shininess, &BasicMaterial::set_shininess_map);
+       add_property("reflectivity", &BasicMaterial::set_reflectivity, &BasicMaterial::set_reflectivity_map);
+       add("receive_shadows", &BasicMaterial::receive_shadows);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/materials/basicmaterial.h b/source/materials/basicmaterial.h
new file mode 100644 (file)
index 0000000..95259f4
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef MSP_GL_BASICMATERIAL_H_
+#define MSP_GL_BASICMATERIAL_H_
+
+#include "material.h"
+
+namespace Msp {
+namespace GL {
+
+class BasicMaterial: public Material
+{
+public:
+       class Loader: public DataFile::DerivedObjectLoader<BasicMaterial, Material::PropertyLoader<BasicMaterial> >
+       {
+       private:
+               static ActionMap shared_actions;
+
+       public:
+               Loader(BasicMaterial &);
+               Loader(BasicMaterial &, Collection &);
+
+       private:
+               virtual void init_actions();
+       };
+
+private:
+       Property<Color> diffuse;
+       Property<Color> specular;
+       Property<float> shininess;
+       Property<Vector3> normal;
+       Property<Color> emission;
+       Property<float> reflectivity;
+       bool receive_shadows;
+
+public:
+       BasicMaterial();
+
+protected:
+       virtual std::string create_program_source() const;
+
+public:
+       virtual void attach_textures_to(Texturing &, ProgramData &) const;
+
+       void set_diffuse(const Color &);
+       void set_diffuse_map(const Texture *);
+       void set_specular(const Color &);
+       void set_specular_map(const Texture *);
+       void set_normal_map(const Texture *);
+       void set_emission(const Color &);
+       void set_emission_map(const Texture *);
+       void set_shininess(float);
+       void set_shininess_map(const Texture *);
+       void set_reflectivity(float);
+       void set_reflectivity_map(const Texture *);
+       void set_receive_shadows(bool);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/materials/light.cpp b/source/materials/light.cpp
new file mode 100644 (file)
index 0000000..e910411
--- /dev/null
@@ -0,0 +1,163 @@
+#include <stdexcept>
+#include <msp/strings/format.h>
+#include "light.h"
+#include "matrix.h"
+#include "misc.h"
+#include "programdata.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Light::Light():
+       diffuse(1),
+       specular(1),
+       position(0, 0, 1, 0),
+       spot_dir(0, 0, -1),
+       spot_exp(0),
+       spot_cutoff(Geometry::Angle<float>::straight())
+{
+       attenuation[0] = 1;
+       attenuation[1] = 0;
+       attenuation[2] = 0;
+}
+
+void Light::update_matrix()
+{
+       Vector3 up_dir;
+       if(20*abs(direction.z)>abs(direction.x)+abs(direction.y))
+               up_dir.y = 1;
+       else
+               up_dir.z = 1;
+       Vector3 right_dir = normalize(cross(direction, up_dir));
+
+       Vector4 columns[4];
+       columns[0] = compose(right_dir, 0.0f);
+       columns[1] = compose(cross(right_dir, direction), 0.0f);
+       columns[2] = compose(-direction, 0.0f);
+       columns[3] = position;
+       matrix = Matrix::from_columns(columns);
+}
+
+void Light::set_diffuse(const Color &c)
+{
+       diffuse = c;
+}
+
+void Light::set_specular(const Color &c)
+{
+       specular = c;
+}
+
+void Light::set_matrix(const Matrix &m)
+{
+       Placeable::set_matrix(m);
+       position = matrix.column(3);
+       spot_dir = normalize(-matrix.column(2).slice<3>(0));
+       direction = (position.w ? spot_dir : normalize(-position.slice<3>(0)));
+       update_matrix();
+}
+
+void Light::set_position(const Vector4 &p)
+{
+       position = p;
+       if(!position.w)
+               direction = normalize(-position.slice<3>(0));
+       update_matrix();
+}
+
+void Light::set_spot_direction(const Vector3 &d)
+{
+       spot_dir = normalize(d);
+       if(position.w)
+               direction = spot_dir;
+       update_matrix();
+}
+
+void Light::set_spot_exponent(float e)
+{
+       if(e<0)
+               throw invalid_argument("Light::set_spot_exponent");
+
+       spot_exp = e;
+}
+
+void Light::set_spot_cutoff(const Geometry::Angle<float> &c)
+{
+       if(c<Geometry::Angle<float>::zero() || (c>Geometry::Angle<float>::right() && c!=Geometry::Angle<float>::straight()))
+               throw invalid_argument("Light::set_spot_cutoff");
+
+       spot_cutoff = c;
+}
+
+void Light::disable_spot_cutoff()
+{
+       set_spot_cutoff(Geometry::Angle<float>::straight());
+}
+
+void Light::set_attenuation(float c, float l, float q)
+{
+       attenuation[0] = c;
+       attenuation[1] = l;
+       attenuation[2] = q;
+}
+
+void Light::update_shader_data(ProgramData &shdata, const Matrix &view_matrix, unsigned i) const
+{
+       string base = format("light_sources[%d]", i);
+       shdata.uniform(base+".position", view_matrix*position);
+       shdata.uniform(base+".diffuse", diffuse);
+       shdata.uniform(base+".specular", specular);
+}
+
+
+Light::Loader::Loader(Light &l):
+       DataFile::ObjectLoader<Light>(l)
+{
+       add("attenuation", &Loader::attenuation);
+       add("diffuse", &Loader::diffuse);
+       add("position", &Loader::position);
+       add("specular", &Loader::specular);
+       add("spot_direction", &Loader::spot_direction);
+       add("spot_exponent", &Loader::spot_exponent);
+       add("spot_cutoff", &Loader::spot_cutoff);
+}
+
+void Light::Loader::attenuation(float c, float l, float q)
+{
+       obj.set_attenuation(c, l, q);
+}
+
+void Light::Loader::diffuse(float r, float g, float b)
+{
+       obj.set_diffuse(Color(r, g, b));
+}
+
+void Light::Loader::position(float x, float y, float z, float w)
+{
+       obj.set_position(Vector4(x, y, z, w));
+}
+
+void Light::Loader::specular(float r, float g, float b)
+{
+       obj.set_specular(Color(r, g, b));
+}
+
+void Light::Loader::spot_direction(float x, float y, float z)
+{
+       obj.set_spot_direction(Vector3(x, y, z));
+}
+
+void Light::Loader::spot_exponent(float e)
+{
+       obj.set_spot_exponent(e);
+}
+
+void Light::Loader::spot_cutoff(float c)
+{
+       obj.set_spot_cutoff(Geometry::Angle<float>::from_degrees(c));
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/materials/light.h b/source/materials/light.h
new file mode 100644 (file)
index 0000000..e94f3c5
--- /dev/null
@@ -0,0 +1,114 @@
+#ifndef MSP_GL_LIGHT_H_
+#define MSP_GL_LIGHT_H_
+
+#include <vector>
+#include <msp/datafile/objectloader.h>
+#include "color.h"
+#include "placeable.h"
+
+namespace Msp {
+namespace GL {
+
+class Matrix;
+class ProgramData;
+
+/**
+Stores properties of a single light source.  Lights can be directional, point
+lights or spotlights.  No explicit type parameter is provided; rather the
+other parameters determine what kind of light it is.  If the fourth component
+of position is zero, it's a directional light.  Otherwise, if the spot cutoff
+is not 180 degrees, it's a spotlight.  Otherwise it's an omnidirectional point
+light.
+
+Lights are usually grouped with a Lighting object, which can be used in a
+Pipeline::Pass.
+
+Lights do not cast shadows by themselves.  See ShadowMap for that.
+*/
+class Light: public Placeable
+{
+public:
+       class Loader: public DataFile::ObjectLoader<Light>
+       {
+       public:
+               Loader(Light &);
+
+       private:
+               void attenuation(float, float, float);
+               void diffuse(float, float, float);
+               void position(float, float, float, float);
+               void specular(float, float, float);
+               void spot_direction(float, float, float);
+               void spot_exponent(float);
+               void spot_cutoff(float);
+       };
+
+private:
+       Color diffuse;
+       Color specular;
+       Vector4 position;
+       Vector3 spot_dir;
+       Vector3 direction;
+       float spot_exp;
+       Geometry::Angle<float> spot_cutoff;
+       float attenuation[3];
+
+public:
+       Light();
+
+private:
+       void update_matrix();
+
+public:
+       /** Sets the diffuse (direction-independent) color of the Light.  Provided
+       to shaders with the name light_sources[i].diffuse. */
+       void set_diffuse(const Color &c);
+
+       /** Sets the specular (direction-dependent) color of the Light.  Provided to
+       shaders with the name light_sources[i].specular. */
+       void set_specular(const Color &c);
+
+       const Color &get_diffuse() const { return diffuse; }
+       const Color &get_specular() const { return specular; }
+
+       /** Sets the postion and orientation of the Light from a matrix.  Negative Z
+       axis is used as the spot direction, other axes are ignored. */
+       virtual void set_matrix(const Matrix &);
+
+       /** Sets the position of the Light.  For a directional light, set the xyz
+       components to a vector pointing towards the light and the w component to 0. */
+       void set_position(const Vector4 &);
+
+       const Vector4 &get_position() const { return position; }
+
+       /** Sets the direction of a spotlight.  Has no effect if spotlight cutoff is
+       not set. */
+       void set_spot_direction(const Vector3 &);
+
+       /** Sets the angular falloff exponent of the spotlight.  Must be >= 0. */
+       void set_spot_exponent(float);
+
+       /** Sets the cutoff angle of a spotlight.  Beyond this angle from its axis
+       the spotlight provides no illumination.  Must be between 0 and 90 degrees,
+       or exactly 180 degrees to indicate a non-spotlight. */
+       void set_spot_cutoff(const Geometry::Angle<float> &);
+
+       /** Disables spotlight, reverting to an omnidirectional point light.
+       Equivalent to setting the spot cutoff to 180 degrees. */
+       void disable_spot_cutoff();
+
+       const Vector3 &get_spot_direction() const { return spot_dir; }
+       float get_spot_exponent() const { return spot_exp; }
+       const Geometry::Angle<float> &get_spot_cutoff() const { return spot_cutoff; }
+       void set_attenuation(float, float, float);
+       const float *get_attenuation() const { return attenuation; }
+
+       /** Updates a ProgramData object with the uniforms for the Light.  A view
+       matrix and light source index must be passed in. */
+       void update_shader_data(ProgramData &, const Matrix &, unsigned) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/materials/lighting.cpp b/source/materials/lighting.cpp
new file mode 100644 (file)
index 0000000..eeb4f30
--- /dev/null
@@ -0,0 +1,162 @@
+#include <stdexcept>
+#include <cmath>
+#include "error.h"
+#include "light.h"
+#include "lighting.h"
+#include "matrix.h"
+#include "misc.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Lighting::Lighting():
+       ambient(0.2),
+       zenith_direction(0, 0, 1),
+       horizon_angle(Geometry::Angle<float>::zero()),
+       fog_color(0.0f, 0.0f, 0.0f, 0.0f),
+       fog_density(0.0f)
+{ }
+
+Lighting::~Lighting()
+{
+       for(vector<Light *>::iterator i=owned_data.begin(); i!=owned_data.end(); ++i)
+               delete *i;
+}
+
+void Lighting::set_ambient(const Color &a)
+{
+       ambient = a;
+}
+
+void Lighting::set_sky_color(const Color &s)
+{
+       sky_color = s;
+}
+
+void Lighting::set_zenith_direction(const Vector3 &d)
+{
+       zenith_direction = d;
+}
+
+void Lighting::set_horizon_angle(const Geometry::Angle<float> &a)
+{
+       horizon_angle = a;
+}
+
+void Lighting::set_fog_color(const Color &c)
+{
+       fog_color = c;
+}
+
+void Lighting::set_fog_density(float d)
+{
+       if(d<0)
+               throw invalid_argument("Lighting::set_fog_density");
+
+       fog_density = d;
+}
+
+void Lighting::set_fog_half_distance(float d)
+{
+       set_fog_density(-log(pow(0.5, 1.0/d)));
+}
+
+void Lighting::attach(unsigned i, const Light &l)
+{
+       if(i>=lights.size())
+               lights.resize(i+1);
+
+       lights[i] = &l;
+}
+
+void Lighting::detach(unsigned i)
+{
+       if(i>=lights.size())
+               return;
+
+       lights[i] = 0;
+}
+
+const Light *Lighting::get_attached_light(unsigned i) const
+{
+       return i<lights.size() ? lights[i] : 0;
+}
+
+void Lighting::update_shader_data(ProgramData &shdata, const Matrix &view_matrix) const
+{
+       shdata.uniform("ambient_color", ambient);
+       shdata.uniform("sky_color", sky_color);
+       shdata.uniform("eye_zenith_dir", view_matrix.block<3, 3>(0, 0)*zenith_direction);
+       shdata.uniform("horizon_limit", horizon_angle.radians());
+       shdata.uniform("fog_color", fog_color);
+       shdata.uniform("fog_density", fog_density);
+
+       // For backwards compatibility
+       shdata.uniform("eye_sky_dir", view_matrix.block<3, 3>(0, 0)*zenith_direction);
+
+       for(unsigned i=0; i<lights.size(); ++i)
+               if(lights[i])
+                       lights[i]->update_shader_data(shdata, view_matrix, i);
+}
+
+
+Lighting::Loader::Loader(Lighting &l):
+       DataFile::ObjectLoader<Lighting>(l)
+{
+       add("ambient", &Loader::ambient);
+       add("fog_color", &Loader::fog_color);
+       add("fog_density", &Loader::fog_density);
+       add("fog_half_distance", &Loader::fog_half_distance);
+       add("horizon_angle", &Loader::horizon_angle);
+       add("light", &Loader::light);
+       add("sky_color", &Loader::sky_color);
+       add("zenith_direction", &Loader::zenith_direction);
+}
+
+void Lighting::Loader::ambient(float r, float g, float b)
+{
+       obj.ambient = Color(r, g, b);
+}
+
+void Lighting::Loader::fog_color(float r, float g, float b)
+{
+       obj.set_fog_color(Color(r, g, b));
+}
+
+void Lighting::Loader::fog_density(float d)
+{
+       obj.set_fog_density(d);
+}
+
+void Lighting::Loader::fog_half_distance(float d)
+{
+       obj.set_fog_half_distance(d);
+}
+
+void Lighting::Loader::horizon_angle(float a)
+{
+       obj.set_horizon_angle(Geometry::Angle<float>::from_degrees(a));
+}
+
+void Lighting::Loader::light(unsigned i)
+{
+       RefPtr<Light> lgt = new Light;
+       load_sub(*lgt);
+       obj.attach(i, *lgt);
+       obj.owned_data.push_back(lgt.release());
+}
+
+void Lighting::Loader::sky_color(float r, float g, float b)
+{
+       obj.set_sky_color(Color(r, g, b));
+}
+
+void Lighting::Loader::zenith_direction(float x, float y, float z)
+{
+       obj.set_zenith_direction(Vector3(x, y, z));
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/materials/lighting.h b/source/materials/lighting.h
new file mode 100644 (file)
index 0000000..ca214fc
--- /dev/null
@@ -0,0 +1,96 @@
+#ifndef MSP_GL_LIGHTING_H_
+#define MSP_GL_LIGHTING_H_
+
+#include <vector>
+#include <msp/geometry/angle.h>
+#include "color.h"
+#include "gl.h"
+#include "programdata.h"
+
+namespace Msp {
+namespace GL {
+
+class Light;
+
+/**
+Encapsulates global lighting parameters and any number of individual light
+sources.
+*/
+class Lighting
+{
+public:
+       class Loader: public DataFile::ObjectLoader<Lighting>
+       {
+       public:
+               Loader(Lighting &);
+
+       private:
+               void ambient(float, float, float);
+               void fog_color(float, float, float);
+               void fog_density(float);
+               void fog_half_distance(float);
+               void horizon_angle(float);
+               void light(unsigned);
+               void sky_color(float, float, float);
+               void zenith_direction(float, float, float);
+       };
+
+private:
+       Color ambient;
+       Color sky_color;
+       Vector3 zenith_direction;
+       Geometry::Angle<float> horizon_angle;
+       Color fog_color;
+       float fog_density;
+       std::vector<const Light *> lights;
+       std::vector<Light *> owned_data;
+
+public:
+       Lighting();
+       ~Lighting();
+
+       /** Sets the ambient lighting color.  Affects all surfaces in the scene. */
+       void set_ambient(const Color &);
+
+       const Color &get_ambient() const { return ambient; }
+
+       /** Sets the color of the sky at zenith.  Has no effect without shaders. */
+       void set_sky_color(const Color &);
+
+       /** Sets the direction of the zenith.  Defaults to positive Z axis.  Has no
+       effect without shaders. */
+       void set_zenith_direction(const Vector3 &);
+
+       /** Sets the angle where skylight cuts off, counted from the true horizon.
+       Has no effect without shaders. */
+       void set_horizon_angle(const Geometry::Angle<float> &);
+
+       /** Sets the fog color, which is blended into distant surfaces. */
+       void set_fog_color(const Color &);
+
+       /** Sets the density of the fog.  Zero means no fog. */
+       void set_fog_density(float);
+
+       /** Sets the density of the fog so that the blending factor at the given
+       distance is 50%. */
+       void set_fog_half_distance(float);
+
+       /** Attaches a light source. */
+       void attach(unsigned, const Light &);
+
+       /** Detaches a light source. */
+       void detach(unsigned);
+
+       /** Returns an attached light.  If no light is attached at that index, null
+       is returned. */
+       const Light *get_attached_light(unsigned) const;
+
+       /** Updates a ProgramData object with the uniforms for the Lighting,
+       including all attached light sources.  A view matrix must be passed in. */
+       void update_shader_data(ProgramData &, const Matrix &) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/materials/material.cpp b/source/materials/material.cpp
new file mode 100644 (file)
index 0000000..810ecd1
--- /dev/null
@@ -0,0 +1,109 @@
+#include <msp/core/hash.h>
+#include <msp/strings/format.h>
+#include "basicmaterial.h"
+#include "gl.h"
+#include "pbrmaterial.h"
+#include "resources.h"
+#include "texturing.h"
+#include "uniform.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Program *Material::create_compatible_shader() const
+{
+       return new Program(create_program_source());
+}
+
+const Program *Material::create_compatible_shader(DataFile::Collection &coll) const
+{
+       string source = create_program_source();
+       string name = format("_material_%016x.glsl", hash64(source));
+       Program *shprog = coll.find<Program>(name);
+       if(shprog)
+               return shprog;
+
+       shprog = new Program(create_program_source());
+       try
+       {
+               coll.add(name, shprog);
+       }
+       catch(...)
+       {
+               delete shprog;
+               throw;
+       }
+
+       return shprog;
+}
+
+void Material::attach_texture_to(const Texture *tex, Texturing &texturing, ProgramData &tex_shdata, const string &name) const
+{
+       if(!tex)
+               return;
+
+       int unit = -1;
+
+       if(const Uniform *uni = tex_shdata.find_uniform(name))
+               if(const Uniform1i *uni_int = dynamic_cast<const Uniform1i *>(uni))
+                       unit = uni_int->get();
+
+       if(unit<0)
+               unit = texturing.find_free_unit(name);
+       if(unit<0)
+               throw runtime_error("no free texunit");
+
+       texturing.attach(unit, *tex, sampler);
+       tex_shdata.uniform(name, unit);
+}
+
+Material::MaterialRegistry &Material::get_material_registry()
+{
+       static MaterialRegistry registry;
+       static bool initialized = false;
+       if(!initialized)
+       {
+               initialized = true;
+               registry.register_type<BasicMaterial>("basic");
+               registry.register_type<PbrMaterial>("pbr");
+       }
+       return registry;
+}
+
+
+Material::Loader::Loader(Material &m):
+       CollectionObjectLoader(m, 0)
+{ }
+
+Material::Loader::Loader(Material &m, Collection &c):
+       CollectionObjectLoader(m, &c)
+{ }
+
+void Material::Loader::init_actions()
+{
+       add("sampler", &Loader::sampler);
+}
+
+void Material::Loader::sampler(const std::string &name)
+{
+       obj.sampler = &get_collection().get<Sampler>(name);
+}
+
+
+DataFile::Loader::ActionMap Material::GenericLoader::shared_actions;
+
+Material::GenericLoader::GenericLoader(DataFile::Collection *c):
+       coll(c)
+{
+       set_actions(shared_actions);
+}
+
+void Material::GenericLoader::init_actions()
+{
+       get_material_registry().add_all(*this);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/materials/material.h b/source/materials/material.h
new file mode 100644 (file)
index 0000000..571f4cb
--- /dev/null
@@ -0,0 +1,207 @@
+#ifndef MSP_GL_MATERIAL_H_
+#define MSP_GL_MATERIAL_H_
+
+#include <msp/datafile/collection.h>
+#include <msp/datafile/loadabletyperegistry.h>
+#include <msp/datafile/objectloader.h>
+#include "color.h"
+#include "programdata.h"
+#include "texture.h"
+
+namespace Msp {
+namespace GL {
+
+class Texturing;
+
+class Material
+{
+private:
+       class Loader: public DataFile::CollectionObjectLoader<Material>
+       {
+       protected:
+               Loader(Material &);
+               Loader(Material &, Collection &);
+
+               virtual void init_actions();
+
+       private:
+               void sampler(const std::string &);
+       };
+
+protected:
+       template<typename T>
+       struct Property
+       {
+               T value;
+               const Texture *texture;
+
+               Property(): value(T()), texture(0) { }
+       };
+
+       template<typename C>
+       class PropertyLoader: public DataFile::DerivedObjectLoader<Material, Loader>
+       {
+       protected:
+               PropertyLoader(C &m): DerivedObjectLoader<Material, Loader>(m) { }
+               PropertyLoader(C &m, Collection &c): DerivedObjectLoader<Material, Loader>(m, c) { }
+
+               void add_property(const std::string &, void (C::*)(float), void (C::*)(const Texture *));
+               void add_property(const std::string &, void (C::*)(const Color &), void (C::*)(const Texture *), bool);
+               void add_property(const std::string &, void (C::*)(const Texture *));
+
+               void property_value_scalar(void (C::*)(float), float);
+               void property_value_rgb(void (C::*)(const Color &), float, float, float);
+               void property_value_rgba(void (C::*)(const Color &), float, float, float, float);
+               void property_value_srgb(void (C::*)(const Color &), float, float, float);
+               void property_value_srgb_alpha(void (C::*)(const Color &), float, float, float, float);
+               void property_texture(void (C::*)(const Texture *), const std::string &);
+       };
+
+public:
+       class GenericLoader: public DataFile::Loader
+       {
+       private:
+               template<typename T>
+               struct AddType
+               {
+                       static void add(GenericLoader &ldr, const std::string &kw) { ldr.add(kw, &GenericLoader::typed_material<T>); }
+               };
+
+               DataFile::Collection *coll;
+               RefPtr<Material> material;
+
+               static ActionMap shared_actions;
+
+       public:
+               GenericLoader(DataFile::Collection * = 0);
+
+               Material *get_material() { return material.release(); }
+       private:
+               virtual void init_actions();
+
+               template<typename T>
+               void typed_material();
+
+               friend class Material;
+       };
+
+private:
+       typedef DataFile::LoadableTypeRegistry<GenericLoader, GenericLoader::AddType> MaterialRegistry;
+
+protected:
+       const Sampler *sampler;
+       ProgramData shdata;
+
+       Material(): sampler(0) { }
+public:
+       virtual ~Material() { }
+
+       virtual Program *create_compatible_shader() const;
+       virtual const Program *create_compatible_shader(DataFile::Collection &) const;
+protected:
+       virtual std::string create_program_source() const = 0;
+
+public:
+       /** Returns the uniforms for the material. */
+       const ProgramData &get_shader_data() const { return shdata; }
+
+protected:
+       void attach_texture_to(const Texture *, Texturing &, ProgramData &, const std::string &) const;
+public:
+       virtual void attach_textures_to(Texturing &, ProgramData &) const = 0;
+
+       template<typename T>
+       static void register_type(const std::string &);
+private:
+       static MaterialRegistry &get_material_registry();
+};
+
+template<typename T>
+void Material::register_type(const std::string &kw)
+{
+       get_material_registry().register_type<T>(kw);
+}
+
+
+template<typename C>
+void Material::PropertyLoader<C>::add_property(const std::string &kw, void (C::*set_value)(float), void (C::*set_texture)(const Texture *))
+{
+       add(kw, &PropertyLoader<C>::property_value_scalar, set_value);
+       add(kw+"_map", &PropertyLoader<C>::property_texture, set_texture);
+}
+
+template<typename C>
+void Material::PropertyLoader<C>::add_property(const std::string &kw, void (C::*set_value)(const Color &), void (C::*set_texture)(const Texture *), bool allow_alpha)
+{
+       add(kw, &PropertyLoader<C>::property_value_rgb, set_value);
+       add(kw+"_srgb", &PropertyLoader<C>::property_value_srgb, set_value);
+       if(allow_alpha)
+       {
+               add(kw, &PropertyLoader<C>::property_value_rgba, set_value);
+               add(kw+"_srgb", &PropertyLoader<C>::property_value_srgb_alpha, set_value);
+       }
+       add(kw+"_map", &PropertyLoader<C>::property_texture, set_texture);
+}
+
+template<typename C>
+void Material::PropertyLoader<C>::add_property(const std::string &kw, void (C::*set_texture)(const Texture *))
+{
+       add(kw+"_map", &PropertyLoader<C>::property_texture, set_texture);
+}
+
+template<typename C>
+void Material::PropertyLoader<C>::property_value_scalar(void (C::*set_value)(float), float value)
+{
+       (static_cast<C &>(obj).*set_value)(value);
+}
+
+template<typename C>
+void Material::PropertyLoader<C>::property_value_rgb(void (C::*set_value)(const Color &), float r, float g, float b)
+{
+       (static_cast<C &>(obj).*set_value)(Color(r, g, b));
+}
+
+template<typename C>
+void Material::PropertyLoader<C>::property_value_rgba(void (C::*set_value)(const Color &), float r, float g, float b, float a)
+{
+       (static_cast<C &>(obj).*set_value)(Color(r, g, b, a));
+}
+
+template<typename C>
+void Material::PropertyLoader<C>::property_value_srgb(void (C::*set_value)(const Color &), float r, float g, float b)
+{
+       (static_cast<C &>(obj).*set_value)(Color(r, g, b).to_linear());
+}
+
+template<typename C>
+void Material::PropertyLoader<C>::property_value_srgb_alpha(void (C::*set_value)(const Color &), float r, float g, float b, float a)
+{
+       (static_cast<C &>(obj).*set_value)(Color(r, g, b, a).to_linear());
+}
+
+template<typename C>
+void Material::PropertyLoader<C>::property_texture(void (C::*set_texture)(const Texture *), const std::string &name)
+{
+       /* The static_cast around get_collection is needed because otherwise Android
+       SDK's g++ 4.9 fails to parse get<Texture> as a template function call */
+       (static_cast<C &>(obj).*set_texture)(&static_cast<Collection &>(get_collection()).get<Texture>(name));
+}
+
+
+template<typename T>
+void Material::GenericLoader::typed_material()
+{
+       if(material)
+               throw std::logic_error("Material was already loaded");
+       RefPtr<T> mat = new T;
+       if(coll)
+               load_sub(*mat, *coll);
+       else
+               load_sub(*mat);
+       material = mat;
+}
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/materials/pbrmaterial.cpp b/source/materials/pbrmaterial.cpp
new file mode 100644 (file)
index 0000000..bc7104c
--- /dev/null
@@ -0,0 +1,138 @@
+#include "pbrmaterial.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+PbrMaterial::PbrMaterial():
+       receive_shadows(false)
+{
+       set_base_color(0.8f);
+       set_metalness(0.0f);
+       set_roughness(0.5f);
+       set_emission(0.0f);
+}
+
+string PbrMaterial::create_program_source() const
+{
+       string source = "import cooktorrance;\n";
+       if(base_color.texture)
+               source += "const bool use_base_color_map = true;\n";
+       if(normal.texture)
+               source += "const bool use_normal_map = true;\n";
+       if(metalness.texture)
+               source += "const bool use_metalness_map = true;\n";
+       if(roughness.texture)
+               source += "const bool use_roughness_map = true;\n";
+       if(occlusion.texture)
+               source += "const bool use_occlusion_map = true;\n";
+       if(emission.texture || emission.value.r || emission.value.g || emission.value.b)
+       {
+               source += "const bool use_emission = true;\n";
+               if(emission.texture)
+                       source += "const bool use_emission_map = true;\n";
+       }
+       if(receive_shadows)
+               source += "const bool use_shadow_map = true;\n";
+       return source;
+}
+
+void PbrMaterial::attach_textures_to(Texturing &texturing, ProgramData &tex_shdata) const
+{
+       attach_texture_to(base_color.texture, texturing, tex_shdata, "base_color_map");
+       attach_texture_to(metalness.texture, texturing, tex_shdata, "metalness_map");
+       attach_texture_to(roughness.texture, texturing, tex_shdata, "roughness_map");
+       attach_texture_to(normal.texture, texturing, tex_shdata, "normal_map");
+       attach_texture_to(occlusion.texture, texturing, tex_shdata, "occlusion_map");
+       attach_texture_to(emission.texture, texturing, tex_shdata, "emission_map");
+}
+
+void PbrMaterial::set_base_color(const Color &color)
+{
+       base_color.value = color;
+       shdata.uniform("pbr_material.base_color", color);
+}
+
+void PbrMaterial::set_base_color_map(const Texture *tex)
+{
+       base_color.texture = tex;
+}
+
+void PbrMaterial::set_normal_map(const Texture *tex)
+{
+       normal.texture = tex;
+}
+
+void PbrMaterial::set_metalness(float value)
+{
+       metalness.value = value;
+       shdata.uniform("pbr_material.metalness", value);
+}
+
+void PbrMaterial::set_metalness_map(const Texture *tex)
+{
+       metalness.texture = tex;
+}
+
+void PbrMaterial::set_roughness(float value)
+{
+       roughness.value = value;
+       shdata.uniform("pbr_material.roughness", value);
+}
+
+void PbrMaterial::set_roughness_map(const Texture *tex)
+{
+       roughness.texture = tex;
+}
+
+void PbrMaterial::set_occlusion_map(const Texture *tex)
+{
+       occlusion.texture = tex;
+}
+
+void PbrMaterial::set_emission(const Color &color)
+{
+       emission.value = color;
+       shdata.uniform("pbr_material.emission", color);
+}
+
+void PbrMaterial::set_emission_map(const Texture *tex)
+{
+       emission.texture = tex;
+}
+
+void PbrMaterial::set_receive_shadows(bool s)
+{
+       receive_shadows = s;
+}
+
+
+DataFile::Loader::ActionMap PbrMaterial::Loader::shared_actions;
+
+PbrMaterial::Loader::Loader(PbrMaterial &m):
+       DerivedObjectLoader<PbrMaterial, Material::PropertyLoader<PbrMaterial> >(m)
+{
+       set_actions(shared_actions);
+}
+
+PbrMaterial::Loader::Loader(PbrMaterial &m, Collection &c):
+       DerivedObjectLoader<PbrMaterial, Material::PropertyLoader<PbrMaterial> >(m, c)
+{
+       set_actions(shared_actions);
+}
+
+void PbrMaterial::Loader::init_actions()
+{
+       Material::PropertyLoader<PbrMaterial>::init_actions();
+       add_property("base_color", &PbrMaterial::set_base_color, &PbrMaterial::set_base_color_map, true);
+       add_property("normal", &PbrMaterial::set_normal_map);
+       add_property("metalness", &PbrMaterial::set_metalness, &PbrMaterial::set_metalness_map);
+       add_property("roughness", &PbrMaterial::set_roughness, &PbrMaterial::set_roughness_map);
+       add_property("occlusion", &PbrMaterial::set_occlusion_map);
+       add_property("emission", &PbrMaterial::set_emission, &PbrMaterial::set_emission_map, false);
+       add("receive_shadows", &PbrMaterial::receive_shadows);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/materials/pbrmaterial.h b/source/materials/pbrmaterial.h
new file mode 100644 (file)
index 0000000..2a0eef3
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef MSP_GL_PBRMATERIAL_H_
+#define MSP_GL_PBRMATERIAL_H_
+
+#include "material.h"
+
+namespace Msp {
+namespace GL {
+
+class PbrMaterial: public Material
+{
+public:
+       class Loader: public DataFile::DerivedObjectLoader<PbrMaterial, Material::PropertyLoader<PbrMaterial> >
+       {
+       private:
+               static ActionMap shared_actions;
+
+       public:
+               Loader(PbrMaterial &);
+               Loader(PbrMaterial &, Collection &);
+
+       private:
+               virtual void init_actions();
+       };
+       
+private:
+       Property<Color> base_color;
+       Property<Vector3> normal;
+       Property<float> metalness;
+       Property<float> roughness;
+       Property<float> occlusion;
+       Property<Color> emission;
+       bool receive_shadows;
+
+public:
+       PbrMaterial();
+
+protected:
+       virtual std::string create_program_source() const;
+
+public:
+       virtual void attach_textures_to(Texturing &, ProgramData &) const;
+
+       void set_base_color(const Color &);
+       void set_base_color_map(const Texture *);
+       void set_normal_map(const Texture *);
+       void set_metalness(float);
+       void set_metalness_map(const Texture *);
+       void set_roughness(float);
+       void set_roughness_map(const Texture *);
+       void set_occlusion_map(const Texture *);
+       void set_emission(const Color &);
+       void set_emission_map(const Texture *);
+       void set_receive_shadows(bool);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/materials/renderpass.cpp b/source/materials/renderpass.cpp
new file mode 100644 (file)
index 0000000..835e78a
--- /dev/null
@@ -0,0 +1,276 @@
+#include <msp/datafile/collection.h>
+#include <msp/strings/format.h>
+#include "error.h"
+#include "material.h"
+#include "renderpass.h"
+#include "program.h"
+#include "programdata.h"
+#include "renderer.h"
+#include "texture.h"
+#include "texture2d.h"
+#include "texturing.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+RenderPass::RenderPass():
+       shprog(0),
+       shprog_from_material(false),
+       shdata(0),
+       material(0),
+       texturing(0),
+       back_faces(false)
+{ }
+
+RenderPass::RenderPass(const RenderPass &other):
+       shprog(other.shprog),
+       shprog_from_material(other.shprog_from_material),
+       shdata(other.shdata),
+       uniform_slots(other.uniform_slots),
+       material(other.material),
+       material_slot(other.material_slot),
+       texturing(other.texturing ? new Texturing(*other.texturing) : 0),
+       tex_names(other.tex_names),
+       back_faces(other.back_faces)
+{ }
+
+RenderPass &RenderPass::operator=(const RenderPass &other)
+{
+       shprog = other.shprog;
+       shprog_from_material = other.shprog_from_material;
+       shdata = other.shdata;
+       uniform_slots = other.uniform_slots;
+       material = other.material;
+       material_slot = other.material_slot;
+       texturing = other.texturing ? new Texturing(*other.texturing) : 0;
+       tex_names = other.tex_names;
+       back_faces = other.back_faces;
+       return *this;
+}
+
+RenderPass::~RenderPass()
+{
+       delete texturing;
+}
+
+void RenderPass::finalize_material(DataFile::Collection *coll)
+{
+       maybe_create_material_shader(coll);
+       ensure_private_shader_data();
+
+       if(!texturing)
+               texturing = new Texturing;
+       material->attach_textures_to(*texturing, *shdata);
+}
+
+void RenderPass::maybe_create_material_shader(DataFile::Collection *coll)
+{
+       if(shprog && !shprog_from_material)
+               return;
+
+       if(coll)
+       {
+               shprog = material->create_compatible_shader(*coll);
+               shprog.keep();
+       }
+       else
+               shprog = material->create_compatible_shader();
+
+       if(shdata)
+               shdata = new ProgramData(*shdata, shprog.get());
+
+       shprog_from_material = true;
+}
+
+void RenderPass::ensure_private_shader_data()
+{
+       if(!shprog)
+               throw invalid_operation("RenderPass::ensure_private_shader_data");
+
+       if(!shdata)
+               shdata = new ProgramData(shprog.get());
+       else if(shdata.refcount()>1)
+               shdata = new ProgramData(*shdata);
+}
+
+void RenderPass::set_shader_program(const Program *prog, const ProgramData *data)
+{
+       shprog = prog;
+       shprog.keep();
+       shprog_from_material = false;
+       shdata = (data ? new ProgramData(*data) : 0);
+       if(material)
+               finalize_material(0);
+}
+
+const string &RenderPass::get_slotted_uniform_name(const string &slot) const
+{
+       map<string, string>::const_iterator i = uniform_slots.find(slot);
+       if(i==uniform_slots.end())
+       {
+               static string empty;
+               return empty;
+       }
+       return i->second;
+}
+
+void RenderPass::set_material(const Material *mat)
+{
+       material = mat;
+       material.keep();
+       finalize_material(0);
+}
+
+void RenderPass::set_texture(unsigned index, const Texture *tex)
+{
+       if(!texturing)
+               texturing = new Texturing;
+
+       texturing->attach(index, *tex, texturing->get_attached_sampler(index));
+}
+
+int RenderPass::get_texture_index(const string &n) const
+{
+       map<string, unsigned>::const_iterator i = tex_names.find(n);
+       if(i==tex_names.end())
+               return -1;
+       return i->second;
+}
+
+void RenderPass::apply(Renderer &renderer) const
+{
+       renderer.set_texturing(texturing);
+       renderer.set_material(material.get());
+       renderer.set_shader_program(shprog.get(), shdata.get());
+       renderer.set_reverse_winding(back_faces);
+}
+
+
+RenderPass::Loader::Loader(RenderPass &p):
+       DataFile::CollectionObjectLoader<RenderPass>(p, 0)
+{
+       init();
+}
+
+RenderPass::Loader::Loader(RenderPass &p, Collection &c):
+       DataFile::CollectionObjectLoader<RenderPass>(p, &c)
+{
+       init();
+}
+
+void RenderPass::Loader::init()
+{
+       add("shader",   &Loader::shader);
+       add("material", &Loader::material_inline);
+       add("material", &Loader::material);
+       add("material_slot", &RenderPass::material_slot);
+       add("back_faces",&RenderPass::back_faces);
+       add("texunit",  &Loader::texunit);
+       add("texunit",  &Loader::texunit_auto);
+       add("texunit",  &Loader::texunit_named);
+       add("uniforms", &Loader::uniforms);
+       add("uniform_slot", &Loader::uniform_slot);
+       add("uniform_slot", &Loader::uniform_slot2);
+}
+
+void RenderPass::Loader::material_inline()
+{
+       Material::GenericLoader ldr(coll);
+       load_sub_with(ldr);
+       obj.material = ldr.get_material();
+       obj.finalize_material(coll);
+}
+
+void RenderPass::Loader::material(const string &name)
+{
+       obj.material = &get_collection().get<Material>(name);
+       obj.material.keep();
+       obj.finalize_material(coll);
+}
+
+void RenderPass::Loader::shader(const string &n)
+{
+       obj.shprog = &get_collection().get<Program>(n);
+       obj.shprog.keep();
+       obj.shprog_from_material = false;
+       if(obj.shdata)
+               obj.shdata = new ProgramData(*obj.shdata, obj.shprog.get());
+       if(obj.material)
+               obj.finalize_material(coll);
+}
+
+void RenderPass::Loader::texunit(unsigned i)
+{
+       if(!obj.texturing)
+               obj.texturing = new Texturing;
+       TextureLoader ldr(*obj.texturing, i, coll);
+       load_sub_with(ldr);
+}
+
+void RenderPass::Loader::texunit_auto(const string &n)
+{
+       if(!obj.texturing)
+               obj.texturing = new Texturing;
+       int i = obj.texturing->find_free_unit(n);
+       if(i<0)
+               throw runtime_error("no free texunit");
+       texunit_named(i, n);
+}
+
+void RenderPass::Loader::texunit_named(unsigned i, const string &n)
+{
+       texunit(i);
+       obj.tex_names[n] = i;
+       obj.ensure_private_shader_data();
+       obj.shdata->uniform(n, static_cast<int>(i));
+}
+
+void RenderPass::Loader::uniforms()
+{
+       obj.ensure_private_shader_data();
+       load_sub(*obj.shdata);
+}
+
+void RenderPass::Loader::uniform_slot(const string &name)
+{
+       uniform_slot2(name, name);
+}
+
+void RenderPass::Loader::uniform_slot2(const string &name, const string &slot)
+{
+       obj.uniform_slots[slot] = name;
+}
+
+
+RenderPass::TextureLoader::TextureLoader(Texturing &t, unsigned i, Collection *c):
+       DataFile::CollectionObjectLoader<Texturing>(t, c),
+       index(i),
+       tex(0),
+       samp(0)
+{
+       add("sampler",   &TextureLoader::sampler);
+       add("texture",   &TextureLoader::texture);
+}
+
+void RenderPass::TextureLoader::finish()
+{
+       if(tex)
+               obj.attach(index, *tex, samp);
+       else if(samp)
+               obj.attach(index, *samp);
+}
+
+void RenderPass::TextureLoader::sampler(const string &name)
+{
+       samp = &get_collection().get<Sampler>(name);
+}
+
+void RenderPass::TextureLoader::texture(const string &name)
+{
+       tex = &get_collection().get<Texture>(name);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/materials/renderpass.h b/source/materials/renderpass.h
new file mode 100644 (file)
index 0000000..9e70437
--- /dev/null
@@ -0,0 +1,103 @@
+#ifndef MSP_GL_RENDERPASS_H_
+#define MSP_GL_RENDERPASS_H_
+
+#include <msp/core/refptr.h>
+#include <msp/datafile/objectloader.h>
+
+namespace Msp {
+namespace GL {
+
+class Material;
+class Program;
+class ProgramData;
+class Renderer;
+class Sampler;
+class Texture;
+class Texturing;
+
+/**
+Encapsulates the data that determines the appearance of a rendered surface.
+This includes shader and data for it, material and texturing.
+*/
+class RenderPass
+{
+public:
+       class Loader: public DataFile::CollectionObjectLoader<RenderPass>
+       {
+       public:
+               Loader(RenderPass &);
+               Loader(RenderPass &, Collection &);
+
+       private:
+               void init();
+
+               void material_inline();
+               void material(const std::string &);
+               void shader(const std::string &);
+               void texunit(unsigned);
+               void texunit_auto(const std::string &);
+               void texunit_named(unsigned, const std::string &);
+               void uniforms();
+               void uniform_slot(const std::string &);
+               void uniform_slot2(const std::string &, const std::string &);
+       };
+
+private:
+       struct TextureLoader: public DataFile::CollectionObjectLoader<Texturing>
+       {
+       private:
+               unsigned index;
+               const Texture *tex;
+               const Sampler *samp;
+
+       public:
+               TextureLoader(Texturing &, unsigned, Collection *);
+       private:
+               virtual void finish();
+
+               void sampler(const std::string &);
+               void texture(const std::string &);
+       };
+
+       RefPtr<const Program> shprog;
+       bool shprog_from_material;
+       RefPtr<ProgramData> shdata;
+       std::map<std::string, std::string> uniform_slots;
+       RefPtr<const Material> material;
+       std::string material_slot;
+       Texturing *texturing;
+       std::map<std::string, unsigned> tex_names;
+       bool back_faces;
+
+public:
+       RenderPass();
+       RenderPass(const RenderPass &);
+       RenderPass &operator=(const RenderPass &);
+       ~RenderPass();
+
+private:
+       void finalize_material(DataFile::Collection *);
+       void maybe_create_material_shader(DataFile::Collection *);
+       void ensure_private_shader_data();
+
+public:
+       void set_shader_program(const Program *, const ProgramData *);
+       const Program *get_shader_program() const { return shprog.get(); }
+       const ProgramData *get_shader_data() const { return shdata.get(); }
+       const std::string &get_slotted_uniform_name(const std::string &) const;
+       void set_material(const Material *);
+       const Material *get_material() const { return material.get(); }
+       const std::string &get_material_slot_name() const { return material_slot; }
+       void set_texture(unsigned, const Texture *);
+       const Texturing *get_texturing() const { return texturing; }
+       int get_texture_index(const std::string &) const;
+       void set_back_faces(bool);
+       bool get_back_faces() const { return back_faces; }
+
+       void apply(Renderer &) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/materials/technique.cpp b/source/materials/technique.cpp
new file mode 100644 (file)
index 0000000..09799ac
--- /dev/null
@@ -0,0 +1,185 @@
+#include <msp/core/refptr.h>
+#include <msp/datafile/collection.h>
+#include <msp/strings/format.h>
+#include "material.h"
+#include "program.h"
+#include "programdata.h"
+#include "tag.h"
+#include "technique.h"
+#include "texture.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+RenderPass &Technique::add_pass(const Tag &tag)
+{
+       return insert_unique(passes, tag, RenderPass())->second;
+}
+
+bool Technique::has_pass(const Tag &tag) const
+{
+       return passes.count(tag);
+}
+
+const RenderPass &Technique::get_pass(const Tag &tag) const
+{
+       return get_item(passes, tag);
+}
+
+const RenderPass *Technique::find_pass(const Tag &tag) const
+{
+       PassMap::const_iterator i = passes.find(tag);
+       return (i!=passes.end() ? &i->second : 0);
+}
+
+bool Technique::replace_texture(const string &slot, const Texture &tex)
+{
+       bool replaced = false;
+       for(PassMap::iterator i=passes.begin(); i!=passes.end(); ++i)
+       {
+               int index = i->second.get_texture_index(slot);
+               if(index>=0)
+               {
+                       i->second.set_texture(index, &tex);
+                       replaced = true;
+               }
+       }
+
+       return replaced;
+}
+
+bool Technique::replace_material(const string &slot, const Material &mat)
+{
+       bool replaced = false;
+       for(PassMap::iterator i=passes.begin(); i!=passes.end(); ++i)
+       {
+               const string &pass_slot = i->second.get_material_slot_name();
+               if(!pass_slot.empty() && pass_slot==slot)
+               {
+                       i->second.set_material(&mat);
+                       replaced = true;
+               }
+       }
+
+       return replaced;
+}
+
+bool Technique::replace_uniforms(const ProgramData &shdata)
+{
+       bool replaced = false;
+       const vector<string> &uniform_names = shdata.get_uniform_names();
+       for(PassMap::iterator i=passes.begin(); i!=passes.end(); ++i)
+       {
+               RefPtr<ProgramData> new_shdata;
+               for(vector<string>::const_iterator j=uniform_names.begin(); j!=uniform_names.end(); ++j)
+               {
+                       const string &name = i->second.get_slotted_uniform_name(*j);
+                       if(name.empty())
+                               continue;
+
+                       if(!new_shdata)
+                               new_shdata = new ProgramData(*i->second.get_shader_data());
+
+                       new_shdata->uniform(name, shdata.get_uniform(*j));
+                       replaced = true;
+               }
+
+               if(new_shdata)
+                       i->second.set_shader_program(i->second.get_shader_program(), new_shdata.get());
+       }
+
+       return replaced;
+}
+
+bool Technique::has_shaders() const
+{
+       for(PassMap::const_iterator i=passes.begin(); i!=passes.end(); ++i)
+               if(i->second.get_shader_program())
+                       return true;
+       return false;
+}
+
+
+Technique::Loader::Loader(Technique &t):
+       DataFile::CollectionObjectLoader<Technique>(t, 0)
+{
+       init();
+}
+
+Technique::Loader::Loader(Technique &t, Collection &c):
+       DataFile::CollectionObjectLoader<Technique>(t, &c)
+{
+       init();
+}
+
+void Technique::Loader::init()
+{
+       add("inherit", &Loader::inherit);
+       add("pass", &Loader::pass);
+}
+
+void Technique::Loader::inherit(const string &n)
+{
+       obj.passes = get_collection().get<Technique>(n).get_passes();
+       InheritLoader ldr(obj, get_collection());
+       load_sub_with(ldr);
+}
+
+void Technique::Loader::pass(const string &n)
+{
+       RenderPass p;
+       if(coll)
+               load_sub(p, get_collection());
+       else
+               load_sub(p);
+
+       if(!p.get_shader_program())
+               throw logic_error("no shader program in pass");
+
+       insert_unique(obj.passes, n, p);
+}
+
+
+Technique::InheritLoader::InheritLoader(Technique &t, Collection &c):
+       DataFile::CollectionObjectLoader<Technique>(t, &c)
+{
+       add("material", &InheritLoader::material);
+       add("texture", &InheritLoader::texture);
+       add("uniforms", &InheritLoader::uniforms);
+}
+
+void Technique::InheritLoader::material(const string &slot, const string &name)
+{
+       const Material &mat = get_collection().get<Material>(name);
+       if(obj.replace_material(slot, mat))
+               return;
+
+       // For backwards compatibility
+       RenderPass &pass = get_item(obj.passes, slot);
+       if(const Material *base_mat = pass.get_material())
+       {
+               for(PassMap::iterator i=obj.passes.begin(); i!=obj.passes.end(); ++i)
+                       if(i->second.get_material()==base_mat)
+                               i->second.set_material(&mat);
+       }
+       else
+               pass.set_material(&mat);
+}
+
+void Technique::InheritLoader::texture(const string &slot, const string &name)
+{
+       if(!obj.replace_texture(slot, get_collection().get<Texture>(name)))
+               throw key_error(slot);
+}
+
+void Technique::InheritLoader::uniforms()
+{
+       ProgramData shdata;
+       load_sub(shdata);
+       obj.replace_uniforms(shdata);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/materials/technique.h b/source/materials/technique.h
new file mode 100644 (file)
index 0000000..762b98f
--- /dev/null
@@ -0,0 +1,64 @@
+#ifndef MSP_GL_TECHNIQUE_H_
+#define MSP_GL_TECHNIQUE_H_
+
+#include "renderpass.h"
+#include "tag.h"
+
+namespace Msp {
+namespace GL {
+
+class Material;
+class Texture;
+
+/**
+Ties multiple tagged render passes together.
+*/
+class Technique
+{
+public:
+       class Loader: public Msp::DataFile::CollectionObjectLoader<Technique>
+       {
+       public:
+               Loader(Technique &);
+               Loader(Technique &, Collection &);
+
+       private:
+               void init();
+               void inherit(const std::string &);
+               void pass(const std::string &);
+       };
+
+private:
+       class InheritLoader: public Msp::DataFile::CollectionObjectLoader<Technique>
+       {
+       public:
+               InheritLoader(Technique &, Collection &);
+
+       private:
+               void material(const std::string &, const std::string &);
+               void texture(const std::string &, const std::string &);
+               void uniforms();
+       };
+
+public:
+       typedef std::map<Tag, RenderPass> PassMap;
+
+private:
+       PassMap passes;
+
+public:
+       RenderPass &add_pass(const Tag &);
+       bool has_pass(const Tag &) const;
+       const RenderPass &get_pass(const Tag &) const;
+       const RenderPass *find_pass(const Tag &) const;
+       const PassMap &get_passes() const { return passes; }
+       bool replace_texture(const std::string &, const Texture &);
+       bool replace_material(const std::string &, const Material &);
+       bool replace_uniforms(const ProgramData &);
+       bool has_shaders() const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/matrix.cpp b/source/matrix.cpp
deleted file mode 100644 (file)
index 311958a..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-#include <algorithm>
-#include <cmath>
-#include <msp/geometry/affinetransformation.h>
-#include "error.h"
-#include "matrix.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Matrix::Matrix():
-       Base(Base::identity())
-{ }
-
-Matrix::Matrix(const float *m):
-       Base(m)
-{ }
-
-Matrix::Matrix(const LinAl::Matrix<float, 4, 4> &other):
-       Base(other)
-{ }
-
-Matrix &Matrix::translate(const Vector3 &t)
-{
-       return multiply(translation(t));
-}
-
-Matrix &Matrix::rotate(const Angle &a, const Vector3 &x)
-{
-       return multiply(rotation(a, x));
-}
-
-Matrix &Matrix::scale(const Vector3 &s)
-{
-       return multiply(scaling(s));
-}
-
-float Matrix::operator[](unsigned i) const
-{
-       if(i>=16)
-               throw out_of_range("Matrix::operator[]");
-       return operator()(i%4, i/4);
-}
-
-Matrix Matrix::translation(const Vector3 &t)
-{
-       return Geometry::AffineTransformation<float, 3>::translation(t).get_matrix();
-}
-
-Matrix Matrix::rotation(const Angle &a, const Vector3 &x)
-{
-       return Geometry::AffineTransformation<float, 3>::rotation(a, x).get_matrix();
-}
-
-Matrix Matrix::scaling(const Vector3 &s)
-{
-       return Geometry::AffineTransformation<float, 3>::scaling(s).get_matrix();
-}
-
-Matrix Matrix::ortho(float l, float r, float b, float t, float n, float f)
-{
-       if(l==r || b==t || n==f)
-               throw invalid_argument("Matrix::ortho");
-
-       Matrix result;
-       result(0, 0) = 2/(r-l);
-       result(1, 1) = 2/(t-b);
-       result(2, 2) = -2/(f-n);
-       result(0, 3) = -(r+l)/(r-l);
-       result(1, 3) = -(t+b)/(t-b);
-       result(2, 3) = -(f+n)/(f-n);
-       return result;
-}
-
-Matrix Matrix::ortho_centered(float w, float h)
-{
-       return ortho(-w/2, w/2, -h/2, h/2, -1, 1);
-}
-
-Matrix Matrix::ortho_bottomleft(float w, float h)
-{
-       return ortho(0, w, 0, h, -1, 1);
-}
-
-Matrix Matrix::ortho_topleft(float w, float h)
-{
-       return ortho(0, w, h, 0, -1, 1);
-}
-
-Matrix Matrix::frustum(float l, float r, float b, float t, float n, float f)
-{
-       if(l==r || b==t || n<=0 || f<=n)
-               throw invalid_argument("Matrix::frustum");
-
-       Matrix result;
-       result(0, 0) = 2*n/(r-l);
-       result(1, 1) = 2*n/(t-b);
-       result(0, 2) = (r+l)/(r-l);
-       result(1, 2) = (t+b)/(t-b);
-       result(2, 2) = -(f+n)/(f-n);
-       result(3, 2) = -1;
-       result(2, 3) = -2*f*n/(f-n);
-       result(3, 3) = 0;
-       return result;
-}
-
-Matrix Matrix::frustum_centered(float w, float h, float n, float f)
-{
-       return frustum(-w/2, w/2, -h/2, h/2, n, f);
-}
-
-Matrix Matrix::perspective(const Angle &h, float a, float n, float f)
-{
-       float hh = tan(h/2.0f)*n;
-       return frustum(-hh*a, hh*a, -hh, hh, n, f);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/matrix.h b/source/matrix.h
deleted file mode 100644 (file)
index cb61e29..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-#ifndef MSP_GL_MATRIX_H_
-#define MSP_GL_MATRIX_H_
-
-#include <vector>
-#include <msp/geometry/angle.h>
-#include <msp/linal/squarematrix.h>
-#include "gl.h"
-#include "vector.h"
-
-namespace Msp {
-namespace GL {
-
-class Matrix: public LinAl::SquareMatrix<float, 4>
-{
-private:
-       typedef LinAl::SquareMatrix<float, 4> Base;
-       typedef Geometry::Angle<float> Angle;
-
-public:
-       Matrix();
-       Matrix(const float *);
-       Matrix(const LinAl::Matrix<float, 4, 4> &);
-
-       const float *data() const { return &Base::operator()(0, 0); }
-
-       Matrix &multiply(const Matrix &m) { return operator*=(m); }
-       Matrix &translate(float x, float y, float z) { return translate(Vector3(x, y, z)); }
-       Matrix &translate(const Vector3 &);
-       Matrix &rotate(const Angle &a, float x, float y, float z) { return rotate(a, Vector3(x, y, z)); }
-       Matrix &rotate(const Angle &, const Vector3 &);
-       Matrix &rotate(float a, float x, float y, float z) { return rotate(Angle::from_radians(a), Vector3(x, y, z)); }
-       Matrix &rotate(float a, const Vector3 &x) { return rotate(Angle::from_radians(a), x); }
-       Matrix &rotate_deg(float a, float x, float y, float z) { return rotate(Angle::from_degrees(a), Vector3(x, y, z)); }
-       Matrix &rotate_deg(float a, const Vector3 & x) { return rotate(Angle::from_degrees(a), x); }
-       Matrix &scale(float s) { return scale(Vector3(s, s, s)); }
-       Matrix &scale(float x, float y, float z) { return scale(Vector3(x, y, z)); }
-       Matrix &scale(const Vector3 &);
-
-       Matrix operator*(const Matrix &m) const { return static_cast<const Base &>(*this)*static_cast<const Base &>(m); }
-       Matrix &operator*=(const Matrix &m) { Base::operator*=(m); return *this; }
-       Matrix operator*(float s) const { return static_cast<const Base &>(*this)*s; }
-       Matrix &operator*=(float s) { Base::operator*=(s); return *this; }
-       Vector4 operator*(const Vector4 &v) const { return static_cast<const Base &>(*this)*v; }
-       Vector3 operator*(const Vector3 &v) const { return ((*this)*compose(v, 1.0f)).slice<3>(0); }
-       float operator[](unsigned) const;
-
-       static Matrix translation(float x, float y, float z) { return translation(Vector3(x, y, z)); }
-       static Matrix translation(const Vector3 &);
-       static Matrix rotation(const Angle &a, float x, float y, float z) { return rotation(a, Vector3(x, y, z)); }
-       static Matrix rotation(const Angle &, const Vector3 &);
-       static Matrix rotation(float a, float x, float y, float z) { return rotation(Angle::from_radians(a), Vector3(x, y, z)); }
-       static Matrix rotation(float a, const Vector3 &x) { return rotation(Angle::from_radians(a), x); }
-       static Matrix rotation_deg(float a, float x, float y, float z) { return rotation(Angle::from_degrees(a), Vector3(x, y, z)); }
-       static Matrix rotation_deg(float a, const Vector3 &x) { return rotation(Angle::from_degrees(a), x); }
-       static Matrix scaling(float s) { return scaling(Vector3(s, s, s)); }
-       static Matrix scaling(float x, float y, float z) { return scaling(Vector3(x, y, z)); }
-       static Matrix scaling(const Vector3 &);
-
-       static Matrix ortho(float, float, float, float, float, float);
-       static Matrix ortho_centered(float, float);
-       static Matrix ortho_bottomleft(float, float);
-       static Matrix ortho_topleft(float, float);
-       static Matrix frustum(float, float, float, float, float, float);
-       static Matrix frustum_centered(float, float, float, float);
-       static Matrix perspective(const Angle &, float, float, float);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/mesh.cpp b/source/mesh.cpp
deleted file mode 100644 (file)
index dba8a99..0000000
+++ /dev/null
@@ -1,316 +0,0 @@
-#include <msp/gl/extensions/arb_vertex_array_object.h>
-#include <msp/gl/extensions/arb_vertex_buffer_object.h>
-#include <msp/gl/extensions/arb_vertex_shader.h>
-#include "buffer.h"
-#include "error.h"
-#include "mesh.h"
-#include "renderer.h"
-#include "resourcemanager.h"
-#include "vertexsetup.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Mesh::Mesh(ResourceManager *rm):
-       vertices(VERTEX3)
-{
-       init(rm);
-}
-
-Mesh::Mesh(const VertexFormat &f, ResourceManager *rm):
-       vertices(f)
-{
-       init(rm);
-}
-
-void Mesh::init(ResourceManager *rm)
-{
-       vbuf = 0;
-       ibuf = 0;
-       dirty = 0;
-       disallow_rendering = false;
-       winding = 0;
-
-       if(rm)
-               set_manager(rm);
-}
-
-Mesh::~Mesh()
-{
-       set_manager(0);
-       batches.clear();
-       delete vbuf;
-       delete ibuf;
-}
-
-void Mesh::clear()
-{
-       vertices.clear();
-       batches.clear();
-}
-
-void Mesh::check_buffers(unsigned mask)
-{
-       if(mask&VERTEX_BUFFER)
-       {
-               unsigned req_size = vertices.get_required_buffer_size();
-               if(!vbuf || (vbuf->get_size()>0 && vbuf->get_size()<req_size))
-               {
-                       delete vbuf;
-                       vbuf = new Buffer(ARRAY_BUFFER);
-                       vertices.use_buffer(vbuf);
-                       vtx_setup.set_vertex_array(vertices);
-                       dirty |= VERTEX_BUFFER;
-               }
-       }
-
-       if(mask&INDEX_BUFFER)
-       {
-               unsigned req_size = (batches.empty() ? 0 : batches.front().get_required_buffer_size());
-               if(!ibuf || (ibuf->get_size()>0 && ibuf->get_size()<req_size))
-               {
-                       delete ibuf;
-                       ibuf = new Buffer(ELEMENT_ARRAY_BUFFER);
-                       if(!batches.empty())
-                               batches.front().change_buffer(ibuf);
-                       vtx_setup.set_index_buffer(*ibuf);
-                       dirty |= INDEX_BUFFER;
-               }
-       }
-}
-
-unsigned Mesh::get_n_vertices() const
-{
-       return vertices.size();
-}
-
-float *Mesh::modify_vertex(unsigned i)
-{
-       return vertices.modify(i);
-}
-
-void Mesh::add_batch(const Batch &b)
-{
-       if(batches.empty())
-       {
-               batches.push_back(b);
-               if(ibuf)
-                       batches.back().use_buffer(ibuf);
-       }
-       else if(batches.back().can_append(b.get_type()))
-               batches.back().append(b);
-       else
-       {
-               bool reallocate = (batches.size()==batches.capacity());
-               if(reallocate)
-               {
-                       for(vector<Batch>::iterator i=batches.end(); i!=batches.begin(); )
-                               (--i)->use_buffer(0);
-               }
-
-               Batch *prev = &batches.back();
-               batches.push_back(b);
-               if(reallocate)
-               {
-                       prev = 0;
-                       for(vector<Batch>::iterator i=batches.begin(); i!=batches.end(); ++i)
-                       {
-                               i->use_buffer(ibuf, prev);
-                               prev = &*i;
-                       }
-               }
-               else
-                       batches.back().use_buffer(ibuf, prev);
-       }
-
-       check_buffers(INDEX_BUFFER);
-}
-
-void Mesh::set_winding(const WindingTest *w)
-{
-       winding = w;
-}
-
-void Mesh::draw(Renderer &renderer) const
-{
-       draw(renderer, 0, 0);
-}
-
-void Mesh::draw_instanced(Renderer &renderer, const VertexSetup &vs, unsigned count) const
-{
-       if(vs.get_vertex_array()!=&vertices)
-               throw invalid_argument("Mesh::draw_instanced");
-
-       draw(renderer, &vs, count);
-}
-
-void Mesh::draw(Renderer &renderer, const VertexSetup *vs, unsigned count) const
-{
-       if(manager)
-       {
-               manager->resource_used(*this);
-               if(disallow_rendering)
-                       return;
-       }
-
-       if(dirty)
-               resize_buffers();
-
-       renderer.set_vertex_setup(vs ? vs : &vtx_setup);
-       renderer.set_winding_test(winding);
-
-       if(!count)
-       {
-               for(vector<Batch>::const_iterator i=batches.begin(); i!=batches.end(); ++i)
-                       renderer.draw(*i);
-       }
-       else
-       {
-               for(vector<Batch>::const_iterator i=batches.begin(); i!=batches.end(); ++i)
-                       renderer.draw_instanced(*i, count);
-       }
-}
-
-void Mesh::resize_buffers() const
-{
-       if(dirty&VERTEX_BUFFER)
-               vbuf->storage(vertices.get_required_buffer_size());
-       if(dirty&INDEX_BUFFER)
-               ibuf->storage(batches.front().get_required_buffer_size());
-       dirty = 0;
-}
-
-Resource::AsyncLoader *Mesh::load(IO::Seekable &io, const Resources *)
-{
-       return new AsyncLoader(*this, io);
-}
-
-UInt64 Mesh::get_data_size() const
-{
-       UInt64 size = 0;
-       if(vbuf)
-               size += vbuf->get_size();
-       if(ibuf)
-               size += ibuf->get_size();
-       return size;
-}
-
-void Mesh::unload()
-{
-       vertices.clear();
-       vertices.use_buffer(0);
-       batches.clear();
-       delete vbuf;
-       delete ibuf;
-       vbuf = 0;
-       ibuf = 0;
-}
-
-
-Mesh::Loader::Loader(Mesh &m, bool g):
-       DataFile::ObjectLoader<Mesh>(m),
-       allow_gl_calls(g)
-{
-       add("batch",    &Loader::batch);
-       add("vertices", &Loader::vertices);
-       add("winding",  &Loader::winding);
-}
-
-void Mesh::Loader::vertices(const vector<VertexComponent> &c)
-{
-       if(c.empty())
-               throw invalid_argument("No vertex components");
-
-       VertexFormat fmt;
-       for(vector<VertexComponent>::const_iterator i=c.begin(); i!=c.end(); ++i)
-               fmt = (fmt, *i);
-       obj.vertices.reset(fmt);
-       load_sub(obj.vertices);
-       if(allow_gl_calls)
-       {
-               obj.check_buffers(VERTEX_BUFFER);
-               obj.vtx_setup.refresh();
-       }
-}
-
-void Mesh::Loader::batch(PrimitiveType p)
-{
-       Batch btc(p);
-       load_sub(btc);
-       obj.add_batch(btc);
-}
-
-void Mesh::Loader::winding(FaceWinding w)
-{
-       if(w==CLOCKWISE)
-               obj.winding = &WindingTest::clockwise();
-       else if(w==COUNTERCLOCKWISE)
-               obj.winding = &WindingTest::counterclockwise();
-}
-
-
-Mesh::AsyncLoader::AsyncLoader(Mesh &m, IO::Seekable &i):
-       mesh(m),
-       io(i),
-       vertex_updater(0),
-       index_updater(0),
-       phase(0)
-{
-       mesh.disallow_rendering = true;
-       mesh.check_buffers(VERTEX_BUFFER|INDEX_BUFFER);
-}
-
-Mesh::AsyncLoader::~AsyncLoader()
-{
-       mesh.disallow_rendering = false;
-       delete vertex_updater;
-       delete index_updater;
-}
-
-bool Mesh::AsyncLoader::needs_sync() const
-{
-       return phase%2;
-}
-
-bool Mesh::AsyncLoader::process()
-{
-       if(phase==0)
-       {
-               // TODO use correct filename
-               DataFile::Parser parser(io, "async");
-               Loader loader(mesh, false);
-               loader.load(parser);
-       }
-       else if(phase==1)
-       {
-               mesh.resize_buffers();
-               mesh.vtx_setup.refresh();
-               vertex_updater = mesh.vertices.refresh_async();
-               if(!mesh.batches.empty())
-                       index_updater = mesh.batches.front().refresh_async();
-       }
-       else if(phase==2)
-       {
-               if(vertex_updater)
-                       vertex_updater->upload_data();
-               if(index_updater)
-                       index_updater->upload_data();
-       }
-       else if(phase==3)
-       {
-               delete vertex_updater;
-               vertex_updater = 0;
-               delete index_updater;
-               index_updater = 0;
-       }
-
-       ++phase;
-       if(phase==1 && !mesh.vbuf && !mesh.ibuf)
-               phase += 3;
-       return phase>3;
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/mesh.h b/source/mesh.h
deleted file mode 100644 (file)
index 57c87b7..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-#ifndef MSP_GL_MESH_H_
-#define MSP_GL_MESH_H_
-
-#include <msp/datafile/objectloader.h>
-#include "batch.h"
-#include "resource.h"
-#include "vertexarray.h"
-#include "vertexsetup.h"
-#include "windingtest.h"
-
-namespace Msp {
-namespace GL {
-
-class Buffer;
-class Renderer;
-
-/**
-Raw mesh data, consisting of a VertexArray and one or more Batches.  Though a
-Mesh can draw itself, it's usually used as part of Renderables rather than on
-its own.
-*/
-class Mesh: public Resource
-{
-       friend class MeshBuilder;
-
-public:
-       class Loader: public DataFile::ObjectLoader<Mesh>
-       {
-       private:
-               bool allow_gl_calls;
-
-       public:
-               Loader(Mesh &, bool = true);
-       private:
-               void vertices(const std::vector<VertexComponent> &);
-               void batch(PrimitiveType);
-               void winding(FaceWinding);
-       };
-
-private:
-       class AsyncLoader: public Resource::AsyncLoader
-       {
-       private:
-               Mesh &mesh;
-               IO::Seekable &io;
-               Bufferable::AsyncUpdater *vertex_updater;
-               Bufferable::AsyncUpdater *index_updater;
-               unsigned phase;
-
-       public:
-               AsyncLoader(Mesh &, IO::Seekable &);
-               ~AsyncLoader();
-
-               virtual bool needs_sync() const;
-               virtual bool process();
-       };
-
-       enum BufferMask
-       {
-               VERTEX_BUFFER = 1,
-               INDEX_BUFFER = 2
-       };
-
-       VertexArray vertices;
-       std::vector<Batch> batches;
-       Buffer *vbuf;
-       Buffer *ibuf;
-       VertexSetup vtx_setup;
-       mutable unsigned short dirty;
-       bool disallow_rendering;
-       const WindingTest *winding;
-
-public:
-       Mesh(ResourceManager * = 0);
-       Mesh(const VertexFormat &, ResourceManager * = 0);
-private:
-       void init(ResourceManager *);
-public:
-       ~Mesh();
-
-       void clear();
-private:
-       void check_buffers(unsigned);
-
-public:
-       const VertexArray &get_vertices() const { return vertices; }
-       const VertexSetup &get_vertex_setup() const { return vtx_setup; }
-       const Buffer *get_index_buffer() const { return ibuf; }
-       unsigned get_n_vertices() const;
-       float *modify_vertex(unsigned);
-
-       void add_batch(const Batch &b);
-       const std::vector<Batch> &get_batches() const { return batches; }
-
-       void set_winding(const WindingTest *);
-
-       void draw(Renderer &) const;
-       void draw_instanced(Renderer &, const VertexSetup &, unsigned) const;
-private:
-       void draw(Renderer &, const VertexSetup *, unsigned) const;
-       void resize_buffers() const;
-
-public:
-       virtual int get_load_priority() const { return 1; }
-       virtual Resource::AsyncLoader *load(IO::Seekable &, const Resources * = 0);
-       virtual UInt64 get_data_size() const;
-       virtual void unload();
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/meshbuilder.cpp b/source/meshbuilder.cpp
deleted file mode 100644 (file)
index 92e2ee0..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#include "mesh.h"
-#include "meshbuilder.h"
-
-namespace Msp {
-namespace GL {
-
-MeshBuilder::MeshBuilder(Mesh &m):
-       PrimitiveBuilder(m.vertices),
-       mesh(m),
-       batch(0)
-{ }
-
-MeshBuilder::~MeshBuilder()
-{
-       mesh.check_buffers(Mesh::VERTEX_BUFFER);
-}
-
-void MeshBuilder::auto_offset()
-{
-       offset(mesh.get_vertices().size());
-}
-
-void MeshBuilder::begin_()
-{
-       batch = new Batch(type);
-}
-
-void MeshBuilder::end_()
-{
-       mesh.add_batch(*batch);
-       delete batch;
-       batch = 0;
-}
-
-void MeshBuilder::element_(unsigned i)
-{
-       batch->append(i);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/meshbuilder.h b/source/meshbuilder.h
deleted file mode 100644 (file)
index 5f4a9dc..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifndef MSP_GL_MESHBUILDER_H_
-#define MSP_GL_MESHBUILDER_H_
-
-#include "primitivebuilder.h"
-
-namespace Msp {
-namespace GL {
-
-class Batch;
-class Mesh;
-
-class MeshBuilder: public PrimitiveBuilder
-{
-private:
-       Mesh &mesh;
-       Batch *batch;
-
-public:
-       MeshBuilder(Mesh &);
-       ~MeshBuilder();
-
-       void auto_offset();
-private:
-       virtual void begin_();
-       virtual void end_();
-       virtual void element_(unsigned);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/misc.cpp b/source/misc.cpp
deleted file mode 100644 (file)
index 39810be..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-#include <msp/gl/extensions/arb_shader_objects.h>
-#include "misc.h"
-
-namespace Msp {
-namespace GL {
-
-void enable(GLenum state)
-{
-       glEnable(state);
-}
-
-void disable(GLenum state)
-{
-       glDisable(state);
-}
-
-void set(GLenum state, bool value)
-{
-       if(value)
-               enable(state);
-       else
-               disable(state);
-}
-
-int get_i(GLenum state)
-{
-       int data;
-       glGetIntegerv(state, &data);
-       return data;
-}
-
-int get_shader_i(unsigned id, GLenum state)
-{
-       int data;
-       glGetShaderiv(id, state, &data);
-       return data;
-}
-
-int get_program_i(unsigned id, GLenum state)
-{
-       int data;
-       glGetProgramiv(id, state, &data);
-       return data;
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/misc.h b/source/misc.h
deleted file mode 100644 (file)
index 32adc9b..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef MSP_GL_MISC_H_
-#define MSP_GL_MISC_H_
-
-#include "gl.h"
-
-namespace Msp {
-namespace GL {
-
-void enable(GLenum);
-void disable(GLenum);
-void set(GLenum, bool);
-
-int get_i(GLenum);
-int get_shader_i(unsigned, GLenum);
-int get_program_i(unsigned, GLenum);
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/object.cpp b/source/object.cpp
deleted file mode 100644 (file)
index dc1f4f3..0000000
+++ /dev/null
@@ -1,271 +0,0 @@
-#include <msp/datafile/collection.h>
-#include <msp/strings/format.h>
-#include "error.h"
-#include "material.h"
-#include "mesh.h"
-#include "object.h"
-#include "objectinstance.h"
-#include "program.h"
-#include "programdata.h"
-#include "renderer.h"
-#include "resourcemanager.h"
-#include "technique.h"
-#include "texturing.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Matrix Object::identity_matrix;
-
-Object::Object():
-       lods(1),
-       lod0_watched(false)
-{ }
-
-Object::Object(const Mesh *m, const Technique *t):
-       lods(1),
-       lod0_watched(false)
-{
-       set_mesh(m);
-       set_technique(t);
-}
-
-// TODO should have copy-c'tor to set watch on lod0 mesh if necessary
-
-Object::~Object()
-{
-       if(lods[0].mesh && lod0_watched)
-               if(ResourceManager *rm = lods[0].mesh->get_manager())
-                       rm->unobserve_resource(*lods[0].mesh, *this);
-}
-
-Object::LevelOfDetail &Object::get_lod(unsigned i, const char *caller)
-{
-       if(i>lods.size())
-               throw out_of_range(caller);
-       if(i>0 && (!lods[0].mesh || !lods[0].technique))
-               throw invalid_operation(caller);
-
-       if(i==lods.size())
-               lods.push_back(lods.back());
-
-       return lods[i];
-}
-
-void Object::set_mesh(unsigned i, const Mesh *m)
-{
-       RefPtr<const Mesh> &ptr = get_lod(i, "Object::set_mesh").mesh;
-       if(i==0 && ptr && lod0_watched)
-               if(ResourceManager *rm = ptr->get_manager())
-                       rm->unobserve_resource(*ptr, *this);
-       ptr = m;
-       ptr.keep();
-       lod0_watched = false;
-
-       if(i==0 && m)
-               if(ResourceManager *rm = m->get_manager())
-               {
-                       rm->observe_resource(*m, *this);
-                       lod0_watched = true;
-               }
-
-       update_bounding_sphere();
-}
-
-void Object::update_bounding_sphere()
-{
-       vector<Vector3> points;
-       for(vector<LevelOfDetail>::const_iterator i=lods.begin(); i!=lods.end(); ++i)
-       {
-               if(!i->mesh || !i->mesh->is_loaded())
-                       continue;
-
-               const VertexArray &vertices = i->mesh->get_vertices();
-
-               int offset = vertices.get_format().offset(VERTEX3);
-               bool three = true;
-               if(offset<0)
-               {
-                       offset = vertices.get_format().offset(VERTEX2);
-                       three = false;
-                       if(offset<0)
-                               continue;
-               }
-
-               unsigned n_vertices = vertices.size();
-               points.reserve(points.size()+n_vertices);
-               for(unsigned j=0; j<n_vertices; ++j)
-               {
-                       const float *v = vertices[j];
-                       points.push_back(Vector3(v[offset], v[offset+1], (three ? v[offset+2] : 0.0f)));
-               }
-       }
-
-       /* Don't touch the bounding sphere if we had no vertices to avoid
-       overwriting a possible hint. */
-       if(points.empty())
-               return;
-
-       bounding_sphere = Geometry::BoundingSphere<float, 3>::from_point_cloud(points.begin(), points.end());
-}
-
-const Mesh *Object::get_mesh(unsigned i) const
-{
-       if(i>=lods.size())
-               return 0;
-
-       return lods[i].mesh.get();
-}
-
-void Object::set_technique(unsigned i, const Technique *t)
-{
-       RefPtr<const Technique> &ptr = get_lod(i, "Object::set_technique").technique;
-       ptr = t;
-       ptr.keep();
-}
-
-const Technique *Object::get_technique(unsigned i) const
-{
-       if(i>=lods.size())
-               return 0;
-
-       return lods[i].technique.get();
-}
-
-void Object::render(Renderer &renderer, const Tag &tag) const
-{
-       const RenderPass *pass = get_pass(tag, 0);
-       if(!pass)
-               return;
-
-       const Mesh *mesh = lods.front().mesh.get();
-       if (!mesh)
-               throw logic_error("no mesh");
-
-       Renderer::Push push(renderer);
-       pass->apply(renderer);
-
-       setup_render(renderer, tag);
-       mesh->draw(renderer);
-       finish_render(renderer, tag);
-}
-
-void Object::render(Renderer &renderer, const ObjectInstance &inst, const Tag &tag) const
-{
-       unsigned lod = min<unsigned>(inst.get_level_of_detail(renderer), lods.size()-1);
-       const RenderPass *pass = get_pass(tag, lod);
-       if(!pass)
-               return;
-
-       const Mesh *mesh = lods[lod].mesh.get();
-       if (!mesh)
-               throw logic_error("no mesh");
-
-       Renderer::Push push(renderer);
-       pass->apply(renderer);
-
-       setup_render(renderer, tag);
-       inst.setup_render(renderer, tag);
-       mesh->draw(renderer);
-       inst.finish_render(renderer, tag);
-       finish_render(renderer, tag);
-}
-
-const RenderPass *Object::get_pass(const Tag &tag, unsigned lod) const
-{
-       const Technique *tech = lods[lod].technique.get();
-       if(!tech)
-               throw logic_error("no technique");
-       return tech->find_pass(tag);
-}
-
-void Object::resource_loaded(Resource &res)
-{
-       if(&res==lods.front().mesh.get() && bounding_sphere.is_empty())
-               update_bounding_sphere();
-}
-
-void Object::resource_removed(Resource &res)
-{
-       if(&res==lods.front().mesh.get())
-               lod0_watched = false;
-}
-
-
-Object::Loader::Loader(Object &o):
-       LodLoader(o, 0, 0)
-{
-       init();
-}
-
-Object::Loader::Loader(Object &o, Collection &c):
-       LodLoader(o, 0, &c)
-{
-       init();
-}
-
-void Object::Loader::init()
-{
-       add("bounding_sphere_hint", &Loader::bounding_sphere_hint);
-       add("level_of_detail", &Loader::level_of_detail);
-}
-
-void Object::Loader::finish()
-{
-       obj.update_bounding_sphere();
-}
-
-void Object::Loader::bounding_sphere_hint(float x, float y, float z, float r)
-{
-       obj.bounding_sphere = Geometry::BoundingSphere<float, 3>(Vector3(x, y, z), r);
-}
-
-void Object::Loader::level_of_detail(unsigned i)
-{
-       LodLoader ldr(obj, i, coll);
-       load_sub_with(ldr);
-}
-
-
-Object::LodLoader::LodLoader(Object &o, unsigned i, Collection *c):
-       DataFile::CollectionObjectLoader<Object>(o, c),
-       index(i),
-       lod(obj.get_lod(index, "Object::LodLoader::LodLoader"))
-{
-       add("mesh",      &LodLoader::mesh_inline);
-       add("mesh",      &LodLoader::mesh);
-       add("technique", &LodLoader::technique_inline);
-       add("technique", &LodLoader::technique);
-}
-
-void Object::LodLoader::mesh(const string &n)
-{
-       obj.set_mesh(index, &get_collection().get<Mesh>(n));
-}
-
-void Object::LodLoader::mesh_inline()
-{
-       RefPtr<Mesh> msh = new Mesh;
-       load_sub(*msh);
-       lod.mesh = msh;
-}
-
-void Object::LodLoader::technique(const std::string &n)
-{
-       obj.set_technique(index, &get_collection().get<Technique>(n));
-}
-
-void Object::LodLoader::technique_inline()
-{
-       RefPtr<Technique> tech = new Technique;
-       if(coll)
-               load_sub(*tech, get_collection());
-       else
-               load_sub(*tech);
-       lod.technique = tech;
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/object.h b/source/object.h
deleted file mode 100644 (file)
index 48e3045..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-#ifndef MSP_GL_OBJECT_H_
-#define MSP_GL_OBJECT_H_
-
-#include <vector>
-#include "bindable.h"
-#include "renderable.h"
-#include "renderpass.h"
-#include "resourceobserver.h"
-
-namespace Msp {
-namespace GL {
-
-class Material;
-class Mesh;
-class ObjectInstance;
-class Technique;
-class Texture;
-
-/**
-Combines a Mesh with a Technique to give it an appearance.  The Technique will
-define which render passes the Object supports.
-
-In many cases, it's desirable to include multiple copies of an Object in a
-Scene, with different model matrices.  ObjectInstances can be used to alter the
-rendering of an object on a per-instance basis.
-
-Objects can have multiple levels of detail.  The most detailed level has index
-0, with increasing indices having less detail.  When rendering an instance, the
-instance's get_level_of_detail method is called to determine which LoD to use.
-*/
-class Object: public Renderable, private ResourceObserver
-{
-private:
-       struct LevelOfDetail;
-
-       class LodLoader: public DataFile::CollectionObjectLoader<Object>
-       {
-       private:
-               unsigned index;
-               LevelOfDetail &lod;
-
-       public:
-               LodLoader(Object &, unsigned, Collection *);
-
-       private:
-               void mesh(const std::string &);
-               void mesh_inline();
-               void technique(const std::string &);
-               void technique_inline();
-       };
-
-public:
-       class Loader: public LodLoader
-       {
-       public:
-               Loader(Object &);
-               Loader(Object &, Collection &);
-       private:
-               void init();
-               virtual void finish();
-
-               void bounding_sphere_hint(float, float, float, float);
-               void level_of_detail(unsigned);
-       };
-
-private:
-       struct LevelOfDetail
-       {
-               RefPtr<const Mesh> mesh;
-               RefPtr<const Technique> technique;
-       };
-
-       std::vector<LevelOfDetail> lods;
-       Geometry::BoundingSphere<float, 3> bounding_sphere;
-       bool lod0_watched;
-
-       static Matrix identity_matrix;
-
-public:
-       Object();
-       Object(const Mesh *, const Technique *);
-       ~Object();
-
-private:
-       LevelOfDetail &get_lod(unsigned, const char *);
-
-public:
-       /** Sets the mesh for the highest level of detail (index 0). */
-       void set_mesh(const Mesh *m) { set_mesh(0, m); }
-
-       /** Sets the mesh for a given level of detail.  Previous LoDs must have been
-       defined. */
-       void set_mesh(unsigned, const Mesh *);
-
-private:
-       void update_bounding_sphere();
-public:
-       const Mesh *get_mesh(unsigned = 0) const;
-
-       /** Sets the technique for the highest level of detail (index 0). */
-       void set_technique(const Technique *t) { set_technique(0, t); }
-
-       /** Sets the technique for a given level of detail.  Previous LoDs must have
-       been defined. */
-       void set_technique(unsigned, const Technique *);
-
-       const Technique *get_technique(unsigned = 0) const;
-       unsigned get_n_lods() const { return lods.size(); }
-
-       virtual const Matrix *get_matrix() const { return &identity_matrix; }
-       virtual const Geometry::BoundingSphere<float, 3> *get_bounding_sphere() const { return &bounding_sphere; }
-
-       virtual void render(Renderer &, const Tag & = Tag()) const;
-
-       /** Renders an instance of the object.  The instance's hook functions are
-       called before and after drawing the mesh. */
-       virtual void render(Renderer &, const ObjectInstance &, const Tag & = Tag()) const;
-
-protected:
-       virtual void setup_render(Renderer &, const Tag &) const { }
-       virtual void finish_render(Renderer &, const Tag &) const { }
-
-private:
-       const RenderPass *get_pass(const Tag &, unsigned) const;
-
-       virtual void resource_loaded(Resource &);
-       virtual void resource_removed(Resource &);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/objectinstance.cpp b/source/objectinstance.cpp
deleted file mode 100644 (file)
index dbbb85b..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#include "objectinstance.h"
-#include "renderer.h"
-#include "transform.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-ObjectInstance::ObjectInstance(const Object &obj):
-       object(obj)
-{ }
-
-void ObjectInstance::render(Renderer &renderer, const Tag &tag) const
-{
-       object.render(renderer, *this, tag);
-}
-
-void ObjectInstance::setup_render(Renderer &renderer, const Tag &) const
-{
-       renderer.transform(matrix);
-}
-
-unsigned ObjectInstance::get_level_of_detail(const Renderer &renderer) const
-{
-       return renderer.get_object_lod_bias();
-}
-
-
-ObjectInstance::Loader::Loader(ObjectInstance &o):
-       DataFile::ObjectLoader<ObjectInstance>(o)
-{
-       add("transform", &Loader::transform);
-}
-
-void ObjectInstance::Loader::transform()
-{
-       Transform trn;
-       load_sub(trn);
-       obj.matrix = trn.to_matrix();
-}
-
-} // namespace GL
-} // namespaec Msp
diff --git a/source/objectinstance.h b/source/objectinstance.h
deleted file mode 100644 (file)
index 0ebef2f..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-#ifndef MSP_GL_OBJETCINSTANCE_H_
-#define MSP_GL_OBJETCINSTANCE_H_
-
-#include <string>
-#include "object.h"
-#include "placeable.h"
-
-namespace Msp {
-namespace GL {
-
-/**
-Represents a single instance of an Object.  Thanks to being derived from
-Placeable in can be positioned without additional effort.  Other instance
-parameters can be set by overriding the hook functions.
-
-ObjectInstances can benefit from being put in an InstanceScene, which will
-render all instances of the same object consecutively.
-*/
-class ObjectInstance: public PlacedRenderable
-{
-public:
-       class Loader: public DataFile::ObjectLoader<ObjectInstance>
-       {
-       public:
-               Loader(ObjectInstance &);
-
-       private:
-               void transform();
-       };
-
-protected:
-       const Object &object;
-
-public:
-       ObjectInstance(const Object &);
-
-       const Object &get_object() const { return object; }
-       virtual IntPtr get_instance_key() const { return reinterpret_cast<IntPtr>(&object); }
-
-       virtual const Geometry::BoundingSphere<float, 3> *get_bounding_sphere() const { return object.get_bounding_sphere(); }
-
-       virtual void render(Renderer &, const Tag & = Tag()) const;
-
-       /** Hook function, called from Object just before rendering the mesh.
-       Renderer state will have been pushed before this is called. */
-       virtual void setup_render(Renderer &, const Tag &) const;
-
-       /** Hook function, called from Object right after rendering the mesh.  Since
-       Object takes care of pushing Renderer state, this rarely needs to do
-       anything. */
-       virtual void finish_render(Renderer &, const Tag &) const { }
-
-       virtual unsigned get_level_of_detail(const Renderer &) const;
-};
-
-} // namespace GL
-} // namespaec Msp
-
-#endif
diff --git a/source/occludedscene.cpp b/source/occludedscene.cpp
deleted file mode 100644 (file)
index 143008c..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-#include <algorithm>
-#include <msp/gl/extensions/arb_occlusion_query.h>
-#include <msp/gl/extensions/arb_occlusion_query2.h>
-#include "camera.h"
-#include "occludedscene.h"
-#include "renderer.h"
-#include "sphere.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-OccludedScene::OccludedScene():
-       bounding_mesh((VERTEX3, NORMAL3)),
-       bounding_shader("occluder.glsl"),
-       occluder_min_size(0.25f),
-       cache_dirty(false)
-{
-       static Require req(ARB_occlusion_query);
-       static Require req2(ARB_occlusion_query2);
-
-       /* Use a slightly larger radius to ensure that all parts of the renderable
-       fit inside the icosahedron */
-       IcoSphereBuilder(1.26f, 1).build(bounding_mesh);
-       bounding_mesh.set_winding(&WindingTest::counterclockwise());
-}
-
-OccludedScene::~OccludedScene()
-{
-       vector<unsigned> queries;
-       queries.reserve(occluded_cache.size());
-       for(OccludedArray::iterator i=occluded_cache.begin(); i!=occluded_cache.end(); ++i)
-               queries.push_back(i->query);
-       glDeleteQueries(queries.size(), &queries[0]);
-}
-
-void OccludedScene::add(Renderable &r)
-{
-       renderables.insert(&r);
-       cache_dirty = true;
-}
-
-void OccludedScene::remove(Renderable &r)
-{
-       renderables.erase(&r);
-       cache_dirty = true;
-}
-
-void OccludedScene::populate_cache() const
-{
-       if(!cache_dirty)
-               return;
-
-       if(occluded_cache.size()<renderables.size())
-       {
-               unsigned old_size = occluded_cache.size();
-               occluded_cache.resize(renderables.size());
-               vector<unsigned> new_queries(occluded_cache.size()-old_size);
-               glGenQueries(new_queries.size(), &new_queries[0]);
-               for(unsigned i=0; i<new_queries.size(); ++i)
-                       occluded_cache[old_size+i].query = new_queries[i];
-       }
-
-       OccludedArray::iterator j = occluded_cache.begin();
-       for(RenderableSet::iterator i=renderables.begin(); i!=renderables.end(); ++i, ++j)
-               j->renderable = *i;
-       for(; j!=occluded_cache.end(); ++j)
-       {
-               j->renderable = 0;
-               j->in_frustum = false;
-       }
-
-       cache_dirty = false;
-}
-
-void OccludedScene::setup_frame(Renderer &renderer)
-{
-       populate_cache();
-       for(OccludedArray::const_iterator i=occluded_cache.begin(); i!=occluded_cache.end(); ++i)
-               i->renderable->setup_frame(renderer);
-}
-
-void OccludedScene::finish_frame()
-{
-       for(OccludedArray::const_iterator i=occluded_cache.begin(); i!=occluded_cache.end(); ++i)
-               i->renderable->finish_frame();
-}
-
-void OccludedScene::render(Renderer &renderer, const Tag &tag) const
-{
-       if(renderables.empty())
-               return;
-
-       populate_cache();
-
-       const Camera *camera = renderer.get_camera();
-       if(!camera)
-       {
-               for(OccludedArray::const_iterator i=occluded_cache.begin(); i!=occluded_cache.end(); ++i)
-                       renderer.render(*i->renderable, tag);
-               return;
-       }
-
-       const Vector3 &camera_pos = camera->get_position();
-       const Vector3 &look_dir = camera->get_look_direction();
-       float near_clip = camera->get_near_clip();
-       float frustum_h = tan(camera->get_field_of_view()/2.0f)*2.0f;
-
-       // Perform frustum culling and render any major occluders
-       bool use_frustum = setup_frustum(renderer);
-       for(OccludedArray::iterator i=occluded_cache.begin(); (i!=occluded_cache.end() && i->renderable); ++i)
-       {
-               i->in_frustum = (!use_frustum || !frustum_cull(*i->renderable));
-               if(!i->in_frustum)
-                       continue;
-
-               const Matrix *matrix = i->renderable->get_matrix();
-               i->bounding_sphere = i->renderable->get_bounding_sphere();
-               if(matrix && i->bounding_sphere)
-               {
-                       float depth = dot(*matrix*i->bounding_sphere->get_center()-camera_pos, look_dir);
-                       float size = i->bounding_sphere->get_radius()*2/max(depth, near_clip);
-                       i->occluder = (size>frustum_h*occluder_min_size);
-               }
-               else
-                       // If the size can't be calculated, treat it as occluder
-                       i->occluder = true;
-
-               if(i->occluder)
-                       renderer.render(*i->renderable, tag);
-       }
-
-       // Move all objects within the frustum to the beginning of the array
-       for(OccludedArray::iterator i=occluded_cache.begin(), j=i+renderables.size()-1; i!=j; )
-       {
-               if(i->in_frustum)
-                       ++i;
-               else if(j->in_frustum)
-                       swap(*i, *j);
-               else
-                       --j;
-       }
-
-       {
-               Renderer::Push push(renderer);
-               renderer.set_shader_program(&bounding_shader);
-
-               glColorMask(false, false, false, false);
-               glDepthMask(false);
-
-               // Fire off the occlusion queries
-               for(OccludedArray::const_iterator i=occluded_cache.begin(); (i!=occluded_cache.end() && i->in_frustum); ++i)
-                       if(!i->occluder)
-                       {
-                               glBeginQuery(GL_ANY_SAMPLES_PASSED, i->query);
-                               Renderer::Push push2(renderer);
-                               renderer.transform(Matrix(*i->renderable->get_matrix())
-                                       .translate(i->bounding_sphere->get_center())
-                                       .scale(i->bounding_sphere->get_radius()));
-                               bounding_mesh.draw(renderer);
-                               glEndQuery(GL_ANY_SAMPLES_PASSED);
-                       }
-
-               glColorMask(true, true, true, true);
-               glDepthMask(true);
-       }
-
-       // Render anything that has a chance of being visible
-       for(OccludedArray::const_iterator i=occluded_cache.begin(); (i!=occluded_cache.end() && i->in_frustum); ++i)
-               if(!i->occluder)
-               {
-                       unsigned any_passed = 0;
-                       glGetQueryObjectuiv(i->query, GL_QUERY_RESULT, &any_passed);
-                       if(any_passed)
-                               renderer.render(*i->renderable, tag);
-               }
-}
-
-
-OccludedScene::OccludedRenderable::OccludedRenderable():
-       renderable(0),
-       bounding_sphere(0),
-       in_frustum(false),
-       occluder(false),
-       query(0)
-{ }
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/occludedscene.h b/source/occludedscene.h
deleted file mode 100644 (file)
index 2898bb3..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-#ifndef MSP_GL_OCCLUDEDSCENE_H_
-#define MSP_GL_OCCLUDEDSCENE_H_
-
-#include <set>
-#include <vector>
-#include "mesh.h"
-#include "program.h"
-#include "scene.h"
-
-namespace Msp {
-namespace GL {
-
-/**
-A scene that performs occlusion queries on renderables to skip those that are
-entirely occluded by others.
-*/
-class OccludedScene: public Scene
-{
-private:
-       struct OccludedRenderable
-       {
-               Renderable *renderable;
-               const Geometry::BoundingSphere<float, 3> *bounding_sphere;
-               bool in_frustum;
-               bool occluder;
-               unsigned query;
-
-               OccludedRenderable();
-       };
-
-       typedef std::set<Renderable *> RenderableSet;
-       typedef std::vector<OccludedRenderable> OccludedArray;
-
-       Mesh bounding_mesh;
-       Program bounding_shader;
-       RenderableSet renderables;
-       float occluder_min_size;
-       mutable OccludedArray occluded_cache;
-       mutable bool cache_dirty;
-
-public:
-       OccludedScene();
-       ~OccludedScene();
-
-       virtual void add(Renderable &);
-       virtual void remove(Renderable &);
-
-private:
-       void populate_cache() const;
-
-public:
-       virtual void setup_frame(Renderer &);
-       virtual void finish_frame();
-
-       virtual void render(Renderer &, const Tag & = Tag()) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/offscreenview.cpp b/source/offscreenview.cpp
deleted file mode 100644 (file)
index 6d0d88b..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#include "offscreenview.h"
-#include "rendertarget.h"
-
-namespace Msp {
-namespace GL {
-
-OffscreenView::OffscreenView(Framebuffer &t):
-       View(t)
-{ }
-
-OffscreenView::OffscreenView(RenderTarget &t):
-       View(t.get_framebuffer())
-{ }
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/offscreenview.h b/source/offscreenview.h
deleted file mode 100644 (file)
index 3de47f1..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef MSP_GL_OFFSCREENVIEW_H_
-#define MSP_GL_OFFSCREENVIEW_H_
-
-#include "view.h"
-
-namespace Msp {
-namespace GL {
-
-class Framebuffer;
-class RenderTarget;
-
-class OffscreenView: public View
-{
-public:
-       OffscreenView(Framebuffer &);
-       OffscreenView(RenderTarget &);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/orderedscene.cpp b/source/orderedscene.cpp
deleted file mode 100644 (file)
index ab1ba45..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-#include <algorithm>
-#include "orderedscene.h"
-#include "renderer.h"
-
-namespace Msp {
-namespace GL {
-
-void OrderedScene::add(Renderable &r)
-{
-       renderables.push_back(&r);
-}
-
-void OrderedScene::remove(Renderable &r)
-{
-       RenderableList::iterator end = std::remove(renderables.begin(), renderables.end(), &r);
-       renderables.erase(end, renderables.end());
-}
-
-void OrderedScene::prepend(Renderable &r)
-{
-       renderables.push_front(&r);
-}
-
-void OrderedScene::insert(unsigned index, Renderable &r)
-{
-       RenderableList::iterator i = renderables.begin();
-       for(; (i!=renderables.end() && index); ++i, --index) ;
-       renderables.insert(i, &r);
-}
-
-void OrderedScene::insert_after(Renderable &after, Renderable &r)
-{
-       RenderableList::iterator i = renderables.begin();
-       for(; (i!=renderables.end() && *i!=&after); ++i) ;
-       renderables.insert(i, &r);
-}
-
-void OrderedScene::setup_frame(Renderer &renderer)
-{
-       for(RenderableList::const_iterator i=renderables.begin(); i!=renderables.end(); ++i)
-               (*i)->setup_frame(renderer);
-}
-
-void OrderedScene::finish_frame()
-{
-       for(RenderableList::const_iterator i=renderables.begin(); i!=renderables.end(); ++i)
-               (*i)->finish_frame();
-}
-
-void OrderedScene::render(Renderer &renderer, const Tag &tag) const
-{
-       if(setup_frustum(renderer))
-       {
-               for(RenderableList::const_iterator i=renderables.begin(); i!=renderables.end(); ++i)
-                       if(!frustum_cull(**i))
-                               renderer.render(**i, tag);
-       }
-       else
-       {
-               for(RenderableList::const_iterator i=renderables.begin(); i!=renderables.end(); ++i)
-                       renderer.render(**i, tag);
-       }
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/orderedscene.h b/source/orderedscene.h
deleted file mode 100644 (file)
index 53f7b00..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#ifndef MSP_GL_ORDEREDSCENE_H_
-#define MSP_GL_ORDEREDSCENE_H_
-
-#include <list>
-#include "scene.h"
-
-namespace Msp {
-namespace GL {
-
-/**
-A scene that renders its contents in a specific order.  Inserting Renderables
-in the middle and removing them are O(N) operations.
-*/
-class OrderedScene: public Scene
-{
-private:
-       typedef std::list<Renderable *> RenderableList;
-
-       RenderableList renderables;
-
-public:
-       virtual void add(Renderable &);
-       virtual void remove(Renderable &);
-       void prepend(Renderable &);
-       void insert(unsigned, Renderable &);
-       void insert_after(Renderable &, Renderable &);
-
-       virtual void setup_frame(Renderer &);
-       virtual void finish_frame();
-
-       using Scene::render;
-       virtual void render(Renderer &, const Tag & = Tag()) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/pbrmaterial.cpp b/source/pbrmaterial.cpp
deleted file mode 100644 (file)
index bc7104c..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-#include "pbrmaterial.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-PbrMaterial::PbrMaterial():
-       receive_shadows(false)
-{
-       set_base_color(0.8f);
-       set_metalness(0.0f);
-       set_roughness(0.5f);
-       set_emission(0.0f);
-}
-
-string PbrMaterial::create_program_source() const
-{
-       string source = "import cooktorrance;\n";
-       if(base_color.texture)
-               source += "const bool use_base_color_map = true;\n";
-       if(normal.texture)
-               source += "const bool use_normal_map = true;\n";
-       if(metalness.texture)
-               source += "const bool use_metalness_map = true;\n";
-       if(roughness.texture)
-               source += "const bool use_roughness_map = true;\n";
-       if(occlusion.texture)
-               source += "const bool use_occlusion_map = true;\n";
-       if(emission.texture || emission.value.r || emission.value.g || emission.value.b)
-       {
-               source += "const bool use_emission = true;\n";
-               if(emission.texture)
-                       source += "const bool use_emission_map = true;\n";
-       }
-       if(receive_shadows)
-               source += "const bool use_shadow_map = true;\n";
-       return source;
-}
-
-void PbrMaterial::attach_textures_to(Texturing &texturing, ProgramData &tex_shdata) const
-{
-       attach_texture_to(base_color.texture, texturing, tex_shdata, "base_color_map");
-       attach_texture_to(metalness.texture, texturing, tex_shdata, "metalness_map");
-       attach_texture_to(roughness.texture, texturing, tex_shdata, "roughness_map");
-       attach_texture_to(normal.texture, texturing, tex_shdata, "normal_map");
-       attach_texture_to(occlusion.texture, texturing, tex_shdata, "occlusion_map");
-       attach_texture_to(emission.texture, texturing, tex_shdata, "emission_map");
-}
-
-void PbrMaterial::set_base_color(const Color &color)
-{
-       base_color.value = color;
-       shdata.uniform("pbr_material.base_color", color);
-}
-
-void PbrMaterial::set_base_color_map(const Texture *tex)
-{
-       base_color.texture = tex;
-}
-
-void PbrMaterial::set_normal_map(const Texture *tex)
-{
-       normal.texture = tex;
-}
-
-void PbrMaterial::set_metalness(float value)
-{
-       metalness.value = value;
-       shdata.uniform("pbr_material.metalness", value);
-}
-
-void PbrMaterial::set_metalness_map(const Texture *tex)
-{
-       metalness.texture = tex;
-}
-
-void PbrMaterial::set_roughness(float value)
-{
-       roughness.value = value;
-       shdata.uniform("pbr_material.roughness", value);
-}
-
-void PbrMaterial::set_roughness_map(const Texture *tex)
-{
-       roughness.texture = tex;
-}
-
-void PbrMaterial::set_occlusion_map(const Texture *tex)
-{
-       occlusion.texture = tex;
-}
-
-void PbrMaterial::set_emission(const Color &color)
-{
-       emission.value = color;
-       shdata.uniform("pbr_material.emission", color);
-}
-
-void PbrMaterial::set_emission_map(const Texture *tex)
-{
-       emission.texture = tex;
-}
-
-void PbrMaterial::set_receive_shadows(bool s)
-{
-       receive_shadows = s;
-}
-
-
-DataFile::Loader::ActionMap PbrMaterial::Loader::shared_actions;
-
-PbrMaterial::Loader::Loader(PbrMaterial &m):
-       DerivedObjectLoader<PbrMaterial, Material::PropertyLoader<PbrMaterial> >(m)
-{
-       set_actions(shared_actions);
-}
-
-PbrMaterial::Loader::Loader(PbrMaterial &m, Collection &c):
-       DerivedObjectLoader<PbrMaterial, Material::PropertyLoader<PbrMaterial> >(m, c)
-{
-       set_actions(shared_actions);
-}
-
-void PbrMaterial::Loader::init_actions()
-{
-       Material::PropertyLoader<PbrMaterial>::init_actions();
-       add_property("base_color", &PbrMaterial::set_base_color, &PbrMaterial::set_base_color_map, true);
-       add_property("normal", &PbrMaterial::set_normal_map);
-       add_property("metalness", &PbrMaterial::set_metalness, &PbrMaterial::set_metalness_map);
-       add_property("roughness", &PbrMaterial::set_roughness, &PbrMaterial::set_roughness_map);
-       add_property("occlusion", &PbrMaterial::set_occlusion_map);
-       add_property("emission", &PbrMaterial::set_emission, &PbrMaterial::set_emission_map, false);
-       add("receive_shadows", &PbrMaterial::receive_shadows);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/pbrmaterial.h b/source/pbrmaterial.h
deleted file mode 100644 (file)
index 2a0eef3..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-#ifndef MSP_GL_PBRMATERIAL_H_
-#define MSP_GL_PBRMATERIAL_H_
-
-#include "material.h"
-
-namespace Msp {
-namespace GL {
-
-class PbrMaterial: public Material
-{
-public:
-       class Loader: public DataFile::DerivedObjectLoader<PbrMaterial, Material::PropertyLoader<PbrMaterial> >
-       {
-       private:
-               static ActionMap shared_actions;
-
-       public:
-               Loader(PbrMaterial &);
-               Loader(PbrMaterial &, Collection &);
-
-       private:
-               virtual void init_actions();
-       };
-       
-private:
-       Property<Color> base_color;
-       Property<Vector3> normal;
-       Property<float> metalness;
-       Property<float> roughness;
-       Property<float> occlusion;
-       Property<Color> emission;
-       bool receive_shadows;
-
-public:
-       PbrMaterial();
-
-protected:
-       virtual std::string create_program_source() const;
-
-public:
-       virtual void attach_textures_to(Texturing &, ProgramData &) const;
-
-       void set_base_color(const Color &);
-       void set_base_color_map(const Texture *);
-       void set_normal_map(const Texture *);
-       void set_metalness(float);
-       void set_metalness_map(const Texture *);
-       void set_roughness(float);
-       void set_roughness_map(const Texture *);
-       void set_occlusion_map(const Texture *);
-       void set_emission(const Color &);
-       void set_emission_map(const Texture *);
-       void set_receive_shadows(bool);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/pipeline.cpp b/source/pipeline.cpp
deleted file mode 100644 (file)
index a7f4130..0000000
+++ /dev/null
@@ -1,287 +0,0 @@
-#include <msp/core/maputils.h>
-#include "blend.h"
-#include "camera.h"
-#include "framebuffer.h"
-#include "lighting.h"
-#include "pipeline.h"
-#include "postprocessor.h"
-#include "renderbuffer.h"
-#include "renderer.h"
-#include "tests.h"
-#include "texture2d.h"
-#include "view.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Pipeline::Pipeline(unsigned w, unsigned h, bool d)
-{
-       init(w, h);
-       hdr = d;
-}
-
-Pipeline::Pipeline(const View &view)
-{
-       init(view.get_width(), view.get_height());
-}
-
-Pipeline::Pipeline(const Framebuffer &fbo)
-{
-       init(fbo.get_width(), fbo.get_height());
-}
-
-void Pipeline::init(unsigned w, unsigned h)
-{
-       camera = 0;
-       width = w;
-       height = h;
-       hdr = false;
-       alpha = false;
-       samples = 0;
-       target_ms = 0;
-       target[0] = 0;
-       target[1] = 0;
-}
-
-Pipeline::~Pipeline()
-{
-       delete target[0];
-       delete target[1];
-       delete target_ms;
-}
-
-void Pipeline::set_hdr(bool h)
-{
-       if(h==hdr)
-               return;
-
-       bool old_hdr = hdr;
-       hdr = h;
-       try
-       {
-               create_targets(2);
-       }
-       catch(...)
-       {
-               hdr = old_hdr;
-               throw;
-       }
-}
-
-void Pipeline::set_alpha(bool a)
-{
-       if(a==alpha)
-               return;
-
-       bool old_alpha = alpha;
-       alpha = a;
-       try
-       {
-               create_targets(2);
-       }
-       catch(...)
-       {
-               alpha = old_alpha;
-               throw;
-       }
-}
-
-void Pipeline::set_multisample(unsigned s)
-{
-       if(s==samples)
-               return;
-
-       unsigned old_samples = samples;
-       samples = s;
-       try
-       {
-               create_targets(1);
-       }
-       catch(...)
-       {
-               samples = old_samples;
-               throw;
-       }
-}
-
-Pipeline::Pass &Pipeline::add_pass(const Tag &tag, Renderable &r)
-{
-       passes.push_back(Pass(tag, &r));
-       return passes.back();
-}
-
-void Pipeline::add_postprocessor(PostProcessor &pp)
-{
-       add_postprocessor(&pp, true);
-}
-
-void Pipeline::add_postprocessor_owned(PostProcessor *pp)
-{
-       add_postprocessor(pp, false);
-}
-
-void Pipeline::add_postprocessor(PostProcessor *pp, bool keep)
-{
-       postproc.push_back(pp);
-       if(keep)
-               postproc.back().keep();
-       try
-       {
-               create_targets(0);
-       }
-       catch(...)
-       {
-               postproc.pop_back();
-               throw;
-       }
-}
-
-void Pipeline::setup_frame(Renderer &renderer)
-{
-       for(PassList::const_iterator i=passes.begin(); i!=passes.end(); ++i)
-               if(Renderable *renderable = i->get_renderable())
-                       renderable->setup_frame(renderer);
-       for(vector<Slot>::const_iterator i=renderables.begin(); i!=renderables.end(); ++i)
-               i->renderable->setup_frame(renderer);
-}
-
-void Pipeline::finish_frame()
-{
-       for(PassList::const_iterator i=passes.begin(); i!=passes.end(); ++i)
-               if(Renderable *renderable = i->get_renderable())
-                       renderable->finish_frame();
-       for(vector<Slot>::const_iterator i=renderables.begin(); i!=renderables.end(); ++i)
-               i->renderable->finish_frame();
-}
-
-void Pipeline::render(Renderer &renderer, const Tag &tag) const
-{
-       if(tag.id)
-               return;
-
-       const Framebuffer *out_fbo = Framebuffer::current();
-       // These are no-ops but will ensure the related state gets restored
-       BindRestore restore_fbo(out_fbo);
-       BindRestore restore_depth_test(DepthTest::current());
-       BindRestore restore_blend(Blend::current());
-
-       if(target[0])
-       {
-               Framebuffer &fbo = (samples ? target_ms : target[0])->get_framebuffer();
-               fbo.bind();
-               fbo.clear();
-       }
-
-       for(PassList::const_iterator i=passes.begin(); i!=passes.end(); ++i)
-       {
-               if(const DepthTest *dt = i->get_depth_test())
-                       dt->bind();
-               else
-                       DepthTest::unbind();
-
-               if(const Blend *b = i->get_blend())
-                       b->bind();
-               else
-                       Blend::unbind();
-
-               renderer.set_lighting(i->get_lighting());
-               renderer.set_clipping(i->get_clipping());
-
-               if(const Renderable *renderable = i->get_renderable())
-                       renderer.render(*renderable, i->get_tag());
-
-               for(vector<Slot>::const_iterator j=renderables.begin(); j!=renderables.end(); ++j)
-                       if(j->passes.empty() || j->passes.count(i->get_tag()))
-                               renderer.render(*j->renderable, i->get_tag());
-       }
-
-       if(target[0])
-       {
-               DepthTest::unbind();
-               Blend::unbind();
-
-               if(samples)
-                       target[0]->blit_from(*target_ms);
-
-               for(unsigned i=0; i<postproc.size(); ++i)
-               {
-                       unsigned j = i%2;
-                       if(i+1<postproc.size())
-                               target[1-j]->get_framebuffer().bind();
-                       else
-                               out_fbo->bind();
-                       const Texture2D &color = target[j]->get_target_texture(RENDER_COLOR);
-                       const Texture2D &depth = target[j]->get_target_texture(RENDER_DEPTH);
-                       postproc[i]->render(renderer, color, depth);
-               }
-       }
-}
-
-void Pipeline::create_targets(unsigned recreate)
-{
-       if(recreate>=2)
-       {
-               delete target[0];
-               delete target[1];
-               target[0] = 0;
-               target[1] = 0;
-       }
-       if(recreate>=1)
-       {
-               delete target_ms;
-               target_ms = 0;
-       }
-
-       PixelFormat color_pf = (hdr ? (alpha ? RGBA16F : RGB16F) : (alpha ? RGBA8 : RGB8));
-       RenderTargetFormat fmt = (RENDER_COLOR,color_pf, RENDER_DEPTH);
-       if(!postproc.empty() || samples)
-       {
-               if(!target[0])
-                       target[0] = new RenderTarget(width, height, fmt);
-               if(!target[1] && postproc.size()>1)
-                       target[1] = new RenderTarget(width, height, fmt);
-       }
-
-       if(!target_ms && samples)
-               target_ms = new RenderTarget(width, height, samples, fmt);
-}
-
-
-Pipeline::Pass::Pass(const Tag &t, Renderable *r):
-       tag(t),
-       lighting(0),
-       depth_test(0),
-       blend(0),
-       clipping(0),
-       renderable(r)
-{ }
-
-void Pipeline::Pass::set_lighting(const Lighting *l)
-{
-       lighting = l;
-}
-
-void Pipeline::Pass::set_depth_test(const DepthTest *d)
-{
-       depth_test = d;
-}
-
-void Pipeline::Pass::set_blend(const Blend *b)
-{
-       blend = b;
-}
-
-void Pipeline::Pass::set_clipping(const Clipping *c)
-{
-       clipping =c;
-}
-
-
-Pipeline::Slot::Slot(Renderable *r):
-       renderable(r)
-{ }
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/pipeline.h b/source/pipeline.h
deleted file mode 100644 (file)
index dd80ef3..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-#ifndef MSP_GL_PIPELINE_H_
-#define MSP_GL_PIPELINE_H_
-
-#include <map>
-#include <set>
-#include "framebuffer.h"
-#include "renderable.h"
-#include "renderbuffer.h"
-#include "rendertarget.h"
-#include "texture2d.h"
-
-namespace Msp {
-namespace GL {
-
-class Blend;
-class Camera;
-class Clipping;
-class DepthTest;
-class Lighting;
-class PostProcessor;
-class View;
-
-/**
-Top-level content class.  Typically a Pipeline is used as the content
-Renderable for a View or effects such as ShadowMap or EnvironmentMap.
-
-A Pipeline contains a sequence of passes.  Each pass has a Renderable along
-with Lighting, Clipping, DepthTest and Blend states.  Scenes can be used to
-organize Renderables within a pass.
-
-PostProcessors can be applied after all of the passes in the Pipeline have been
-rendered.  Framebuffer objects are automatically used to pass render results to
-the PostProcessors.  High dynamic range and multisample rendering can be
-requested for increased quality.
-*/
-class Pipeline: public Renderable
-{
-public:
-       class Pass
-       {
-       private:
-               Tag tag;
-               const Lighting *lighting;
-               const DepthTest *depth_test;
-               const Blend *blend;
-               const Clipping *clipping;
-               Renderable *renderable;
-
-       public:
-               Pass(const Tag &, Renderable *);
-
-               const Tag &get_tag() const { return tag; }
-
-               void set_lighting(const Lighting *);
-               void set_depth_test(const DepthTest *);
-               void set_blend(const Blend *);
-               void set_clipping(const Clipping *);
-               const Lighting *get_lighting() const { return lighting; }
-               const DepthTest *get_depth_test() const { return depth_test; }
-               const Blend *get_blend() const { return blend; }
-               const Clipping *get_clipping() const { return clipping; }
-               Renderable *get_renderable() const { return renderable; }
-       };
-
-private:
-       struct Slot
-       {
-               Renderable *renderable;
-               std::set<Tag> passes;
-
-               Slot(Renderable *);
-       };
-
-       typedef std::list<Pass> PassList;
-
-       PassList passes;
-       const Camera *camera;
-       std::vector<Slot> renderables;
-       std::vector<RefPtr<PostProcessor> > postproc;
-       unsigned width;
-       unsigned height;
-       bool hdr;
-       bool alpha;
-       unsigned samples;
-       RenderTarget *target[2];
-       RenderTarget *target_ms;
-
-public:
-       Pipeline(unsigned, unsigned, bool = false);
-       Pipeline(const View &);
-       Pipeline(const Framebuffer &);
-private:
-       void init(unsigned, unsigned);
-public:
-       ~Pipeline();
-
-       /* Sets high dynamic range mode.  Requires floating-point texture support.
-       A ColorCurve postprocessor is recommended for full benefit. */
-       void set_hdr(bool);
-
-       /* Enable or disable alpha channel.  When enabled, all render targets are
-       created with an RGBA pixel format instead of RGB. */
-       void set_alpha(bool);
-
-       void set_multisample(unsigned);
-
-       unsigned get_width() const { return width; }
-       unsigned get_height() const { return height; }
-       bool get_hdr() const { return hdr; }
-       unsigned get_multisample() const { return samples; }
-
-       /** Adds a pass to the pipeline.  It's permissible to add the same
-       Renderable multiple times. */
-       Pass &add_pass(const Tag &, Renderable &);
-
-       /** Adds a postprocessor to the pipeline. */
-       void add_postprocessor(PostProcessor &);
-
-       /** Adds a postprocessor to the pipeline, transferring ownership.  The
-       postprocessor will be deleted together with with pipeline.  It is also
-       deleted if this call throws an exception. */
-       void add_postprocessor_owned(PostProcessor *);
-
-private:
-       void add_postprocessor(PostProcessor *, bool);
-
-public:
-       virtual void setup_frame(Renderer &);
-       virtual void finish_frame();
-
-       virtual void render(Renderer &, const Tag &tag = Tag()) const;
-
-private:
-       void create_targets(unsigned);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/pipelinebuilder.cpp b/source/pipelinebuilder.cpp
deleted file mode 100644 (file)
index 51e9cc8..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-#include <msp/core/algorithm.h>
-#include <msp/core/maputils.h>
-#include "error.h"
-#include "pipeline.h"
-#include "pipelinebuilder.h"
-#include "pipelinetemplate.h"
-#include "renderbuffer.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-PipelineBuilder::PipelineBuilder(const PipelineTemplate &t):
-       tmpl(t)
-{
-       const vector<PipelineTemplate::Pass> &passes = tmpl.get_passes();
-       for(vector<PipelineTemplate::Pass>::const_iterator i=passes.begin(); i!=passes.end(); ++i)
-               renderables[i->renderable_name] = 0;
-       const vector<PipelineTemplate::PostProcessor> &postprocs = tmpl.get_postprocessors();
-       for(PipelineTemplate::PostProcessorArray::const_iterator i=postprocs.begin(); i!=postprocs.end(); ++i)
-               if(!i->slot_name.empty())
-                       postprocessors[i->slot_name] = 0;
-}
-
-void PipelineBuilder::set_renderable(const string &name, Renderable &rend)
-{
-       get_item(renderables, name) = &rend;
-}
-
-void PipelineBuilder::set_postprocessor(const string &name, PostProcessor &pproc)
-{
-       get_item(postprocessors, name) = &pproc;
-}
-
-void PipelineBuilder::build(Pipeline &pipeline) const
-{
-       pipeline.set_hdr(tmpl.get_hdr());
-       pipeline.set_alpha(tmpl.get_alpha());
-       unsigned samples = min(tmpl.get_maximum_multisample(), Renderbuffer::get_max_samples());
-       if(samples<tmpl.get_required_multisample())
-               throw invalid_operation("PipelineBuilder::build");
-
-       pipeline.set_multisample(samples);
-
-       const PipelineTemplate::PassArray &passes = tmpl.get_passes();
-       for(PipelineTemplate::PassArray::const_iterator i=passes.begin(); i!=passes.end(); ++i)
-       {
-               Renderable *renderable = get_item(renderables, i->renderable_name);
-               if(!renderable)
-                       continue;
-
-               Pipeline::Pass &pass = pipeline.add_pass(i->tag, *renderable);
-               pass.set_blend(i->blend.get());
-               pass.set_depth_test(i->depth_test.get());
-               pass.set_lighting(i->lighting.get());
-       }
-
-       const PipelineTemplate::PostProcessorArray &postprocs = tmpl.get_postprocessors();
-       for(PipelineTemplate::PostProcessorArray::const_iterator i=postprocs.begin(); i!=postprocs.end(); ++i)
-       {
-               PostProcessor *proc = 0;
-               if(!i->slot_name.empty())
-                       proc = get_item(postprocessors, i->slot_name);
-               if(proc)
-                       pipeline.add_postprocessor(*proc);
-               else if(i->postprocessor_template)
-               {
-                       proc = i->postprocessor_template->create(pipeline.get_width(), pipeline.get_height());
-                       if(proc)
-                               pipeline.add_postprocessor_owned(proc);
-               }
-       }
-}
-
-Pipeline *PipelineBuilder::build(unsigned w, unsigned h) const
-{
-       RefPtr<Pipeline> pipeline = new Pipeline(w, h);
-       build(*pipeline);
-       return pipeline.release();
-}
-
-Pipeline *PipelineBuilder::build(const View &view) const
-{
-       RefPtr<Pipeline> pipeline = new Pipeline(view);
-       build(*pipeline);
-       return pipeline.release();
-}
-
-Pipeline *PipelineBuilder::build(const Framebuffer &fbo) const
-{
-       RefPtr<Pipeline> pipeline = new Pipeline(fbo);
-       build(*pipeline);
-       return pipeline.release();
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/pipelinebuilder.h b/source/pipelinebuilder.h
deleted file mode 100644 (file)
index ab0dc4b..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-#ifndef PIPELINEBUILDER_H_
-#define PIPELINEBUILDER_H_
-
-#include <map>
-#include <string>
-
-namespace Msp {
-namespace GL {
-
-class Framebuffer;
-class Pipeline;
-class PipelineTemplate;
-class PostProcessor;
-class Renderable;
-class View;
-
-class PipelineBuilder
-{
-private:
-       const PipelineTemplate &tmpl;
-       std::map<std::string, Renderable *> renderables;
-       std::map<std::string, PostProcessor *> postprocessors;
-
-public:
-       PipelineBuilder(const PipelineTemplate &);
-
-       void set_renderable(const std::string &, Renderable &);
-       void set_postprocessor(const std::string &, PostProcessor &);
-
-       void build(Pipeline &) const;
-       Pipeline *build(unsigned, unsigned) const;
-       Pipeline *build(const View &) const;
-       Pipeline *build(const Framebuffer &) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/pipelinetemplate.cpp b/source/pipelinetemplate.cpp
deleted file mode 100644 (file)
index b78b4c4..0000000
+++ /dev/null
@@ -1,202 +0,0 @@
-#include <msp/core/maputils.h>
-#include <msp/datafile/collection.h>
-#include "ambientocclusion.h"
-#include "blend.h"
-#include "bloom.h"
-#include "colorcurve.h"
-#include "lighting.h"
-#include "pipelinetemplate.h"
-#include "tests.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-PipelineTemplate::PipelineTemplate():
-       hdr(false),
-       alpha(false),
-       required_multisample(0),
-       max_multisample(0)
-{ }
-
-PipelineTemplate::~PipelineTemplate()
-{
-       for(PostProcessorArray::iterator i=postprocessors.begin(); i!=postprocessors.end(); ++i)
-               delete i->postprocessor_template;
-}
-
-
-PipelineTemplate::PostProcessorRegistry &PipelineTemplate::get_postprocessor_registry()
-{
-       static PostProcessorRegistry registry;
-       static bool initialized = false;
-       if(!initialized)
-       {
-               registry.register_type<AmbientOcclusion>("ambient_occlusion");
-               registry.register_type<Bloom>("bloom");
-               registry.register_type<ColorCurve>("colorcurve");
-               initialized = true;
-       }
-       return registry;
-}
-
-
-PipelineTemplate::Pass::~Pass()
-{ }
-
-
-PipelineTemplate::PostProcessor::PostProcessor(GL::PostProcessor::Template *ppt):
-       postprocessor_template(ppt)
-{ }
-
-
-PipelineTemplate::PostProcLoader::PostProcLoader()
-{
-       get_postprocessor_registry().add_all(*this);
-}
-
-
-PipelineTemplate::Loader::Loader(PipelineTemplate &t):
-       DataFile::CollectionObjectLoader<PipelineTemplate>(t, 0)
-{
-       init();
-}
-
-PipelineTemplate::Loader::Loader(PipelineTemplate &t, Collection &c):
-       DataFile::CollectionObjectLoader<PipelineTemplate>(t, &c)
-{
-       init();
-}
-
-void PipelineTemplate::Loader::init()
-{
-       add("hdr", &PipelineTemplate::hdr);
-       add("alpha", &PipelineTemplate::alpha);
-       add("multisample", &Loader::multisample);
-       add("multisample", &Loader::multisample_range);
-       add("pass", &Loader::pass);
-       add("postprocessor", &Loader::postprocessor);
-}
-
-void PipelineTemplate::Loader::postprocessor_loaded()
-{
-       obj.postprocessors.push_back(get_postprocessor_template());
-}
-
-void PipelineTemplate::Loader::multisample(unsigned samples)
-{
-       obj.required_multisample = samples;
-       obj.max_multisample = samples;
-}
-
-void PipelineTemplate::Loader::multisample_range(unsigned req, unsigned max)
-{
-       obj.required_multisample = req;
-       obj.max_multisample = max;
-}
-
-void PipelineTemplate::Loader::pass(const string &tag, const string &rend)
-{
-       Pass pss;;
-       pss.tag = tag;
-       pss.renderable_name = rend;
-       if(coll)
-               load_sub(pss, *coll);
-       else
-               load_sub(pss);
-
-       obj.passes.push_back(pss);
-}
-
-void PipelineTemplate::Loader::postprocessor(const std::string &slot)
-{
-       PostProcLoader ldr;
-       load_sub_with(ldr);
-       PostProcessor pp;
-       pp.postprocessor_template = ldr.get_postprocessor_template();
-       pp.slot_name = slot;
-       obj.postprocessors.push_back(pp);
-}
-
-
-PipelineTemplate::Pass::Loader::Loader(Pass &p):
-       DataFile::CollectionObjectLoader<Pass>(p, 0)
-{
-       init();
-}
-
-PipelineTemplate::Pass::Loader::Loader(Pass &p, Collection &c):
-       DataFile::CollectionObjectLoader<Pass>(p, &c)
-{
-       init();
-}
-
-void PipelineTemplate::Pass::Loader::init()
-{
-       add("blend", &Loader::blend);
-       add("blend", &Loader::blend_predefined);
-       add("depth_test", &Loader::depth_test);
-       add("depth_test", &Loader::depth_test_predefined);
-       add("lighting", &Loader::lighting);
-       add("lighting", &Loader::lighting_inline);
-}
-
-void PipelineTemplate::Pass::Loader::blend_predefined(const string &name)
-{
-       const Blend *bln = 0;
-       if(name=="alpha")
-               bln = &Blend::alpha();
-       else if(name=="additive")
-               bln = &Blend::additive();
-       else if(name=="additive_alpha")
-               bln = &Blend::additive_alpha();
-       else
-               throw key_error(name);
-
-       obj.blend = bln;
-       obj.blend.keep();
-}
-
-void PipelineTemplate::Pass::Loader::blend(BlendFactor src, BlendFactor dest)
-{
-       obj.blend = new Blend(src, dest);
-}
-
-void PipelineTemplate::Pass::Loader::depth_test_predefined(const string &name)
-{
-       const DepthTest *dtest = 0;
-       if(name=="lequal")
-               dtest = &DepthTest::lequal();
-       else
-               throw key_error(name);
-
-       obj.depth_test = dtest;
-       obj.depth_test.keep();
-}
-
-void PipelineTemplate::Pass::Loader::depth_test(Predicate pred)
-{
-       obj.depth_test = new DepthTest(pred);
-}
-
-void PipelineTemplate::Pass::Loader::lighting_inline()
-{
-       RefPtr<Lighting> lightn = new Lighting;
-       load_sub(*lightn);
-       obj.lighting = lightn;
-}
-
-void PipelineTemplate::Pass::Loader::lighting(const string &name)
-{
-       obj.lighting = &get_collection().get<Lighting>(name);
-       obj.lighting.keep();
-}
-
-/*void PipelineTemplate::Pass::Loader::scene(const string &name)
-{
-       obj.default_renderable = get_collection().get<Scene>(name);
-}*/
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/pipelinetemplate.h b/source/pipelinetemplate.h
deleted file mode 100644 (file)
index fb24ea2..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-#ifndef PIPELINETEMPLATE_H_
-#define PIPELINETEMPLATE_H_
-
-#include <string>
-#include <vector>
-#include <msp/datafile/loadabletyperegistry.h>
-#include <msp/datafile/objectloader.h>
-#include "blend.h"
-#include "postprocessor.h"
-#include "predicate.h"
-
-namespace Msp {
-namespace GL {
-
-class DepthTest;
-class Lighting;
-
-class PipelineTemplate
-{
-private:
-       class PostProcLoader: virtual public DataFile::Loader
-       {
-       private:
-               template<typename T>
-               struct AddPostProc
-               {
-                       static void add(PostProcLoader &ldr, const std::string &kw) { ldr.add(kw, &PostProcLoader::postprocessor<T>); }
-               };
-
-       protected:
-               RefPtr<PostProcessor::Template> postproc;
-
-       public:
-               PostProcLoader();
-
-               PostProcessor::Template *get_postprocessor_template() { return postproc.release(); }
-
-       protected:
-               virtual void postprocessor_loaded() { }
-
-       private:
-               template<typename T>
-               void postprocessor();
-
-               friend class PipelineTemplate;
-       };
-
-public:
-       class Loader: public DataFile::CollectionObjectLoader<PipelineTemplate>, public PostProcLoader
-       {
-       public:
-               Loader(PipelineTemplate &);
-               Loader(PipelineTemplate &, Collection &);
-       private:
-               void init();
-
-               virtual void postprocessor_loaded();
-               void multisample(unsigned);
-               void multisample_range(unsigned, unsigned);
-               void pass(const std::string &, const std::string &);
-               void postprocessor(const std::string &);
-       };
-
-       struct Pass
-       {
-               class Loader: public DataFile::CollectionObjectLoader<Pass>
-               {
-               public:
-                       Loader(Pass &);
-                       Loader(Pass &, Collection &);
-               private:
-                       void init();
-
-                       void blend(BlendFactor, BlendFactor);
-                       void blend_predefined(const std::string &);
-                       void depth_test(Predicate);
-                       void depth_test_predefined(const std::string &);
-                       void lighting(const std::string &);
-                       void lighting_inline();
-                       // TODO requires support for scenes in Resources
-                       //void scene(const std::string &);
-               };
-
-               std::string tag;
-               RefPtr<Lighting> lighting;
-               RefPtr<const DepthTest> depth_test;
-               RefPtr<const Blend> blend;
-               std::string renderable_name;
-               //Renderable *default_renderable;
-
-               ~Pass();
-       };
-
-       struct PostProcessor
-       {
-               GL::PostProcessor::Template *postprocessor_template;
-               std::string slot_name;
-
-               PostProcessor(GL::PostProcessor::Template * = 0);
-       };
-
-       typedef std::vector<Pass> PassArray;
-       typedef std::vector<PostProcessor> PostProcessorArray;
-
-private:
-       typedef DataFile::LoadableTypeRegistry<PostProcLoader, PostProcLoader::AddPostProc> PostProcessorRegistry;
-
-       bool hdr;
-       bool alpha;
-       unsigned required_multisample;
-       unsigned max_multisample;
-       PassArray passes;
-       PostProcessorArray postprocessors;
-
-public:
-       PipelineTemplate();
-       ~PipelineTemplate();
-
-       bool get_hdr() const { return hdr; }
-       bool get_alpha() const { return alpha; }
-       unsigned get_required_multisample() const { return required_multisample; }
-       unsigned get_maximum_multisample() const { return max_multisample; }
-       const PassArray &get_passes() const { return passes; }
-       const PostProcessorArray &get_postprocessors() const { return postprocessors; }
-
-       template<typename T>
-       static void register_postprocessor(const std::string &);
-private:
-       static PostProcessorRegistry &get_postprocessor_registry();
-};
-
-template<typename T>
-void PipelineTemplate::register_postprocessor(const std::string &kw)
-{
-       get_postprocessor_registry().register_type<T>(kw);
-}
-
-template<typename T>
-void PipelineTemplate::PostProcLoader::postprocessor()
-{
-       if(postproc)
-               throw std::logic_error("Only one postprocessor allowed per slot");
-       RefPtr<typename T::Template> pp = new typename T::Template;
-       load_sub(*pp);
-       postproc = pp;
-       pp = 0;
-       postprocessor_loaded();
-}
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/pixelformat.cpp b/source/pixelformat.cpp
deleted file mode 100644 (file)
index 87191c3..0000000
+++ /dev/null
@@ -1,389 +0,0 @@
-#include <msp/gl/extensions/arb_texture_float.h>
-#include <msp/io/print.h>
-#include <msp/strings/format.h>
-#include "pixelformat.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-void operator>>(const LexicalConverter &conv, PixelComponents &comp)
-{
-       if(conv.get()=="STENCIL_INDEX")
-               comp = STENCIL_INDEX;
-       else if(conv.get()=="DEPTH_COMPONENT")
-               comp = DEPTH_COMPONENT;
-       else if(conv.get()=="RED")
-               comp = RED;
-       else if(conv.get()=="RG")
-               comp = RG;
-       else if(conv.get()=="RGB")
-               comp = RGB;
-       else if(conv.get()=="RGBA")
-               comp = RGBA;
-       else if(conv.get()=="BGR")
-               comp = BGR;
-       else if(conv.get()=="BGRA")
-               comp = BGRA;
-       else if(conv.get()=="LUMINANCE")
-               comp = LUMINANCE;
-       else if(conv.get()=="LUMINANCE_ALPHA")
-               comp = LUMINANCE_ALPHA;
-       else
-               throw lexical_error(format("conversion of '%s' to PixelFormat", conv.get()));
-}
-
-void operator>>(const LexicalConverter &conv, PixelFormat &fmt)
-{
-       if(conv.get()=="R8")
-               fmt = R8;
-       else if(conv.get()=="R16F")
-               fmt = R16F;
-       else if(conv.get()=="R32F")
-               fmt = R32F;
-       else if(conv.get()=="RG8")
-               fmt = RG8;
-       else if(conv.get()=="RG16F")
-               fmt = RG16F;
-       else if(conv.get()=="RG32F")
-               fmt = RG32F;
-       else if(conv.get()=="RGB8")
-               fmt = RGB8;
-       else if(conv.get()=="RGB16F")
-               fmt = RGB16F;
-       else if(conv.get()=="RGB32F")
-               fmt = RGB32F;
-       else if(conv.get()=="RGBA8")
-               fmt = RGBA8;
-       else if(conv.get()=="RGBA16F")
-               fmt = RGBA16F;
-       else if(conv.get()=="RGBA32F")
-               fmt = RGBA32F;
-       else if(conv.get()=="SRGB8")
-               fmt = SRGB8;
-       else if(conv.get()=="SRGB8_ALPHA8")
-               fmt = SRGB8_ALPHA8;
-       else if(conv.get()=="BGR8")
-               fmt = BGR8;
-       else if(conv.get()=="BGRA8")
-               fmt = BGRA8;
-       else if(conv.get()=="LUMINANCE8")
-               fmt = LUMINANCE8;
-       else if(conv.get()=="LUMINANCE8_ALPHA8")
-               fmt = LUMINANCE8_ALPHA8;
-       else if(conv.get()=="DEPTH_COMPONENT16")
-               fmt = DEPTH_COMPONENT16;
-       else if(conv.get()=="DEPTH_COMPONENT24")
-               fmt = DEPTH_COMPONENT24;
-       else if(conv.get()=="DEPTH_COMPONENT32")
-               fmt = DEPTH_COMPONENT32;
-       else if(conv.get()=="DEPTH_COMPONENT32F")
-               fmt = DEPTH_COMPONENT32F;
-       else
-       {
-               PixelComponents comp;
-               conv >> comp;
-               fmt = make_pixelformat(comp, (comp==DEPTH_COMPONENT ? FLOAT : UNSIGNED_BYTE));
-               IO::print(IO::cerr, "Warning: deprecated conversion of '%s' to PixelFormat\n", conv.get());
-       }
-}
-
-PixelComponents pixelformat_from_graphics(Graphics::PixelFormat pf)
-{
-       switch(pf)
-       {
-       case Graphics::LUMINANCE: return LUMINANCE;
-       case Graphics::LUMINANCE_ALPHA: return LUMINANCE_ALPHA;
-       case Graphics::RGB: return RGB;
-       case Graphics::RGBX:
-       case Graphics::RGBA: return RGBA;
-       case Graphics::BGR: return BGR;
-       case Graphics::BGRX:
-       case Graphics::BGRA: return BGRA;
-       default: throw invalid_argument("pixelformat_from_graphics");
-       }
-}
-
-PixelComponents storage_pixelformat_from_graphics(Graphics::PixelFormat pf)
-{
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-       switch(pf)
-       {
-       case Graphics::RGBX:
-       case Graphics::BGR:
-       case Graphics::BGRX: return RGB;
-       case Graphics::BGRA: return RGBA;
-       default: return pixelformat_from_graphics(pf);
-       }
-#pragma GCC diagnostic pop
-}
-
-PixelFormat pixelformat_from_image(const Graphics::Image &image)
-{
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-       PixelComponents comp = pixelformat_from_graphics(image.get_format());
-#pragma GCC diagnostic pop
-       return make_pixelformat(comp, UNSIGNED_BYTE);
-}
-
-PixelFormat make_pixelformat(PixelComponents comp, DataType type, bool srgb)
-{
-       if(srgb && type!=UNSIGNED_BYTE && comp!=RGB && comp!=RGBA && comp!=BGR && comp!=BGRA)
-               throw invalid_argument("make_pixelformat");
-
-       switch(comp)
-       {
-       case RED:
-               switch(type)
-               {
-               case UNSIGNED_BYTE: return R8;
-               case HALF_FLOAT: return R16F;
-               case FLOAT: return R32F;
-               default: throw invalid_argument("make_pixelformat");
-               }
-       case RG:
-               switch(type)
-               {
-               case UNSIGNED_BYTE: return RG8;
-               case HALF_FLOAT: return RG16F;
-               case FLOAT: return RG32F;
-               default: throw invalid_argument("make_pixelformat");
-               }
-       case RGB:
-               switch(type)
-               {
-               case UNSIGNED_BYTE: return (srgb ? SRGB8 : RGB8);
-               case HALF_FLOAT: return RGB16F;
-               case FLOAT: return RGB32F;
-               default: throw invalid_argument("make_pixelformat");
-               }
-       case RGBA:
-               switch(type)
-               {
-               case UNSIGNED_BYTE: return (srgb ? SRGB8_ALPHA8 : RGBA8);
-               case HALF_FLOAT: return RGBA16F;
-               case FLOAT: return RGBA32F;
-               default: throw invalid_argument("make_pixelformat");
-               }
-       case BGR:
-               if(type!=UNSIGNED_BYTE)
-                       throw invalid_argument("make_pixelformat");
-               return (srgb ? BGR8 : SBGR8);
-       case BGRA:
-               if(type!=UNSIGNED_BYTE)
-                       throw invalid_argument("make_pixelformat");
-               return (srgb ? BGRA8 : SBGR8_ALPHA8);
-       case LUMINANCE:
-               if(type!=UNSIGNED_BYTE)
-                       throw invalid_argument("make_pixelformat");
-               return LUMINANCE8;
-       case LUMINANCE_ALPHA:
-               if(type!=UNSIGNED_BYTE)
-                       throw invalid_argument("make_pixelformat");
-               return LUMINANCE8;
-       case STENCIL_INDEX:
-               if(type!=UNSIGNED_BYTE)
-                       throw invalid_argument("make_pixelformat");
-               return STENCIL_INDEX8;
-       case DEPTH_COMPONENT:
-               switch(type)
-               {
-               case UNSIGNED_SHORT: return DEPTH_COMPONENT16;
-               case UNSIGNED_INT: return DEPTH_COMPONENT32;
-               case FLOAT: return DEPTH_COMPONENT32F;
-               default: throw invalid_argument("make_pixelformat");
-               }
-       default:
-               throw invalid_argument("make_pixelformat");
-       }
-}
-
-PixelFormat get_base_pixelformat(PixelFormat pf)
-{
-       switch(pf)
-       {
-       case SRGB8: return RGB8;
-       case SRGB8_ALPHA8: return RGBA8;
-       default: return pf;
-       }
-}
-
-PixelComponents get_components(PixelFormat pf)
-{
-       switch(pf)
-       {
-       case R8:
-       case R16F:
-       case R32F: return RED;
-       case RG8:
-       case RG16F:
-       case RG32F: return RG;
-       case RGB8:
-       case RGB16F:
-       case RGB32F:
-       case SRGB8: return RGB;
-       case RGBA8:
-       case RGBA16F:
-       case RGBA32F:
-       case SRGB8_ALPHA8: return RGBA;
-       case BGR8:
-       case SBGR8: return BGR;
-       case BGRA8:
-       case SBGR8_ALPHA8: return BGRA;
-       case LUMINANCE8: return LUMINANCE;
-       case LUMINANCE8_ALPHA8: return LUMINANCE_ALPHA;
-       case STENCIL_INDEX8: return STENCIL_INDEX;
-       case DEPTH_COMPONENT16:
-       case DEPTH_COMPONENT24:
-       case DEPTH_COMPONENT32:
-       case DEPTH_COMPONENT32F: return DEPTH_COMPONENT;
-       default: throw invalid_argument("get_components");
-       }
-}
-
-PixelFormat get_default_sized_pixelformat(PixelComponents comp)
-{
-       DataType type = UNSIGNED_BYTE;
-       if(comp==DEPTH_COMPONENT)
-       {
-               if(get_gl_api()==OPENGL_ES2 && !ARB_depth_buffer_float)
-                       type = UNSIGNED_SHORT;
-               else
-                       type = FLOAT;
-       }
-       return make_pixelformat(comp, type);
-}
-
-PixelFormat get_srgb_pixelformat(PixelFormat pf)
-{
-       switch(pf)
-       {
-       case RGB8: return SRGB8;
-       case RGBA8: return SRGB8_ALPHA8;
-       default: return pf;
-       }
-}
-
-unsigned get_component_count(PixelComponents comp)
-{
-       switch(comp)
-       {
-       case RED:
-       case LUMINANCE:
-       case DEPTH_COMPONENT:
-       case STENCIL_INDEX:
-               return 1;
-       case RG:
-       case LUMINANCE_ALPHA:
-               return 2;
-       case RGB:
-       case BGR:
-               return 3;
-       case RGBA:
-       case BGRA:
-               return 4;
-       default:
-               throw invalid_argument("get_component_count");
-       }
-}
-
-DataType get_component_type(PixelFormat pf)
-{
-       switch(pf)
-       {
-       case R8:
-       case RG8:
-       case RGB8:
-       case RGBA8:
-       case SRGB8:
-       case SRGB8_ALPHA8:
-       case BGR8:
-       case BGRA8:
-       case SBGR8:
-       case SBGR8_ALPHA8:
-       case LUMINANCE8:
-       case LUMINANCE8_ALPHA8:
-               return UNSIGNED_BYTE;
-       case R16F:
-       case RG16F:
-       case RGB16F:
-       case RGBA16F:
-               return HALF_FLOAT;
-       case DEPTH_COMPONENT16:
-               return UNSIGNED_SHORT;
-       case R32F:
-       case RG32F:
-       case RGB32F:
-       case RGBA32F:
-       case DEPTH_COMPONENT32:
-               return UNSIGNED_INT;
-       case DEPTH_COMPONENT32F:
-               return FLOAT;
-       case DEPTH_COMPONENT24:
-               // There's no DataType value with 24-bit size
-       default:
-               throw invalid_argument("get_component_type");
-       }
-}
-
-unsigned get_pixel_size(PixelFormat pf)
-{
-       return get_component_count(pf)*get_type_size(get_component_type(pf));
-}
-
-void require_pixelformat(PixelFormat pf)
-{
-       /* TODO These checks are only accurate for textures.  On OpenGL ES some
-       formats are allowed for render buffers earlier than textures.  In particular
-       it's possible to create a 16-bit depth renderbuffer on OpenGL ES 2.0 but
-       depth textures are only available with 3.0 or the OES_depth_texture
-       extension.*/
-       switch(pf)
-       {
-       case RGB8:
-       case RGBA8:
-               { static Require _req(OES_required_internalformat); }
-               break;
-       case R8:
-       case RG8:
-               { static Require _req(ARB_texture_rg); }
-               break;
-       case R16F:
-       case R32F:
-       case RG16F:
-       case RG32F:
-               { static Require _req(ARB_texture_rg); }
-               { static Require _req(ARB_texture_float); }
-               break;
-       case RGB16F:
-       case RGB32F:
-       case RGBA16F:
-       case RGBA32F:
-               { static Require _req(ARB_texture_float); }
-               break;
-       case SRGB8:
-       case SRGB8_ALPHA8:
-               { static Require _req(EXT_texture_sRGB); }
-               break;
-       case DEPTH_COMPONENT16:
-       case DEPTH_COMPONENT24:
-       case DEPTH_COMPONENT32:
-               { static Require _req(ARB_depth_texture); }
-               { static Require _req(OES_required_internalformat); }
-               break;
-       case DEPTH_COMPONENT32F:
-               { static Require _req(ARB_depth_buffer_float); }
-               break;
-       case STENCIL_INDEX8:
-               { static Require _req(OES_texture_stencil8); }
-               break;
-       default:
-               throw invalid_argument("require_pixelformat");
-       }
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/pixelformat.h b/source/pixelformat.h
deleted file mode 100644 (file)
index 213328a..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-#ifndef MSP_GL_PIXELFORMAT_H_
-#define MSP_GL_PIXELFORMAT_H_
-
-#include <msp/core/attributes.h>
-#include <msp/graphics/image.h>
-#include <msp/strings/lexicalcast.h>
-#include "gl.h"
-#include <msp/gl/extensions/arb_depth_buffer_float.h>
-#include <msp/gl/extensions/arb_depth_texture.h>
-#include <msp/gl/extensions/arb_texture_float.h>
-#include <msp/gl/extensions/arb_texture_rg.h>
-#include <msp/gl/extensions/ext_bgra.h>
-#include <msp/gl/extensions/ext_texture_srgb.h>
-#include <msp/gl/extensions/oes_required_internalformat.h>
-#include <msp/gl/extensions/oes_texture_stencil8.h>
-#include <msp/gl/extensions/msp_luminance_formats.h>
-#include "datatype.h"
-
-namespace Msp {
-namespace GL {
-
-enum PixelComponents
-{
-       RED             = GL_RED,
-       RG              = GL_RG,
-       RGB             = GL_RGB,
-       RGBA            = GL_RGBA,
-       BGR             = GL_BGR,
-       BGRA            = GL_BGRA,
-       LUMINANCE       = GL_LUMINANCE,
-       LUMINANCE_ALPHA = GL_LUMINANCE_ALPHA,
-       DEPTH_COMPONENT = GL_DEPTH_COMPONENT,
-       STENCIL_INDEX   = GL_STENCIL_INDEX
-};
-
-enum PixelFormat
-{
-       R8              = GL_R8,
-       R16F            = GL_R16F,
-       R32F            = GL_R32F,
-       RG8             = GL_RG8,
-       RG16F           = GL_RG16F,
-       RG32F           = GL_RG32F,
-       RGB8            = GL_RGB8,
-       RGB16F          = GL_RGB16F,
-       RGB32F          = GL_RGB32F,
-       RGBA8           = GL_RGBA8,
-       RGBA16F         = GL_RGBA16F,
-       RGBA32F         = GL_RGBA32F,
-       SRGB8           = GL_SRGB8,
-       SRGB8_ALPHA8    = GL_SRGB8_ALPHA8,
-       BGR8            = 200000,
-       BGRA8           = 200001,
-       SBGR8           = 200002,
-       SBGR8_ALPHA8    = 200003,
-       LUMINANCE8      = GL_LUMINANCE8,
-       LUMINANCE8_ALPHA8 = GL_LUMINANCE8_ALPHA8,
-       DEPTH_COMPONENT16 = GL_DEPTH_COMPONENT16,
-       DEPTH_COMPONENT24 = GL_DEPTH_COMPONENT24,
-       DEPTH_COMPONENT32 = GL_DEPTH_COMPONENT32,
-       DEPTH_COMPONENT32F = GL_DEPTH_COMPONENT32F,
-       STENCIL_INDEX8 = GL_STENCIL_INDEX8
-};
-
-void operator>>(const LexicalConverter &, PixelComponents &);
-void operator>>(const LexicalConverter &, PixelFormat &);
-
-DEPRECATED PixelComponents pixelformat_from_graphics(Graphics::PixelFormat);
-DEPRECATED PixelComponents storage_pixelformat_from_graphics(Graphics::PixelFormat, bool);
-PixelFormat pixelformat_from_image(const Graphics::Image &);
-
-PixelFormat make_pixelformat(PixelComponents, DataType, bool = false);
-DEPRECATED PixelFormat get_base_pixelformat(PixelFormat);
-PixelComponents get_components(PixelFormat);
-DEPRECATED PixelFormat get_default_sized_pixelformat(PixelComponents);
-DEPRECATED PixelFormat get_srgb_pixelformat(PixelFormat);
-
-unsigned get_component_count(PixelComponents);
-inline unsigned get_component_count(PixelFormat f)
-{ return get_component_count(get_components(f)); }
-
-DataType get_component_type(PixelFormat);
-inline unsigned get_component_size(PixelFormat f)
-{ return get_type_size(get_component_type(f)); }
-
-unsigned get_pixel_size(PixelFormat);
-
-void require_pixelformat(PixelFormat);
-
-DEPRECATED inline PixelFormat get_sized_pixelformat(PixelComponents c, unsigned s = 1)
-{ return make_pixelformat(c, (s==2 ? HALF_FLOAT : s==4 ? FLOAT : UNSIGNED_BYTE)); }
-
-DEPRECATED inline PixelComponents get_unsized_pixelformat(PixelFormat f)
-{ return get_components(f); }
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/pixelstore.cpp b/source/pixelstore.cpp
deleted file mode 100644 (file)
index 409f0f9..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-#include <algorithm>
-#include <msp/gl/extensions/ext_texture3d.h>
-#include <msp/gl/extensions/ext_unpack_subimage.h>
-#include "gl.h"
-#include "pixelformat.h"
-#include "pixelstore.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-PixelStore::PixelStore():
-       row_length(0),
-       image_height(0),
-       skip_pixels(0),
-       skip_rows(0),
-       skip_images(0),
-       alignment(4)
-{ }
-
-PixelStore PixelStore::from_image(const Graphics::Image &img)
-{
-       PixelStore pstore;
-       unsigned stride = img.get_stride();
-       unsigned ncomp = get_component_count(pixelformat_from_image(img));
-       pstore.set_canvas_size(img.get_stride()/ncomp, 0);
-       pstore.set_alignment(min(stride&~(stride-1), 8U));
-       return pstore;
-}
-
-void PixelStore::update_parameter(int mask) const
-{
-       if(cur_obj!=this)
-               return;
-
-       if(mask&SIZE)
-       {
-               glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length);
-               if(EXT_texture3D)
-                       glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, image_height);
-       }
-       if(mask&ORIGIN)
-       {
-               glPixelStorei(GL_UNPACK_SKIP_PIXELS, skip_pixels);
-               glPixelStorei(GL_UNPACK_SKIP_ROWS, skip_rows);
-               if(EXT_texture3D)
-                       glPixelStorei(GL_UNPACK_SKIP_IMAGES, skip_images);
-       }
-       if(mask&ALIGNMENT)
-               glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
-}
-
-void PixelStore::set_canvas_size(unsigned w, unsigned h)
-{
-       static Require _req(EXT_unpack_subimage);
-       row_length = w;
-       image_height = h;
-       update_parameter(SIZE);
-}
-
-void PixelStore::set_origin(unsigned x, unsigned y, unsigned z)
-{
-       static Require _req(EXT_unpack_subimage);
-       if(z>0)
-               static Require _req3d(EXT_texture3D);
-       skip_pixels = x;
-       skip_rows = y;
-       skip_images = z;
-       update_parameter(ORIGIN);
-}
-
-void PixelStore::set_alignment(unsigned a)
-{
-       alignment = a;
-       update_parameter(ALIGNMENT);
-}
-
-void PixelStore::bind() const
-{
-       if(set_current(this))
-               update_parameter(-1);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/pixelstore.h b/source/pixelstore.h
deleted file mode 100644 (file)
index 80032c3..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#ifndef MSP_GL_PIXELSTORE_H_
-#define MSP_GL_PIXELSTORE_H_
-
-#include <msp/graphics/image.h>
-#include "bindable.h"
-
-namespace Msp {
-namespace GL {
-
-class PixelStore: public BindableWithDefault<PixelStore>
-{
-private:
-       enum ParameterMask
-       {
-               SIZE = 1,
-               ORIGIN = 2,
-               ALIGNMENT = 4
-       };
-
-       unsigned row_length;
-       unsigned image_height;
-       unsigned skip_pixels;
-       unsigned skip_rows;
-       unsigned skip_images;
-       unsigned alignment;
-
-public:
-       PixelStore();
-
-       static PixelStore from_image(const Graphics::Image &);
-
-private:
-       void update_parameter(int) const;
-
-public:
-       void set_canvas_size(unsigned, unsigned);
-       void set_origin(unsigned, unsigned, unsigned);
-       void set_alignment(unsigned);
-
-       void bind() const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/placeable.cpp b/source/placeable.cpp
deleted file mode 100644 (file)
index c7ea792..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#include "placeable.h"
-
-namespace Msp {
-namespace GL {
-
-void Placeable::set_matrix(const Matrix &m)
-{
-       matrix = m;
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/placeable.h b/source/placeable.h
deleted file mode 100644 (file)
index 35999a0..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef MSP_GL_PLACEABLE_H_
-#define MSP_GL_PLACEABLE_H_
-
-#include "matrix.h"
-#include "renderable.h"
-
-namespace Msp {
-namespace GL {
-
-/**
-A base class for things that can be positioned and oriented in 3D space.
-*/
-class Placeable
-{
-protected:
-       Matrix matrix;
-
-       Placeable() { }
-public:
-       virtual ~Placeable() { }
-
-       virtual void set_matrix(const Matrix &);
-
-       /** Returns the Placeable's matrix.  This function returns a pointer for
-       compatibility with Renderable.  The returned pointer is never null. */
-       virtual const Matrix *get_matrix() const { return &matrix; }
-};
-
-
-class PlacedRenderable: public Renderable, public Placeable
-{
-protected:
-       PlacedRenderable() { }
-
-public:
-       /* Reimplement to clear ambiguity between Renderable and Placeable.  This
-       overrides both base classes' implementations. */
-       virtual const Matrix *get_matrix() const { return &matrix; }
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/pose.cpp b/source/pose.cpp
deleted file mode 100644 (file)
index 99fa6bb..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-#include <msp/datafile/collection.h>
-#include "armature.h"
-#include "error.h"
-#include "pose.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Pose::Pose():
-       armature(0)
-{ }
-
-Pose::Pose(const Armature &a):
-       armature(0)
-{
-       set_armature(a);
-}
-
-void Pose::set_armature(const Armature &a)
-{
-       if(armature)
-               throw invalid_operation("Pose::set_armature");
-       armature = &a;
-       links.resize(armature->get_max_link_index()+1);
-}
-
-void Pose::rotate_link(unsigned i, float angle, const Vector3 &axis)
-{
-       if(i>=links.size())
-               throw out_of_range("Pose::rotate_link");
-
-       const Armature::Link &arm_link = armature->get_link(i);
-       links[i].local_matrix.rotate(angle, axis);
-
-       // Keep the base point stationary
-       Vector3 base = arm_link.get_base();
-       Vector3 new_base = links[i].local_matrix*base;
-       links[i].local_matrix = Matrix::translation(base-new_base)*links[i].local_matrix;
-
-       if(const Armature::Link *parent = arm_link.get_parent())
-               links[i].matrix = links[parent->get_index()].matrix*links[i].local_matrix;
-
-       // XXX apply matrix to descendants of the link
-}
-
-const Matrix &Pose::get_link_matrix(unsigned i) const
-{
-       if(i>=links.size())
-               throw out_of_range("Pose::get_link_matrix");
-       return links[i].matrix;
-}
-
-
-Pose::Loader::Loader(Pose &p, Collection &c):
-       DataFile::CollectionObjectLoader<Pose>(p, &c)
-{
-       add("armature", &Loader::armature);
-       add("link",     &Loader::link);
-}
-
-void Pose::Loader::armature(const string &n)
-{
-       obj.set_armature(get_collection().get<Armature>(n));
-}
-
-void Pose::Loader::link(const string &n)
-{
-       if(!obj.armature)
-               throw logic_error("Armature must be specified first");
-       LinkLoader ldr(obj, obj.armature->get_link(n).get_index());
-       load_sub_with(ldr);
-}
-
-
-Pose::LinkLoader::LinkLoader(Pose &p, unsigned l):
-       DataFile::ObjectLoader<Pose>(p),
-       link_index(l)
-{
-       add("rotation", &LinkLoader::rotation);
-}
-
-void Pose::LinkLoader::rotation(float a, float x, float y, float z)
-{
-       obj.rotate_link(link_index, a, Vector3(x, y, z));
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/pose.h b/source/pose.h
deleted file mode 100644 (file)
index cbd8137..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-#ifndef MSP_GL_POSE_H_
-#define MSP_GL_POSE_H_
-
-#include <map>
-#include <msp/datafile/objectloader.h>
-#include "matrix.h"
-
-namespace Msp {
-namespace GL {
-
-class Armature;
-
-class Pose
-{
-public:
-       class Loader: public DataFile::CollectionObjectLoader<Pose>
-       {
-       public:
-               Loader(Pose &, Collection &);
-       private:
-               void armature(const std::string &);
-               void link(const std::string &);
-       };
-
-private:
-       struct Link
-       {
-               Matrix matrix;
-               Matrix local_matrix;
-       };
-
-       class LinkLoader: public DataFile::ObjectLoader<Pose>
-       {
-       private:
-               unsigned link_index;
-
-       public:
-               LinkLoader(Pose &, unsigned);
-       private:
-               void rotation(float, float, float, float);
-       };
-
-       const Armature *armature;
-       std::vector<Link> links;
-
-public:
-       Pose();
-       Pose(const Armature &);
-
-       void set_armature(const Armature &);
-       const Armature *get_armature() const { return armature; }
-       void rotate_link(unsigned, float, const Vector3 &);
-       const Matrix &get_link_matrix(unsigned) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/postprocessor.cpp b/source/postprocessor.cpp
deleted file mode 100644 (file)
index 6af069e..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-#include "mesh.h"
-#include "meshbuilder.h"
-#include "postprocessor.h"
-#include "sampler.h"
-#include "shader.h"
-
-namespace Msp {
-namespace GL {
-
-WeakPtr<Mesh> PostProcessor::fullscreen_quad;
-WeakPtr<Sampler> PostProcessor::nearest_sampler;
-WeakPtr<Sampler> PostProcessor::linear_sampler;
-
-void PostProcessor::render(Renderer &, const Texture2D &color, const Texture2D &depth)
-{
-       render(color, depth);
-}
-
-RefPtr<Mesh> PostProcessor::get_fullscreen_quad()
-{
-       RefPtr<Mesh> mesh = fullscreen_quad;
-       if(!mesh)
-       {
-               mesh = new Mesh(VERTEX2);
-               MeshBuilder builder(*mesh);
-               builder.begin(TRIANGLE_STRIP);
-               builder.vertex(-1, 1);
-               builder.vertex(-1, -1);
-               builder.vertex(1, 1);
-               builder.vertex(1, -1);
-               builder.end();
-               fullscreen_quad = mesh;
-       }
-       return mesh;
-}
-
-RefPtr<Sampler> PostProcessor::get_nearest_sampler()
-{
-       RefPtr<Sampler> sampler = nearest_sampler;
-       if(!sampler)
-       {
-               sampler = new Sampler;
-               sampler->set_filter(NEAREST);
-               sampler->set_wrap(CLAMP_TO_EDGE);
-               nearest_sampler = sampler;
-       }
-       return sampler;
-}
-
-RefPtr<Sampler> PostProcessor::get_linear_sampler()
-{
-       RefPtr<Sampler> sampler = linear_sampler;
-       if(!sampler)
-       {
-               sampler = new Sampler;
-               sampler->set_filter(LINEAR);
-               sampler->set_wrap(CLAMP_TO_EDGE);
-               linear_sampler = sampler;
-       }
-       return sampler;
-}
-
-
-PostProcessor::Template::Template():
-       size_divisor(1)
-{ }
-
-
-PostProcessor::Template::Loader::Loader(Template &t):
-       DataFile::ObjectLoader<Template>(t)
-{
-       add("size_divisor", &Template::size_divisor);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/postprocessor.h b/source/postprocessor.h
deleted file mode 100644 (file)
index c107d88..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-#ifndef MSP_GL_POSTPROCESSOR_H_
-#define MSP_GL_POSTPROCESSOR_H_
-
-#include <msp/datafile/objectloader.h>
-
-namespace Msp {
-namespace GL {
-
-class Mesh;
-class Renderer;
-class Sampler;
-class Shader;
-class Texture2D;
-
-/**
-Base class for post-processing effects.  Post-processors receive the contents
-of the entire framebuffer as a texture and render it back, altering it in the
-process.
-*/
-class PostProcessor
-{
-public:
-       struct Template
-       {
-               class Loader: public Msp::DataFile::ObjectLoader<Template>
-               {
-               public:
-                       Loader(Template &);
-               };
-
-               unsigned size_divisor;
-
-               Template();
-               virtual ~Template() { }
-
-               virtual PostProcessor *create(unsigned, unsigned) const = 0;
-       };
-
-private:
-       static WeakPtr<Mesh> fullscreen_quad;
-       static WeakPtr<Sampler> nearest_sampler;
-       static WeakPtr<Sampler> linear_sampler;
-
-protected:
-       PostProcessor() { }
-public:
-       virtual ~PostProcessor() { }
-
-       /// Renders the effect.
-       virtual void render(const Texture2D &, const Texture2D &) { }
-
-       virtual void render(Renderer &, const Texture2D &, const Texture2D &);
-
-protected:
-       /** Returns a mesh consisting of a single quad, covering the entire screen.
-       The vertices are in normalized device coordinates. */
-       static RefPtr<Mesh> get_fullscreen_quad();
-
-       static RefPtr<Sampler> get_nearest_sampler();
-       static RefPtr<Sampler> get_linear_sampler();
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/predicate.cpp b/source/predicate.cpp
deleted file mode 100644 (file)
index a195373..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-#include <msp/strings/format.h>
-#include "predicate.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-void operator>>(const LexicalConverter &conv, Predicate &pred)
-{
-       const string &str = conv.get();
-       if(str=="NEVER")
-               pred = NEVER;
-       else if(str=="ALWAYS")
-               pred = ALWAYS;
-       else if(str=="LESS")
-               pred = LESS;
-       else if(str=="LEQUAL")
-               pred = LEQUAL;
-       else if(str=="EQUAL")
-               pred = EQUAL;
-       else if(str=="GREATER")
-               pred = GREATER;
-       else if(str=="GEQUAL")
-               pred = GEQUAL;
-       else if(str=="NOTEQUAL")
-               pred = NOTEQUAL;
-       else
-               throw lexical_error(format("conversion of '%s' to Predicate", str));
-}
-
-void operator<<(LexicalConverter &conv, Predicate pred)
-{
-       switch(pred)
-       {
-       case NEVER: conv.result("NEVER"); break;
-       case ALWAYS: conv.result("ALWAYS"); break;
-       case LESS: conv.result("LESS"); break;
-       case LEQUAL: conv.result("LEQUAL"); break;
-       case EQUAL: conv.result("EQUAL"); break;
-       case GREATER: conv.result("GREATER"); break;
-       case GEQUAL: conv.result("GEQUAL"); break;
-       case NOTEQUAL: conv.result("NOTEQUAL"); break;
-       default: conv.result(format("Predicate(%#x)", static_cast<int>(pred))); break;
-       }
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/predicate.h b/source/predicate.h
deleted file mode 100644 (file)
index 13c22e8..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef MSP_GL_PREDICATE_H_
-#define MSP_GL_PREDICATE_H_
-
-#include <msp/strings/lexicalcast.h>
-#include "gl.h"
-
-namespace Msp {
-namespace GL {
-
-enum Predicate
-{
-       NEVER    = GL_NEVER,
-       ALWAYS   = GL_ALWAYS,
-       LESS     = GL_LESS,
-       LEQUAL   = GL_LEQUAL,
-       EQUAL    = GL_EQUAL,
-       GREATER  = GL_GREATER,
-       GEQUAL   = GL_GEQUAL,
-       NOTEQUAL = GL_NOTEQUAL
-};
-
-void operator>>(const LexicalConverter &, Predicate &);
-void operator<<(LexicalConverter &, Predicate);
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/primitivebuilder.cpp b/source/primitivebuilder.cpp
deleted file mode 100644 (file)
index dd86990..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-#include "error.h"
-#include "primitivebuilder.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-PrimitiveBuilder::PrimitiveBuilder(VertexArray &a):
-       array(a),
-       vab(array),
-       in_batch(false),
-       offs(0)
-{ }
-
-void PrimitiveBuilder::begin(PrimitiveType t)
-{
-       if(in_batch)
-               throw invalid_operation("PrimitiveBuilder::begin");
-
-       type = t;
-       in_batch = true;
-
-       begin_();
-}
-
-void PrimitiveBuilder::end()
-{
-       if(!in_batch)
-               throw invalid_operation("PrimitiveBuilder::end");
-
-       in_batch = false;
-
-       end_();
-}
-
-void PrimitiveBuilder::offset(unsigned o)
-{
-       if(o>array.size())
-               throw out_of_range("PrimitiveBuilder::offset");
-       offs = o;
-}
-
-void PrimitiveBuilder::element(unsigned i)
-{
-       if(!in_batch)
-               throw invalid_operation("PrimitiveBuilder::element");
-       if(offs+i>=array.size())
-               throw out_of_range("PrimitiveBuilder::element");
-       element_(offs+i);
-}
-
-PrimitiveType PrimitiveBuilder::get_type() const
-{
-       if(!in_batch)
-               throw invalid_operation("PrimitiveBuilder::get_type");
-       return type;
-}
-
-void PrimitiveBuilder::vertex_(const Vector4 &v)
-{
-       vab.color(col);
-       vab.normal(nor);
-       for(std::map<unsigned, Vector4>::iterator i=texc.begin(); i!=texc.end(); ++i)
-               vab.multitexcoord(i->first, i->second);
-       for(std::map<unsigned, Vector4>::iterator i=attr.begin(); i!=attr.end(); ++i)
-               vab.attrib(i->first, i->second);
-       vab.vertex(v);
-
-       if(in_batch)
-               element_(array.size()-1);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/primitivebuilder.h b/source/primitivebuilder.h
deleted file mode 100644 (file)
index 0ffd170..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef MSP_GL_PRIMITIVEBUILDER_H_
-#define MSP_GL_PRIMITIVEBUILDER_H_
-
-#include "primitivetype.h"
-#include "vertexarray.h"
-#include "vertexbuilder.h"
-
-namespace Msp {
-namespace GL {
-
-class VertexArray;
-class VertexArrayBuilder;
-
-/**
-Base class for primitive builders.  This is derived from VertexBuilder and adds
-begin() and end() functions for specifying batches of primitives instead of
-just vertices.
-*/
-class PrimitiveBuilder: public VertexBuilder
-{
-protected:
-       VertexArray &array;
-       VertexArrayBuilder vab;
-       PrimitiveType type;
-       bool in_batch;
-       unsigned offs;
-
-       PrimitiveBuilder(VertexArray &);
-public:
-       void begin(PrimitiveType);
-       void end();
-       void offset(unsigned);
-       void element(unsigned);
-       PrimitiveType get_type() const;
-protected:
-       virtual void vertex_(const Vector4 &);
-       virtual void begin_() = 0;
-       virtual void end_() = 0;
-       virtual void element_(unsigned) = 0;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/primitivetype.cpp b/source/primitivetype.cpp
deleted file mode 100644 (file)
index 8137584..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#include <msp/strings/format.h>
-#include "primitivetype.h"
-
-namespace Msp {
-namespace GL {
-
-void operator>>(const LexicalConverter &conv, PrimitiveType &pt)
-{
-       if(conv.get()=="POINTS")
-               pt = POINTS;
-       else if(conv.get()=="LINES")
-               pt = LINES;
-       else if(conv.get()=="LINE_LOOP")
-               pt = LINE_LOOP;
-       else if(conv.get()=="LINE_STRIP")
-               pt = LINE_STRIP;
-       else if(conv.get()=="TRIANGLES")
-               pt = TRIANGLES;
-       else if(conv.get()=="TRIANGLE_STRIP")
-               pt = TRIANGLE_STRIP;
-       else if(conv.get()=="TRIANGLE_FAN")
-               pt = TRIANGLE_FAN;
-       else
-               throw lexical_error(format("conversion of '%s' to PrimitiveType", conv.get()));
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/primitivetype.h b/source/primitivetype.h
deleted file mode 100644 (file)
index 8e33b1d..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-#ifndef MSP_GL_PRIMITIVETYPE_H_
-#define MSP_GL_PRIMITIVETYPE_H_
-
-#include <msp/strings/lexicalcast.h>
-#include "gl.h"
-
-namespace Msp {
-namespace GL {
-
-enum PrimitiveType
-{
-       POINTS         = GL_POINTS,
-       LINES          = GL_LINES,
-       LINE_STRIP     = GL_LINE_STRIP,
-       LINE_LOOP      = GL_LINE_LOOP,
-       TRIANGLES      = GL_TRIANGLES,
-       TRIANGLE_STRIP = GL_TRIANGLE_STRIP,
-       TRIANGLE_FAN   = GL_TRIANGLE_FAN
-};
-
-void operator>>(const LexicalConverter &, PrimitiveType &);
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/program.cpp b/source/program.cpp
deleted file mode 100644 (file)
index 00bca83..0000000
+++ /dev/null
@@ -1,421 +0,0 @@
-#include <algorithm>
-#include <cstring>
-#include <set>
-#include <msp/core/hash.h>
-#include <msp/core/maputils.h>
-#include <msp/gl/extensions/arb_shader_objects.h>
-#include <msp/gl/extensions/arb_uniform_buffer_object.h>
-#include <msp/gl/extensions/arb_vertex_shader.h>
-#include <msp/gl/extensions/ext_gpu_shader4.h>
-#include <msp/gl/extensions/nv_non_square_matrices.h>
-#include <msp/io/print.h>
-#include <msp/strings/format.h>
-#include "buffer.h"
-#include "error.h"
-#include "misc.h"
-#include "program.h"
-#include "programcompiler.h"
-#include "resources.h"
-#include "shader.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Program::Program()
-{
-       init();
-}
-
-Program::Program(const std::string &source)
-{
-       init();
-
-       ProgramCompiler compiler;
-       if(source.find(';')==string::npos && source.size()>5 && !source.compare(source.size()-5, 5, ".glsl"))
-       {
-               if(RefPtr<IO::Seekable> io = Resources::get_builtins().open(source))
-                       compiler.compile(*io, source);
-               else
-                       throw IO::file_not_found(source);
-       }
-       else
-               compiler.compile(source);
-       compiler.add_shaders(*this);
-       link();
-}
-
-Program::Program(const string &vert, const string &frag)
-{
-       init();
-
-       attach_shader_owned(new VertexShader(vert));
-       attach_shader_owned(new FragmentShader(frag));
-       link();
-}
-
-void Program::init()
-{
-       static Require _req(ARB_shader_objects);
-
-       linked = false;
-       id = glCreateProgram();
-}
-
-Program::~Program()
-{
-       for(ShaderList::iterator i=owned_data.begin(); i!=owned_data.end(); ++i)
-               delete *i;
-       glDeleteProgram(id);
-}
-
-void Program::attach_shader(Shader &shader)
-{
-       if(find(shaders.begin(), shaders.end(), &shader)==shaders.end())
-       {
-               glAttachShader(id, shader.get_id());
-               shaders.push_back(&shader);
-       }
-}
-
-void Program::attach_shader_owned(Shader *shader)
-{
-       attach_shader(*shader);
-       if(find(owned_data.begin(), owned_data.end(), shader)==owned_data.end())
-               owned_data.push_back(shader);
-}
-
-void Program::detach_shader(Shader &shader)
-{
-       ShaderList::iterator i = remove(shaders.begin(), shaders.end(), &shader);
-       if(i!=shaders.end())
-       {
-               shaders.erase(i, shaders.end());
-               glDetachShader(id, shader.get_id());
-       }
-}
-
-void Program::bind_attribute(unsigned index, const string &name)
-{
-       static Require _req(ARB_vertex_shader);
-       glBindAttribLocation(id, index, name.c_str());
-}
-
-void Program::bind_attribute(VertexComponent comp, const string &name)
-{
-       bind_attribute(get_component_type(comp), name);
-}
-
-void Program::bind_fragment_data(unsigned index, const string &name)
-{
-       static Require _req(EXT_gpu_shader4);
-       glBindFragDataLocation(id, index, name.c_str());
-}
-
-void Program::link()
-{
-       for(ShaderList::iterator i=shaders.begin(); i!=shaders.end(); ++i)
-               if(!(*i)->is_compiled())
-                       (*i)->compile();
-
-       uniforms.clear();
-
-       glLinkProgram(id);
-       linked = get_program_i(id, GL_LINK_STATUS);
-       if(!linked)
-               throw compile_error(get_info_log());
-
-#ifdef DEBUG
-       std::string info_log = get_info_log();
-       if(!info_log.empty())
-               IO::print("Program link info log:\n%s", info_log);
-#endif
-
-       query_uniforms();
-       query_attributes();
-
-       for(UniformMap::const_iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
-               require_type(i->second.type);
-       for(AttributeMap::const_iterator i=attributes.begin(); i!=attributes.end(); ++i)
-               require_type(i->second.type);
-}
-
-void Program::require_type(GLenum t)
-{
-       switch(t)
-       {
-       case GL_FLOAT_MAT2x3:
-       case GL_FLOAT_MAT2x4:
-       case GL_FLOAT_MAT3x2:
-       case GL_FLOAT_MAT3x4:
-       case GL_FLOAT_MAT4x2:
-       case GL_FLOAT_MAT4x3:
-               { static Require _req(NV_non_square_matrices); }
-               break;
-       }
-}
-
-void Program::query_uniforms()
-{
-       unsigned count = get_program_i(id, GL_ACTIVE_UNIFORMS);
-       vector<UniformInfo *> uniforms_by_index(count);
-       for(unsigned i=0; i<count; ++i)
-       {
-               char name[128];
-               int len = 0;
-               int size;
-               GLenum type;
-               glGetActiveUniform(id, i, sizeof(name), &len, &size, &type, name);
-               if(len && strncmp(name, "gl_", 3))
-               {
-                       /* Some implementations report the first element of a uniform array,
-                       others report just the name of the array itself. */
-                       if(len>3 && !strcmp(name+len-3, "[0]"))
-                               name[len-3] = 0;
-
-                       UniformInfo &info = uniforms[name];
-                       info.block = 0;
-                       info.name = name;
-                       info.size = size;
-                       info.array_stride = 0;
-                       info.matrix_stride = 0;
-                       info.type = type;
-                       uniforms_by_index[i] = &info;
-               }
-       }
-
-       if(ARB_uniform_buffer_object)
-               query_uniform_blocks(uniforms_by_index);
-
-       UniformBlockInfo &default_block = uniform_blocks[string()];
-       default_block.data_size = 0;
-       default_block.bind_point = -1;
-
-       for(UniformMap::iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
-               if(!i->second.block)
-               {
-                       i->second.location = glGetUniformLocation(id, i->second.name.c_str());
-                       i->second.block = &default_block;
-                       default_block.uniforms.push_back(&i->second);
-               }
-
-       default_block.layout_hash = compute_layout_hash(default_block.uniforms);
-
-       string layout_descriptor;
-       for(UniformBlockMap::const_iterator i=uniform_blocks.begin(); i!=uniform_blocks.end(); ++i)
-               layout_descriptor += format("%d:%x\n", i->second.bind_point, i->second.layout_hash);
-       uniform_layout_hash = hash32(layout_descriptor);
-}
-
-void Program::query_uniform_blocks(const vector<UniformInfo *> &uniforms_by_index)
-{
-       uniform_blocks.clear();
-
-       std::set<unsigned> used_bind_points;
-       unsigned count = get_program_i(id, GL_ACTIVE_UNIFORM_BLOCKS);
-       for(unsigned i=0; i<count; ++i)
-       {
-               char name[128];
-               int len;
-               glGetActiveUniformBlockName(id, i, sizeof(name), &len, name);
-               UniformBlockInfo &info = uniform_blocks[name];
-               info.name = name;
-
-               int value;
-               glGetActiveUniformBlockiv(id, i, GL_UNIFORM_BLOCK_DATA_SIZE, &value);
-               info.data_size = value;
-
-               glGetActiveUniformBlockiv(id, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &value);
-               vector<int> indices(value);
-               glGetActiveUniformBlockiv(id, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, &indices[0]);
-               for(vector<int>::iterator j=indices.begin(); j!=indices.end(); ++j)
-               {
-                       if(!uniforms_by_index[*j])
-                               throw logic_error("Program::link");
-                       info.uniforms.push_back(uniforms_by_index[*j]);
-                       uniforms_by_index[*j]->block = &info;
-               }
-
-               vector<unsigned> indices2(indices.begin(), indices.end());
-               vector<int> values(indices.size());
-               glGetActiveUniformsiv(id, indices.size(), &indices2[0], GL_UNIFORM_OFFSET, &values[0]);
-               for(unsigned j=0; j<indices.size(); ++j)
-                       uniforms_by_index[indices[j]]->location = values[j];
-
-               indices2.clear();
-               for(vector<int>::iterator j=indices.begin(); j!=indices.end(); ++j)
-                       if(uniforms_by_index[*j]->size>1)
-                               indices2.push_back(*j);
-               if(!indices2.empty())
-               {
-                       glGetActiveUniformsiv(id, indices2.size(), &indices2[0], GL_UNIFORM_ARRAY_STRIDE, &values[0]);
-                       for(unsigned j=0; j<indices2.size(); ++j)
-                               uniforms_by_index[indices2[j]]->array_stride = values[j];
-               }
-
-               indices2.clear();
-               for(vector<int>::iterator j=indices.begin(); j!=indices.end(); ++j)
-               {
-                       GLenum t = uniforms_by_index[*j]->type;
-                       if(t==GL_FLOAT_MAT4 || t==GL_FLOAT_MAT3 || t==GL_FLOAT_MAT2 ||
-                               t==GL_FLOAT_MAT2x3 || t==GL_FLOAT_MAT2x4 || t==GL_FLOAT_MAT3x2 ||
-                               t==GL_FLOAT_MAT3x4 || t==GL_FLOAT_MAT4x2 || t==GL_FLOAT_MAT4x3)
-                               indices2.push_back(*j);
-               }
-               if(!indices2.empty())
-               {
-                       glGetActiveUniformsiv(id, indices2.size(), &indices2[0], GL_UNIFORM_MATRIX_STRIDE, &values[0]);
-                       for(unsigned j=0; j<indices2.size(); ++j)
-                               uniforms_by_index[indices2[j]]->matrix_stride = values[j];
-               }
-
-               sort(info.uniforms.begin(), info.uniforms.end(), uniform_location_compare);
-               info.layout_hash = compute_layout_hash(info.uniforms);
-               unsigned n_bindings = BufferRange::get_n_uniform_buffer_bindings();
-               info.bind_point = info.layout_hash%n_bindings;
-               while(used_bind_points.count(info.bind_point))
-                       info.bind_point = (info.bind_point+1)%n_bindings;
-               glUniformBlockBinding(id, i, info.bind_point);
-               used_bind_points.insert(info.bind_point);
-       }
-}
-
-void Program::query_attributes()
-{
-       unsigned count = get_program_i(id, GL_ACTIVE_ATTRIBUTES);
-       for(unsigned i=0; i<count; ++i)
-       {
-               char name[128];
-               int len = 0;
-               int size;
-               GLenum type;
-               glGetActiveAttrib(id, i, sizeof(name), &len, &size, &type, name);
-               if(len && strncmp(name, "gl_", 3))
-               {
-                       if(len>3 && !strcmp(name+len-3, "[0]"))
-                               name[len-3] = 0;
-
-                       AttributeInfo &info = attributes[name];
-                       info.name = name;
-                       info.location = glGetAttribLocation(id, name);
-                       info.size = size;
-                       info.type = type;
-               }
-       }
-}
-
-Program::LayoutHash Program::compute_layout_hash(const vector<const UniformInfo *> &uniforms)
-{
-       string layout_descriptor;
-       for(vector<const UniformInfo *>::const_iterator i = uniforms.begin(); i!=uniforms.end(); ++i)
-               layout_descriptor += format("%d:%s:%x:%d\n", (*i)->location, (*i)->name, (*i)->type, (*i)->size);
-       return hash32(layout_descriptor);
-}
-
-bool Program::uniform_location_compare(const UniformInfo *uni1, const UniformInfo *uni2)
-{
-       return uni1->location<uni2->location;
-}
-
-string Program::get_info_log() const
-{
-       GLsizei len = get_program_i(id, GL_INFO_LOG_LENGTH);
-       string log(len+1, 0);
-       glGetProgramInfoLog(id, len+1, &len, &log[0]);
-       log.erase(len);
-       return log;
-}
-
-const Program::UniformBlockInfo &Program::get_uniform_block_info(const string &name) const
-{
-       return get_item(uniform_blocks, name);
-}
-
-const Program::UniformInfo &Program::get_uniform_info(const string &name) const
-{
-       return get_item(uniforms, name);
-}
-
-int Program::get_uniform_location(const string &n) const
-{
-       if(n[n.size()-1]==']')
-               throw invalid_argument("Program::get_uniform_location");
-
-       UniformMap::const_iterator i = uniforms.find(n);
-       if(i==uniforms.end())
-               return -1;
-
-       return i->second.block->bind_point<0 ? i->second.location : -1;
-}
-
-const Program::AttributeInfo &Program::get_attribute_info(const string &name) const
-{
-       return get_item(attributes, name);
-}
-
-int Program::get_attribute_location(const string &n) const
-{
-       if(n[n.size()-1]==']')
-               throw invalid_argument("Program::get_attribute_location");
-
-       AttributeMap::const_iterator i = attributes.find(n);
-       return i!=attributes.end() ? i->second.location : -1;
-}
-
-void Program::bind() const
-{
-       if(!linked)
-               throw invalid_operation("Program::bind");
-
-       if(!set_current(this))
-               return;
-
-       glUseProgram(id);
-}
-
-void Program::unbind()
-{
-       if(!set_current(0))
-               return;
-
-       glUseProgram(0);
-}
-
-
-Program::Loader::Loader(Program &p):
-       DataFile::ObjectLoader<Program>(p)
-{
-       add("attribute",       &Loader::attribute);
-       add("fragment_shader", &Loader::fragment_shader);
-       add("geometry_shader", &Loader::geometry_shader);
-       add("vertex_shader",   &Loader::vertex_shader);
-}
-
-void Program::Loader::finish()
-{
-       obj.link();
-}
-
-void Program::Loader::attribute(unsigned i, const string &n)
-{
-       obj.bind_attribute(i, n);
-}
-
-void Program::Loader::fragment_shader(const string &src)
-{
-       obj.attach_shader_owned(new FragmentShader(src));
-}
-
-void Program::Loader::geometry_shader(const string &src)
-{
-       obj.attach_shader_owned(new GeometryShader(src));
-}
-
-void Program::Loader::vertex_shader(const string &src)
-{
-       obj.attach_shader_owned(new VertexShader(src));
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/program.h b/source/program.h
deleted file mode 100644 (file)
index fc60db4..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-#ifndef MSP_GL_PROGRAM_H_
-#define MSP_GL_PROGRAM_H_
-
-#include <string>
-#include <vector>
-#include <msp/datafile/objectloader.h>
-#include "bindable.h"
-#include "gl.h"
-#include "vertexformat.h"
-
-namespace Msp {
-namespace GL {
-
-class Shader;
-
-/**
-A complete shader program.  Programs can be assembled of individual Shaders or
-generated with a set of standard features.
-*/
-class Program: public Bindable<Program>
-{
-public:
-       class Loader: public DataFile::ObjectLoader<Program>
-       {
-       public:
-               Loader(Program &);
-
-       private:
-               virtual void finish();
-
-               void attribute(unsigned, const std::string &);
-               void fragment_shader(const std::string &);
-               void geometry_shader(const std::string &);
-               void vertex_shader(const std::string &);
-       };
-
-       typedef unsigned LayoutHash;
-       struct UniformBlockInfo;
-
-       struct UniformInfo
-       {
-               std::string name;
-               const UniformBlockInfo *block;
-               unsigned location;
-               unsigned size;
-               unsigned array_stride;
-               unsigned matrix_stride;
-               GLenum type;
-       };
-
-       struct UniformBlockInfo
-       {
-               std::string name;
-               unsigned data_size;
-               int bind_point;
-               std::vector<const UniformInfo *> uniforms;
-               LayoutHash layout_hash;
-       };
-
-       struct AttributeInfo
-       {
-               std::string name;
-               unsigned location;
-               unsigned size;
-               GLenum type;
-       };
-
-       typedef std::vector<Shader *> ShaderList;
-       typedef std::map<std::string, UniformInfo> UniformMap;
-       typedef std::map<std::string, UniformBlockInfo> UniformBlockMap;
-       typedef std::map<std::string, AttributeInfo> AttributeMap;
-
-private:
-       unsigned id;
-       ShaderList shaders;
-       ShaderList owned_data;
-       bool linked;
-       UniformBlockMap uniform_blocks;
-       UniformMap uniforms;
-       LayoutHash uniform_layout_hash;
-       AttributeMap attributes;
-
-public:
-       /// Constructs an empty Program with no Shaders attached.
-       Program();
-
-       /// Constructs a Program from unified source code using ProgramCompiler.
-       Program(const std::string &);
-
-       /// Constructs a Program from vertex and fragment shader source code.
-       Program(const std::string &, const std::string &);
-
-private:
-       void init();
-public:
-       virtual ~Program();
-
-       void attach_shader(Shader &shader);
-       void attach_shader_owned(Shader *shader);
-       void detach_shader(Shader &shader);
-       const ShaderList &get_attached_shaders() const { return shaders; }
-
-       void bind_attribute(unsigned, const std::string &);
-       void bind_attribute(VertexComponent, const std::string &);
-       void bind_fragment_data(unsigned, const std::string &);
-
-       void link();
-private:
-       static void require_type(GLenum);
-       void query_uniforms();
-       void query_uniform_blocks(const std::vector<UniformInfo *> &);
-       void query_attributes();
-       static LayoutHash compute_layout_hash(const std::vector<const UniformInfo *> &);
-       static bool uniform_location_compare(const UniformInfo *, const UniformInfo *);
-public:
-       bool is_linked() const { return linked; }
-       std::string get_info_log() const;
-
-       LayoutHash get_uniform_layout_hash() const { return uniform_layout_hash; }
-       const UniformBlockMap &get_uniform_blocks() const { return uniform_blocks; }
-       const UniformBlockInfo &get_uniform_block_info(const std::string &) const;
-       const UniformMap &get_uniforms() const { return uniforms; }
-       const UniformInfo &get_uniform_info(const std::string &) const;
-       int get_uniform_location(const std::string &) const;
-       const AttributeMap &get_attributes() const { return attributes; }
-       const AttributeInfo &get_attribute_info(const std::string &) const;
-       int get_attribute_location(const std::string &) const;
-
-       void bind() const;
-       static void unbind();
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/programcompiler.cpp b/source/programcompiler.cpp
deleted file mode 100644 (file)
index d021ab4..0000000
+++ /dev/null
@@ -1,2096 +0,0 @@
-#include <msp/core/algorithm.h>
-#include <msp/core/raii.h>
-#include <msp/gl/extensions/arb_explicit_attrib_location.h>
-#include <msp/gl/extensions/arb_gpu_shader5.h>
-#include <msp/gl/extensions/arb_uniform_buffer_object.h>
-#include <msp/gl/extensions/ext_gpu_shader4.h>
-#include <msp/gl/extensions/ext_texture_array.h>
-#include <msp/strings/format.h>
-#include <msp/strings/regex.h>
-#include <msp/strings/utils.h>
-#include "error.h"
-#include "program.h"
-#include "programcompiler.h"
-#include "resources.h"
-#include "shader.h"
-
-#undef interface
-
-using namespace std;
-
-namespace {
-
-const char builtins_src[] =
-       "#pragma MSP stage(vertex)\n"
-       "out gl_PerVertex {\n"
-       "  vec4 gl_Position;\n"
-       "  float gl_ClipDistance[];\n"
-       "};\n"
-       "#pragma MSP stage(geometry)\n"
-       "in gl_PerVertex {\n"
-       "  vec4 gl_Position;\n"
-       "  float gl_ClipDistance[];\n"
-       "} gl_in[];\n"
-       "out gl_PerVertex {\n"
-       "  vec4 gl_Position;\n"
-       "  float gl_ClipDistance[];\n"
-       "};\n";
-
-}
-
-namespace Msp {
-namespace GL {
-
-using namespace ProgramSyntax;
-
-ProgramCompiler::ProgramCompiler():
-       resources(0),
-       module(0)
-{ }
-
-ProgramCompiler::~ProgramCompiler()
-{
-       delete module;
-}
-
-void ProgramCompiler::compile(const string &source, const string &src_name)
-{
-       resources = 0;
-       delete module;
-       module = new Module();
-       ProgramParser parser;
-       imported_names.push_back(src_name);
-       append_module(parser.parse(source, src_name, 1));
-       process();
-}
-
-void ProgramCompiler::compile(IO::Base &io, Resources *res, const string &src_name)
-{
-       resources = res;
-       delete module;
-       module = new Module();
-       ProgramParser parser;
-       imported_names.push_back(src_name);
-       append_module(parser.parse(io, src_name, 1));
-       process();
-}
-
-void ProgramCompiler::compile(IO::Base &io, const string &src_name)
-{
-       compile(io, 0, src_name);
-}
-
-void ProgramCompiler::add_shaders(Program &program)
-{
-       if(!module)
-               throw invalid_operation("ProgramCompiler::add_shaders");
-
-       try
-       {
-               for(list<Stage>::iterator i=module->stages.begin(); i!=module->stages.end(); ++i)
-               {
-                       if(i->type==VERTEX)
-                       {
-                               program.attach_shader_owned(new VertexShader(apply<Formatter>(*i)));
-                               for(map<string, unsigned>::iterator j=i->locations.begin(); j!=i->locations.end(); ++j)
-                                       program.bind_attribute(j->second, j->first);
-                       }
-                       else if(i->type==GEOMETRY)
-                               program.attach_shader_owned(new GeometryShader(apply<Formatter>(*i)));
-                       else if(i->type==FRAGMENT)
-                       {
-                               program.attach_shader_owned(new FragmentShader(apply<Formatter>(*i)));
-                               if(EXT_gpu_shader4)
-                               {
-                                       for(map<string, unsigned>::iterator j=i->locations.begin(); j!=i->locations.end(); ++j)
-                                               program.bind_fragment_data(j->second, j->first);
-                               }
-                       }
-               }
-       }
-       catch(const compile_error &e)
-       {
-               static const Regex r_message("^(([0-9]+)\\(([0-9]+)\\) :|ERROR: ([0-9]+):([0-9]+):) (.*)$");
-               vector<string> lines = split(e.what(), '\n');
-               string translated;
-               for(vector<string>::const_iterator i=lines.begin(); i!=lines.end(); ++i)
-               {
-                       RegMatch m = r_message.match(*i);
-                       if(m)
-                       {
-                               unsigned index = 0;
-                               unsigned line = 0;
-                               if(m[2])
-                               {
-                                       index = lexical_cast<unsigned>(m[2].str);
-                                       line = lexical_cast<unsigned>(m[3].str);
-                               }
-                               else if(m[4])
-                               {
-                                       index = lexical_cast<unsigned>(m[4].str);
-                                       line = lexical_cast<unsigned>(m[5].str);
-                               }
-                               const char *src = "<unknown>";
-                               if(index==0)
-                                       src = "<generated>";
-                               else if(index-1<imported_names.size())
-                                       src = imported_names[index-1].c_str();
-                               translated += format("%s:%d: %s", src, line, m[6].str);
-                       }
-                       else
-                               translated += *i;
-                       translated += '\n';
-               }
-
-               throw compile_error(translated);
-       }
-}
-
-Module *ProgramCompiler::create_builtins_module()
-{
-       ProgramParser parser;
-       Module *module = new Module(parser.parse(builtins_src, "<builtin>"));
-       for(list<Stage>::iterator i=module->stages.begin(); i!=module->stages.end(); ++i)
-       {
-               VariableResolver resolver;
-               i->content.visit(resolver);
-               for(map<string, VariableDeclaration *>::iterator j=i->content.variables.begin(); j!=i->content.variables.end(); ++j)
-                       j->second->linked_declaration = j->second;
-       }
-       return module;
-}
-
-Module &ProgramCompiler::get_builtins_module()
-{
-       static RefPtr<Module> builtins_module = create_builtins_module();
-       return *builtins_module;
-}
-
-Stage *ProgramCompiler::get_builtins(StageType type)
-{
-       Module &module = get_builtins_module();
-       for(list<Stage>::iterator i=module.stages.begin(); i!=module.stages.end(); ++i)
-               if(i->type==type)
-                       return &*i;
-       return 0;
-}
-
-void ProgramCompiler::append_module(ProgramSyntax::Module &mod)
-{
-       vector<Import *> imports = apply<NodeGatherer<Import> >(mod.shared);
-       for(vector<Import *>::iterator i=imports.begin(); i!=imports.end(); ++i)
-               import((*i)->module);
-       apply<NodeRemover>(mod.shared, set<Node *>(imports.begin(), imports.end()));
-
-       append_stage(mod.shared);
-       for(list<Stage>::iterator i=mod.stages.begin(); i!=mod.stages.end(); ++i)
-               append_stage(*i);
-}
-
-void ProgramCompiler::append_stage(Stage &stage)
-{
-       Stage *target = 0;
-       if(stage.type==SHARED)
-               target = &module->shared;
-       else
-       {
-               list<Stage>::iterator i;
-               for(i=module->stages.begin(); (i!=module->stages.end() && i->type<stage.type); ++i) ;
-               if(i==module->stages.end() || i->type>stage.type)
-               {
-                       list<Stage>::iterator j = module->stages.insert(i, stage.type);
-                       if(i!=module->stages.end())
-                               i->previous = &*j;
-                       i = j;
-                       if(i!=module->stages.begin())
-                               i->previous = &*--j;
-               }
-
-               target = &*i;
-       }
-
-       if(stage.required_version>target->required_version)
-               target->required_version = stage.required_version;
-       for(NodeList<Statement>::iterator i=stage.content.body.begin(); i!=stage.content.body.end(); ++i)
-               target->content.body.push_back(*i);
-       apply<DeclarationCombiner>(*target);
-}
-
-void ProgramCompiler::process()
-{
-       for(list<Stage>::iterator i=module->stages.begin(); i!=module->stages.end(); ++i)
-               generate(*i);
-       for(list<Stage>::iterator i=module->stages.begin(); i!=module->stages.end(); )
-       {
-               if(optimize(*i))
-                       i = module->stages.begin();
-               else
-                       ++i;
-       }
-       for(list<Stage>::iterator i=module->stages.begin(); i!=module->stages.end(); ++i)
-               finalize(*i);
-}
-
-void ProgramCompiler::import(const string &name)
-{
-       string fn = name+".glsl";
-       if(find(imported_names, fn)!=imported_names.end())
-               return;
-       imported_names.push_back(fn);
-
-       RefPtr<IO::Seekable> io = (resources ? resources->open_raw(fn) : Resources::get_builtins().open(fn));
-       if(!io)
-               throw runtime_error(format("module %s not found", name));
-       ProgramParser import_parser;
-       append_module(import_parser.parse(*io, fn, imported_names.size()));
-}
-
-void ProgramCompiler::generate(Stage &stage)
-{
-       if(module->shared.required_version>stage.required_version)
-               stage.required_version = module->shared.required_version;
-       inject_block(stage.content, module->shared.content);
-
-       apply<DeclarationReorderer>(stage);
-       apply<FunctionResolver>(stage);
-       apply<VariableResolver>(stage);
-       apply<InterfaceGenerator>(stage);
-       apply<VariableResolver>(stage);
-       apply<DeclarationReorderer>(stage);
-       apply<FunctionResolver>(stage);
-       apply<LegacyConverter>(stage);
-}
-
-bool ProgramCompiler::optimize(Stage &stage)
-{
-       apply<ConstantConditionEliminator>(stage);
-
-       set<FunctionDeclaration *> inlineable = apply<InlineableFunctionLocator>(stage);
-       apply<FunctionInliner>(stage, inlineable);
-
-       set<Node *> unused = apply<UnusedVariableLocator>(stage);
-       set<Node *> unused2 = apply<UnusedFunctionLocator>(stage);
-       unused.insert(unused2.begin(), unused2.end());
-       apply<NodeRemover>(stage, unused);
-
-       return !unused.empty();
-}
-
-void ProgramCompiler::finalize(Stage &stage)
-{
-       if(get_gl_api()==OPENGL_ES2)
-               apply<DefaultPrecisionGenerator>(stage);
-       else
-               apply<PrecisionRemover>(stage);
-}
-
-void ProgramCompiler::inject_block(Block &target, const Block &source)
-{
-       NodeList<Statement>::iterator insert_point = target.body.begin();
-       for(NodeList<Statement>::const_iterator i=source.body.begin(); i!=source.body.end(); ++i)
-               target.body.insert(insert_point, (*i)->clone());
-}
-
-template<typename T>
-typename T::ResultType ProgramCompiler::apply(Stage &stage)
-{
-       T visitor;
-       visitor.apply(stage);
-       return visitor.get_result();
-}
-
-template<typename T, typename A>
-typename T::ResultType ProgramCompiler::apply(Stage &stage, const A &arg)
-{
-       T visitor(arg);
-       visitor.apply(stage);
-       return visitor.get_result();
-}
-
-
-ProgramCompiler::Visitor::Visitor():
-       stage(0)
-{ }
-
-void ProgramCompiler::Visitor::apply(Stage &s)
-{
-       SetForScope<Stage *> set(stage, &s);
-       stage->content.visit(*this);
-}
-
-
-ProgramCompiler::BlockModifier::BlockModifier():
-       remove_node(false)
-{ }
-
-void ProgramCompiler::BlockModifier::flatten_block(Block &block)
-{
-       insert_nodes.insert(insert_nodes.end(), block.body.begin(), block.body.end());
-       remove_node = true;
-}
-
-void ProgramCompiler::BlockModifier::apply_and_increment(Block &block, NodeList<Statement>::iterator &i)
-{
-       block.body.insert(i, insert_nodes.begin(), insert_nodes.end());
-       insert_nodes.clear();
-
-       if(remove_node)
-               block.body.erase(i++);
-       else
-               ++i;
-       remove_node = false;
-}
-
-void ProgramCompiler::BlockModifier::visit(Block &block)
-{
-       for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); )
-       {
-               (*i)->visit(*this);
-               apply_and_increment(block, i);
-       }
-}
-
-
-ProgramCompiler::Formatter::Formatter():
-       source_index(0),
-       source_line(1),
-       indent(0),
-       parameter_list(false)
-{ }
-
-void ProgramCompiler::Formatter::apply(ProgramSyntax::Stage &s)
-{
-       GLApi api = get_gl_api();
-       const Version &ver = s.required_version;
-
-       if(ver)
-       {
-               append(format("#version %d%02d", ver.major, ver.minor));
-               if(api==OPENGL_ES2 && ver>=Version(3, 0))
-                       append(" es");
-               formatted += '\n';
-       }
-
-       for(vector<const Extension *>::const_iterator i=s.required_extensions.begin(); i!=s.required_extensions.end(); ++i)
-               append(format("#extension %s: require\n", (*i)->get_name()));
-       if(!s.required_extensions.empty())
-               formatted += '\n';
-
-       Visitor::apply(s);
-}
-
-void ProgramCompiler::Formatter::append(const string &text)
-{
-       formatted += text;
-       for(string::const_iterator i=text.begin(); i!=text.end(); ++i)
-               if(*i=='\n')
-                       ++source_line;
-}
-
-void ProgramCompiler::Formatter::append(char c)
-{
-       formatted += c;
-       if(c=='\n')
-               ++source_line;
-}
-
-void ProgramCompiler::Formatter::set_source(unsigned index, unsigned line)
-{
-       if(index!=source_index || (index && line!=source_line))
-       {
-               if(index==source_index && line==source_line+1)
-                       formatted += '\n';
-               else
-               {
-                       unsigned l = line;
-                       if(stage->required_version<Version(3, 30))
-                               --l;
-                       formatted += format("#line %d %d\n", l, index);
-               }
-       }
-       source_index = index;
-       source_line = line;
-}
-
-void ProgramCompiler::Formatter::visit(Literal &literal)
-{
-       append(literal.token);
-}
-
-void ProgramCompiler::Formatter::visit(ParenthesizedExpression &parexpr)
-{
-       append('(');
-       parexpr.expression->visit(*this);
-       append(')');
-}
-
-void ProgramCompiler::Formatter::visit(VariableReference &var)
-{
-       append(var.name);
-}
-
-void ProgramCompiler::Formatter::visit(MemberAccess &memacc)
-{
-       memacc.left->visit(*this);
-       append(format(".%s", memacc.member));
-}
-
-void ProgramCompiler::Formatter::visit(UnaryExpression &unary)
-{
-       if(unary.prefix)
-               append(unary.oper);
-       unary.expression->visit(*this);
-       if(!unary.prefix)
-               append(unary.oper);
-}
-
-void ProgramCompiler::Formatter::visit(BinaryExpression &binary)
-{
-       binary.left->visit(*this);
-       append(binary.oper);
-       binary.right->visit(*this);
-       append(binary.after);
-}
-
-void ProgramCompiler::Formatter::visit(Assignment &assign)
-{
-       assign.left->visit(*this);
-       append(format(" %s ", assign.oper));
-       assign.right->visit(*this);
-}
-
-void ProgramCompiler::Formatter::visit(FunctionCall &call)
-{
-       append(format("%s(", call.name));
-       for(NodeArray<Expression>::iterator i=call.arguments.begin(); i!=call.arguments.end(); ++i)
-       {
-               if(i!=call.arguments.begin())
-                       append(", ");
-               (*i)->visit(*this);
-       }
-       append(')');
-}
-
-void ProgramCompiler::Formatter::visit(ExpressionStatement &expr)
-{
-       expr.expression->visit(*this);
-       append(';');
-}
-
-void ProgramCompiler::Formatter::visit(Block &block)
-{
-       unsigned brace_indent = indent;
-       bool use_braces = (block.use_braces || (indent && block.body.size()!=1));
-       if(use_braces)
-               append(format("%s{\n", string(brace_indent*2, ' ')));
-
-       SetForScope<unsigned> set(indent, indent+(indent>0 || use_braces));
-       string spaces(indent*2, ' ');
-       for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
-       {
-               if(i!=block.body.begin())
-                       append('\n');
-               set_source((*i)->source, (*i)->line);
-               append(spaces);
-               (*i)->visit(*this);
-       }
-
-       if(use_braces)
-               append(format("\n%s}", string(brace_indent*2, ' ')));
-}
-
-void ProgramCompiler::Formatter::visit(Import &import)
-{
-       append(format("import %s;", import.module));
-}
-
-void ProgramCompiler::Formatter::visit(Precision &prec)
-{
-       append(format("precision %s %s;", prec.precision, prec.type));
-}
-
-void ProgramCompiler::Formatter::visit(Layout &layout)
-{
-       append("layout(");
-       for(vector<Layout::Qualifier>::const_iterator i=layout.qualifiers.begin(); i!=layout.qualifiers.end(); ++i)
-       {
-               if(i!=layout.qualifiers.begin())
-                       append(", ");
-               append(i->identifier);
-               if(!i->value.empty())
-                       append(format("=%s", i->value));
-       }
-       append(')');
-}
-
-void ProgramCompiler::Formatter::visit(InterfaceLayout &layout)
-{
-       layout.layout.visit(*this);
-       append(format(" %s;", layout.interface));
-}
-
-void ProgramCompiler::Formatter::visit(StructDeclaration &strct)
-{
-       append(format("struct %s\n", strct.name));
-       strct.members.visit(*this);
-       append(';');
-}
-
-void ProgramCompiler::Formatter::visit(VariableDeclaration &var)
-{
-       if(var.layout)
-       {
-               var.layout->visit(*this);
-               append(' ');
-       }
-       if(var.constant)
-               append("const ");
-       if(!var.interpolation.empty())
-               append(format("%s ", var.interpolation));
-       if(!var.sampling.empty())
-               append(format("%s ", var.sampling));
-       if(!var.interface.empty() && var.interface!=block_interface)
-       {
-               string interface = var.interface;
-               if(stage->required_version<Version(1, 30))
-               {
-                       if(stage->type==VERTEX && var.interface=="in")
-                               interface = "attribute";
-                       else if((stage->type==VERTEX && var.interface=="out") || (stage->type==FRAGMENT && var.interface=="in"))
-                               interface = "varying";
-               }
-               append(format("%s ", interface));
-       }
-       if(!var.precision.empty())
-               append(format("%s ", var.precision));
-       append(format("%s %s", var.type, var.name));
-       if(var.array)
-       {
-               append('[');
-               if(var.array_size)
-                       var.array_size->visit(*this);
-               append(']');
-       }
-       if(var.init_expression)
-       {
-               append(" = ");
-               var.init_expression->visit(*this);
-       }
-       if(!parameter_list)
-               append(';');
-}
-
-void ProgramCompiler::Formatter::visit(InterfaceBlock &iface)
-{
-       SetForScope<string> set(block_interface, iface.interface);
-       append(format("%s %s\n", iface.interface, iface.name));
-       iface.members.visit(*this);
-       append(';');
-}
-
-void ProgramCompiler::Formatter::visit(FunctionDeclaration &func)
-{
-       append(format("%s %s(", func.return_type, func.name));
-       for(NodeArray<VariableDeclaration>::iterator i=func.parameters.begin(); i!=func.parameters.end(); ++i)
-       {
-               if(i!=func.parameters.begin())
-                       append(", ");
-               SetFlag set(parameter_list);
-               (*i)->visit(*this);
-       }
-       append(')');
-       if(func.definition==&func)
-       {
-               append('\n');
-               func.body.visit(*this);
-       }
-       else
-               append(';');
-}
-
-void ProgramCompiler::Formatter::visit(Conditional &cond)
-{
-       append("if(");
-       cond.condition->visit(*this);
-       append(")\n");
-
-       cond.body.visit(*this);
-       if(!cond.else_body.body.empty())
-       {
-               Conditional *else_cond = dynamic_cast<Conditional *>(cond.else_body.body.front().get());
-               if(cond.else_body.body.size()==1 && else_cond)
-               {
-                       append('\n');
-                       set_source(else_cond->source, else_cond->line);
-                       append(format("%selse ", string(indent*2, ' ')));
-                       else_cond->visit(*this);
-               }
-               else
-               {
-                       append(format("\n%selse\n", string(indent*2, ' ')));
-                       cond.else_body.visit(*this);
-               }
-       }
-}
-
-void ProgramCompiler::Formatter::visit(Iteration &iter)
-{
-       if(!iter.init_statement && iter.condition && !iter.loop_expression)
-       {
-               append("while(");
-               iter.condition->visit(*this);
-               append(')');
-       }
-       else
-       {
-               append("for(");
-               if(iter.init_statement)
-                       iter.init_statement->visit(*this);
-               else
-                       append(';');
-               if(iter.condition)
-               {
-                       append(' ');
-                       iter.condition->visit(*this);
-               }
-               append(';');
-               if(iter.loop_expression)
-               {
-                       append(' ');
-                       iter.loop_expression->visit(*this);
-               }
-               append(')');
-       }
-
-       if(iter.body.body.empty())
-               append(" { }");
-       else
-       {
-               append('\n');
-               iter.body.visit(*this);
-       }
-}
-
-void ProgramCompiler::Formatter::visit(Return &ret)
-{
-       append("return");
-       if(ret.expression)
-       {
-               append(' ');
-               ret.expression->visit(*this);
-       }
-       append(';');
-}
-
-void ProgramCompiler::Formatter::visit(Jump &jump)
-{
-       append(jump.keyword);
-       append(';');
-}
-
-
-ProgramCompiler::DeclarationCombiner::DeclarationCombiner():
-       toplevel(true)
-{ }
-
-void ProgramCompiler::DeclarationCombiner::visit(Block &block)
-{
-       if(!toplevel)
-               return;
-
-       SetForScope<bool> set(toplevel, false);
-       BlockModifier::visit(block);
-}
-
-void ProgramCompiler::DeclarationCombiner::visit(FunctionDeclaration &func)
-{
-       vector<FunctionDeclaration *> &decls = functions[func.name];
-       if(func.definition)
-       {
-               for(vector<FunctionDeclaration *>::iterator i=decls.begin(); i!=decls.end(); ++i)
-               {
-                       (*i)->definition = func.definition;
-                       (*i)->body.body.clear();
-               }
-       }
-       decls.push_back(&func);
-}
-
-void ProgramCompiler::DeclarationCombiner::visit(VariableDeclaration &var)
-{
-       VariableDeclaration *&ptr = variables[var.name];
-       if(ptr)
-       {
-               ptr->type = var.type;
-               if(var.init_expression)
-                       ptr->init_expression = var.init_expression;
-               if(var.layout)
-               {
-                       if(ptr->layout)
-                       {
-                               for(vector<Layout::Qualifier>::iterator i=var.layout->qualifiers.begin(); i!=var.layout->qualifiers.end(); ++i)
-                               {
-                                       bool found = false;
-                                       for(vector<Layout::Qualifier>::iterator j=ptr->layout->qualifiers.begin(); (!found && j!=ptr->layout->qualifiers.end()); ++j)
-                                               if(j->identifier==i->identifier)
-                                               {
-                                                       j->value = i->value;
-                                                       found = true;
-                                               }
-
-                                       if(!found)
-                                               ptr->layout->qualifiers.push_back(*i);
-                               }
-                       }
-                       else
-                               ptr->layout = var.layout;
-               }
-               remove_node = true;
-       }
-       else
-               ptr = &var;
-}
-
-
-ProgramCompiler::VariableResolver::VariableResolver():
-       anonymous(false),
-       record_target(false),
-       assignment_target(0),
-       self_referencing(false)
-{ }
-
-void ProgramCompiler::VariableResolver::apply(Stage &s)
-{
-       SetForScope<Stage *> set(stage, &s);
-       Stage *builtins = get_builtins(stage->type);
-       if(builtins)
-               blocks.push_back(&builtins->content);
-       stage->content.visit(*this);
-       if(builtins)
-               blocks.pop_back();
-}
-
-void ProgramCompiler::VariableResolver::visit(Block &block)
-{
-       blocks.push_back(&block);
-       block.variables.clear();
-       TraversingVisitor::visit(block);
-       blocks.pop_back();
-}
-
-void ProgramCompiler::VariableResolver::visit(VariableReference &var)
-{
-       var.declaration = 0;
-       type = 0;
-       for(vector<Block *>::iterator i=blocks.end(); i!=blocks.begin(); )
-       {
-               --i;
-               map<string, VariableDeclaration *>::iterator j = (*i)->variables.find(var.name);
-               if(j!=(*i)->variables.end())
-               {
-                       var.declaration = j->second;
-                       type = j->second->type_declaration;
-                       break;
-               }
-       }
-
-       if(record_target)
-       {
-               if(assignment_target)
-               {
-                       record_target = false;
-                       assignment_target = 0;
-               }
-               else
-                       assignment_target = var.declaration;
-       }
-       else if(var.declaration && var.declaration==assignment_target)
-               self_referencing = true;
-}
-
-void ProgramCompiler::VariableResolver::visit(MemberAccess &memacc)
-{
-       type = 0;
-       TraversingVisitor::visit(memacc);
-       memacc.declaration = 0;
-       if(type)
-       {
-               map<string, VariableDeclaration *>::iterator i = type->members.variables.find(memacc.member);
-               if(i!=type->members.variables.end())
-               {
-                       memacc.declaration = i->second;
-                       type = i->second->type_declaration;
-               }
-               else
-                       type = 0;
-       }
-}
-
-void ProgramCompiler::VariableResolver::visit(BinaryExpression &binary)
-{
-       if(binary.oper=="[")
-       {
-               {
-                       SetForScope<bool> set(record_target, false);
-                       binary.right->visit(*this);
-               }
-               type = 0;
-               binary.left->visit(*this);
-       }
-       else
-       {
-               TraversingVisitor::visit(binary);
-               type = 0;
-       }
-}
-
-void ProgramCompiler::VariableResolver::visit(Assignment &assign)
-{
-       {
-               SetFlag set(record_target);
-               assignment_target = 0;
-               assign.left->visit(*this);
-       }
-
-       self_referencing = false;
-       assign.right->visit(*this);
-
-       assign.self_referencing = (self_referencing || assign.oper!="=");
-       assign.target_declaration = assignment_target;
-}
-
-void ProgramCompiler::VariableResolver::visit(StructDeclaration &strct)
-{
-       TraversingVisitor::visit(strct);
-       blocks.back()->types[strct.name] = &strct;
-}
-
-void ProgramCompiler::VariableResolver::visit(VariableDeclaration &var)
-{
-       for(vector<Block *>::iterator i=blocks.end(); i!=blocks.begin(); )
-       {
-               --i;
-               map<string, StructDeclaration *>::iterator j = (*i)->types.find(var.type);
-               if(j!=(*i)->types.end())
-                       var.type_declaration = j->second;
-       }
-
-       if(!block_interface.empty() && var.interface.empty())
-               var.interface = block_interface;
-
-       TraversingVisitor::visit(var);
-       blocks.back()->variables[var.name] = &var;
-       if(anonymous && blocks.size()>1)
-               blocks[blocks.size()-2]->variables[var.name] = &var;
-}
-
-void ProgramCompiler::VariableResolver::visit(InterfaceBlock &iface)
-{
-       SetFlag set(anonymous);
-       SetForScope<string> set2(block_interface, iface.interface);
-       TraversingVisitor::visit(iface);
-}
-
-
-void ProgramCompiler::FunctionResolver::visit(FunctionCall &call)
-{
-       map<string, vector<FunctionDeclaration *> >::iterator i = functions.find(call.name);
-       if(i!=functions.end())
-               call.declaration = i->second.back();
-
-       TraversingVisitor::visit(call);
-}
-
-void ProgramCompiler::FunctionResolver::visit(FunctionDeclaration &func)
-{
-       vector<FunctionDeclaration *> &decls = functions[func.name];
-       if(func.definition)
-       {
-               for(vector<FunctionDeclaration *>::iterator i=decls.begin(); i!=decls.end(); ++i)
-                       (*i)->definition = func.definition;
-               decls.clear();
-               decls.push_back(&func);
-       }
-       else if(!decls.empty() && decls.back()->definition)
-               func.definition = decls.back()->definition;
-       else
-               decls.push_back(&func);
-
-       TraversingVisitor::visit(func);
-}
-
-
-ProgramCompiler::InterfaceGenerator::InterfaceGenerator():
-       scope_level(0)
-{ }
-
-string ProgramCompiler::InterfaceGenerator::get_out_prefix(StageType type)
-{
-       if(type==VERTEX)
-               return "_vs_out_";
-       else if(type==GEOMETRY)
-               return "_gs_out_";
-       else
-               return string();
-}
-
-void ProgramCompiler::InterfaceGenerator::apply(Stage &s)
-{
-       SetForScope<Stage *> set(stage, &s);
-       if(stage->previous)
-               in_prefix = get_out_prefix(stage->previous->type);
-       out_prefix = get_out_prefix(stage->type);
-       stage->content.visit(*this);
-}
-
-void ProgramCompiler::InterfaceGenerator::visit(Block &block)
-{
-       SetForScope<unsigned> set(scope_level, scope_level+1);
-       for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); )
-       {
-               (*i)->visit(*this);
-
-               if(scope_level==1)
-               {
-                       for(map<string, RefPtr<VariableDeclaration> >::iterator j=iface_declarations.begin(); j!=iface_declarations.end(); ++j)
-                       {
-                               NodeList<Statement>::iterator k = block.body.insert(i, j->second);
-                               (*k)->visit(*this);
-                       }
-                       iface_declarations.clear();
-               }
-
-               apply_and_increment(block, i);
-       }
-}
-
-string ProgramCompiler::InterfaceGenerator::change_prefix(const string &name, const string &prefix) const
-{
-       unsigned offset = (name.compare(0, in_prefix.size(), in_prefix) ? 0 : in_prefix.size());
-       return prefix+name.substr(offset);
-}
-
-bool ProgramCompiler::InterfaceGenerator::generate_interface(VariableDeclaration &var, const string &iface, const string &name)
-{
-       const map<string, VariableDeclaration *> &stage_vars = (iface=="in" ? stage->in_variables : stage->out_variables);
-       if(stage_vars.count(name) || iface_declarations.count(name))
-               return false;
-
-       VariableDeclaration* iface_var = new VariableDeclaration;
-       iface_var->sampling = var.sampling;
-       iface_var->interface = iface;
-       iface_var->type = var.type;
-       iface_var->type_declaration = var.type_declaration;
-       iface_var->name = name;
-       if(stage->type==GEOMETRY)
-               iface_var->array = ((var.array && var.interface!="in") || iface=="in");
-       else
-               iface_var->array = var.array;
-       if(iface_var->array)
-               iface_var->array_size = var.array_size;
-       if(iface=="in")
-               iface_var->linked_declaration = &var;
-       iface_declarations[name] = iface_var;
-
-       return true;
-}
-
-ExpressionStatement &ProgramCompiler::InterfaceGenerator::insert_assignment(const string &left, ProgramSyntax::Expression *right)
-{
-       Assignment *assign = new Assignment;
-       VariableReference *ref = new VariableReference;
-       ref->name = left;
-       assign->left = ref;
-       assign->oper = "=";
-       assign->right = right;
-
-       ExpressionStatement *stmt = new ExpressionStatement;
-       stmt->expression = assign;
-       stmt->visit(*this);
-       insert_nodes.push_back(stmt);
-
-       return *stmt;
-}
-
-void ProgramCompiler::InterfaceGenerator::visit(VariableReference &var)
-{
-       if(var.declaration || !stage->previous)
-               return;
-       if(iface_declarations.count(var.name))
-               return;
-
-       const map<string, VariableDeclaration *> &prev_out = stage->previous->out_variables;
-       map<string, VariableDeclaration *>::const_iterator i = prev_out.find(var.name);
-       if(i==prev_out.end())
-               i = prev_out.find(in_prefix+var.name);
-       if(i!=prev_out.end())
-       {
-               generate_interface(*i->second, "in", i->second->name);
-               var.name = i->second->name;
-       }
-}
-
-void ProgramCompiler::InterfaceGenerator::visit(VariableDeclaration &var)
-{
-       if(var.interface=="out")
-       {
-               if(scope_level==1)
-                       stage->out_variables[var.name] = &var;
-               else if(generate_interface(var, "out", change_prefix(var.name, string())))
-               {
-                       remove_node = true;
-                       if(var.init_expression)
-                       {
-                               ExpressionStatement &stmt = insert_assignment(var.name, var.init_expression->clone());
-                               stmt.source = var.source;
-                               stmt.line = var.line;
-                               return;
-                       }
-               }
-       }
-       else if(var.interface=="in")
-       {
-               stage->in_variables[var.name] = &var;
-               if(var.linked_declaration)
-                       var.linked_declaration->linked_declaration = &var;
-               else if(stage->previous)
-               {
-                       const map<string, VariableDeclaration *> &prev_out = stage->previous->out_variables;
-                       map<string, VariableDeclaration *>::const_iterator i = prev_out.find(var.name);
-                       if(i!=prev_out.end())
-                       {
-                               var.linked_declaration = i->second;
-                               i->second->linked_declaration = &var;
-                       }
-               }
-       }
-
-       TraversingVisitor::visit(var);
-}
-
-void ProgramCompiler::InterfaceGenerator::visit(Passthrough &pass)
-{
-       vector<VariableDeclaration *> pass_vars;
-
-       for(map<string, VariableDeclaration *>::const_iterator i=stage->in_variables.begin(); i!=stage->in_variables.end(); ++i)
-               pass_vars.push_back(i->second);
-       for(map<string, RefPtr<VariableDeclaration> >::const_iterator i=iface_declarations.begin(); i!=iface_declarations.end(); ++i)
-               if(i->second->interface=="in")
-                       pass_vars.push_back(i->second.get());
-
-       if(stage->previous)
-       {
-               const map<string, VariableDeclaration *> &prev_out = stage->previous->out_variables;
-               for(map<string, VariableDeclaration *>::const_iterator i=prev_out.begin(); i!=prev_out.end(); ++i)
-               {
-                       bool linked = false;
-                       for(vector<VariableDeclaration *>::const_iterator j=pass_vars.begin(); (!linked && j!=pass_vars.end()); ++j)
-                               linked = ((*j)->linked_declaration==i->second);
-
-                       if(!linked && generate_interface(*i->second, "in", i->second->name))
-                               pass_vars.push_back(i->second);
-               }
-       }
-
-       if(stage->type==GEOMETRY)
-       {
-               VariableReference *ref = new VariableReference;
-               ref->name = "gl_in";
-
-               BinaryExpression *subscript = new BinaryExpression;
-               subscript->left = ref;
-               subscript->oper = "[";
-               subscript->right = pass.subscript;
-               subscript->after = "]";
-
-               MemberAccess *memacc = new MemberAccess;
-               memacc->left = subscript;
-               memacc->member = "gl_Position";
-
-               insert_assignment("gl_Position", memacc);
-       }
-
-       for(vector<VariableDeclaration *>::const_iterator i=pass_vars.begin(); i!=pass_vars.end(); ++i)
-       {
-               string out_name = change_prefix((*i)->name, out_prefix);
-               generate_interface(**i, "out", out_name);
-
-               VariableReference *ref = new VariableReference;
-               ref->name = (*i)->name;
-               if(pass.subscript)
-               {
-                       BinaryExpression *subscript = new BinaryExpression;
-                       subscript->left = ref;
-                       subscript->oper = "[";
-                       subscript->right = pass.subscript;
-                       subscript->after = "]";
-                       insert_assignment(out_name, subscript);
-               }
-               else
-                       insert_assignment(out_name, ref);
-       }
-
-       remove_node = true;
-}
-
-
-ProgramCompiler::DeclarationReorderer::DeclarationReorderer():
-       scope_level(0),
-       kind(NO_DECLARATION)
-{ }
-
-void ProgramCompiler::DeclarationReorderer::visit(FunctionCall &call)
-{
-       FunctionDeclaration *def = call.declaration;
-       if(def)
-               def = def->definition;
-       if(def && !ordered_funcs.count(def))
-               needed_funcs.insert(def);
-}
-
-void ProgramCompiler::DeclarationReorderer::visit(Block &block)
-{
-       SetForScope<unsigned> set(scope_level, scope_level+1);
-       if(scope_level>1)
-               return Visitor::visit(block);
-
-       NodeList<Statement>::iterator struct_insert_point = block.body.end();
-       NodeList<Statement>::iterator variable_insert_point = block.body.end();
-       NodeList<Statement>::iterator function_insert_point = block.body.end();
-       unsigned unordered_func_count = 0;
-       bool ordered_any_funcs = false;
-
-       for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); )
-       {
-               kind = NO_DECLARATION;
-               (*i)->visit(*this);
-
-               bool moved = false;
-               if(kind==STRUCT && struct_insert_point!=block.body.end())
-               {
-                       block.body.insert(struct_insert_point, *i);
-                       moved = true;
-               }
-               else if(kind>STRUCT && struct_insert_point==block.body.end())
-                       struct_insert_point = i;
-
-               if(kind==VARIABLE && variable_insert_point!=block.body.end())
-               {
-                       block.body.insert(variable_insert_point, *i);
-                       moved = true;
-               }
-               else if(kind>VARIABLE && variable_insert_point==block.body.end())
-                       variable_insert_point = i;
-
-               if(kind==FUNCTION)
-               {
-                       if(function_insert_point==block.body.end())
-                               function_insert_point = i;
-
-                       if(needed_funcs.empty())
-                       {
-                               ordered_funcs.insert(i->get());
-                               if(i!=function_insert_point)
-                               {
-                                       block.body.insert(function_insert_point, *i);
-                                       moved = true;
-                               }
-                               else
-                                       ++function_insert_point;
-                               ordered_any_funcs = true;
-                       }
-                       else
-                               ++unordered_func_count;
-               }
-
-               if(moved)
-               {
-                       if(function_insert_point==i)
-                               ++function_insert_point;
-                       block.body.erase(i++);
-               }
-               else
-                       ++i;
-
-               if(i==block.body.end() && unordered_func_count)
-               {
-                       if(!ordered_any_funcs)
-                               // A subset of the remaining functions forms a recursive loop
-                               /* TODO pick a function and move it up, adding any necessary
-                               declarations */
-                               break;
-
-                       i = function_insert_point;
-                       unordered_func_count = 0;
-               }
-       }
-}
-
-void ProgramCompiler::DeclarationReorderer::visit(ProgramSyntax::VariableDeclaration &var)
-{
-       Visitor::visit(var);
-       kind = VARIABLE;
-}
-
-void ProgramCompiler::DeclarationReorderer::visit(FunctionDeclaration &func)
-{
-       needed_funcs.clear();
-       func.body.visit(*this);
-       needed_funcs.erase(&func);
-       kind = FUNCTION;
-}
-
-
-ProgramCompiler::InlineableFunctionLocator::InlineableFunctionLocator():
-       in_function(0)
-{ }
-
-void ProgramCompiler::InlineableFunctionLocator::visit(FunctionCall &call)
-{
-       FunctionDeclaration *def = call.declaration;
-       if(def && def->definition!=def)
-               def = def->definition;
-
-       if(def)
-       {
-               unsigned &count = refcounts[def];
-               ++count;
-               if(count>1 || def==in_function)
-                       inlineable.erase(def);
-       }
-
-       TraversingVisitor::visit(call);
-}
-
-void ProgramCompiler::InlineableFunctionLocator::visit(FunctionDeclaration &func)
-{
-       unsigned &count = refcounts[func.definition];
-       if(!count && func.parameters.empty())
-               inlineable.insert(func.definition);
-
-       SetForScope<FunctionDeclaration *> set(in_function, &func);
-       TraversingVisitor::visit(func);
-}
-
-
-ProgramCompiler::FunctionInliner::FunctionInliner():
-       extract_result(0)
-{ }
-
-ProgramCompiler::FunctionInliner::FunctionInliner(const set<FunctionDeclaration *> &in):
-       inlineable(in),
-       extract_result(0)
-{ }
-
-void ProgramCompiler::FunctionInliner::visit_and_inline(RefPtr<Expression> &ptr)
-{
-       inline_result = 0;
-       ptr->visit(*this);
-       if(inline_result)
-               ptr = inline_result;
-}
-
-void ProgramCompiler::FunctionInliner::visit(Block &block)
-{
-       if(extract_result)
-               --extract_result;
-
-       for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
-       {
-               (*i)->visit(*this);
-               if(extract_result)
-                       --extract_result;
-       }
-}
-
-void ProgramCompiler::FunctionInliner::visit(UnaryExpression &unary)
-{
-       visit_and_inline(unary.expression);
-       inline_result = 0;
-}
-
-void ProgramCompiler::FunctionInliner::visit(BinaryExpression &binary)
-{
-       visit_and_inline(binary.left);
-       visit_and_inline(binary.right);
-       inline_result = 0;
-}
-
-void ProgramCompiler::FunctionInliner::visit(MemberAccess &memacc)
-{
-       visit_and_inline(memacc.left);
-       inline_result = 0;
-}
-
-void ProgramCompiler::FunctionInliner::visit(FunctionCall &call)
-{
-       for(NodeArray<Expression>::iterator i=call.arguments.begin(); i!=call.arguments.end(); ++i)
-               visit_and_inline(*i);
-
-       FunctionDeclaration *def = call.declaration;
-       if(def && def->definition!=def)
-               def = def->definition;
-
-       if(def && inlineable.count(def))
-       {
-               extract_result = 2;
-               def->visit(*this);
-       }
-       else
-               inline_result = 0;
-}
-
-void ProgramCompiler::FunctionInliner::visit(VariableDeclaration &var)
-{
-       if(var.init_expression)
-               visit_and_inline(var.init_expression);
-       inline_result = 0;
-}
-
-void ProgramCompiler::FunctionInliner::visit(Return &ret)
-{
-       TraversingVisitor::visit(ret);
-
-       if(extract_result)
-               inline_result = ret.expression->clone();
-}
-
-
-ProgramCompiler::ExpressionEvaluator::ExpressionEvaluator():
-       variable_values(0),
-       result(0.0f),
-       result_valid(false)
-{ }
-
-ProgramCompiler::ExpressionEvaluator::ExpressionEvaluator(const ValueMap &v):
-       variable_values(&v),
-       result(0.0f),
-       result_valid(false)
-{ }
-
-void ProgramCompiler::ExpressionEvaluator::visit(Literal &literal)
-{
-       if(literal.token=="true")
-               result = 1.0f;
-       else if(literal.token=="false")
-               result = 0.0f;
-       else
-               result = lexical_cast<float>(literal.token);
-       result_valid = true;
-}
-
-void ProgramCompiler::ExpressionEvaluator::visit(ParenthesizedExpression &parexp)
-{
-       parexp.expression->visit(*this);
-}
-
-void ProgramCompiler::ExpressionEvaluator::visit(VariableReference &var)
-{
-       if(!var.declaration)
-               return;
-
-       if(variable_values)
-       {
-               ValueMap::const_iterator i = variable_values->find(var.declaration);
-               if(i!=variable_values->end())
-                       i->second->visit(*this);
-       }
-       else if(var.declaration->init_expression)
-               var.declaration->init_expression->visit(*this);
-}
-
-void ProgramCompiler::ExpressionEvaluator::visit(UnaryExpression &unary)
-{
-       result_valid = false;
-       unary.expression->visit(*this);
-       if(!result_valid)
-               return;
-
-       if(unary.oper=="!")
-               result = !result;
-       else
-               result_valid = false;
-}
-
-void ProgramCompiler::ExpressionEvaluator::visit(BinaryExpression &binary)
-{
-       result_valid = false;
-       binary.left->visit(*this);
-       if(!result_valid)
-               return;
-
-       float left_result = result;
-       result_valid = false;
-       binary.right->visit(*this);
-       if(!result_valid)
-               return;
-
-       if(binary.oper=="<")
-               result = (left_result<result);
-       else if(binary.oper=="<=")
-               result = (left_result<=result);
-       else if(binary.oper==">")
-               result = (left_result>result);
-       else if(binary.oper==">=")
-               result = (left_result>=result);
-       else if(binary.oper=="==")
-               result = (left_result==result);
-       else if(binary.oper=="!=")
-               result = (left_result!=result);
-       else if(binary.oper=="&&")
-               result = (left_result && result);
-       else if(binary.oper=="||")
-               result = (left_result || result);
-       else
-               result_valid = false;
-}
-
-
-ProgramCompiler::ConstantConditionEliminator::ConstantConditionEliminator():
-       scope_level(0),
-       record_only(false)
-{ }
-
-void ProgramCompiler::ConstantConditionEliminator::visit(Block &block)
-{
-       SetForScope<unsigned> set(scope_level, scope_level+1);
-       BlockModifier::visit(block);
-
-       for(map<string, VariableDeclaration *>::const_iterator i=block.variables.begin(); i!=block.variables.end(); ++i)
-               variable_values.erase(i->second);
-}
-
-void ProgramCompiler::ConstantConditionEliminator::visit(UnaryExpression &unary)
-{
-       if(VariableReference *var = dynamic_cast<VariableReference *>(unary.expression.get()))
-               if(unary.oper=="++" || unary.oper=="--")
-                       variable_values.erase(var->declaration);
-}
-
-void ProgramCompiler::ConstantConditionEliminator::visit(Assignment &assign)
-{
-       variable_values.erase(assign.target_declaration);
-}
-
-void ProgramCompiler::ConstantConditionEliminator::visit(VariableDeclaration &var)
-{
-       if(var.constant || scope_level>1)
-               variable_values[&var] = var.init_expression.get();
-}
-
-void ProgramCompiler::ConstantConditionEliminator::visit(Conditional &cond)
-{
-       if(!record_only)
-       {
-               ExpressionEvaluator eval(variable_values);
-               cond.condition->visit(eval);
-               if(eval.result_valid)
-               {
-                       flatten_block(eval.result ? cond.body : cond.else_body);
-                       return;
-               }
-       }
-
-       TraversingVisitor::visit(cond);
-}
-
-void ProgramCompiler::ConstantConditionEliminator::visit(Iteration &iter)
-{
-       if(!record_only)
-       {
-               if(iter.condition)
-               {
-                       /* If the loop condition is always false on the first iteration, the
-                       entire loop can be removed */
-                       if(iter.init_statement)
-                               iter.init_statement->visit(*this);
-                       ExpressionEvaluator eval(variable_values);
-                       iter.condition->visit(eval);
-                       if(eval.result_valid && !eval.result)
-                       {
-                               remove_node = true;
-                               return;
-                       }
-               }
-
-               /* Record all assignments that occur inside the loop body so those
-               variables won't be considered as constant */
-               SetFlag set_record(record_only);
-               TraversingVisitor::visit(iter);
-       }
-
-       TraversingVisitor::visit(iter);
-
-       if(VariableDeclaration *init_decl = dynamic_cast<VariableDeclaration *>(iter.init_statement.get()))
-               variable_values.erase(init_decl);
-}
-
-
-ProgramCompiler::UnusedVariableLocator::UnusedVariableLocator():
-       aggregate(0),
-       assignment(0),
-       assignment_target(false),
-       assign_to_subscript(false),
-       global_scope(true)
-{ }
-
-void ProgramCompiler::UnusedVariableLocator::apply(Stage &s)
-{
-       variables.push_back(BlockVariableMap());
-       Visitor::apply(s);
-       BlockVariableMap &global_variables = variables.back();
-       for(BlockVariableMap::iterator i=global_variables.begin(); i!=global_variables.end(); ++i)
-       {
-               if(i->first->interface=="out" && (s.type==FRAGMENT || i->first->linked_declaration || !i->first->name.compare(0, 3, "gl_")))
-                       continue;
-               if(!i->second.referenced)
-               {
-                       unused_nodes.insert(i->first);
-                       clear_assignments(i->second, true);
-               }
-       }
-       variables.pop_back();
-}
-
-void ProgramCompiler::UnusedVariableLocator::visit(VariableReference &var)
-{
-       map<VariableDeclaration *, Node *>::iterator i = aggregates.find(var.declaration);
-       if(i!=aggregates.end())
-               unused_nodes.erase(i->second);
-
-       if(var.declaration && !assignment_target)
-       {
-               VariableInfo &var_info = variables.back()[var.declaration];
-               var_info.assignments.clear();
-               var_info.referenced = true;
-       }
-}
-
-void ProgramCompiler::UnusedVariableLocator::visit(MemberAccess &memacc)
-{
-       TraversingVisitor::visit(memacc);
-       unused_nodes.erase(memacc.declaration);
-}
-
-void ProgramCompiler::UnusedVariableLocator::visit(BinaryExpression &binary)
-{
-       if(binary.oper=="[")
-       {
-               if(assignment_target)
-                       assign_to_subscript = true;
-               binary.left->visit(*this);
-               SetForScope<bool> set(assignment_target, false);
-               binary.right->visit(*this);
-       }
-       else
-               TraversingVisitor::visit(binary);
-}
-
-void ProgramCompiler::UnusedVariableLocator::visit(Assignment &assign)
-{
-       {
-               assign_to_subscript = false;
-               SetForScope<bool> set(assignment_target, !assign.self_referencing);
-               assign.left->visit(*this);
-       }
-       assign.right->visit(*this);
-       assignment = &assign;
-}
-
-void ProgramCompiler::UnusedVariableLocator::record_assignment(VariableDeclaration &var, Node &node, bool chained)
-{
-       VariableInfo &var_info = variables.back()[&var];
-       if(!chained)
-               clear_assignments(var_info, true);
-       var_info.assignments.push_back(&node);
-       var_info.conditionally_assigned = false;
-}
-
-void ProgramCompiler::UnusedVariableLocator::clear_assignments(VariableInfo &var_info, bool mark_unused)
-{
-       if(mark_unused)
-       {
-               for(vector<Node *>::iterator i=var_info.assignments.begin(); i!=var_info.assignments.end(); ++i)
-                       unused_nodes.insert(*i);
-       }
-       var_info.assignments.clear();
-}
-
-void ProgramCompiler::UnusedVariableLocator::visit(ExpressionStatement &expr)
-{
-       assignment = 0;
-       TraversingVisitor::visit(expr);
-       if(assignment && assignment->target_declaration)
-               record_assignment(*assignment->target_declaration, expr, (assignment->self_referencing || assign_to_subscript));
-}
-
-void ProgramCompiler::UnusedVariableLocator::visit(StructDeclaration &strct)
-{
-       SetForScope<Node *> set(aggregate, &strct);
-       unused_nodes.insert(&strct);
-       TraversingVisitor::visit(strct);
-}
-
-void ProgramCompiler::UnusedVariableLocator::visit(VariableDeclaration &var)
-{
-       if(aggregate)
-               aggregates[&var] = aggregate;
-       else
-       {
-               variables.back()[&var].local = true;
-               if(var.init_expression)
-                       record_assignment(var, *var.init_expression, false);
-       }
-       unused_nodes.erase(var.type_declaration);
-       TraversingVisitor::visit(var);
-}
-
-void ProgramCompiler::UnusedVariableLocator::visit(InterfaceBlock &iface)
-{
-       SetForScope<Node *> set(aggregate, &iface);
-       unused_nodes.insert(&iface);
-       TraversingVisitor::visit(iface);
-}
-
-void ProgramCompiler::UnusedVariableLocator::visit(FunctionDeclaration &func)
-{
-       variables.push_back(BlockVariableMap());
-
-       {
-               SetForScope<bool> set(global_scope, false);
-               for(NodeArray<VariableDeclaration>::iterator i=func.parameters.begin(); i!=func.parameters.end(); ++i)
-                       (*i)->visit(*this);
-               func.body.visit(*this);
-       }
-
-       BlockVariableMap &block_variables = variables.back();
-       for(BlockVariableMap::iterator i=block_variables.begin(); i!=block_variables.end(); ++i)
-               i->second.conditionally_assigned = true;
-       for(NodeArray<VariableDeclaration>::iterator i=func.parameters.begin(); i!=func.parameters.end(); ++i)
-               block_variables[i->get()].referenced = true;
-       merge_down_variables();
-}
-
-void ProgramCompiler::UnusedVariableLocator::merge_down_variables()
-{
-       BlockVariableMap &parent_variables = variables[variables.size()-2];
-       BlockVariableMap &block_variables = variables.back();
-       for(BlockVariableMap::iterator i=block_variables.begin(); i!=block_variables.end(); ++i)
-       {
-               if(i->second.local)
-               {
-                       if(!i->second.referenced)
-                               unused_nodes.insert(i->first);
-                       clear_assignments(i->second, i->first->interface!="out");
-                       continue;
-               }
-
-               BlockVariableMap::iterator j = parent_variables.find(i->first);
-               if(j==parent_variables.end())
-                       parent_variables.insert(*i);
-               else
-               {
-                       if(i->second.referenced || !i->second.conditionally_assigned)
-                               clear_assignments(j->second, !i->second.referenced);
-                       j->second.conditionally_assigned = i->second.conditionally_assigned;
-                       j->second.referenced |= i->second.referenced;
-                       j->second.assignments.insert(j->second.assignments.end(), i->second.assignments.begin(), i->second.assignments.end());
-               }
-       }
-       variables.pop_back();
-}
-
-void ProgramCompiler::UnusedVariableLocator::visit(Conditional &cond)
-{
-       cond.condition->visit(*this);
-       variables.push_back(BlockVariableMap());
-       cond.body.visit(*this);
-
-       BlockVariableMap if_variables;
-       swap(variables.back(), if_variables);
-       cond.else_body.visit(*this);
-
-       BlockVariableMap &else_variables = variables.back();
-       for(BlockVariableMap::iterator i=else_variables.begin(); i!=else_variables.end(); ++i)
-       {
-               BlockVariableMap::iterator j = if_variables.find(i->first);
-               if(j!=if_variables.end())
-               {
-                       i->second.assignments.insert(i->second.assignments.end(), j->second.assignments.begin(), j->second.assignments.end());
-                       i->second.conditionally_assigned |= j->second.conditionally_assigned;
-                       if_variables.erase(j);
-               }
-               else
-                       i->second.conditionally_assigned = true;
-       }
-
-       for(BlockVariableMap::iterator i=if_variables.begin(); i!=if_variables.end(); ++i)
-       {
-               i->second.conditionally_assigned = true;
-               else_variables.insert(*i);
-       }
-
-       merge_down_variables();
-}
-
-void ProgramCompiler::UnusedVariableLocator::visit(Iteration &iter)
-{
-       variables.push_back(BlockVariableMap());
-       TraversingVisitor::visit(iter);
-
-       BlockVariableMap &block_variables = variables.back();
-       for(BlockVariableMap::iterator i=block_variables.begin(); i!=block_variables.end(); ++i)
-               if(!i->second.local && i->second.referenced)
-                       i->second.assignments.clear();
-
-       merge_down_variables();
-}
-
-
-ProgramCompiler::UnusedVariableLocator::VariableInfo::VariableInfo():
-       local(false),
-       conditionally_assigned(false),
-       referenced(false)
-{ }
-
-
-void ProgramCompiler::UnusedFunctionLocator::visit(FunctionCall &call)
-{
-       TraversingVisitor::visit(call);
-
-       unused_nodes.erase(call.declaration);
-       if(call.declaration && call.declaration->definition!=call.declaration)
-               used_definitions.insert(call.declaration->definition);
-}
-
-void ProgramCompiler::UnusedFunctionLocator::visit(FunctionDeclaration &func)
-{
-       TraversingVisitor::visit(func);
-
-       if((func.name!="main" || func.body.body.empty()) && !used_definitions.count(&func))
-               unused_nodes.insert(&func);
-}
-
-
-ProgramCompiler::NodeRemover::NodeRemover(const set<Node *> &r):
-       to_remove(r)
-{ }
-
-void ProgramCompiler::NodeRemover::visit(Block &block)
-{
-       for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); )
-       {
-               (*i)->visit(*this);
-               if(to_remove.count(i->get()))
-                       block.body.erase(i++);
-               else
-                       ++i;
-       }
-}
-
-void ProgramCompiler::NodeRemover::visit(VariableDeclaration &var)
-{
-       if(to_remove.count(&var))
-       {
-               stage->in_variables.erase(var.name);
-               stage->out_variables.erase(var.name);
-               stage->locations.erase(var.name);
-               if(var.linked_declaration)
-                       var.linked_declaration->linked_declaration = 0;
-       }
-       else if(var.init_expression && to_remove.count(var.init_expression.get()))
-               var.init_expression = 0;
-}
-
-void ProgramCompiler::NodeRemover::visit(Iteration &iter)
-{
-       if(to_remove.count(iter.init_statement.get()))
-               iter.init_statement = 0;
-       TraversingVisitor::visit(iter);
-}
-
-
-void ProgramCompiler::PrecisionRemover::visit(Precision &)
-{
-       remove_node = true;
-}
-
-void ProgramCompiler::PrecisionRemover::visit(VariableDeclaration &var)
-{
-       var.precision.clear();
-}
-
-
-ProgramCompiler::DefaultPrecisionGenerator::DefaultPrecisionGenerator():
-       toplevel(true)
-{ }
-
-void ProgramCompiler::DefaultPrecisionGenerator::visit(Block &block)
-{
-       if(toplevel)
-       {
-               SetForScope<bool> set(toplevel, false);
-               BlockModifier::visit(block);
-       }
-       else
-               Visitor::visit(block);
-}
-
-void ProgramCompiler::DefaultPrecisionGenerator::visit(Precision &prec)
-{
-       have_default.insert(prec.type);
-}
-
-void ProgramCompiler::DefaultPrecisionGenerator::visit(VariableDeclaration &var)
-{
-       if(var.type_declaration)
-               return;
-
-       string type = var.type;
-       if(!type.compare(0, 3, "vec") || !type.compare(0, 3, "mat"))
-               type = "float";
-       else if(!type.compare(0, 3, "ivec") || type=="uint")
-               type = "int";
-
-       if(!have_default.count(type))
-       {
-               Precision *prec = new Precision;
-               if(!type.compare(0, 7, "sampler"))
-                       prec->precision = "lowp";
-               else if(stage->type==FRAGMENT)
-                       prec->precision = "mediump";
-               else
-                       prec->precision = "highp";
-               prec->type = type;
-               insert_nodes.push_back(prec);
-
-               have_default.insert(type);
-       }
-}
-
-
-ProgramCompiler::LegacyConverter::LegacyConverter():
-       target_api(get_gl_api()),
-       target_version(get_glsl_version()),
-       frag_out(0)
-{ }
-
-ProgramCompiler::LegacyConverter::LegacyConverter(const Version &v):
-       target_api(get_gl_api()),
-       target_version(v),
-       frag_out(0)
-{ }
-
-bool ProgramCompiler::LegacyConverter::check_version(const Version &feature_version) const
-{
-       if(target_version<feature_version)
-               return false;
-       else if(stage->required_version<feature_version)
-               stage->required_version = feature_version;
-
-       return true;
-}
-
-bool ProgramCompiler::LegacyConverter::check_extension(const Extension &extension) const
-{
-       if(!extension)
-               return false;
-
-       vector<const Extension *>::iterator i = find(stage->required_extensions, &extension);
-       if(i==stage->required_extensions.end())
-               stage->required_extensions.push_back(&extension);
-
-       return true;
-}
-
-bool ProgramCompiler::LegacyConverter::supports_unified_interface_syntax() const
-{
-       if(target_api==OPENGL_ES2)
-               return check_version(Version(3, 0));
-       else
-               return check_version(Version(1, 30));
-}
-
-void ProgramCompiler::LegacyConverter::visit(VariableReference &var)
-{
-       if(var.declaration==frag_out && !supports_unified_interface_syntax())
-       {
-               var.name = "gl_FragColor";
-               var.declaration = 0;
-               type = "vec4";
-       }
-       else if(var.declaration)
-               type = var.declaration->type;
-       else
-               type = string();
-}
-
-void ProgramCompiler::LegacyConverter::visit(Assignment &assign)
-{
-       TraversingVisitor::visit(assign);
-       if(assign.target_declaration==frag_out && !supports_unified_interface_syntax())
-               assign.target_declaration = 0;
-}
-
-bool ProgramCompiler::LegacyConverter::supports_unified_sampling_functions() const
-{
-       if(target_api==OPENGL_ES2)
-               return check_version(Version(3, 0));
-       else
-               return check_version(Version(1, 30));
-}
-
-void ProgramCompiler::LegacyConverter::visit(FunctionCall &call)
-{
-       if(call.name=="texture" && !call.declaration && !supports_unified_sampling_functions())
-       {
-               NodeArray<Expression>::iterator i = call.arguments.begin();
-               if(i!=call.arguments.end())
-               {
-                       (*i)->visit(*this);
-                       if(type=="sampler1D")
-                               call.name = "texture1D";
-                       else if(type=="sampler2D")
-                               call.name = "texture2D";
-                       else if(type=="sampler3D")
-                               call.name = "texture3D";
-                       else if(type=="samplerCube")
-                               call.name = "textureCube";
-                       else if(type=="sampler1DShadow")
-                               call.name = "shadow1D";
-                       else if(type=="sampler2DShadow")
-                               call.name = "shadow2D";
-                       else if(type=="sampler1DArray")
-                       {
-                               check_extension(EXT_texture_array);
-                               call.name = "texture1DArray";
-                       }
-                       else if(type=="sampler2DArray")
-                       {
-                               check_extension(EXT_texture_array);
-                               call.name = "texture2DArray";
-                       }
-                       else if(type=="sampler1DArrayShadow")
-                       {
-                               check_extension(EXT_texture_array);
-                               call.name = "shadow1DArray";
-                       }
-                       else if(type=="sampler2DArrayShadow")
-                       {
-                               check_extension(EXT_texture_array);
-                               call.name = "shadow2DArray";
-                       }
-
-                       for(; i!=call.arguments.end(); ++i)
-                               (*i)->visit(*this);
-               }
-       }
-       else
-               TraversingVisitor::visit(call);
-}
-
-bool ProgramCompiler::LegacyConverter::supports_interface_layouts() const
-{
-       if(target_api==OPENGL_ES2)
-               return check_version(Version(3, 0));
-       else if(check_version(Version(3, 30)))
-               return true;
-       else
-               return check_extension(ARB_explicit_attrib_location);
-}
-
-bool ProgramCompiler::LegacyConverter::supports_centroid_sampling() const
-{
-       if(target_api==OPENGL_ES2)
-               return check_version(Version(3, 0));
-       else if(check_version(Version(1, 20)))
-               return true;
-       else
-               return check_extension(EXT_gpu_shader4);
-}
-
-bool ProgramCompiler::LegacyConverter::supports_sample_sampling() const
-{
-       if(target_api==OPENGL_ES2)
-               return check_version(Version(3, 20));
-       else if(check_version(Version(4, 0)))
-               return true;
-       else
-               return check_extension(ARB_gpu_shader5);
-}
-
-void ProgramCompiler::LegacyConverter::visit(VariableDeclaration &var)
-{
-       if(var.layout && !supports_interface_layouts())
-       {
-               vector<Layout::Qualifier>::iterator i;
-               for(i=var.layout->qualifiers.begin(); (i!=var.layout->qualifiers.end() && i->identifier!="location"); ++i) ;
-               if(i!=var.layout->qualifiers.end())
-               {
-                       unsigned location = lexical_cast<unsigned>(i->value);
-                       if(stage->type==VERTEX && var.interface=="in")
-                       {
-                               stage->locations[var.name] = location;
-                               var.layout->qualifiers.erase(i);
-                       }
-                       else if(stage->type==FRAGMENT && var.interface=="out")
-                       {
-                               if(location!=0)
-                                       static Require _req(EXT_gpu_shader4);
-                               stage->locations[var.name] = location;
-                               var.layout->qualifiers.erase(i);
-                       }
-
-                       if(var.layout->qualifiers.empty())
-                               var.layout = 0;
-               }
-       }
-
-       if(var.sampling=="centroid")
-       {
-               if(!supports_centroid_sampling())
-                       var.sampling = string();
-       }
-       else if(var.sampling=="sample")
-       {
-               if(!supports_sample_sampling())
-                       var.sampling = string();
-       }
-
-       if((var.interface=="in" || var.interface=="out") && !supports_unified_interface_syntax())
-       {
-               if(stage->type==FRAGMENT && var.interface=="out")
-               {
-                       frag_out = &var;
-                       remove_node = true;
-               }
-       }
-
-       TraversingVisitor::visit(var);
-}
-
-bool ProgramCompiler::LegacyConverter::supports_interface_blocks(const string &iface) const
-{
-       if(target_api==OPENGL_ES2)
-       {
-               if(iface=="uniform")
-                       return check_version(Version(3, 0));
-               else
-                       return check_version(Version(3, 20));
-       }
-       else if(check_version(Version(1, 50)))
-               return true;
-       else if(iface=="uniform")
-               return check_extension(ARB_uniform_buffer_object);
-       else
-               return false;
-}
-
-void ProgramCompiler::LegacyConverter::visit(InterfaceBlock &iface)
-{
-       if(!supports_interface_blocks(iface.interface))
-               flatten_block(iface.members);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/programcompiler.h b/source/programcompiler.h
deleted file mode 100644 (file)
index 0bff354..0000000
+++ /dev/null
@@ -1,416 +0,0 @@
-#ifndef MSP_GL_PROGRAMCOMPILER_H_
-#define MSP_GL_PROGRAMCOMPILER_H_
-
-#include <set>
-#include "programparser.h"
-#include "programsyntax.h"
-
-namespace Msp {
-namespace GL {
-
-class Program;
-class Resources;
-
-class ProgramCompiler
-{
-private:
-       struct Visitor: ProgramSyntax::TraversingVisitor
-       {
-               typedef void ResultType;
-
-               ProgramSyntax::Stage *stage;
-
-               Visitor();
-
-               virtual void apply(ProgramSyntax::Stage &);
-               void get_result() const { }
-       };
-
-       struct BlockModifier: Visitor
-       {
-               bool remove_node;
-               std::vector<RefPtr<ProgramSyntax::Statement> > insert_nodes;
-
-               BlockModifier();
-
-               void flatten_block(ProgramSyntax::Block &);
-               void apply_and_increment(ProgramSyntax::Block &, ProgramSyntax::NodeList<ProgramSyntax::Statement>::iterator &);
-               using Visitor::visit;
-               virtual void visit(ProgramSyntax::Block &);
-       };
-
-       struct Formatter: Visitor
-       {
-               typedef std::string ResultType;
-
-               std::string formatted;
-               unsigned source_index;
-               unsigned source_line;
-               unsigned indent;
-               bool parameter_list;
-               std::string block_interface;
-
-               Formatter();
-
-               virtual void apply(ProgramSyntax::Stage &);
-               const std::string &get_result() const { return formatted; }
-               using Visitor::visit;
-               void append(const std::string &);
-               void append(char);
-               void set_source(unsigned, unsigned);
-               virtual void visit(ProgramSyntax::Block &);
-               virtual void visit(ProgramSyntax::Literal &);
-               virtual void visit(ProgramSyntax::ParenthesizedExpression &);
-               virtual void visit(ProgramSyntax::VariableReference &);
-               virtual void visit(ProgramSyntax::MemberAccess &);
-               virtual void visit(ProgramSyntax::UnaryExpression &);
-               virtual void visit(ProgramSyntax::BinaryExpression &);
-               virtual void visit(ProgramSyntax::Assignment &);
-               virtual void visit(ProgramSyntax::FunctionCall &);
-               virtual void visit(ProgramSyntax::ExpressionStatement &);
-               virtual void visit(ProgramSyntax::Import &);
-               virtual void visit(ProgramSyntax::Precision &);
-               virtual void visit(ProgramSyntax::Layout &);
-               virtual void visit(ProgramSyntax::InterfaceLayout &);
-               virtual void visit(ProgramSyntax::StructDeclaration &);
-               virtual void visit(ProgramSyntax::VariableDeclaration &);
-               virtual void visit(ProgramSyntax::InterfaceBlock &);
-               virtual void visit(ProgramSyntax::FunctionDeclaration &);
-               virtual void visit(ProgramSyntax::Conditional &);
-               virtual void visit(ProgramSyntax::Iteration &);
-               virtual void visit(ProgramSyntax::Return &);
-               virtual void visit(ProgramSyntax::Jump &);
-       };
-
-       template<typename T>
-       struct NodeGatherer: Visitor
-       {
-               typedef std::vector<T *> ResultType;
-
-               std::vector<T *> nodes;
-
-               const ResultType &get_result() const { return nodes; }
-               using Visitor::visit;
-               virtual void visit(T &n) { nodes.push_back(&n); }
-       };
-
-       struct DeclarationCombiner: BlockModifier
-       {
-               bool toplevel;
-               std::map<std::string, std::vector<ProgramSyntax::FunctionDeclaration *> > functions;
-               std::map<std::string, ProgramSyntax::VariableDeclaration *> variables;
-
-               DeclarationCombiner();
-
-               using Visitor::visit;
-               virtual void visit(ProgramSyntax::Block &);
-               virtual void visit(ProgramSyntax::FunctionDeclaration &);
-               virtual void visit(ProgramSyntax::VariableDeclaration &);
-       };
-
-       struct VariableResolver: Visitor
-       {
-               std::vector<ProgramSyntax::Block *> blocks;
-               ProgramSyntax::StructDeclaration *type;
-               bool anonymous;
-               std::string block_interface;
-               bool record_target;
-               ProgramSyntax::VariableDeclaration *assignment_target;
-               bool self_referencing;
-
-               VariableResolver();
-
-               virtual void apply(ProgramSyntax::Stage &);
-               using Visitor::visit;
-               virtual void visit(ProgramSyntax::Block &);
-               virtual void visit(ProgramSyntax::VariableReference &);
-               virtual void visit(ProgramSyntax::MemberAccess &);
-               virtual void visit(ProgramSyntax::BinaryExpression &);
-               virtual void visit(ProgramSyntax::Assignment &);
-               virtual void visit(ProgramSyntax::StructDeclaration &);
-               virtual void visit(ProgramSyntax::VariableDeclaration &);
-               virtual void visit(ProgramSyntax::InterfaceBlock &);
-       };
-
-       struct FunctionResolver: Visitor
-       {
-               std::map<std::string, std::vector<ProgramSyntax::FunctionDeclaration *> > functions;
-
-               using Visitor::visit;
-               virtual void visit(ProgramSyntax::FunctionCall &);
-               virtual void visit(ProgramSyntax::FunctionDeclaration &);
-       };
-
-       struct InterfaceGenerator: BlockModifier
-       {
-               std::string in_prefix;
-               std::string out_prefix;
-               unsigned scope_level;
-               std::map<std::string, RefPtr<ProgramSyntax::VariableDeclaration> > iface_declarations;
-
-               InterfaceGenerator();
-
-               static std::string get_out_prefix(ProgramSyntax::StageType);
-               virtual void apply(ProgramSyntax::Stage &);
-               using Visitor::visit;
-               virtual void visit(ProgramSyntax::Block &);
-               std::string change_prefix(const std::string &, const std::string &) const;
-               bool generate_interface(ProgramSyntax::VariableDeclaration &, const std::string &, const std::string &);
-               ProgramSyntax::ExpressionStatement &insert_assignment(const std::string &, ProgramSyntax::Expression *);
-               virtual void visit(ProgramSyntax::VariableReference &);
-               virtual void visit(ProgramSyntax::VariableDeclaration &);
-               virtual void visit(ProgramSyntax::Passthrough &);
-       };
-
-       struct DeclarationReorderer: Visitor
-       {
-               enum DeclarationKind
-               {
-                       NO_DECLARATION,
-                       LAYOUT,
-                       STRUCT,
-                       VARIABLE,
-                       FUNCTION
-               };
-
-               unsigned scope_level;
-               DeclarationKind kind;
-               std::set<ProgramSyntax::Node *> ordered_funcs;
-               std::set<ProgramSyntax::Node *> needed_funcs;
-
-               DeclarationReorderer();
-
-               using Visitor::visit;
-               virtual void visit(ProgramSyntax::Block &);
-               virtual void visit(ProgramSyntax::FunctionCall &);
-               virtual void visit(ProgramSyntax::InterfaceLayout &) { kind = LAYOUT; }
-               virtual void visit(ProgramSyntax::StructDeclaration &) { kind = STRUCT; }
-               virtual void visit(ProgramSyntax::VariableDeclaration &);
-               virtual void visit(ProgramSyntax::InterfaceBlock &) { kind = VARIABLE; }
-               virtual void visit(ProgramSyntax::FunctionDeclaration &);
-       };
-
-       struct InlineableFunctionLocator: Visitor
-       {
-               typedef std::set<ProgramSyntax::FunctionDeclaration *> ResultType;
-
-               std::map<ProgramSyntax::FunctionDeclaration *, unsigned> refcounts;
-               std::set<ProgramSyntax::FunctionDeclaration *> inlineable;
-               ProgramSyntax::FunctionDeclaration *in_function;
-
-               InlineableFunctionLocator();
-
-               const ResultType &get_result() const { return inlineable; }
-               using Visitor::visit;
-               virtual void visit(ProgramSyntax::FunctionCall &);
-               virtual void visit(ProgramSyntax::FunctionDeclaration &);
-       };
-
-       struct FunctionInliner: Visitor
-       {
-               std::set<ProgramSyntax::FunctionDeclaration *> inlineable;
-               unsigned extract_result;
-               RefPtr<ProgramSyntax::Expression> inline_result;
-
-               FunctionInliner();
-               FunctionInliner(const std::set<ProgramSyntax::FunctionDeclaration *> &);
-
-               void visit_and_inline(RefPtr<ProgramSyntax::Expression> &);
-               using Visitor::visit;
-               virtual void visit(ProgramSyntax::Block &);
-               virtual void visit(ProgramSyntax::UnaryExpression &);
-               virtual void visit(ProgramSyntax::BinaryExpression &);
-               virtual void visit(ProgramSyntax::MemberAccess &);
-               virtual void visit(ProgramSyntax::FunctionCall &);
-               virtual void visit(ProgramSyntax::VariableDeclaration &);
-               virtual void visit(ProgramSyntax::Return &);
-       };
-
-       struct ExpressionEvaluator: ProgramSyntax::NodeVisitor
-       {
-               typedef std::map<ProgramSyntax::VariableDeclaration *, ProgramSyntax::Expression *> ValueMap;
-
-               const ValueMap *variable_values;
-               float result;
-               bool result_valid;
-
-               ExpressionEvaluator();
-               ExpressionEvaluator(const ValueMap &);
-
-               using ProgramSyntax::NodeVisitor::visit;
-               virtual void visit(ProgramSyntax::Literal &);
-               virtual void visit(ProgramSyntax::ParenthesizedExpression &);
-               virtual void visit(ProgramSyntax::VariableReference &);
-               virtual void visit(ProgramSyntax::UnaryExpression &);
-               virtual void visit(ProgramSyntax::BinaryExpression &);
-       };
-
-       struct ConstantConditionEliminator: BlockModifier
-       {
-               unsigned scope_level;
-               bool record_only;
-               ExpressionEvaluator::ValueMap variable_values;
-
-               ConstantConditionEliminator();
-
-               using Visitor::visit;
-               virtual void visit(ProgramSyntax::Block &);
-               virtual void visit(ProgramSyntax::UnaryExpression &);
-               virtual void visit(ProgramSyntax::Assignment &);
-               virtual void visit(ProgramSyntax::VariableDeclaration &);
-               virtual void visit(ProgramSyntax::Conditional &);
-               virtual void visit(ProgramSyntax::Iteration &);
-       };
-
-       struct UnusedVariableLocator: Visitor
-       {
-               struct VariableInfo
-               {
-                       bool local;
-                       std::vector<ProgramSyntax::Node *> assignments;
-                       bool conditionally_assigned;
-                       bool referenced;
-
-                       VariableInfo();
-               };
-
-               typedef std::set<ProgramSyntax::Node *> ResultType;
-               typedef std::map<ProgramSyntax::VariableDeclaration *, VariableInfo> BlockVariableMap;
-
-               std::set<ProgramSyntax::Node *> unused_nodes;
-               std::map<ProgramSyntax::VariableDeclaration *, ProgramSyntax::Node *> aggregates;
-               ProgramSyntax::Node *aggregate;
-               std::vector<BlockVariableMap> variables;
-               ProgramSyntax::Assignment *assignment;
-               bool assignment_target;
-               bool assign_to_subscript;
-               bool global_scope;
-
-               UnusedVariableLocator();
-
-               virtual void apply(ProgramSyntax::Stage &);
-               const ResultType &get_result() const { return unused_nodes; }
-               using Visitor::visit;
-               virtual void visit(ProgramSyntax::VariableReference &);
-               virtual void visit(ProgramSyntax::MemberAccess &);
-               virtual void visit(ProgramSyntax::BinaryExpression &);
-               virtual void visit(ProgramSyntax::Assignment &);
-               void record_assignment(ProgramSyntax::VariableDeclaration &, ProgramSyntax::Node &, bool);
-               void clear_assignments(VariableInfo &, bool);
-               virtual void visit(ProgramSyntax::ExpressionStatement &);
-               virtual void visit(ProgramSyntax::StructDeclaration &);
-               virtual void visit(ProgramSyntax::VariableDeclaration &);
-               virtual void visit(ProgramSyntax::InterfaceBlock &);
-               virtual void visit(ProgramSyntax::FunctionDeclaration &);
-               void merge_down_variables();
-               virtual void visit(ProgramSyntax::Conditional &);
-               virtual void visit(ProgramSyntax::Iteration &);
-       };
-
-       struct UnusedFunctionLocator: Visitor
-       {
-               typedef std::set<ProgramSyntax::Node *> ResultType;
-
-               std::set<ProgramSyntax::Node *> unused_nodes;
-               std::set<ProgramSyntax::FunctionDeclaration *> used_definitions;
-
-               const ResultType &get_result() const { return unused_nodes; }
-               using Visitor::visit;
-               virtual void visit(ProgramSyntax::FunctionCall &);
-               virtual void visit(ProgramSyntax::FunctionDeclaration &);
-       };
-
-       struct NodeRemover: Visitor
-       {
-               std::set<ProgramSyntax::Node *> to_remove;
-
-               NodeRemover() { }
-               NodeRemover(const std::set<ProgramSyntax::Node *> &);
-
-               using Visitor::visit;
-               virtual void visit(ProgramSyntax::Block &);
-               virtual void visit(ProgramSyntax::VariableDeclaration &);
-               virtual void visit(ProgramSyntax::Iteration &);
-       };
-
-       struct PrecisionRemover: BlockModifier
-       {
-               using Visitor::visit;
-               virtual void visit(ProgramSyntax::Precision &);
-               virtual void visit(ProgramSyntax::VariableDeclaration &);
-       };
-
-       struct DefaultPrecisionGenerator: BlockModifier
-       {
-               bool toplevel;
-               std::set<std::string> have_default;
-
-               DefaultPrecisionGenerator();
-
-               using Visitor::visit;
-               virtual void visit(ProgramSyntax::Block &);
-               virtual void visit(ProgramSyntax::Precision &);
-               virtual void visit(ProgramSyntax::VariableDeclaration &);
-       };
-
-       struct LegacyConverter: BlockModifier
-       {
-               GLApi target_api;
-               Version target_version;
-               std::string type;
-               ProgramSyntax::VariableDeclaration *frag_out;
-
-               LegacyConverter();
-               LegacyConverter(const Version &);
-
-               bool check_version(const Version &) const;
-               bool check_extension(const Extension &) const;
-               using Visitor::visit;
-               bool supports_unified_interface_syntax() const;
-               virtual void visit(ProgramSyntax::VariableReference &);
-               virtual void visit(ProgramSyntax::Assignment &);
-               bool supports_unified_sampling_functions() const;
-               virtual void visit(ProgramSyntax::FunctionCall &);
-               bool supports_interface_layouts() const;
-               bool supports_centroid_sampling() const;
-               bool supports_sample_sampling() const;
-               virtual void visit(ProgramSyntax::VariableDeclaration &);
-               bool supports_interface_blocks(const std::string &) const;
-               virtual void visit(ProgramSyntax::InterfaceBlock &);
-       };
-
-       Resources *resources;
-       ProgramSyntax::Module *module;
-       std::vector<std::string> imported_names;
-
-public:
-       ProgramCompiler();
-       ~ProgramCompiler();
-
-       void compile(const std::string &, const std::string & = "<string>");
-       void compile(IO::Base &, Resources * = 0, const std::string & = "<file>");
-       void compile(IO::Base &, const std::string &);
-       void add_shaders(Program &);
-
-private:
-       static ProgramSyntax::Module *create_builtins_module();
-       static ProgramSyntax::Module &get_builtins_module();
-       static ProgramSyntax::Stage *get_builtins(ProgramSyntax::StageType);
-       void append_module(ProgramSyntax::Module &);
-       void append_stage(ProgramSyntax::Stage &);
-       void process();
-       void import(const std::string &);
-       void generate(ProgramSyntax::Stage &);
-       bool optimize(ProgramSyntax::Stage &);
-       void finalize(ProgramSyntax::Stage &);
-       static void inject_block(ProgramSyntax::Block &, const ProgramSyntax::Block &);
-       template<typename T>
-       static typename T::ResultType apply(ProgramSyntax::Stage &);
-       template<typename T, typename A>
-       static typename T::ResultType apply(ProgramSyntax::Stage &, const A &);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/programdata.cpp b/source/programdata.cpp
deleted file mode 100644 (file)
index 5f124a9..0000000
+++ /dev/null
@@ -1,962 +0,0 @@
-#include <msp/core/maputils.h>
-#include <msp/debug/demangle.h>
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include <msp/io/print.h>
-#include "buffer.h"
-#include "color.h"
-#include "error.h"
-#include "matrix.h"
-#include "program.h"
-#include "programdata.h"
-#include "uniform.h"
-#include "uniformblock.h"
-#include "vector.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-ProgramData::ProgramData(const Program *p):
-       tied_program(p),
-       last_block(0),
-       buffer(0),
-       dirty(0)
-{ }
-
-// Blocks are intentionally left uncopied
-ProgramData::ProgramData(const ProgramData &other):
-       tied_program(other.tied_program),
-       uniforms(other.uniforms),
-       last_block(0),
-       buffer(0),
-       dirty(0)
-{
-       for(vector<NamedUniform>::iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
-               i->value = i->value->clone();
-}
-
-ProgramData::ProgramData(const ProgramData &other, const Program *p):
-       tied_program(p),
-       last_block(0),
-       buffer(0),
-       dirty(0)
-{
-       if(tied_program)
-       {
-               for(vector<NamedUniform>::const_iterator i=other.uniforms.begin(); i!=other.uniforms.end(); ++i)
-                       tied_program->get_uniform_info(i->name);
-       }
-
-       uniforms = other.uniforms;
-       for(vector<NamedUniform>::iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
-               i->value = i->value->clone();
-}
-
-ProgramData &ProgramData::operator=(const ProgramData &other)
-{
-       tied_program = other.tied_program;
-
-       uniforms = other.uniforms;
-       for(vector<NamedUniform>::iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
-               i->value = i->value->clone();
-
-       for(BlockMap::iterator i=blocks.begin(); i!=blocks.end(); ++i)
-               delete i->second.block;
-       programs.clear();
-
-       last_block = 0;
-       buffer = 0;
-       dirty = 0;
-
-       return *this;
-}
-
-ProgramData::~ProgramData()
-{
-       for(vector<NamedUniform>::iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
-               delete i->value;
-       for(BlockMap::iterator i=blocks.begin(); i!=blocks.end(); ++i)
-       {
-               if(i->second.indices.type_flag==0xFE)
-                       delete i->second.indices.dynamic.values;
-               delete i->second.block;
-       }
-       delete buffer;
-}
-
-void ProgramData::uniform(const string &name, Uniform *uni)
-{
-       try
-       {
-               if(!validate_name(name))
-               {
-                       delete uni;
-                       return;
-               }
-       }
-       catch(...)
-       {
-               delete uni;
-               throw;
-       }
-
-       int i = find_uniform_index(name);
-       if(i<0)
-               return add_uniform(name, uni);
-
-       uniforms[i].replace_value(uni);
-       dirty |= 1<<i;
-}
-
-template<typename T, typename V>
-void ProgramData::uniform(const string &name, V value)
-{
-       if(!validate_name(name))
-               return;
-
-       int i = find_uniform_index(name);
-       if(i<0)
-               return add_uniform(name, new T(value));
-
-       if(T *uni = dynamic_cast<T *>(uniforms[i].value))
-               uni->set(value);
-       else
-               uniforms[i].replace_value(new T(value));
-
-       dirty |= 1<<i;
-}
-
-template<typename T, typename V>
-void ProgramData::uniform_array(const string &name, unsigned n, V value)
-{
-       if(!validate_name(name))
-               return;
-
-       int i = find_uniform_index(name);
-       if(i<0)
-               return add_uniform(name, new UniformArray<T>(n, value));
-
-       UniformArray<T> *uni = dynamic_cast<UniformArray<T> *>(uniforms[i].value);
-       if(uni && n==uni->size())
-               uni->set(value);
-       else
-               uniforms[i].replace_value(new UniformArray<T>(n, value));
-
-       dirty |= 1<<i;
-}
-
-bool ProgramData::validate_name(const string &name) const
-{
-#ifdef DEBUG
-       try
-#endif
-       {
-               if(tied_program)
-                       tied_program->get_uniform_info(name);
-               else if(name[name.size()-1]==']')
-                       throw invalid_argument("ProgramData::uniform");
-               return true;
-       }
-#ifdef DEBUG
-       catch(const exception &e)
-       {
-               IO::print(IO::cerr, "Error while setting uniform %s: %s: %s\n", name, Debug::demangle(typeid(e).name()), e.what());
-               return false;
-       }
-#endif
-}
-
-void ProgramData::add_uniform(const string &name, Uniform *uni)
-{
-       if(uniforms.size()>=MASK_BITS)
-       {
-               delete uni;
-               throw too_many_uniforms(name);
-       }
-
-       vector<NamedUniform>::iterator j = lower_bound(uniforms.begin(), uniforms.end(), name, uniform_name_compare);
-
-       NamedUniform nu;
-       nu.name = name;
-       nu.value = uni;
-       uniforms.insert(j, nu);
-
-       dirty = ALL_ONES;
-}
-
-void ProgramData::uniform(const string &name, const Uniform &u)
-{
-       uniform(name, u.clone());
-}
-
-void ProgramData::uniform(const string &name, int v)
-{
-       uniform<Uniform1i>(name, v);
-}
-
-void ProgramData::uniform(const string &name, float v)
-{
-       uniform<Uniform1f>(name, v);
-}
-
-void ProgramData::uniform(const string &name, int v0, int v1)
-{
-       int va[2] = { v0, v1 };
-       uniform2(name, va);
-}
-
-void ProgramData::uniform(const string &name, float v0, float v1)
-{
-       float va[2] = { v0, v1 };
-       uniform2(name, va);
-}
-
-void ProgramData::uniform2(const string &name, const int *v)
-{
-       uniform<Uniform2i>(name, v);
-}
-
-void ProgramData::uniform2(const string &name, const float *v)
-{
-       uniform<Uniform2f>(name, v);
-}
-
-void ProgramData::uniform(const string &name, int v0, int v1, int v2)
-{
-       int va[3] = { v0, v1, v2 };
-       uniform3(name, va);
-}
-
-void ProgramData::uniform(const string &name, float v0, float v1, float v2)
-{
-       float va[3] = { v0, v1, v2 };
-       uniform3(name, va);
-}
-
-void ProgramData::uniform(const string &name, const Vector3 &v)
-{
-       uniform(name, v.x, v.y, v.z);
-}
-
-void ProgramData::uniform3(const string &name, const int *v)
-{
-       uniform<Uniform3i>(name, v);
-}
-
-void ProgramData::uniform3(const string &name, const float *v)
-{
-       uniform<Uniform3f>(name, v);
-}
-
-void ProgramData::uniform(const string &name, int v0, int v1, int v2, int v3)
-{
-       int va[4] = { v0, v1, v2, v3 };
-       uniform4(name, va);
-}
-
-void ProgramData::uniform(const string &name, float v0, float v1, float v2, float v3)
-{
-       float va[4] = { v0, v1, v2, v3 };
-       uniform4(name, va);
-}
-
-void ProgramData::uniform(const string &name, const Vector4 &v)
-{
-       uniform(name, v.x, v.y, v.z, v.w);
-}
-
-void ProgramData::uniform(const string &name, const Color &c)
-{
-       uniform(name, c.r, c.g, c.b, c.a);
-}
-
-void ProgramData::uniform4(const string &name, const int *v)
-{
-       uniform<Uniform4i>(name, v);
-}
-
-void ProgramData::uniform4(const string &name, const float *v)
-{
-       uniform<Uniform4f>(name, v);
-}
-
-void ProgramData::uniform(const string &name, const LinAl::Matrix<float, 2, 2> &m)
-{
-       uniform_matrix2(name, &m(0, 0));
-}
-
-void ProgramData::uniform_matrix2(const string &name, const float *v)
-{
-       uniform<UniformMatrix2x2f>(name, v);
-}
-
-void ProgramData::uniform(const string &name, const LinAl::Matrix<float, 2, 3> &m)
-{
-       uniform_matrix3x2(name, &m(0, 0));
-}
-
-void ProgramData::uniform_matrix3x2(const string &name, const float *v)
-{
-       uniform<UniformMatrix3x2f>(name, v);
-}
-
-void ProgramData::uniform(const string &name, const LinAl::Matrix<float, 2, 4> &m)
-{
-       uniform_matrix4x2(name, &m(0, 0));
-}
-
-void ProgramData::uniform_matrix4x2(const string &name, const float *v)
-{
-       uniform<UniformMatrix4x2f>(name, v);
-}
-
-void ProgramData::uniform(const string &name, const LinAl::Matrix<float, 3, 2> &m)
-{
-       uniform_matrix2x3(name, &m(0, 0));
-}
-
-void ProgramData::uniform_matrix2x3(const string &name, const float *v)
-{
-       uniform<UniformMatrix2x3f>(name, v);
-}
-
-void ProgramData::uniform(const string &name, const LinAl::Matrix<float, 3, 3> &m)
-{
-       uniform_matrix3(name, &m(0, 0));
-}
-
-void ProgramData::uniform_matrix3(const string &name, const float *v)
-{
-       uniform<UniformMatrix3x3f>(name, v);
-}
-
-void ProgramData::uniform(const string &name, const LinAl::Matrix<float, 3, 4> &m)
-{
-       uniform_matrix4x3(name, &m(0, 0));
-}
-
-void ProgramData::uniform_matrix4x3(const string &name, const float *v)
-{
-       uniform<UniformMatrix4x3f>(name, v);
-}
-
-void ProgramData::uniform(const string &name, const LinAl::Matrix<float, 4, 2> &m)
-{
-       uniform_matrix2x4(name, &m(0, 0));
-}
-
-void ProgramData::uniform_matrix2x4(const string &name, const float *v)
-{
-       uniform<UniformMatrix2x4f>(name, v);
-}
-
-void ProgramData::uniform(const string &name, const LinAl::Matrix<float, 4, 3> &m)
-{
-       uniform_matrix3x4(name, &m(0, 0));
-}
-
-void ProgramData::uniform_matrix3x4(const string &name, const float *v)
-{
-       uniform<UniformMatrix3x4f>(name, v);
-}
-
-void ProgramData::uniform(const string &name, const Matrix &m)
-{
-       uniform_matrix4(name, m.data());
-}
-
-void ProgramData::uniform_matrix4(const string &name, const float *v)
-{
-       uniform<UniformMatrix4x4f>(name, v);
-}
-
-void ProgramData::uniform1_array(const string &name, unsigned n, const int *v)
-{
-       uniform_array<Uniform1i>(name, n, v);
-}
-
-void ProgramData::uniform1_array(const string &name, unsigned n, const float *v)
-{
-       uniform_array<Uniform1f>(name, n, v);
-}
-
-void ProgramData::uniform2_array(const string &name, unsigned n, const int *v)
-{
-       uniform_array<Uniform2i>(name, n, v);
-}
-
-void ProgramData::uniform2_array(const string &name, unsigned n, const float *v)
-{
-       uniform_array<Uniform2f>(name, n, v);
-}
-
-void ProgramData::uniform3_array(const string &name, unsigned n, const int *v)
-{
-       uniform_array<Uniform3i>(name, n, v);
-}
-
-void ProgramData::uniform3_array(const string &name, unsigned n, const float *v)
-{
-       uniform_array<Uniform3f>(name, n, v);
-}
-
-void ProgramData::uniform4_array(const string &name, unsigned n, const int *v)
-{
-       uniform_array<Uniform4i>(name, n, v);
-}
-
-void ProgramData::uniform4_array(const string &name, unsigned n, const float *v)
-{
-       uniform_array<Uniform4f>(name, n, v);
-}
-
-void ProgramData::uniform_matrix2_array(const string &name, unsigned n, const float *v)
-{
-       uniform_array<UniformMatrix2x2f>(name, n, v);
-}
-
-void ProgramData::uniform_matrix3x2_array(const string &name, unsigned n, const float *v)
-{
-       uniform_array<UniformMatrix3x2f>(name, n, v);
-}
-
-void ProgramData::uniform_matrix4x2_array(const string &name, unsigned n, const float *v)
-{
-       uniform_array<UniformMatrix4x2f>(name, n, v);
-}
-
-void ProgramData::uniform_matrix2x3_array(const string &name, unsigned n, const float *v)
-{
-       uniform_array<UniformMatrix2x3f>(name, n, v);
-}
-
-void ProgramData::uniform_matrix3_array(const string &name, unsigned n, const float *v)
-{
-       uniform_array<UniformMatrix3x3f>(name, n, v);
-}
-
-void ProgramData::uniform_matrix4x3_array(const string &name, unsigned n, const float *v)
-{
-       uniform_array<UniformMatrix4x3f>(name, n, v);
-}
-
-void ProgramData::uniform_matrix2x4_array(const string &name, unsigned n, const float *v)
-{
-       uniform_array<UniformMatrix2x4f>(name, n, v);
-}
-
-void ProgramData::uniform_matrix3x4_array(const string &name, unsigned n, const float *v)
-{
-       uniform_array<UniformMatrix3x4f>(name, n, v);
-}
-
-void ProgramData::uniform_matrix4_array(const string &name, unsigned n, const float *v)
-{
-       uniform_array<UniformMatrix4x4f>(name, n, v);
-}
-
-void ProgramData::remove_uniform(const string &name)
-{
-       vector<NamedUniform>::const_iterator i = lower_bound(uniforms.begin(), uniforms.end(), name, uniform_name_compare);
-       if(i==uniforms.end() || i->name!=name)
-               return;
-
-       delete i->value;
-       uniforms.erase(i);
-
-       dirty = ALL_ONES;
-}
-
-vector<string> ProgramData::get_uniform_names() const
-{
-       vector<string> names;
-       names.reserve(uniforms.size());
-       for(vector<NamedUniform>::const_iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
-               names.push_back(i->name);
-       return names;
-}
-
-const Uniform &ProgramData::get_uniform(const string &name) const
-{
-       int i = find_uniform_index(name);
-       if(i<0)
-               throw key_error(name);
-       return *uniforms[i].value;
-}
-
-const Uniform *ProgramData::find_uniform(const string &name) const
-{
-       int i = find_uniform_index(name);
-       return (i>=0 ? uniforms[i].value : 0);
-}
-
-bool ProgramData::uniform_name_compare(const NamedUniform &nu, const string &name)
-{
-       return nu.name<name;
-}
-
-int ProgramData::find_uniform_index(const string &name) const
-{
-       vector<NamedUniform>::const_iterator i = lower_bound(uniforms.begin(), uniforms.end(), name, uniform_name_compare);
-       return ((i!=uniforms.end() && i->name==name) ? i-uniforms.begin() : -1);
-}
-
-void ProgramData::update_block_uniform_indices(SharedBlock &block, const Program::UniformBlockInfo &info) const
-{
-       UInt8 *indices = block.indices.values;
-       if(info.uniforms.size()>16)
-       {
-               if(block.indices.type_flag==0xFD)
-               {
-                       block.indices.dynamic.values = new UInt8[info.uniforms.size()];
-                       block.indices.type_flag = 0xFE;
-               }
-               indices = block.indices.dynamic.values;
-       }
-
-       block.used = 0;
-       for(unsigned i=0; i<info.uniforms.size(); ++i)
-       {
-               int j = find_uniform_index(info.uniforms[i]->name);
-               if(j>=0)
-               {
-                       indices[i] = j;
-                       if(static_cast<unsigned>(j)<MASK_BITS)
-                               block.used |= 1<<j;
-               }
-               else
-                       indices[i] = 0xFF;
-       }
-
-       block.dirty = block.used;
-}
-
-void ProgramData::update_block(SharedBlock &block, const Program::UniformBlockInfo &info) const
-{
-       const UInt8 *indices = block.get_uniform_indices();
-       for(unsigned i=0; i<info.uniforms.size(); ++i)
-               if(indices[i]!=0xFF)
-                       block.block->attach(*info.uniforms[i], *uniforms[indices[i]].value);
-}
-
-ProgramData::SharedBlock *ProgramData::get_shared_block(const Program::UniformBlockInfo &info) const
-{
-       BlockMap::iterator i = blocks.find(info.layout_hash);
-       if(i==blocks.end())
-       {
-               bool any_found = false;
-               bool all_found = true;
-               for(vector<const Program::UniformInfo *>::const_iterator j=info.uniforms.begin(); j!=info.uniforms.end(); ++j)
-               {
-                       if(find_uniform_index((*j)->name)>=0)
-                               any_found = true;
-                       else
-                               all_found = false;
-               }
-
-               if(!any_found)
-                       return 0;
-               else if(!all_found && info.bind_point>=0)
-               {
-#ifdef DEBUG
-                       IO::print(IO::cerr, "Warning: not all uniforms for block %s are present\n", info.name);
-#else
-                       throw incomplete_uniform_block(info.name);
-#endif
-               }
-
-               UniformBlock *block;
-               if(info.bind_point>=0)
-               {
-                       if(!buffer)
-                               buffer = new Buffer(UNIFORM_BUFFER);
-
-                       block = new UniformBlock(info.data_size);
-                       block->use_buffer(buffer, last_block);
-                       last_block = block;
-               }
-               else
-                       block = new UniformBlock;
-
-               i = blocks.insert(BlockMap::value_type(info.layout_hash, SharedBlock(block))).first;
-               update_block_uniform_indices(i->second, info);
-       }
-
-       return &i->second;
-}
-
-void ProgramData::apply() const
-{
-       const Program *prog = Program::current();
-       if(!prog)
-               throw invalid_operation("ProgramData::apply");
-
-       Program::LayoutHash layout = prog->get_uniform_layout_hash();
-       ProgramUniforms &pu = programs[layout];
-
-       Mask force_dirty = (dirty==ALL_ONES ? ALL_ONES : 0U);
-       Mask affected = (dirty&pu.used) | force_dirty;
-       if(affected|pu.dirty)
-       {
-               /* If the global dirty flag affects this program, add it to per-program
-               dirty flags and clear the global flag.  A previously unseen program will
-               cause this to happen if there's any dirty uniforms. */
-               if(affected)
-               {
-                       for(BlockMap::iterator i=blocks.begin(); i!=blocks.end(); ++i)
-                               i->second.dirty |= (dirty&i->second.used) | force_dirty;
-                       for(ProgramMap::iterator i=programs.begin(); i!=programs.end(); ++i)
-                               i->second.dirty |= (dirty&i->second.used) | force_dirty;
-                       dirty = 0;
-               }
-
-               const Program::UniformBlockMap &prog_blocks = prog->get_uniform_blocks();
-
-               UniformBlock *old_last_block = last_block;
-               if(pu.dirty==ALL_ONES)
-               {
-                       /* The set of uniforms has changed since this program was last used.
-                       Regenerate the list of uniform blocks. */
-                       pu.blocks.clear();
-                       pu.blocks.reserve(prog_blocks.size());
-
-                       pu.used = 0;
-                       for(Program::UniformBlockMap::const_iterator i=prog_blocks.begin(); i!=prog_blocks.end(); ++i)
-                       {
-                               SharedBlock *shared = get_shared_block(i->second);
-                               if(shared)
-                               {
-                                       if(shared->dirty==ALL_ONES)
-                                               update_block_uniform_indices(*shared, i->second);
-                                       pu.used |= shared->used;
-                               }
-
-                               pu.blocks.push_back(ProgramBlock(i->second.bind_point, shared));
-                       }
-               }
-
-               // Update the contents of all dirty blocks.
-               bool buffered_blocks_updated = false;
-               std::vector<ProgramBlock>::iterator j = pu.blocks.begin();
-               for(Program::UniformBlockMap::const_iterator i=prog_blocks.begin(); i!=prog_blocks.end(); ++i, ++j)
-               {
-                       if(!j->shared || !j->shared->dirty)
-                               continue;
-
-                       update_block(*j->shared, i->second);
-                       j->shared->dirty = 0;
-                       buffered_blocks_updated |= (j->bind_point>=0);
-               }
-
-               pu.dirty = 0;
-
-               /* If any blocks stored in the buffer were updated, bind the buffer here
-               to avoid state thrashing. */
-               if(buffered_blocks_updated && !ARB_direct_state_access)
-                       buffer->bind();
-
-               if(last_block!=old_last_block)
-               {
-                       unsigned required_size = last_block->get_required_buffer_size();
-                       if(last_block->get_required_buffer_size()>buffer->get_size())
-                       {
-                               if(buffer->get_size()>0)
-                               {
-                                       delete buffer;
-                                       buffer = new Buffer(UNIFORM_BUFFER);
-                                       last_block->change_buffer(buffer);
-                               }
-
-                               buffer->storage(required_size);
-                       }
-               }
-       }
-
-       for(vector<ProgramBlock>::iterator i=pu.blocks.begin(); i!=pu.blocks.end(); ++i)
-               if(i->block)
-                       i->block->apply(i->bind_point);
-}
-
-
-ProgramData::NamedUniform::NamedUniform():
-       value(0)
-{ }
-
-void ProgramData::NamedUniform::replace_value(Uniform *v)
-{
-       /* UniformBlock does not copy the uniforms, so existing default blocks
-       will be left with stale pointers.  This is not a problem as long as no
-       one stores pointers to the blocks and expects them to stay valid. */
-       delete value;
-       value = v;
-}
-
-
-ProgramData::SharedBlock::SharedBlock(UniformBlock *b):
-       used(0),
-       dirty(0),
-       block(b)
-{
-       indices.type_flag = 0xFD;
-}
-
-const UInt8 *ProgramData::SharedBlock::get_uniform_indices() const
-{
-       return (indices.type_flag==0xFE ? indices.dynamic.values : indices.values);
-}
-
-
-ProgramData::ProgramBlock::ProgramBlock():
-       bind_point(-1),
-       block(0),
-       shared(0)
-{ }
-
-ProgramData::ProgramBlock::ProgramBlock(int p, SharedBlock *b):
-       bind_point(p),
-       block((b && b->used) ? b->block : 0),
-       shared(b)
-{ }
-
-
-ProgramData::ProgramUniforms::ProgramUniforms():
-       used(ALL_ONES),
-       dirty(ALL_ONES)
-{ }
-
-
-ProgramData::Loader::Loader(ProgramData &pd):
-       DataFile::ObjectLoader<ProgramData>(pd)
-{
-       add("uniform", &Loader::uniform1i);
-       add("uniform1i", &Loader::uniform1i);
-       add("uniform", &Loader::uniform1f);
-       add("uniform1f", &Loader::uniform1f);
-       add("uniform", &Loader::uniform2i);
-       add("uniform2i", &Loader::uniform2i);
-       add("uniform", &Loader::uniform2f);
-       add("uniform2f", &Loader::uniform2f);
-       add("uniform", &Loader::uniform3i);
-       add("uniform3i", &Loader::uniform3i);
-       add("uniform", &Loader::uniform3f);
-       add("uniform3f", &Loader::uniform3f);
-       add("uniform", &Loader::uniform4i);
-       add("uniform4i", &Loader::uniform4i);
-       add("uniform", &Loader::uniform4f);
-       add("uniform4f", &Loader::uniform4f);
-       add("uniform1i_array", &Loader::uniform1i_array);
-       add("uniform1f_array", &Loader::uniform1f_array);
-       add("uniform2f_array", &Loader::uniform2f_array);
-       add("uniform3f_array", &Loader::uniform3f_array);
-       add("uniform4f_array", &Loader::uniform4f_array);
-       add("uniform_array", &Loader::uniform_array);
-}
-
-void ProgramData::Loader::uniform1i(const string &n, int v)
-{
-       obj.uniform(n, v);
-}
-
-void ProgramData::Loader::uniform1f(const string &n, float v)
-{
-       obj.uniform(n, v);
-}
-
-void ProgramData::Loader::uniform2i(const string &n, int v0, int v1)
-{
-       obj.uniform(n, v0, v1);
-}
-
-void ProgramData::Loader::uniform2f(const string &n, float v0, float v1)
-{
-       obj.uniform(n, v0, v1);
-}
-
-void ProgramData::Loader::uniform3i(const string &n, int v0, int v1, int v2)
-{
-       obj.uniform(n, v0, v1, v2);
-}
-
-void ProgramData::Loader::uniform3f(const string &n, float v0, float v1, float v2)
-{
-       obj.uniform(n, v0, v1, v2);
-}
-
-void ProgramData::Loader::uniform4i(const string &n, int v0, int v1, int v2, int v3)
-{
-       obj.uniform(n, v0, v1, v2, v3);
-}
-
-void ProgramData::Loader::uniform4f(const string &n, float v0, float v1, float v2, float v3)
-{
-       obj.uniform(n, v0, v1, v2, v3);
-}
-
-void ProgramData::Loader::uniform_array_(const string &n, DataType t, unsigned e)
-{
-       ArrayLoader ldr(t, e);
-       load_sub_with(ldr);
-       unsigned size = ldr.get_size();
-       if(!size)
-               throw logic_error("empty uniform array");
-
-       DataType type = ldr.get_data_type();
-       unsigned elem_size = ldr.get_element_size();
-       if(type==INT)
-       {
-               const int *data = reinterpret_cast<const int *>(ldr.get_data());
-               if(elem_size==1)
-                       obj.uniform1_array(n, size, data);
-               else if(elem_size==2)
-                       obj.uniform2_array(n, size, data);
-               else if(elem_size==3)
-                       obj.uniform3_array(n, size, data);
-               else if(elem_size==4)
-                       obj.uniform4_array(n, size, data);
-               else
-                       throw logic_error("unsupported combination of array type and element size");
-       }
-       else if(type==FLOAT)
-       {
-               const float *data = reinterpret_cast<const float *>(ldr.get_data());
-               if(elem_size==1)
-                       obj.uniform1_array(n, size, data);
-               else if(elem_size==2)
-                       obj.uniform2_array(n, size, data);
-               else if(elem_size==3)
-                       obj.uniform3_array(n, size, data);
-               else if(elem_size==4)
-                       obj.uniform4_array(n, size, data);
-               else
-                       throw logic_error("unsupported combination of array type and element size");
-       }
-       else
-               throw logic_error("unsupported array type");
-}
-
-void ProgramData::Loader::uniform1i_array(const string &n)
-{
-       uniform_array_(n, INT, 1);
-}
-
-void ProgramData::Loader::uniform1f_array(const string &n)
-{
-       uniform_array_(n, FLOAT, 1);
-}
-
-void ProgramData::Loader::uniform2i_array(const string &n)
-{
-       uniform_array_(n, INT, 2);
-}
-
-void ProgramData::Loader::uniform2f_array(const string &n)
-{
-       uniform_array_(n, FLOAT, 2);
-}
-
-void ProgramData::Loader::uniform3i_array(const string &n)
-{
-       uniform_array_(n, INT, 3);
-}
-
-void ProgramData::Loader::uniform3f_array(const string &n)
-{
-       uniform_array_(n, FLOAT, 3);
-}
-
-void ProgramData::Loader::uniform4i_array(const string &n)
-{
-       uniform_array_(n, INT, 4);
-}
-
-void ProgramData::Loader::uniform4f_array(const string &n)
-{
-       uniform_array_(n, FLOAT, 4);
-}
-
-void ProgramData::Loader::uniform_array(const string &n)
-{
-       uniform_array_(n, static_cast<DataType>(0), 0);
-}
-
-
-ProgramData::ArrayLoader::ArrayLoader(DataType t, unsigned e):
-       type(t),
-       element_size(e)
-{
-       add("uniform", &ArrayLoader::uniform1i);
-       add("uniform1i", &ArrayLoader::uniform1i);
-       add("uniform", &ArrayLoader::uniform1f);
-       add("uniform1f", &ArrayLoader::uniform1f);
-       add("uniform", &ArrayLoader::uniform2f);
-       add("uniform2f", &ArrayLoader::uniform2f);
-       add("uniform", &ArrayLoader::uniform3f);
-       add("uniform3f", &ArrayLoader::uniform3f);
-       add("uniform", &ArrayLoader::uniform4f);
-       add("uniform4f", &ArrayLoader::uniform4f);
-}
-
-void ProgramData::ArrayLoader::uniform(DataType t, unsigned e, const void *v)
-{
-       if(element_size && (t!=type || e!=element_size))
-               throw logic_error("heterogeneous array contents");
-
-       if(!element_size)
-       {
-               type = t;
-               element_size = e;
-       }
-
-       const char *cv = reinterpret_cast<const char *>(v);
-       data.insert(data.end(), cv, cv+element_size*4);
-}
-
-void ProgramData::ArrayLoader::uniform1i(int v)
-{
-       uniform(INT, 1, &v);
-}
-
-void ProgramData::ArrayLoader::uniform1f(float v)
-{
-       uniform(FLOAT, 1, &v);
-}
-
-void ProgramData::ArrayLoader::uniform2i(int v0, int v1)
-{
-       int va[2] = { v0, v1 };
-       uniform(INT, 2, va);
-}
-
-void ProgramData::ArrayLoader::uniform2f(float v0, float v1)
-{
-       float va[2] = { v0, v1 };
-       uniform(FLOAT, 2, va);
-}
-
-void ProgramData::ArrayLoader::uniform3i(int v0, int v1, int v2)
-{
-       int va[3] = { v0, v1, v2 };
-       uniform(INT, 3, va);
-}
-
-void ProgramData::ArrayLoader::uniform3f(float v0, float v1, float v2)
-{
-       float va[3] = { v0, v1, v2 };
-       uniform(FLOAT, 3, va);
-}
-
-void ProgramData::ArrayLoader::uniform4i(int v0, int v1, int v2, int v3)
-{
-       int va[4] = { v0, v1, v2, v3 };
-       uniform(INT, 4, va);
-}
-
-void ProgramData::ArrayLoader::uniform4f(float v0, float v1, float v2, float v3)
-{
-       float va[4] = { v0, v1, v2, v3 };
-       uniform(FLOAT, 4, va);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/programdata.h b/source/programdata.h
deleted file mode 100644 (file)
index e96a73a..0000000
+++ /dev/null
@@ -1,254 +0,0 @@
-#ifndef MSP_GL_PROGRAMDATA_H_
-#define MSP_GL_PROGRAMDATA_H_
-
-#include <map>
-#include <stdexcept>
-#include <msp/datafile/objectloader.h>
-#include "datatype.h"
-#include "matrix.h"
-#include "program.h"
-#include "vector.h"
-
-namespace Msp {
-namespace GL {
-
-class too_many_uniforms: public std::runtime_error
-{
-public:
-       too_many_uniforms(const std::string &w): std::runtime_error(w) { }
-       virtual ~too_many_uniforms() throw() { }
-};
-
-class Buffer;
-class Uniform;
-class UniformBlock;
-struct Color;
-
-/**
-Stores uniform variables for shader programs.  The uniforms are stored in a
-program-independent way, and UniformBlocks are created to match the uniform
-layouts of different programs.  If multiple programs have the same layout, the
-same block is used for them.
-
-The class is optimized for an access pattern where the set of uniforms and
-programs stays constants, with only the values changing.
-*/
-class ProgramData
-{
-public:
-       class Loader: public DataFile::ObjectLoader<ProgramData>
-       {
-       public:
-               Loader(ProgramData &);
-       private:
-               void uniform1i(const std::string &, int);
-               void uniform1f(const std::string &, float);
-               void uniform2i(const std::string &, int, int);
-               void uniform2f(const std::string &, float, float);
-               void uniform3i(const std::string &, int, int, int);
-               void uniform3f(const std::string &, float, float, float);
-               void uniform4i(const std::string &, int, int, int, int);
-               void uniform4f(const std::string &, float, float, float, float);
-               void uniform_array_(const std::string &, DataType, unsigned);
-               void uniform1i_array(const std::string &);
-               void uniform1f_array(const std::string &);
-               void uniform2i_array(const std::string &);
-               void uniform2f_array(const std::string &);
-               void uniform3i_array(const std::string &);
-               void uniform3f_array(const std::string &);
-               void uniform4i_array(const std::string &);
-               void uniform4f_array(const std::string &);
-               void uniform_array(const std::string &);
-       };
-
-private:
-       class ArrayLoader: public DataFile::Loader
-       {
-       private:
-               DataType type;
-               unsigned element_size;
-               std::vector<char> data;
-
-       public:
-               ArrayLoader(DataType, unsigned);
-
-               DataType get_data_type() const { return type; }
-               unsigned get_element_size() const { return element_size; }
-               const void *get_data() const { return &data[0]; }
-               unsigned get_size() const { return data.size()/(4*element_size); }
-
-       private:
-               void uniform(DataType, unsigned, const void *);
-               void uniform1i(int);
-               void uniform1f(float);
-               void uniform2i(int, int);
-               void uniform2f(float, float);
-               void uniform3i(int, int, int);
-               void uniform3f(float, float, float);
-               void uniform4i(int, int, int, int);
-               void uniform4f(float, float, float, float);
-       };
-
-       typedef unsigned Mask;
-
-       enum
-       {
-               MASK_BITS = sizeof(Mask)*8,
-               ALL_ONES = static_cast<Mask>(-1)
-       };
-
-       struct NamedUniform
-       {
-               std::string name;
-               Uniform *value;
-
-               NamedUniform();
-
-               bool compare_name(const std::string &, unsigned) const;
-               void replace_value(Uniform *);
-       };
-
-       struct SharedBlock
-       {
-               Mask used;
-               Mask dirty;
-               UniformBlock *block;
-               union
-               {
-                       UInt8 type_flag;
-                       UInt8 values[16];
-                       struct
-                       {
-                               UInt8 type_flag;
-                               UInt8 *values;
-                       } dynamic;
-               } indices;
-
-               SharedBlock(UniformBlock *);
-
-               const UInt8 *get_uniform_indices() const;
-       };
-
-       struct ProgramBlock
-       {
-               int bind_point;
-               UniformBlock *block;
-               SharedBlock *shared;
-
-               ProgramBlock();
-               ProgramBlock(int, SharedBlock *);
-       };
-
-       struct ProgramUniforms
-       {
-               std::vector<ProgramBlock> blocks;
-               Mask used;
-               Mask dirty;
-
-               ProgramUniforms();
-       };
-
-       typedef std::map<Program::LayoutHash, SharedBlock> BlockMap;
-       typedef std::map<Program::LayoutHash, ProgramUniforms> ProgramMap;
-
-       // XXX All these mutables are a bit silly, but I'm out of better ideas
-       const Program *tied_program;
-       std::vector<NamedUniform> uniforms;
-       mutable BlockMap blocks;
-       mutable ProgramMap programs;
-       mutable UniformBlock *last_block;
-       mutable Buffer *buffer;
-       mutable unsigned dirty;
-
-public:
-       ProgramData(const Program * = 0);
-       ProgramData(const ProgramData &);
-       ProgramData(const ProgramData &, const Program *);
-       ProgramData &operator=(const ProgramData &);
-       ~ProgramData();
-
-private:
-       void uniform(const std::string &, Uniform *);
-       template<typename T, typename V>
-       void uniform(const std::string &, V);
-       template<typename T, typename V>
-       void uniform_array(const std::string &, unsigned, V);
-       bool validate_name(const std::string &) const;
-       void add_uniform(const std::string &, Uniform *);
-public:
-       void uniform(const std::string &, const Uniform &);
-       void uniform(const std::string &, int);
-       void uniform(const std::string &, float);
-       void uniform(const std::string &, int, int);
-       void uniform(const std::string &, float, float);
-       void uniform2(const std::string &, const int *);
-       void uniform2(const std::string &, const float *);
-       void uniform(const std::string &, int, int, int);
-       void uniform(const std::string &, float, float, float);
-       void uniform(const std::string &, const Vector3 &);
-       void uniform3(const std::string &, const int *);
-       void uniform3(const std::string &, const float *);
-       void uniform(const std::string &, int, int, int, int);
-       void uniform(const std::string &, float, float, float, float);
-       void uniform(const std::string &, const Vector4 &);
-       void uniform(const std::string &, const Color &);
-       void uniform4(const std::string &, const int *);
-       void uniform4(const std::string &, const float *);
-       void uniform(const std::string &, const LinAl::Matrix<float, 2, 2> &);
-       void uniform_matrix2(const std::string &, const float *);
-       void uniform(const std::string &, const LinAl::Matrix<float, 2, 3> &);
-       void uniform_matrix3x2(const std::string &, const float *);
-       void uniform(const std::string &, const LinAl::Matrix<float, 2, 4> &);
-       void uniform_matrix4x2(const std::string &, const float *);
-       void uniform(const std::string &, const LinAl::Matrix<float, 3, 2> &);
-       void uniform_matrix2x3(const std::string &, const float *);
-       void uniform(const std::string &, const LinAl::Matrix<float, 3, 3> &);
-       void uniform_matrix3(const std::string &, const float *);
-       void uniform(const std::string &, const LinAl::Matrix<float, 3, 4> &);
-       void uniform_matrix4x3(const std::string &, const float *);
-       void uniform(const std::string &, const LinAl::Matrix<float, 4, 2> &);
-       void uniform_matrix2x4(const std::string &, const float *);
-       void uniform(const std::string &, const LinAl::Matrix<float, 4, 3> &);
-       void uniform_matrix3x4(const std::string &, const float *);
-       void uniform(const std::string &, const Matrix &);
-       void uniform_matrix4(const std::string &, const float *);
-       void uniform1_array(const std::string &, unsigned, const int *);
-       void uniform1_array(const std::string &, unsigned, const float *);
-       void uniform2_array(const std::string &, unsigned, const int *);
-       void uniform2_array(const std::string &, unsigned, const float *);
-       void uniform3_array(const std::string &, unsigned, const int *);
-       void uniform3_array(const std::string &, unsigned, const float *);
-       void uniform4_array(const std::string &, unsigned, const int *);
-       void uniform4_array(const std::string &, unsigned, const float *);
-       void uniform_matrix2_array(const std::string &, unsigned, const float *);
-       void uniform_matrix3x2_array(const std::string &, unsigned, const float *);
-       void uniform_matrix4x2_array(const std::string &, unsigned, const float *);
-       void uniform_matrix2x3_array(const std::string &, unsigned, const float *);
-       void uniform_matrix3_array(const std::string &, unsigned, const float *);
-       void uniform_matrix4x3_array(const std::string &, unsigned, const float *);
-       void uniform_matrix2x4_array(const std::string &, unsigned, const float *);
-       void uniform_matrix3x4_array(const std::string &, unsigned, const float *);
-       void uniform_matrix4_array(const std::string &, unsigned, const float *);
-       void remove_uniform(const std::string &);
-
-       std::vector<std::string> get_uniform_names() const;
-       const Uniform &get_uniform(const std::string &) const;
-       const Uniform *find_uniform(const std::string &) const;
-
-private:
-       static bool uniform_name_compare(const NamedUniform &, const std::string &);
-       int find_uniform_index(const std::string &) const;
-       void update_block_uniform_indices(SharedBlock &, const Program::UniformBlockInfo &) const;
-       void update_block(SharedBlock &, const Program::UniformBlockInfo &) const;
-       SharedBlock *get_shared_block(const Program::UniformBlockInfo &) const;
-
-public:
-       /** Applies uniform blocks for the currently bound program, creating them
-       if needed. */
-       void apply() const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/programparser.cpp b/source/programparser.cpp
deleted file mode 100644 (file)
index 83a2879..0000000
+++ /dev/null
@@ -1,962 +0,0 @@
-#include <msp/core/raii.h>
-#include <msp/strings/format.h>
-#include <msp/strings/regex.h>
-#include "programparser.h"
-
-#undef interface
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-using namespace ProgramSyntax;
-
-ProgramParser::Operator ProgramParser::operators[] =
-{
-       { "[", 2, BINARY, LEFT_TO_RIGHT },
-       { "(", 2, BINARY, LEFT_TO_RIGHT },
-       { ".", 2, BINARY, LEFT_TO_RIGHT },
-       { "++", 2, POSTFIX, LEFT_TO_RIGHT },
-       { "--", 2, POSTFIX, LEFT_TO_RIGHT },
-       { "++", 3, PREFIX, RIGHT_TO_LEFT },
-       { "--", 3, PREFIX, RIGHT_TO_LEFT },
-       { "+", 3, PREFIX, RIGHT_TO_LEFT },
-       { "-", 3, PREFIX, RIGHT_TO_LEFT },
-       { "~", 3, PREFIX, RIGHT_TO_LEFT },
-       { "!", 3, PREFIX, RIGHT_TO_LEFT },
-       { "*", 4, BINARY, LEFT_TO_RIGHT },
-       { "/", 4, BINARY, LEFT_TO_RIGHT },
-       { "%", 4, BINARY, LEFT_TO_RIGHT },
-       { "+", 5, BINARY, LEFT_TO_RIGHT },
-       { "-", 5, BINARY, LEFT_TO_RIGHT },
-       { "<<", 6, BINARY, LEFT_TO_RIGHT },
-       { ">>", 6, BINARY, LEFT_TO_RIGHT },
-       { "<", 7, BINARY, LEFT_TO_RIGHT },
-       { ">", 7, BINARY, LEFT_TO_RIGHT },
-       { "<=", 7, BINARY, LEFT_TO_RIGHT },
-       { ">=", 7, BINARY, LEFT_TO_RIGHT },
-       { "==", 8, BINARY, LEFT_TO_RIGHT },
-       { "!=", 8, BINARY, LEFT_TO_RIGHT },
-       { "&", 9, BINARY, LEFT_TO_RIGHT },
-       { "^", 10, BINARY, LEFT_TO_RIGHT },
-       { "|", 11, BINARY, LEFT_TO_RIGHT },
-       { "&&", 12, BINARY, LEFT_TO_RIGHT },
-       { "^^", 13, BINARY, LEFT_TO_RIGHT },
-       { "||", 14, BINARY, LEFT_TO_RIGHT },
-       { "?", 15, BINARY, RIGHT_TO_LEFT },
-       { ":", 15, BINARY, RIGHT_TO_LEFT },
-       { "=", 16, BINARY, RIGHT_TO_LEFT },
-       { "+=", 16, BINARY, RIGHT_TO_LEFT },
-       { "-=", 16, BINARY, RIGHT_TO_LEFT },
-       { "*=", 16, BINARY, RIGHT_TO_LEFT },
-       { "/=", 16, BINARY, RIGHT_TO_LEFT },
-       { "%=", 16, BINARY, RIGHT_TO_LEFT },
-       { "<<=", 16, BINARY, RIGHT_TO_LEFT },
-       { ">>=", 16, BINARY, RIGHT_TO_LEFT },
-       { "&=", 16, BINARY, RIGHT_TO_LEFT },
-       { "^=", 16, BINARY, RIGHT_TO_LEFT },
-       { "|=", 16, BINARY, RIGHT_TO_LEFT },
-       { ",", 17, BINARY, LEFT_TO_RIGHT },
-       { { 0 }, 18, NO_OPERATOR, LEFT_TO_RIGHT }
-};
-
-ProgramParser::ProgramParser():
-       module(0)
-{ }
-
-ProgramParser::~ProgramParser()
-{
-       delete module;
-}
-
-Module &ProgramParser::parse(const string &s, const string &n, unsigned i)
-{
-       source = s;
-       source_name = n;
-       source_index = i;
-       parse_source();
-       return *module;
-}
-
-Module &ProgramParser::parse(IO::Base &io, const string &n, unsigned i)
-{
-       source = string();
-       source_name = n;
-       source_index = i;
-       while(!io.eof())
-       {
-               char buffer[4096];
-               unsigned len = io.read(buffer, sizeof(buffer));
-               source.append(buffer, len);
-       }
-       parse_source();
-       return *module;
-}
-
-void ProgramParser::parse_source()
-{
-       while(1)
-       {
-               string::size_type slashes = source.find("//////");
-               if(slashes==string::npos)
-                       break;
-
-               string::size_type newline = source.find('\n', slashes);
-               string pragma = format("#pragma MSP stage(%s)", source.substr(slashes+6, newline-slashes-6));
-               source.replace(slashes, newline-slashes, pragma);
-       }
-
-       delete module;
-       module = new Module;
-       cur_stage = &module->shared;
-       iter = source.begin();
-       source_end = source.end();
-       current_line = 1;
-       allow_preprocess = true;
-       while(RefPtr<Statement> statement = parse_global_declaration())
-               cur_stage->content.body.push_back(statement);
-}
-
-string ProgramParser::format_error(const std::string &message)
-{
-       string location = format("%s:%d: ", source_name, current_line);
-       return location+message;
-}
-
-string ProgramParser::format_syntax_error(const std::string &expected)
-{
-       return format_error(format("Syntax error at '%s': expected %s", last_token, expected));
-}
-
-const string &ProgramParser::peek_token(unsigned index)
-{
-       while(next_tokens.size()<=index)
-               next_tokens.push_back(parse_token_());
-       return (last_token = next_tokens[index]);
-}
-
-const string &ProgramParser::parse_token()
-{
-       if(!next_tokens.empty())
-       {
-               last_token = next_tokens.front();
-               next_tokens.pop_front();
-               return last_token;
-       }
-
-       return (last_token = parse_token_());
-}
-
-string ProgramParser::parse_token_()
-{
-       while(1)
-       {
-               skip_comment_and_whitespace();
-               if(iter==source_end)
-                       return string();
-               else if(allow_preprocess && *iter=='#')
-               {
-                       allow_preprocess = false;
-                       SetForScope<deque<string> > clear_tokens(next_tokens, deque<string>());
-                       preprocess();
-               }
-               else if(isalpha(*iter) || *iter=='_')
-                       return parse_identifier();
-               else if(isdigit(*iter))
-                       return parse_number();
-               else
-                       return parse_other();
-       }
-}
-
-string ProgramParser::parse_identifier()
-{
-       string ident;
-       while(iter!=source_end)
-       {
-               if(isalnum(*iter) || *iter=='_')
-                       ident += *iter++;
-               else
-                       break;
-       }
-
-       return ident;
-}
-
-string ProgramParser::parse_number()
-{
-       bool accept_sign = false;
-       string number;
-       while(iter!=source_end)
-       {
-               if(isdigit(*iter) || *iter=='.')
-                       number += *iter++;
-               else if(*iter=='e' || *iter=='E')
-               {
-                       number += *iter++;
-                       accept_sign = true;
-               }
-               else if(accept_sign && (*iter=='+' || *iter=='-'))
-                       number += *iter++;
-               else
-                       break;
-       }
-
-       return number;
-}
-
-string ProgramParser::parse_other()
-{
-       if(iter==source_end)
-               return string();
-
-       string token(1, *iter++);
-       for(unsigned i=1; (i<3 && iter!=source_end); ++i)
-       {
-               bool matched = false;
-               for(const Operator *j=operators; (!matched && j->type); ++j)
-               {
-                       matched = (j->token[i]==*iter);
-                       for(unsigned k=0; (matched && k<i && j->token[k]); ++k)
-                               matched = (j->token[k]==token[k]);
-               }
-
-               if(!matched)
-                       break;
-
-               token += *iter++;
-       }
-
-       return token;
-}
-
-void ProgramParser::skip_comment_and_whitespace()
-{
-       unsigned comment = 0;
-       while(iter!=source_end)
-       {
-               if(comment==0)
-               {
-                       if(*iter=='/')
-                               comment = 1;
-                       else if(!isspace(*iter))
-                               break;
-               }
-               else if(comment==1)
-               {
-                       if(*iter=='/')
-                               comment = 2;
-                       else if(*iter=='*')
-                               comment = 3;
-                       else
-                       {
-                               comment = 0;
-                               --iter;
-                               break;
-                       }
-               }
-               else if(comment==2)
-               {
-                       if(*iter=='\n')
-                               comment = 0;
-               }
-               else if(comment==3 && *iter=='*')
-                       comment = 4;
-               else if(comment==4)
-               {
-                       if(*iter=='/')
-                               comment = 0;
-                       else if(*iter!='*')
-                               comment = 3;
-               }
-
-               if(*iter=='\n')
-               {
-                       ++current_line;
-                       allow_preprocess = (comment<3);
-               }
-
-               ++iter;
-       }
-}
-
-void ProgramParser::expect(const string &token)
-{
-       string parsed = parse_token();
-       if(parsed!=token)
-               throw runtime_error(format_syntax_error(format("'%s'", token)));
-}
-
-string ProgramParser::expect_type()
-{
-       string token = parse_token();
-       if(!is_type(token))
-               throw runtime_error(format_syntax_error("a type"));
-       return token;
-}
-
-string ProgramParser::expect_identifier()
-{
-       string token = parse_token();
-       if(!is_identifier(token))
-               throw runtime_error(format_syntax_error("an identifier"));
-       return token;
-}
-
-bool ProgramParser::check(const string &token)
-{
-       bool result = (peek_token()==token);
-       if(result)
-               parse_token();
-       return result;
-}
-
-bool ProgramParser::is_interface_qualifier(const string &token)
-{
-       return (token=="uniform" || token=="in" || token=="out");
-}
-
-bool ProgramParser::is_sampling_qualifier(const string &token)
-{
-       return (token=="centroid" || token=="sample");
-}
-
-bool ProgramParser::is_interpolation_qualifier(const string &token)
-{
-       return (token=="smooth" || token=="flat" || token=="noperspective");
-}
-
-bool ProgramParser::is_precision_qualifier(const string &token)
-{
-       return (token=="highp" || token=="mediump" || token=="lowp");
-}
-
-bool ProgramParser::is_qualifier(const string &token)
-{
-       return (token=="const" ||
-               is_interface_qualifier(token) ||
-               is_sampling_qualifier(token) ||
-               is_interpolation_qualifier(token) ||
-               is_precision_qualifier(token));
-}
-
-bool ProgramParser::is_builtin_type(const string &token)
-{
-       static Regex re("^(void|float|int|bool|[ib]?vec[234]|mat[234](x[234])?|sampler((1D|2D|Cube)(Array)?(Shadow)?|3D))$");
-       return re.match(token);
-}
-
-bool ProgramParser::is_type(const string &token)
-{
-       return is_builtin_type(token) || declared_types.count(token);
-}
-
-bool ProgramParser::is_identifier(const string &token)
-{
-       static Regex re("^[a-zA-Z_][a-zA-Z0-9_]*$");
-       return re.match(token);
-}
-
-void ProgramParser::preprocess()
-{
-       expect("#");
-
-       string::const_iterator line_end = iter;
-       for(; (line_end!=source_end && *line_end!='\n'); ++line_end) ;
-       SetForScope<string::const_iterator> stop_at_line_end(source_end, line_end);
-
-       string token = peek_token();
-       if(token=="pragma")
-               preprocess_pragma();
-       else if(token=="version")
-               preprocess_version();
-       else if(token=="define" || token=="undef" || token=="if" || token=="ifdef" || token=="ifndef" || token=="else" ||
-               token=="elif" || token=="endif" || token=="error" || token=="extension" || token=="line")
-               throw runtime_error(format_error(format("Unsupported preprocessor directive '%s'", token)));
-       else if(!token.empty())
-               throw runtime_error(format_syntax_error("a preprocessor directive"));
-
-       iter = line_end;
-}
-
-void ProgramParser::preprocess_version()
-{
-       expect("version");
-       string token = parse_token();
-       unsigned version = lexical_cast<unsigned>(token);
-       cur_stage->required_version = Version(version/100, version%100);
-
-       token = parse_token();
-       if(!token.empty())
-               throw runtime_error(format_syntax_error("end of line"));
-}
-
-void ProgramParser::preprocess_pragma()
-{
-       expect("pragma");
-       string token = parse_token();
-       if(token=="MSP")
-               preprocess_pragma_msp();
-}
-
-void ProgramParser::preprocess_pragma_msp()
-{
-       string token = peek_token();
-       if(token=="stage")
-               preprocess_stage();
-       else
-               throw runtime_error(format_error(format("Unrecognized MSP pragma '%s'", token)));
-
-       token = parse_token();
-       if(!token.empty())
-               throw runtime_error(format_syntax_error("end of line"));
-}
-
-void ProgramParser::preprocess_stage()
-{
-       if(!allow_stage_change)
-               throw runtime_error(format_error("Changing stage not allowed here"));
-
-       expect("stage");
-       expect("(");
-       string token = expect_identifier();
-       StageType stage = SHARED;
-       if(token=="vertex")
-               stage = VERTEX;
-       else if(token=="geometry")
-               stage = GEOMETRY;
-       else if(token=="fragment")
-               stage = FRAGMENT;
-       else
-               throw runtime_error(format_syntax_error("stage identifier"));
-       expect(")");
-
-       if(stage<=cur_stage->type)
-               throw runtime_error(format_error(format("Stage '%s' not allowed here", token)));
-
-       module->stages.push_back(stage);
-
-       if(cur_stage->type!=SHARED)
-               module->stages.back().previous = cur_stage;
-       cur_stage = &module->stages.back();
-}
-
-RefPtr<Statement> ProgramParser::parse_global_declaration()
-{
-       allow_stage_change = true;
-       string token = peek_token();
-       allow_stage_change = false;
-
-       if(token=="import")
-               return parse_import();
-       else if(token=="precision")
-               return parse_precision();
-       else if(token=="layout")
-       {
-               RefPtr<Layout> layout = parse_layout();
-               token = peek_token();
-               if(is_interface_qualifier(token) && peek_token(1)==";")
-               {
-                       RefPtr<InterfaceLayout> iface_lo = new InterfaceLayout;
-                       iface_lo->source = source_index;
-                       iface_lo->line = current_line;
-                       iface_lo->layout.qualifiers = layout->qualifiers;
-                       iface_lo->interface = parse_token();
-                       expect(";");
-                       return iface_lo;
-               }
-               else
-               {
-                       RefPtr<VariableDeclaration> var = parse_variable_declaration();
-                       var->layout = layout;
-                       return var;
-               }
-       }
-       else if(token=="struct")
-               return parse_struct_declaration();
-       else if(is_interface_qualifier(token))
-       {
-               string next = peek_token(1);
-               if(is_type(next) || is_qualifier(next))
-                       return parse_variable_declaration();
-               else
-                       return parse_interface_block();
-       }
-       else if(is_qualifier(token))
-               return parse_variable_declaration();
-       else if(is_type(token))
-       {
-               if(peek_token(2)=="(")
-                       return parse_function_declaration();
-               else
-                       return parse_variable_declaration();
-       }
-       else if(token.empty())
-               return 0;
-       else
-               throw runtime_error(format_syntax_error("a global declaration"));
-}
-
-RefPtr<Statement> ProgramParser::parse_statement()
-{
-       string token = peek_token();
-       if(token=="if")
-               return parse_conditional();
-       else if(token=="for")
-               return parse_for();
-       else if(token=="while")
-               return parse_while();
-       else if(token=="passthrough")
-               return parse_passthrough();
-       else if(token=="return")
-               return parse_return();
-       else if(token=="break" || token=="continue" || token=="discard")
-       {
-               RefPtr<Jump> jump = new Jump;
-               jump->source = source_index;
-               jump->line = current_line;
-               jump->keyword = parse_token();
-               expect(";");
-
-               return jump;
-       }
-       else if(is_qualifier(token) || is_type(token))
-               return parse_variable_declaration();
-       else if(!token.empty())
-       {
-               RefPtr<ExpressionStatement> expr = new ExpressionStatement;
-               expr->source = source_index;
-               expr->line = current_line;
-               expr->expression = parse_expression();
-               expect(";");
-
-               return expr;
-       }
-       else
-               throw runtime_error(format_syntax_error("a statement"));
-}
-
-RefPtr<Import> ProgramParser::parse_import()
-{
-       if(cur_stage->type!=SHARED)
-               throw runtime_error(format_error("Imports are only allowed in the shared section"));
-
-       expect("import");
-       RefPtr<Import> import = new Import;
-       import->source = source_index;
-       import->line = current_line;
-       import->module = expect_identifier();
-       expect(";");
-       return import;
-}
-
-RefPtr<Precision> ProgramParser::parse_precision()
-{
-       expect("precision");
-       RefPtr<Precision> precision = new Precision;
-       precision->source = source_index;
-       precision->line = current_line;
-
-       precision->precision = parse_token();
-       if(!is_precision_qualifier(precision->precision))
-               throw runtime_error(format_syntax_error("a precision qualifier"));
-
-       precision->type = parse_token();
-       // Not entirely accurate; only float, int and sampler types are allowed
-       if(!is_builtin_type(precision->type))
-               throw runtime_error(format_syntax_error("a builtin type"));
-
-       expect(";");
-
-       return precision;
-}
-
-RefPtr<Layout> ProgramParser::parse_layout()
-{
-       expect("layout");
-       expect("(");
-       RefPtr<Layout> layout = new Layout;
-       while(1)
-       {
-               string token = parse_token();
-               if(token==")")
-                       throw runtime_error(format_syntax_error("a layout qualifier name"));
-
-               layout->qualifiers.push_back(Layout::Qualifier());
-               Layout::Qualifier &qual = layout->qualifiers.back();
-               qual.identifier = token;
-
-               if(check("="))
-                       qual.value = parse_token();
-
-               if(peek_token()==")")
-                       break;
-
-               expect(",");
-       }
-       expect(")");
-
-       return layout;
-}
-
-void ProgramParser::parse_block(Block &block, bool require_braces)
-{
-       bool have_braces = (require_braces || peek_token()=="{");
-       if(have_braces)
-               expect("{");
-
-       if(have_braces)
-       {
-               while(peek_token()!="}")
-                       block.body.push_back(parse_statement());
-       }
-       else
-               block.body.push_back(parse_statement());
-
-       block.use_braces = (require_braces || block.body.size()!=1);
-
-       if(have_braces)
-               expect("}");
-}
-
-RefPtr<Expression> ProgramParser::parse_expression(unsigned precedence)
-{
-       RefPtr<Expression> left;
-       VariableReference *left_var = 0;
-       while(1)
-       {
-               string token = peek_token();
-
-               const Operator *oper = 0;
-               for(Operator *i=operators; (!oper && i->type); ++i)
-                       if(token==i->token && (!left || i->type!=PREFIX) && (left || i->type!=POSTFIX))
-                               oper = i;
-
-               if(token==";" || token==")" || token=="]" || token=="," || (oper && precedence && oper->precedence>=precedence))
-               {
-                       if(left)
-                               return left;
-                       else
-                               throw runtime_error(format_syntax_error("an expression"));
-               }
-               else if(left)
-               {
-                       if(token=="(")
-                       {
-                               if(!left_var)
-                                       throw runtime_error(format_error("Syntax error before '(': function name must be an identifier"));
-                               left = parse_function_call(*left_var);
-                       }
-                       else if(token==".")
-                       {
-                               RefPtr<MemberAccess> memacc = new MemberAccess;
-                               memacc->left = left;
-                               parse_token();
-                               memacc->member = expect_identifier();
-                               left = memacc;
-                       }
-                       else if(oper && oper->type==POSTFIX)
-                       {
-                               RefPtr<UnaryExpression> unary = new UnaryExpression;
-                               unary->oper = parse_token();
-                               unary->prefix = false;
-                               unary->expression = left;
-                               left = unary;
-                       }
-                       else if(oper && oper->type==BINARY)
-                               left = parse_binary(left, oper);
-                       else
-                               throw runtime_error(format_syntax_error("an operator"));
-                       left_var = 0;
-               }
-               else
-               {
-                       if(token=="(")
-                       {
-                               parse_token();
-                               RefPtr<ParenthesizedExpression> parexpr = new ParenthesizedExpression;
-                               parexpr->expression = parse_expression();
-                               expect(")");
-                               left = parexpr;
-                       }
-                       else if(isdigit(token[0]) || token=="true" || token=="false")
-                       {
-                               RefPtr<Literal> literal = new Literal;
-                               literal->token = parse_token();
-                               left = literal;
-                       }
-                       else if(is_identifier(token))
-                       {
-                               RefPtr<VariableReference> var = new VariableReference;
-                               var->name = expect_identifier();
-                               left = var;
-                               left_var = var.get();
-                       }
-                       else if(oper && oper->type==PREFIX)
-                       {
-                               RefPtr<UnaryExpression> unary = new UnaryExpression;
-                               unary->oper = parse_token();
-                               unary->prefix = true;
-                               unary->expression = parse_expression(oper->precedence);
-                               left = unary;
-                       }
-                       else
-                               throw runtime_error(format_syntax_error("an expression"));
-               }
-       }
-}
-
-RefPtr<BinaryExpression> ProgramParser::parse_binary(const RefPtr<Expression> &left, const Operator *oper)
-{
-       RefPtr<BinaryExpression> binary = (oper->precedence==16 ? new Assignment : new BinaryExpression);
-       binary->left = left;
-       binary->oper = parse_token();
-       if(binary->oper=="[")
-       {
-               binary->right = parse_expression();
-               expect("]");
-               binary->after = "]";
-       }
-       else
-               binary->right = parse_expression(oper->precedence+(oper->assoc==RIGHT_TO_LEFT));
-       return binary;
-}
-
-RefPtr<FunctionCall> ProgramParser::parse_function_call(const VariableReference &var)
-{
-       RefPtr<FunctionCall> call = new FunctionCall;
-       call->name = var.name;
-       call->constructor = is_type(call->name);
-       expect("(");
-       while(peek_token()!=")")
-       {
-               if(!call->arguments.empty())
-                       expect(",");
-               call->arguments.push_back(parse_expression());
-       }
-       expect(")");
-       return call;
-}
-
-RefPtr<StructDeclaration> ProgramParser::parse_struct_declaration()
-{
-       expect("struct");
-       RefPtr<StructDeclaration> strct = new StructDeclaration;
-       strct->source = source_index;
-       strct->line = current_line;
-
-       strct->name = expect_identifier();
-       parse_block(strct->members, true);
-       expect(";");
-
-       declared_types.insert(strct->name);
-       return strct;
-}
-
-RefPtr<VariableDeclaration> ProgramParser::parse_variable_declaration()
-{
-       RefPtr<VariableDeclaration> var = new VariableDeclaration;
-       var->source = source_index;
-       var->line = current_line;
-
-       string token = peek_token();
-       while(is_qualifier(token))
-       {
-               parse_token();
-               if(is_interface_qualifier(token))
-                       var->interface = token;
-               else if(is_sampling_qualifier(token))
-                       var->sampling = token;
-               else if(is_interpolation_qualifier(token))
-                       var->interpolation = token;
-               else if(is_precision_qualifier(token))
-                       var->precision = token;
-               else if(token=="const")
-                       var->constant = true;
-               token = peek_token();
-       }
-
-       var->type = expect_type();
-       var->name = expect_identifier();
-
-       if(check("["))
-       {
-               var->array = true;
-               if(!check("]"))
-               {
-                       var->array_size = parse_expression();
-                       expect("]");
-               }
-       }
-
-       if(check("="))
-               var->init_expression = parse_expression();
-
-       expect(";");
-       return var;
-}
-
-RefPtr<FunctionDeclaration> ProgramParser::parse_function_declaration()
-{
-       RefPtr<FunctionDeclaration> func = new FunctionDeclaration;
-       func->source = source_index;
-       func->line = current_line;
-
-       func->return_type = expect_type();
-       func->name = expect_identifier();
-       expect("(");
-       while(peek_token()!=")")
-       {
-               if(!func->parameters.empty())
-                       expect(",");
-
-               RefPtr<VariableDeclaration> var = new VariableDeclaration;
-               string token = peek_token();
-               if(token=="in" || token=="out" || token=="inout")
-                       var->interface = parse_token();
-               var->type = expect_type();
-               var->name = expect_identifier();
-               func->parameters.push_back(var);
-       }
-       expect(")");
-
-       string token = peek_token();
-       if(token=="{")
-       {
-               func->definition = func.get();
-               parse_block(func->body, true);
-       }
-       else if(token==";")
-               parse_token();
-       else
-               throw runtime_error(format_syntax_error("'{' or ';'"));
-
-       return func;
-}
-
-RefPtr<InterfaceBlock> ProgramParser::parse_interface_block()
-{
-       RefPtr<InterfaceBlock> iface = new InterfaceBlock;
-       iface->source = source_index;
-       iface->line = current_line;
-
-       iface->interface = parse_token();
-       if(!is_interface_qualifier(iface->interface))
-               throw runtime_error(format_syntax_error("an interface qualifier"));
-
-       iface->name = expect_identifier();
-       parse_block(iface->members, true);
-       if(!check(";"))
-       {
-               iface->instance_name = expect_identifier();
-               if(check("["))
-               {
-                       iface->array = true;
-                       expect("]");
-               }
-               expect(";");
-       }
-
-       return iface;
-}
-
-RefPtr<Conditional> ProgramParser::parse_conditional()
-{
-       expect("if");
-       RefPtr<Conditional> cond = new Conditional;
-       cond->source = source_index;
-       cond->line = current_line;
-       expect("(");
-       cond->condition = parse_expression();
-       expect(")");
-
-       parse_block(cond->body, false);
-
-       string token = peek_token();
-       if(token=="else")
-       {
-               parse_token();
-               parse_block(cond->else_body, false);
-       }
-
-       return cond;
-}
-
-RefPtr<Iteration> ProgramParser::parse_for()
-{
-       expect("for");
-       RefPtr<Iteration> loop = new Iteration;
-       loop->source = source_index;
-       loop->line = current_line;
-       expect("(");
-       string token = peek_token();
-       if(is_type(token))
-               loop->init_statement = parse_statement();
-       else
-       {
-               if(token!=";")
-               {
-                       RefPtr<ExpressionStatement> expr = new ExpressionStatement;
-                       expr->expression = parse_expression();
-                       loop->init_statement = expr;
-               }
-               expect(";");
-       }
-       if(peek_token()!=";")
-               loop->condition = parse_expression();
-       expect(";");
-       if(peek_token()!=")")
-               loop->loop_expression = parse_expression();
-       expect(")");
-
-       parse_block(loop->body, false);
-
-       return loop;
-}
-
-RefPtr<Iteration> ProgramParser::parse_while()
-{
-       expect("while");
-       RefPtr<Iteration> loop = new Iteration;
-       loop->source = source_index;
-       loop->line = current_line;
-       expect("(");
-       loop->condition = parse_expression();
-       expect(")");
-
-       parse_block(loop->body, false);
-
-       return loop;
-}
-
-RefPtr<Passthrough> ProgramParser::parse_passthrough()
-{
-       expect("passthrough");
-       RefPtr<Passthrough> pass = new Passthrough;
-       pass->source = source_index;
-       pass->line = current_line;
-       if(cur_stage->type==GEOMETRY)
-       {
-               expect("[");
-               pass->subscript = parse_expression();
-               expect("]");
-       }
-       expect(";");
-       return pass;
-}
-
-RefPtr<Return> ProgramParser::parse_return()
-{
-       expect("return");
-       RefPtr<Return> ret = new Return;
-       ret->source = source_index;
-       ret->line = current_line;
-       if(peek_token()!=";")
-               ret->expression = parse_expression();
-       expect(";");
-       return ret;
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/programparser.h b/source/programparser.h
deleted file mode 100644 (file)
index e366047..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-#ifndef MSP_GL_PROGRAMPARSER_H_
-#define MSP_GL_PROGRAMPARSER_H_
-
-#include <deque>
-#include <map>
-#include <set>
-#include <string>
-#include <msp/io/base.h>
-#include "programsyntax.h"
-
-namespace Msp {
-namespace GL {
-
-class ProgramParser
-{
-private:
-       enum OperatorType
-       {
-               NO_OPERATOR,
-               BINARY,
-               PREFIX,
-               POSTFIX
-       };
-
-       enum Associativity
-       {
-               LEFT_TO_RIGHT,
-               RIGHT_TO_LEFT
-       };
-
-       struct Operator
-       {
-               const char token[4];
-               unsigned precedence;
-               OperatorType type;
-               Associativity assoc;
-       };
-
-       std::string source;
-       std::string source_name;
-       unsigned source_index;
-       unsigned current_line;
-       std::string::const_iterator iter;
-       std::string::const_iterator source_end;
-       bool allow_preprocess;
-       bool allow_stage_change;
-       std::string last_token;
-       std::deque<std::string> next_tokens;
-       ProgramSyntax::Module *module;
-       ProgramSyntax::Stage *cur_stage;
-       std::set<std::string> declared_types;
-
-       static Operator operators[];
-
-public:
-       ProgramParser();
-       ~ProgramParser();
-
-       ProgramSyntax::Module &parse(const std::string &, const std::string &, unsigned = 0);
-       ProgramSyntax::Module &parse(IO::Base &, const std::string &, unsigned = 0);
-
-private:
-       void parse_source();
-
-       std::string format_error(const std::string &);
-       std::string format_syntax_error(const std::string &);
-
-       const std::string &peek_token(unsigned = 0);
-       const std::string &parse_token();
-       std::string parse_token_();
-       std::string parse_identifier();
-       std::string parse_number();
-       std::string parse_other();
-       void skip_comment_and_whitespace();
-       void expect(const std::string &);
-       std::string expect_type();
-       std::string expect_identifier();
-       bool check(const std::string &);
-
-       static bool is_interface_qualifier(const std::string &);
-       static bool is_sampling_qualifier(const std::string &);
-       static bool is_interpolation_qualifier(const std::string &);
-       static bool is_precision_qualifier(const std::string &);
-       static bool is_qualifier(const std::string &);
-       static bool is_builtin_type(const std::string &);
-       bool is_type(const std::string &);
-       bool is_identifier(const std::string &);
-
-       void preprocess();
-       void preprocess_version();
-       void preprocess_pragma();
-       void preprocess_pragma_msp();
-       void preprocess_stage();
-
-       RefPtr<ProgramSyntax::Statement> parse_global_declaration();
-       RefPtr<ProgramSyntax::Statement> parse_statement();
-       RefPtr<ProgramSyntax::Import> parse_import();
-       RefPtr<ProgramSyntax::Precision> parse_precision();
-       RefPtr<ProgramSyntax::Layout> parse_layout();
-       void parse_block(ProgramSyntax::Block &, bool);
-       RefPtr<ProgramSyntax::Expression> parse_expression(unsigned = 0);
-       RefPtr<ProgramSyntax::BinaryExpression> parse_binary(const RefPtr<ProgramSyntax::Expression> &, const Operator *);
-       RefPtr<ProgramSyntax::FunctionCall> parse_function_call(const ProgramSyntax::VariableReference &);
-       RefPtr<ProgramSyntax::StructDeclaration> parse_struct_declaration();
-       RefPtr<ProgramSyntax::VariableDeclaration> parse_variable_declaration();
-       RefPtr<ProgramSyntax::FunctionDeclaration> parse_function_declaration();
-       RefPtr<ProgramSyntax::InterfaceBlock> parse_interface_block();
-       RefPtr<ProgramSyntax::Conditional> parse_conditional();
-       RefPtr<ProgramSyntax::Iteration> parse_for();
-       RefPtr<ProgramSyntax::Iteration> parse_while();
-       RefPtr<ProgramSyntax::Passthrough> parse_passthrough();
-       RefPtr<ProgramSyntax::Return> parse_return();
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/programsyntax.cpp b/source/programsyntax.cpp
deleted file mode 100644 (file)
index 6dea029..0000000
+++ /dev/null
@@ -1,333 +0,0 @@
-#include "programsyntax.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-namespace ProgramSyntax {
-
-template<typename C>
-NodeContainer<C>::NodeContainer(const NodeContainer &c):
-       C(c)
-{
-       for(typename C::iterator i=this->begin(); i!=this->end(); ++i)
-               *i = (*i)->clone();
-}
-
-
-Statement::Statement():
-       source(0),
-       line(1)
-{ }
-
-
-Block::Block():
-       use_braces(false)
-{ }
-
-void Block::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-void Literal::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-void ParenthesizedExpression::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-VariableReference::VariableReference():
-       declaration(0)
-{ }
-
-void VariableReference::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-void MemberAccess::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-UnaryExpression::UnaryExpression():
-       prefix(true)
-{ }
-
-void UnaryExpression::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-void BinaryExpression::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-Assignment::Assignment():
-       self_referencing(false),
-       target_declaration(0)
-{ }
-
-void Assignment::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-FunctionCall::FunctionCall():
-       declaration(0),
-       constructor(false)
-{ }
-
-void FunctionCall::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-void ExpressionStatement::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-void Import::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-void Precision::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-void Layout::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-void InterfaceLayout::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-StructDeclaration::StructDeclaration()
-{
-       members.use_braces = true;
-}
-
-void StructDeclaration::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-VariableDeclaration::VariableDeclaration():
-       constant(false),
-       type_declaration(0),
-       array(false),
-       linked_declaration(0)
-{ }
-
-void VariableDeclaration::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-InterfaceBlock::InterfaceBlock():
-       array(false)
-{
-       members.use_braces = true;
-}
-
-void InterfaceBlock::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-FunctionDeclaration::FunctionDeclaration():
-       definition(0)
-{ }
-
-FunctionDeclaration::FunctionDeclaration(const FunctionDeclaration &other):
-       return_type(other.return_type),
-       name(other.name),
-       parameters(other.parameters),
-       definition(other.definition==&other ? this : other.definition),
-       body(other.body)
-{ }
-
-void FunctionDeclaration::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-void Conditional::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-void Iteration::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-void Passthrough::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-void Return::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-void Jump::visit(NodeVisitor &visitor)
-{
-       visitor.visit(*this);
-}
-
-
-void NodeVisitor::visit(Assignment &assign)
-{
-       visit(static_cast<BinaryExpression &>(assign));
-}
-
-
-void TraversingVisitor::visit(Block &block)
-{
-       for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
-               (*i)->visit(*this);
-}
-
-void TraversingVisitor::visit(ParenthesizedExpression &parexpr)
-{
-       parexpr.expression->visit(*this);
-}
-
-void TraversingVisitor::visit(MemberAccess &memacc)
-{
-       memacc.left->visit(*this);
-}
-
-void TraversingVisitor::visit(UnaryExpression &unary)
-{
-       unary.expression->visit(*this);
-}
-
-void TraversingVisitor::visit(BinaryExpression &binary)
-{
-       binary.left->visit(*this);
-       binary.right->visit(*this);
-}
-
-void TraversingVisitor::visit(FunctionCall &call)
-{
-       for(NodeArray<Expression>::iterator i=call.arguments.begin(); i!=call.arguments.end(); ++i)
-               (*i)->visit(*this);
-}
-
-void TraversingVisitor::visit(ExpressionStatement &expr)
-{
-       expr.expression->visit(*this);
-}
-
-void TraversingVisitor::visit(InterfaceLayout &layout)
-{
-       layout.layout.visit(*this);
-}
-
-void TraversingVisitor::visit(StructDeclaration &strct)
-{
-       strct.members.visit(*this);
-}
-
-void TraversingVisitor::visit(VariableDeclaration &var)
-{
-       if(var.layout)
-               var.layout->visit(*this);
-       if(var.init_expression)
-               var.init_expression->visit(*this);
-       if(var.array_size)
-               var.array_size->visit(*this);
-}
-
-void TraversingVisitor::visit(InterfaceBlock &iface)
-{
-       iface.members.visit(*this);
-}
-
-void TraversingVisitor::visit(FunctionDeclaration &func)
-{
-       for(NodeArray<VariableDeclaration>::iterator i=func.parameters.begin(); i!=func.parameters.end(); ++i)
-               (*i)->visit(*this);
-       func.body.visit(*this);
-}
-
-void TraversingVisitor::visit(Conditional &cond)
-{
-       cond.condition->visit(*this);
-       cond.body.visit(*this);
-       cond.else_body.visit(*this);
-}
-
-void TraversingVisitor::visit(Iteration &iter)
-{
-       if(iter.init_statement)
-               iter.init_statement->visit(*this);
-       if(iter.condition)
-               iter.condition->visit(*this);
-       if(iter.loop_expression)
-               iter.loop_expression->visit(*this);
-       iter.body.visit(*this);
-}
-
-void TraversingVisitor::visit(Passthrough &pass)
-{
-       if(pass.subscript)
-               pass.subscript->visit(*this);
-}
-
-void TraversingVisitor::visit(Return &ret)
-{
-       if(ret.expression)
-               ret.expression->visit(*this);
-}
-
-
-Stage::Stage(StageType t):
-       type(t),
-       previous(0)
-{ }
-
-
-Module::Module():
-       shared(SHARED)
-{ }
-
-} // namespace ProgramSyntax
-} // namespace GL
-} // namespace Msp
diff --git a/source/programsyntax.h b/source/programsyntax.h
deleted file mode 100644 (file)
index e7b5111..0000000
+++ /dev/null
@@ -1,425 +0,0 @@
-#ifndef MSP_GL_PROGRAMSYNTAX_H_
-#define MSP_GL_PROGRAMSYNTAX_H_
-
-#include <list>
-#include <map>
-#include <string>
-#include <vector>
-#include <msp/core/refptr.h>
-#include "extension.h"
-#include "uniform.h"
-
-#pragma push_macro("interface")
-#undef interface
-
-namespace Msp {
-namespace GL {
-namespace ProgramSyntax {
-
-struct NodeVisitor;
-
-struct Node
-{
-protected:
-       Node() { }
-       Node(const Node &) { }
-private:
-       Node &operator=(const Node &);
-public:
-       virtual ~Node() { }
-
-       virtual Node *clone() const = 0;
-       virtual void visit(NodeVisitor &) = 0;
-};
-
-template<typename T>
-class NodePtr: public RefPtr<T>
-{
-public:
-       NodePtr() { }
-       NodePtr(T *p): RefPtr<T>(p) { }
-       NodePtr(const NodePtr &p): RefPtr<T>(p ? p->clone() : 0) { }
-       NodePtr &operator=(const NodePtr &p) { RefPtr<T>::operator=(p); return *this; }
-
-       template<typename U>
-       NodePtr(const RefPtr<U> &p): RefPtr<T>(p) { }
-
-       template<typename U>
-       NodePtr(const NodePtr<U> &p): RefPtr<T>(p ? p->clone() : 0) { }
-};
-
-template<typename C>
-class NodeContainer: public C
-{
-public:
-       NodeContainer() { }
-       NodeContainer(const NodeContainer &);
-};
-
-template<typename T>
-class NodeList: public NodeContainer<std::list<RefPtr<T> > >
-{ };
-
-template<typename T>
-class NodeArray: public NodeContainer<std::vector<RefPtr<T> > >
-{ };
-
-struct StructDeclaration;
-struct VariableDeclaration;
-struct FunctionDeclaration;
-
-struct Statement: Node
-{
-       unsigned source;
-       unsigned line;
-
-       Statement();
-
-       virtual Statement *clone() const = 0;
-};
-
-struct Block: Node
-{
-       NodeList<Statement> body;
-       bool use_braces;
-       std::map<std::string, StructDeclaration *> types;
-       std::map<std::string, VariableDeclaration *> variables;
-
-       Block();
-
-       virtual Block *clone() const { return new Block(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct Expression: Node
-{
-       virtual Expression *clone() const = 0;
-};
-
-struct Literal: Expression
-{
-       std::string token;
-
-       virtual Literal *clone() const { return new Literal(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct ParenthesizedExpression: Expression
-{
-       NodePtr<Expression> expression;
-
-       virtual ParenthesizedExpression *clone() const { return new ParenthesizedExpression(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct VariableReference: Expression
-{
-       std::string name;
-       VariableDeclaration *declaration;
-
-       VariableReference();
-
-       virtual VariableReference *clone() const { return new VariableReference(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct MemberAccess: Expression
-{
-       NodePtr<Expression> left;
-       std::string member;
-       VariableDeclaration *declaration;
-
-       virtual MemberAccess *clone() const { return new MemberAccess(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct UnaryExpression: Expression
-{
-       std::string oper;
-       NodePtr<Expression> expression;
-       bool prefix;
-
-       UnaryExpression();
-
-       virtual UnaryExpression *clone() const { return new UnaryExpression(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct BinaryExpression: Expression
-{
-       NodePtr<Expression> left;
-       std::string oper;
-       NodePtr<Expression> right;
-       std::string after;
-
-       virtual BinaryExpression *clone() const { return new BinaryExpression(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct Assignment: BinaryExpression
-{
-       bool self_referencing;
-       VariableDeclaration *target_declaration;
-
-       Assignment();
-
-       virtual Assignment *clone() const { return new Assignment(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct FunctionCall: Expression
-{
-       std::string name;
-       FunctionDeclaration *declaration;
-       bool constructor;
-       NodeArray<Expression> arguments;
-
-       FunctionCall();
-
-       virtual FunctionCall *clone() const { return new FunctionCall(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct ExpressionStatement: Statement
-{
-       NodePtr<Expression> expression;
-
-       virtual ExpressionStatement *clone() const { return new ExpressionStatement(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct Import: Statement
-{
-       std::string module;
-
-       virtual Import *clone() const { return new Import(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct Precision: Statement
-{
-       std::string precision;
-       std::string type;
-
-       virtual Precision *clone() const { return new Precision(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct Layout: Node
-{
-       struct Qualifier
-       {
-               // TODO the standard calls this name, not identifier
-               std::string identifier;
-               std::string value;
-       };
-
-       std::vector<Qualifier> qualifiers;
-
-       virtual Layout *clone() const { return new Layout(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct InterfaceLayout: Statement
-{
-       std::string interface;
-       Layout layout;
-
-       virtual InterfaceLayout *clone() const { return new InterfaceLayout(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct StructDeclaration: Statement
-{
-       std::string name;
-       Block members;
-
-       StructDeclaration();
-
-       virtual StructDeclaration *clone() const { return new StructDeclaration(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct VariableDeclaration: Statement
-{
-       bool constant;
-       std::string sampling;
-       std::string interpolation;
-       std::string interface;
-       std::string precision;
-       std::string type;
-       StructDeclaration *type_declaration;
-       std::string name;
-       bool array;
-       NodePtr<Expression> array_size;
-       NodePtr<Expression> init_expression;
-       VariableDeclaration *linked_declaration;
-       NodePtr<Layout> layout;
-
-       VariableDeclaration();
-
-       virtual VariableDeclaration *clone() const { return new VariableDeclaration(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct InterfaceBlock: Statement
-{
-       std::string interface;
-       std::string name;
-       Block members;
-       std::string instance_name;
-       bool array;
-
-       InterfaceBlock();
-
-       virtual InterfaceBlock *clone() const { return new InterfaceBlock(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct FunctionDeclaration: Statement
-{
-       std::string return_type;
-       std::string name;
-       NodeArray<VariableDeclaration> parameters;
-       FunctionDeclaration *definition;
-       Block body;
-
-       FunctionDeclaration();
-       FunctionDeclaration(const FunctionDeclaration &);
-
-       virtual FunctionDeclaration *clone() const { return new FunctionDeclaration(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct Conditional: Statement
-{
-       NodePtr<Expression> condition;
-       Block body;
-       Block else_body;
-
-       virtual Conditional *clone() const { return new Conditional(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct Iteration: Statement
-{
-       NodePtr<Node> init_statement;
-       NodePtr<Expression> condition;
-       NodePtr<Expression> loop_expression;
-       Block body;
-
-       virtual Iteration *clone() const { return new Iteration(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct Passthrough: Statement
-{
-       NodePtr<Expression> subscript;
-
-       virtual Passthrough *clone() const { return new Passthrough(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct Return: Statement
-{
-       NodePtr<Expression> expression;
-
-       virtual Return *clone() const { return new Return(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct Jump: Statement
-{
-       std::string keyword;
-
-       virtual Jump *clone() const { return new Jump(*this); }
-       virtual void visit(NodeVisitor &);
-};
-
-struct NodeVisitor
-{
-       virtual ~NodeVisitor() { }
-
-       virtual void visit(Block &) { }
-       virtual void visit(Literal &) { }
-       virtual void visit(ParenthesizedExpression &) { }
-       virtual void visit(VariableReference &) { }
-       virtual void visit(MemberAccess &) { }
-       virtual void visit(UnaryExpression &) { }
-       virtual void visit(BinaryExpression &) { }
-       virtual void visit(Assignment &);
-       virtual void visit(FunctionCall &) { }
-       virtual void visit(ExpressionStatement &) { }
-       virtual void visit(Import &) { }
-       virtual void visit(Precision &) { }
-       virtual void visit(Layout &) { }
-       virtual void visit(InterfaceLayout &) { }
-       virtual void visit(StructDeclaration &) { }
-       virtual void visit(VariableDeclaration &) { }
-       virtual void visit(InterfaceBlock &) { }
-       virtual void visit(FunctionDeclaration &) { }
-       virtual void visit(Conditional &) { }
-       virtual void visit(Iteration &) { }
-       virtual void visit(Passthrough &) { }
-       virtual void visit(Return &) { }
-       virtual void visit(Jump &) { }
-};
-
-struct TraversingVisitor: NodeVisitor
-{
-       using NodeVisitor::visit;
-       virtual void visit(Block &);
-       virtual void visit(ParenthesizedExpression &);
-       virtual void visit(MemberAccess &);
-       virtual void visit(UnaryExpression &);
-       virtual void visit(BinaryExpression &);
-       virtual void visit(FunctionCall &);
-       virtual void visit(ExpressionStatement &);
-       virtual void visit(InterfaceLayout &);
-       virtual void visit(StructDeclaration &);
-       virtual void visit(VariableDeclaration &);
-       virtual void visit(InterfaceBlock &);
-       virtual void visit(FunctionDeclaration &);
-       virtual void visit(Conditional &);
-       virtual void visit(Iteration &);
-       virtual void visit(Passthrough &);
-       virtual void visit(Return &);
-};
-
-enum StageType
-{
-       SHARED,
-       VERTEX,
-       GEOMETRY,
-       FRAGMENT
-};
-
-struct Stage
-{
-       StageType type;
-       Stage *previous;
-       ProgramSyntax::Block content;
-       std::map<std::string, VariableDeclaration *> in_variables;
-       std::map<std::string, VariableDeclaration *> out_variables;
-       std::map<std::string, unsigned> locations;
-       Version required_version;
-       std::vector<const Extension *> required_extensions;
-
-       Stage(StageType);
-};
-
-struct Module
-{
-       Stage shared;
-       std::list<Stage> stages;
-
-       Module();
-};
-
-} // namespace ProgramSyntax
-} // namespace GL
-} // namespace Msp
-
-#pragma pop_macro("interface")
-
-#endif
diff --git a/source/render/camera.cpp b/source/render/camera.cpp
new file mode 100644 (file)
index 0000000..3bba0ea
--- /dev/null
@@ -0,0 +1,201 @@
+#include <cmath>
+#include "camera.h"
+#include "matrix.h"
+
+namespace Msp {
+namespace GL {
+
+Camera::Camera():
+       fov(Geometry::Angle<float>::from_turns(0.125)),
+       height(0),
+       aspect(4.0/3.0),
+       clip_near(0.1),
+       clip_far(10),
+       frustum_x(0),
+       frustum_y(0),
+       position(0, 0, 0),
+       look_dir(0, 0, -1),
+       up_dir(0, 1, 0)
+{
+       update_projection_matrix();
+       update_object_matrix();
+}
+
+void Camera::set_field_of_view(const Geometry::Angle<float> &f)
+{
+       fov = f;
+       update_projection_matrix();
+}
+
+void Camera::set_orthographic(float w, float h)
+{
+       fov = Geometry::Angle<float>::zero();
+       height = h;
+       if(w)
+               aspect = w/h;
+       update_projection_matrix();
+}
+
+void Camera::set_aspect_ratio(float a)
+{
+       aspect = a;
+       update_projection_matrix();
+}
+
+void Camera::set_depth_clip(float n, float f)
+{
+       clip_near = n;
+       clip_far = f;
+       update_projection_matrix();
+}
+
+void Camera::set_frustum_axis(float x, float y)
+{
+       frustum_x = x;
+       frustum_y = y;
+       update_projection_matrix();
+}
+
+void Camera::set_frustum_rotation(const Geometry::Angle<float> &r)
+{
+       rotate = r;
+       update_projection_matrix();
+}
+
+void Camera::set_position(const Vector3 &p)
+{
+       position = p;
+       update_object_matrix();
+}
+
+void Camera::set_look_direction(const Vector3 &l)
+{
+       look_dir = normalize(l);
+       update_object_matrix();
+}
+
+void Camera::look_at(const Vector3 &p)
+{
+       set_look_direction(p-position);
+}
+
+void Camera::set_up_direction(const Vector3 &u)
+{
+       up_dir = normalize(u);
+       update_object_matrix();
+}
+
+void Camera::set_object_matrix(const Matrix &m)
+{
+       position = m.column(3).slice<3>(0);
+       look_dir = normalize(-m.column(2).slice<3>(0));
+       up_dir = normalize(m.column(1).slice<3>(0));
+       update_object_matrix();
+}
+
+Vector3 Camera::project(const Vector4 &p) const
+{
+       Vector4 r = proj_matrix*(view_matrix*p);
+       return r.slice<3>(0)/r.w;
+}
+
+Vector3 Camera::project(const Vector3 &p) const
+{
+       return project(Vector4(p.x, p.y, p.z, 1.0));
+}
+
+Vector4 Camera::unproject(const Vector4 &p) const
+{
+       Vector4 r = invert(proj_matrix)*Vector4(p.x, p.y, p.z, 1.0f);
+       r = matrix*Vector4(r.x/r.w, r.y/r.w, r.z/r.w, p.w);
+       return r;
+}
+
+Vector3 Camera::unproject(const Vector3 &p) const
+{
+       return unproject(Vector4(p.x, p.y, p.z, 1.0f)).slice<3>(0);
+}
+
+void Camera::update_projection_matrix()
+{
+       float frustum_h = (fov!=Geometry::Angle<float>::zero() ? tan(fov/2.0f)*clip_near : height/2);
+       float frustum_w = frustum_h*aspect;
+       float left = frustum_w*(frustum_x-1.0f);
+       float right = frustum_w*(frustum_x+1.0f);
+       float bottom = frustum_h*(frustum_y-1.0f);
+       float top = frustum_h*(frustum_y+1.0f);
+       if(fov>Geometry::Angle<float>::zero())
+               proj_matrix = Matrix::frustum(left, right, bottom, top, clip_near, clip_far);
+       else
+               proj_matrix = Matrix::ortho(left, right, bottom, top, clip_near, clip_far);
+       proj_matrix = Matrix::rotation(rotate, Vector3(0, 0, 1))*proj_matrix;
+}
+
+void Camera::update_object_matrix()
+{
+       Vector3 right_dir = normalize(cross(look_dir, up_dir));
+       Vector4 columns[4];
+       columns[0] = compose(right_dir, 0.0f);
+       columns[1] = compose(cross(right_dir, look_dir), 0.0f);
+       columns[2] = compose(-look_dir, 0.0f);
+       columns[3] = compose(position, 1.0f);
+       matrix = Matrix::from_columns(columns);
+       view_matrix = invert(matrix);
+}
+
+
+Camera::Loader::Loader(Camera &c):
+       DataFile::ObjectLoader<Camera>(c)
+{
+       add("aspect_ratio", &Loader::aspect_ratio);
+       add("depth_clip", &Loader::depth_clip);
+       add("field_of_view", &Loader::field_of_view);
+       add("look_at", &Loader::look_at);
+       add("look_direction", &Loader::look_direction);
+       add("orthographic", &Loader::orthographic);
+       add("position", &Loader::position);
+       add("up_direction", &Loader::up_direction);
+}
+
+void Camera::Loader::aspect_ratio(float a)
+{
+       obj.set_aspect_ratio(a);
+}
+
+void Camera::Loader::depth_clip(float n, float f)
+{
+       obj.set_depth_clip(n, f);
+}
+
+void Camera::Loader::field_of_view(float a)
+{
+       obj.set_field_of_view(Geometry::Angle<float>::from_degrees(a));
+}
+
+void Camera::Loader::look_at(float x, float y, float z)
+{
+       obj.look_at(Vector3(x, y, z));
+}
+
+void Camera::Loader::look_direction(float x, float y, float z)
+{
+       obj.set_look_direction(Vector3(x, y, z));
+}
+
+void Camera::Loader::orthographic(float w, float h)
+{
+       obj.set_orthographic(w, h);
+}
+
+void Camera::Loader::position(float x, float y, float z)
+{
+       obj.set_position(Vector3(x, y, z));
+}
+
+void Camera::Loader::up_direction(float x, float y, float z)
+{
+       obj.set_up_direction(Vector3(x, y, z));
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/render/camera.h b/source/render/camera.h
new file mode 100644 (file)
index 0000000..b10ccb9
--- /dev/null
@@ -0,0 +1,100 @@
+#ifndef MSP_GL_CAMERA_H_
+#define MSP_GL_CAMERA_H_
+
+#include <msp/datafile/objectloader.h>
+#include "placeable.h"
+
+namespace Msp {
+namespace GL {
+
+class Camera: public Placeable
+{
+public:
+       class Loader: public DataFile::ObjectLoader<Camera>
+       {
+       public:
+               Loader(Camera &);
+
+       private:
+               void aspect_ratio(float);
+               void depth_clip(float, float);
+               void field_of_view(float);
+               void look_at(float, float, float);
+               void look_direction(float, float, float);
+               void orthographic(float, float);
+               void position(float, float, float);
+               void up_direction(float, float, float);
+       };
+
+private:
+       Geometry::Angle<float> fov;
+       float height;
+       float aspect;
+       // Some compilers have "near" and "far" keywords
+       float clip_near;
+       float clip_far;
+       float frustum_x;
+       float frustum_y;
+       Geometry::Angle<float> rotate;
+       Vector3 position;
+       Vector3 look_dir;
+       Vector3 up_dir;
+       Matrix view_matrix;
+       Matrix proj_matrix;
+
+public:
+       Camera();
+
+       void set_field_of_view(const Geometry::Angle<float> &);
+       void set_orthographic(float, float);
+       void set_aspect_ratio(float);
+       void set_depth_clip(float, float);
+       void set_frustum_axis(float, float);
+       void set_frustum_rotation(const Geometry::Angle<float> &);
+       const Geometry::Angle<float> &get_field_of_view() const { return fov; }
+       bool is_orthographic() const { return fov==Geometry::Angle<float>::zero(); }
+       float get_orthographic_width() const { return height*aspect; }
+       float get_orthographic_height() const { return height; }
+       float get_aspect_ratio() const { return aspect; }
+       float get_near_clip() const { return clip_near; }
+       float get_far_clip() const { return clip_far; }
+       const Geometry::Angle<float> &get_frustum_rotation() const { return rotate; }
+
+       void set_position(const Vector3 &);
+       void set_look_direction(const Vector3 &);
+       void look_at(const Vector3 &);
+       void set_up_direction(const Vector3 &);
+       const Vector3 &get_position() const { return position; }
+       const Vector3 &get_look_direction() const { return look_dir; }
+       const Vector3 &get_up_direction() const { return up_dir; }
+
+       virtual void set_matrix(const Matrix &m) { set_object_matrix(m); }
+
+       /** Sets the position and orientation of the camera from an object matrix. */
+       void set_object_matrix(const Matrix &);
+
+       /** Returns the view matrix, used to transform coordinates from world space
+       to eye space. */
+       const Matrix &get_view_matrix() const { return view_matrix; }
+
+       /** Returns the object matrix, used to transform coordinates from eye space
+       to world space. */
+       const Matrix &get_object_matrix() const { return matrix; }
+
+       /** Returns the projection matrix. */
+       const Matrix &get_projection_matrix() const { return proj_matrix; }
+
+       Vector3 project(const Vector4 &) const;
+       Vector3 project(const Vector3 &) const;
+       Vector4 unproject(const Vector4 &) const;
+       Vector3 unproject(const Vector3 &) const;
+
+private:
+       void update_projection_matrix();
+       void update_object_matrix();
+};
+
+} // namespace GL
+} // namespcae Msp
+
+#endif
diff --git a/source/render/instancearray.cpp b/source/render/instancearray.cpp
new file mode 100644 (file)
index 0000000..abd9fcd
--- /dev/null
@@ -0,0 +1,152 @@
+#include <msp/core/algorithm.h>
+#include <msp/core/maputils.h>
+#include <msp/gl/extensions/arb_draw_instanced.h>
+#include <msp/gl/extensions/arb_instanced_arrays.h>
+#include <msp/gl/extensions/arb_vertex_array_object.h>
+#include <msp/gl/extensions/arb_vertex_shader.h>
+#include "buffer.h"
+#include "camera.h"
+#include "instancearray.h"
+#include "mesh.h"
+#include "object.h"
+#include "objectinstance.h"
+#include "renderer.h"
+#include "technique.h"
+#include "vertexsetup.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+InstanceArray::InstanceArray(const Object &o):
+       object(o),
+       instance_data(0),
+       instance_buffer(0),
+       vtx_setup(0),
+       matrix_location(-1),
+       matrix_offset(0)
+{
+       const Technique *tech = object.get_technique();
+       const Technique::PassMap &passes = tech->get_passes();
+       for(Technique::PassMap::const_iterator i=passes.begin(); i!=passes.end(); ++i)
+       {
+               const Program *shprog = i->second.get_shader_program();
+               if(!shprog)
+                       throw invalid_argument("InstanceArray::InstanceArray");
+
+               int loc = shprog->get_attribute_location("instance_transform");
+               if(matrix_location<0)
+                       matrix_location = loc;
+               else if(loc!=matrix_location)
+                       throw invalid_argument("InstanceArray::InstanceArray");
+       }
+
+       if(ARB_vertex_array_object && ARB_instanced_arrays && ARB_draw_instanced)
+       {
+               instance_data = new VertexArray((ATTRIB4,matrix_location, ATTRIB4,matrix_location+1, ATTRIB4,matrix_location+2));
+               const VertexFormat &fmt = instance_data->get_format();
+               matrix_offset = fmt.offset(make_indexed_component(ATTRIB4, matrix_location));
+
+               instance_buffer = new Buffer(ARRAY_BUFFER);
+               instance_data->use_buffer(instance_buffer);
+
+               vtx_setup = new VertexSetup;
+               vtx_setup->set_vertex_array(object.get_mesh()->get_vertices());
+               vtx_setup->set_index_buffer(*object.get_mesh()->get_index_buffer());
+               vtx_setup->set_instance_array(instance_data);
+       }
+       else
+               static Require req(ARB_vertex_shader);
+}
+
+InstanceArray::~InstanceArray()
+{
+       for(std::vector<ObjectInstance *>::iterator i=instances.begin(); i!=instances.end(); ++i)
+               delete *i;
+       delete vtx_setup;
+       delete instance_data;
+       delete instance_buffer;
+}
+
+void InstanceArray::append(ObjectInstance *inst)
+{
+       instances.push_back(inst);
+       if(instance_data)
+       {
+               if(instance_data->size()<instances.size())
+               {
+                       instance_data->append();
+                       unsigned req_size = instance_data->get_required_buffer_size();
+                       if(instance_buffer->get_size()>0 && instance_buffer->get_size()<req_size)
+                       {
+                               delete instance_buffer;
+                               instance_buffer = new Buffer(ARRAY_BUFFER);
+                               instance_data->use_buffer(instance_buffer);
+                       }
+               }
+               update_instance_matrix(instances.size()-1);
+       }
+}
+
+void InstanceArray::remove(ObjectInstance &inst)
+{
+       vector<ObjectInstance *>::iterator i = find(instances, &inst);
+       if(i==instances.end())
+               throw key_error(&inst);
+
+       delete *i;
+       *i = instances.back();
+       instances.pop_back();
+}
+
+void InstanceArray::update_instance_matrix(unsigned index)
+{
+       if(!instance_data)
+               return;
+
+       const Matrix &m = *instances[index]->get_matrix();
+
+       float *d = instance_data->modify(instances.size()-1);
+       for(unsigned i=0; i<12; ++i)
+               d[matrix_offset+i] = m(i/4, i%4);
+}
+
+void InstanceArray::render(Renderer &renderer, const Tag &tag) const
+{
+       if(instances.empty())
+               return;
+
+       if(instance_data)
+       {
+               const Technique *tech = object.get_technique();
+               if(!tech)
+                       throw logic_error("no technique");
+               const RenderPass *pass = tech->find_pass(tag);
+               if(!pass)
+                       return;
+
+               const Mesh *mesh = object.get_mesh();
+               mesh->get_vertices().refresh();
+               if(instance_buffer->get_size()==0)
+                       instance_buffer->storage(instance_data->get_required_buffer_size());
+               instance_data->refresh();
+
+               Renderer::Push push(renderer);
+               pass->apply(renderer);
+               mesh->draw_instanced(renderer, *vtx_setup, instances.size());
+       }
+       else
+       {
+               for(vector<ObjectInstance *>::const_iterator i=instances.begin(); i!=instances.end(); ++i)
+               {
+                       const Matrix &m = *(*i)->get_matrix();
+                       for(unsigned j=0; j<3; ++j)
+                               glVertexAttrib4f(matrix_location+j, m(j, 0), m(j, 1), m(j, 2), m(j, 3));
+                       (*i)->render(renderer, tag);
+               }
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/render/instancearray.h b/source/render/instancearray.h
new file mode 100644 (file)
index 0000000..627051f
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef MSP_GL_INSTANCEARRAY_H_
+#define MSP_GL_INSTANCEARRAY_H_
+
+#include <vector>
+#include "programdata.h"
+#include "renderable.h"
+
+namespace Msp {
+namespace GL {
+
+class Buffer;
+class Object;
+class ObjectInstance;
+class VertexArray;
+class VertexSetup;
+
+/**
+Renders multiple instances of an Object in an efficient manner.  If instanced
+rendering is supported, only one draw call per Batch needs to be issued.
+
+Changing the Mesh of the Object while an InstanceArray exists is not supported.
+*/
+class InstanceArray: public Renderable
+{
+public:
+       template<typename T>
+       class Instance: public T
+       {
+       private:
+               InstanceArray &array;
+               unsigned index;
+
+       public:
+               Instance(const Object &o, InstanceArray &a, unsigned i): T(o), array(a), index(i) { }
+
+               virtual void set_matrix(const Matrix &);
+       };
+
+private:
+       const Object &object;
+       std::vector<ObjectInstance *> instances;
+       VertexArray *instance_data;
+       Buffer *instance_buffer;
+       VertexSetup *vtx_setup;
+       int matrix_location;
+       unsigned matrix_offset;
+
+public:
+       InstanceArray(const Object &);
+       ~InstanceArray();
+
+       void set_matrix_attribute(const std::string &);
+
+       template<typename T = ObjectInstance>
+       T &append();
+private:
+       void append(ObjectInstance *);
+       void update_instance_matrix(unsigned);
+public:
+       void remove(ObjectInstance &);
+
+       virtual void render(Renderer &, const Tag &) const;
+};
+
+template<typename T>
+T &InstanceArray::append()
+{
+       Instance<T> *inst = new Instance<T>(object, *this, instances.size());
+       append(inst);
+       return *inst;
+}
+
+template<typename T>
+void InstanceArray::Instance<T>::set_matrix(const Matrix &m)
+{
+       T::set_matrix(m);
+       array.update_instance_matrix(index);
+}
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/render/instancescene.cpp b/source/render/instancescene.cpp
new file mode 100644 (file)
index 0000000..b533bae
--- /dev/null
@@ -0,0 +1,57 @@
+#include "object.h"
+#include "objectinstance.h"
+#include "instancescene.h"
+#include "renderer.h"
+
+namespace Msp {
+namespace GL {
+
+void InstanceScene::add(Renderable &r)
+{
+       renderables[r.get_instance_key()].insert(&r);
+}
+
+void InstanceScene::remove(Renderable &r)
+{
+       InstanceMap::iterator i = renderables.find(r.get_instance_key());
+       if(i!=renderables.end())
+       {
+               i->second.erase(&r);
+               if(i->second.empty())
+                       renderables.erase(i);
+       }
+}
+
+void InstanceScene::setup_frame(Renderer &renderer)
+{
+       for(InstanceMap::const_iterator i=renderables.begin(); i!=renderables.end(); ++i)
+               for(RenderableSet::const_iterator j=i->second.begin(); j!=i->second.end(); ++j)
+                       (*j)->setup_frame(renderer);
+}
+
+void InstanceScene::finish_frame()
+{
+       for(InstanceMap::const_iterator i=renderables.begin(); i!=renderables.end(); ++i)
+               for(RenderableSet::const_iterator j=i->second.begin(); j!=i->second.end(); ++j)
+                       (*j)->finish_frame();
+}
+
+void InstanceScene::render(Renderer &renderer, const Tag &tag) const
+{
+       if(setup_frustum(renderer))
+       {
+               for(InstanceMap::const_iterator i=renderables.begin(); i!=renderables.end(); ++i)
+                       for(RenderableSet::const_iterator j=i->second.begin(); j!=i->second.end(); ++j)
+                               if(!frustum_cull(**j))
+                                       renderer.render(**j, tag);
+       }
+       else
+       {
+               for(InstanceMap::const_iterator i=renderables.begin(); i!=renderables.end(); ++i)
+                       for(RenderableSet::const_iterator j=i->second.begin(); j!=i->second.end(); ++j)
+                               renderer.render(**j, tag);
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/render/instancescene.h b/source/render/instancescene.h
new file mode 100644 (file)
index 0000000..a5ac2cd
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef MSP_GL_INSTANCESCENE_H_
+#define MSP_GL_INSTANCESCENE_H_
+
+#include <map>
+#include <set>
+#include <msp/core/inttypes.h>
+#include "scene.h"
+
+namespace Msp {
+namespace GL {
+
+/**
+A Scene optimized for rendering instanced Renderables, such as ObjectInstances.
+All Renderables with the same instance key are rendered consecutively; within
+the same key rendering order is unspecified.
+*/
+class InstanceScene: public Scene
+{
+private:
+       typedef std::set<Renderable *> RenderableSet;
+       typedef std::map<IntPtr, RenderableSet> InstanceMap;
+
+       InstanceMap renderables;
+
+public:
+       virtual void add(Renderable &);
+       virtual void remove(Renderable &);
+
+       virtual void setup_frame(Renderer &);
+       virtual void finish_frame();
+
+       virtual void render(Renderer &, const Tag &tag = Tag()) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/render/object.cpp b/source/render/object.cpp
new file mode 100644 (file)
index 0000000..dc1f4f3
--- /dev/null
@@ -0,0 +1,271 @@
+#include <msp/datafile/collection.h>
+#include <msp/strings/format.h>
+#include "error.h"
+#include "material.h"
+#include "mesh.h"
+#include "object.h"
+#include "objectinstance.h"
+#include "program.h"
+#include "programdata.h"
+#include "renderer.h"
+#include "resourcemanager.h"
+#include "technique.h"
+#include "texturing.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Matrix Object::identity_matrix;
+
+Object::Object():
+       lods(1),
+       lod0_watched(false)
+{ }
+
+Object::Object(const Mesh *m, const Technique *t):
+       lods(1),
+       lod0_watched(false)
+{
+       set_mesh(m);
+       set_technique(t);
+}
+
+// TODO should have copy-c'tor to set watch on lod0 mesh if necessary
+
+Object::~Object()
+{
+       if(lods[0].mesh && lod0_watched)
+               if(ResourceManager *rm = lods[0].mesh->get_manager())
+                       rm->unobserve_resource(*lods[0].mesh, *this);
+}
+
+Object::LevelOfDetail &Object::get_lod(unsigned i, const char *caller)
+{
+       if(i>lods.size())
+               throw out_of_range(caller);
+       if(i>0 && (!lods[0].mesh || !lods[0].technique))
+               throw invalid_operation(caller);
+
+       if(i==lods.size())
+               lods.push_back(lods.back());
+
+       return lods[i];
+}
+
+void Object::set_mesh(unsigned i, const Mesh *m)
+{
+       RefPtr<const Mesh> &ptr = get_lod(i, "Object::set_mesh").mesh;
+       if(i==0 && ptr && lod0_watched)
+               if(ResourceManager *rm = ptr->get_manager())
+                       rm->unobserve_resource(*ptr, *this);
+       ptr = m;
+       ptr.keep();
+       lod0_watched = false;
+
+       if(i==0 && m)
+               if(ResourceManager *rm = m->get_manager())
+               {
+                       rm->observe_resource(*m, *this);
+                       lod0_watched = true;
+               }
+
+       update_bounding_sphere();
+}
+
+void Object::update_bounding_sphere()
+{
+       vector<Vector3> points;
+       for(vector<LevelOfDetail>::const_iterator i=lods.begin(); i!=lods.end(); ++i)
+       {
+               if(!i->mesh || !i->mesh->is_loaded())
+                       continue;
+
+               const VertexArray &vertices = i->mesh->get_vertices();
+
+               int offset = vertices.get_format().offset(VERTEX3);
+               bool three = true;
+               if(offset<0)
+               {
+                       offset = vertices.get_format().offset(VERTEX2);
+                       three = false;
+                       if(offset<0)
+                               continue;
+               }
+
+               unsigned n_vertices = vertices.size();
+               points.reserve(points.size()+n_vertices);
+               for(unsigned j=0; j<n_vertices; ++j)
+               {
+                       const float *v = vertices[j];
+                       points.push_back(Vector3(v[offset], v[offset+1], (three ? v[offset+2] : 0.0f)));
+               }
+       }
+
+       /* Don't touch the bounding sphere if we had no vertices to avoid
+       overwriting a possible hint. */
+       if(points.empty())
+               return;
+
+       bounding_sphere = Geometry::BoundingSphere<float, 3>::from_point_cloud(points.begin(), points.end());
+}
+
+const Mesh *Object::get_mesh(unsigned i) const
+{
+       if(i>=lods.size())
+               return 0;
+
+       return lods[i].mesh.get();
+}
+
+void Object::set_technique(unsigned i, const Technique *t)
+{
+       RefPtr<const Technique> &ptr = get_lod(i, "Object::set_technique").technique;
+       ptr = t;
+       ptr.keep();
+}
+
+const Technique *Object::get_technique(unsigned i) const
+{
+       if(i>=lods.size())
+               return 0;
+
+       return lods[i].technique.get();
+}
+
+void Object::render(Renderer &renderer, const Tag &tag) const
+{
+       const RenderPass *pass = get_pass(tag, 0);
+       if(!pass)
+               return;
+
+       const Mesh *mesh = lods.front().mesh.get();
+       if (!mesh)
+               throw logic_error("no mesh");
+
+       Renderer::Push push(renderer);
+       pass->apply(renderer);
+
+       setup_render(renderer, tag);
+       mesh->draw(renderer);
+       finish_render(renderer, tag);
+}
+
+void Object::render(Renderer &renderer, const ObjectInstance &inst, const Tag &tag) const
+{
+       unsigned lod = min<unsigned>(inst.get_level_of_detail(renderer), lods.size()-1);
+       const RenderPass *pass = get_pass(tag, lod);
+       if(!pass)
+               return;
+
+       const Mesh *mesh = lods[lod].mesh.get();
+       if (!mesh)
+               throw logic_error("no mesh");
+
+       Renderer::Push push(renderer);
+       pass->apply(renderer);
+
+       setup_render(renderer, tag);
+       inst.setup_render(renderer, tag);
+       mesh->draw(renderer);
+       inst.finish_render(renderer, tag);
+       finish_render(renderer, tag);
+}
+
+const RenderPass *Object::get_pass(const Tag &tag, unsigned lod) const
+{
+       const Technique *tech = lods[lod].technique.get();
+       if(!tech)
+               throw logic_error("no technique");
+       return tech->find_pass(tag);
+}
+
+void Object::resource_loaded(Resource &res)
+{
+       if(&res==lods.front().mesh.get() && bounding_sphere.is_empty())
+               update_bounding_sphere();
+}
+
+void Object::resource_removed(Resource &res)
+{
+       if(&res==lods.front().mesh.get())
+               lod0_watched = false;
+}
+
+
+Object::Loader::Loader(Object &o):
+       LodLoader(o, 0, 0)
+{
+       init();
+}
+
+Object::Loader::Loader(Object &o, Collection &c):
+       LodLoader(o, 0, &c)
+{
+       init();
+}
+
+void Object::Loader::init()
+{
+       add("bounding_sphere_hint", &Loader::bounding_sphere_hint);
+       add("level_of_detail", &Loader::level_of_detail);
+}
+
+void Object::Loader::finish()
+{
+       obj.update_bounding_sphere();
+}
+
+void Object::Loader::bounding_sphere_hint(float x, float y, float z, float r)
+{
+       obj.bounding_sphere = Geometry::BoundingSphere<float, 3>(Vector3(x, y, z), r);
+}
+
+void Object::Loader::level_of_detail(unsigned i)
+{
+       LodLoader ldr(obj, i, coll);
+       load_sub_with(ldr);
+}
+
+
+Object::LodLoader::LodLoader(Object &o, unsigned i, Collection *c):
+       DataFile::CollectionObjectLoader<Object>(o, c),
+       index(i),
+       lod(obj.get_lod(index, "Object::LodLoader::LodLoader"))
+{
+       add("mesh",      &LodLoader::mesh_inline);
+       add("mesh",      &LodLoader::mesh);
+       add("technique", &LodLoader::technique_inline);
+       add("technique", &LodLoader::technique);
+}
+
+void Object::LodLoader::mesh(const string &n)
+{
+       obj.set_mesh(index, &get_collection().get<Mesh>(n));
+}
+
+void Object::LodLoader::mesh_inline()
+{
+       RefPtr<Mesh> msh = new Mesh;
+       load_sub(*msh);
+       lod.mesh = msh;
+}
+
+void Object::LodLoader::technique(const std::string &n)
+{
+       obj.set_technique(index, &get_collection().get<Technique>(n));
+}
+
+void Object::LodLoader::technique_inline()
+{
+       RefPtr<Technique> tech = new Technique;
+       if(coll)
+               load_sub(*tech, get_collection());
+       else
+               load_sub(*tech);
+       lod.technique = tech;
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/render/object.h b/source/render/object.h
new file mode 100644 (file)
index 0000000..48e3045
--- /dev/null
@@ -0,0 +1,133 @@
+#ifndef MSP_GL_OBJECT_H_
+#define MSP_GL_OBJECT_H_
+
+#include <vector>
+#include "bindable.h"
+#include "renderable.h"
+#include "renderpass.h"
+#include "resourceobserver.h"
+
+namespace Msp {
+namespace GL {
+
+class Material;
+class Mesh;
+class ObjectInstance;
+class Technique;
+class Texture;
+
+/**
+Combines a Mesh with a Technique to give it an appearance.  The Technique will
+define which render passes the Object supports.
+
+In many cases, it's desirable to include multiple copies of an Object in a
+Scene, with different model matrices.  ObjectInstances can be used to alter the
+rendering of an object on a per-instance basis.
+
+Objects can have multiple levels of detail.  The most detailed level has index
+0, with increasing indices having less detail.  When rendering an instance, the
+instance's get_level_of_detail method is called to determine which LoD to use.
+*/
+class Object: public Renderable, private ResourceObserver
+{
+private:
+       struct LevelOfDetail;
+
+       class LodLoader: public DataFile::CollectionObjectLoader<Object>
+       {
+       private:
+               unsigned index;
+               LevelOfDetail &lod;
+
+       public:
+               LodLoader(Object &, unsigned, Collection *);
+
+       private:
+               void mesh(const std::string &);
+               void mesh_inline();
+               void technique(const std::string &);
+               void technique_inline();
+       };
+
+public:
+       class Loader: public LodLoader
+       {
+       public:
+               Loader(Object &);
+               Loader(Object &, Collection &);
+       private:
+               void init();
+               virtual void finish();
+
+               void bounding_sphere_hint(float, float, float, float);
+               void level_of_detail(unsigned);
+       };
+
+private:
+       struct LevelOfDetail
+       {
+               RefPtr<const Mesh> mesh;
+               RefPtr<const Technique> technique;
+       };
+
+       std::vector<LevelOfDetail> lods;
+       Geometry::BoundingSphere<float, 3> bounding_sphere;
+       bool lod0_watched;
+
+       static Matrix identity_matrix;
+
+public:
+       Object();
+       Object(const Mesh *, const Technique *);
+       ~Object();
+
+private:
+       LevelOfDetail &get_lod(unsigned, const char *);
+
+public:
+       /** Sets the mesh for the highest level of detail (index 0). */
+       void set_mesh(const Mesh *m) { set_mesh(0, m); }
+
+       /** Sets the mesh for a given level of detail.  Previous LoDs must have been
+       defined. */
+       void set_mesh(unsigned, const Mesh *);
+
+private:
+       void update_bounding_sphere();
+public:
+       const Mesh *get_mesh(unsigned = 0) const;
+
+       /** Sets the technique for the highest level of detail (index 0). */
+       void set_technique(const Technique *t) { set_technique(0, t); }
+
+       /** Sets the technique for a given level of detail.  Previous LoDs must have
+       been defined. */
+       void set_technique(unsigned, const Technique *);
+
+       const Technique *get_technique(unsigned = 0) const;
+       unsigned get_n_lods() const { return lods.size(); }
+
+       virtual const Matrix *get_matrix() const { return &identity_matrix; }
+       virtual const Geometry::BoundingSphere<float, 3> *get_bounding_sphere() const { return &bounding_sphere; }
+
+       virtual void render(Renderer &, const Tag & = Tag()) const;
+
+       /** Renders an instance of the object.  The instance's hook functions are
+       called before and after drawing the mesh. */
+       virtual void render(Renderer &, const ObjectInstance &, const Tag & = Tag()) const;
+
+protected:
+       virtual void setup_render(Renderer &, const Tag &) const { }
+       virtual void finish_render(Renderer &, const Tag &) const { }
+
+private:
+       const RenderPass *get_pass(const Tag &, unsigned) const;
+
+       virtual void resource_loaded(Resource &);
+       virtual void resource_removed(Resource &);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/render/objectinstance.cpp b/source/render/objectinstance.cpp
new file mode 100644 (file)
index 0000000..dbbb85b
--- /dev/null
@@ -0,0 +1,44 @@
+#include "objectinstance.h"
+#include "renderer.h"
+#include "transform.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+ObjectInstance::ObjectInstance(const Object &obj):
+       object(obj)
+{ }
+
+void ObjectInstance::render(Renderer &renderer, const Tag &tag) const
+{
+       object.render(renderer, *this, tag);
+}
+
+void ObjectInstance::setup_render(Renderer &renderer, const Tag &) const
+{
+       renderer.transform(matrix);
+}
+
+unsigned ObjectInstance::get_level_of_detail(const Renderer &renderer) const
+{
+       return renderer.get_object_lod_bias();
+}
+
+
+ObjectInstance::Loader::Loader(ObjectInstance &o):
+       DataFile::ObjectLoader<ObjectInstance>(o)
+{
+       add("transform", &Loader::transform);
+}
+
+void ObjectInstance::Loader::transform()
+{
+       Transform trn;
+       load_sub(trn);
+       obj.matrix = trn.to_matrix();
+}
+
+} // namespace GL
+} // namespaec Msp
diff --git a/source/render/objectinstance.h b/source/render/objectinstance.h
new file mode 100644 (file)
index 0000000..0ebef2f
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef MSP_GL_OBJETCINSTANCE_H_
+#define MSP_GL_OBJETCINSTANCE_H_
+
+#include <string>
+#include "object.h"
+#include "placeable.h"
+
+namespace Msp {
+namespace GL {
+
+/**
+Represents a single instance of an Object.  Thanks to being derived from
+Placeable in can be positioned without additional effort.  Other instance
+parameters can be set by overriding the hook functions.
+
+ObjectInstances can benefit from being put in an InstanceScene, which will
+render all instances of the same object consecutively.
+*/
+class ObjectInstance: public PlacedRenderable
+{
+public:
+       class Loader: public DataFile::ObjectLoader<ObjectInstance>
+       {
+       public:
+               Loader(ObjectInstance &);
+
+       private:
+               void transform();
+       };
+
+protected:
+       const Object &object;
+
+public:
+       ObjectInstance(const Object &);
+
+       const Object &get_object() const { return object; }
+       virtual IntPtr get_instance_key() const { return reinterpret_cast<IntPtr>(&object); }
+
+       virtual const Geometry::BoundingSphere<float, 3> *get_bounding_sphere() const { return object.get_bounding_sphere(); }
+
+       virtual void render(Renderer &, const Tag & = Tag()) const;
+
+       /** Hook function, called from Object just before rendering the mesh.
+       Renderer state will have been pushed before this is called. */
+       virtual void setup_render(Renderer &, const Tag &) const;
+
+       /** Hook function, called from Object right after rendering the mesh.  Since
+       Object takes care of pushing Renderer state, this rarely needs to do
+       anything. */
+       virtual void finish_render(Renderer &, const Tag &) const { }
+
+       virtual unsigned get_level_of_detail(const Renderer &) const;
+};
+
+} // namespace GL
+} // namespaec Msp
+
+#endif
diff --git a/source/render/occludedscene.cpp b/source/render/occludedscene.cpp
new file mode 100644 (file)
index 0000000..143008c
--- /dev/null
@@ -0,0 +1,190 @@
+#include <algorithm>
+#include <msp/gl/extensions/arb_occlusion_query.h>
+#include <msp/gl/extensions/arb_occlusion_query2.h>
+#include "camera.h"
+#include "occludedscene.h"
+#include "renderer.h"
+#include "sphere.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+OccludedScene::OccludedScene():
+       bounding_mesh((VERTEX3, NORMAL3)),
+       bounding_shader("occluder.glsl"),
+       occluder_min_size(0.25f),
+       cache_dirty(false)
+{
+       static Require req(ARB_occlusion_query);
+       static Require req2(ARB_occlusion_query2);
+
+       /* Use a slightly larger radius to ensure that all parts of the renderable
+       fit inside the icosahedron */
+       IcoSphereBuilder(1.26f, 1).build(bounding_mesh);
+       bounding_mesh.set_winding(&WindingTest::counterclockwise());
+}
+
+OccludedScene::~OccludedScene()
+{
+       vector<unsigned> queries;
+       queries.reserve(occluded_cache.size());
+       for(OccludedArray::iterator i=occluded_cache.begin(); i!=occluded_cache.end(); ++i)
+               queries.push_back(i->query);
+       glDeleteQueries(queries.size(), &queries[0]);
+}
+
+void OccludedScene::add(Renderable &r)
+{
+       renderables.insert(&r);
+       cache_dirty = true;
+}
+
+void OccludedScene::remove(Renderable &r)
+{
+       renderables.erase(&r);
+       cache_dirty = true;
+}
+
+void OccludedScene::populate_cache() const
+{
+       if(!cache_dirty)
+               return;
+
+       if(occluded_cache.size()<renderables.size())
+       {
+               unsigned old_size = occluded_cache.size();
+               occluded_cache.resize(renderables.size());
+               vector<unsigned> new_queries(occluded_cache.size()-old_size);
+               glGenQueries(new_queries.size(), &new_queries[0]);
+               for(unsigned i=0; i<new_queries.size(); ++i)
+                       occluded_cache[old_size+i].query = new_queries[i];
+       }
+
+       OccludedArray::iterator j = occluded_cache.begin();
+       for(RenderableSet::iterator i=renderables.begin(); i!=renderables.end(); ++i, ++j)
+               j->renderable = *i;
+       for(; j!=occluded_cache.end(); ++j)
+       {
+               j->renderable = 0;
+               j->in_frustum = false;
+       }
+
+       cache_dirty = false;
+}
+
+void OccludedScene::setup_frame(Renderer &renderer)
+{
+       populate_cache();
+       for(OccludedArray::const_iterator i=occluded_cache.begin(); i!=occluded_cache.end(); ++i)
+               i->renderable->setup_frame(renderer);
+}
+
+void OccludedScene::finish_frame()
+{
+       for(OccludedArray::const_iterator i=occluded_cache.begin(); i!=occluded_cache.end(); ++i)
+               i->renderable->finish_frame();
+}
+
+void OccludedScene::render(Renderer &renderer, const Tag &tag) const
+{
+       if(renderables.empty())
+               return;
+
+       populate_cache();
+
+       const Camera *camera = renderer.get_camera();
+       if(!camera)
+       {
+               for(OccludedArray::const_iterator i=occluded_cache.begin(); i!=occluded_cache.end(); ++i)
+                       renderer.render(*i->renderable, tag);
+               return;
+       }
+
+       const Vector3 &camera_pos = camera->get_position();
+       const Vector3 &look_dir = camera->get_look_direction();
+       float near_clip = camera->get_near_clip();
+       float frustum_h = tan(camera->get_field_of_view()/2.0f)*2.0f;
+
+       // Perform frustum culling and render any major occluders
+       bool use_frustum = setup_frustum(renderer);
+       for(OccludedArray::iterator i=occluded_cache.begin(); (i!=occluded_cache.end() && i->renderable); ++i)
+       {
+               i->in_frustum = (!use_frustum || !frustum_cull(*i->renderable));
+               if(!i->in_frustum)
+                       continue;
+
+               const Matrix *matrix = i->renderable->get_matrix();
+               i->bounding_sphere = i->renderable->get_bounding_sphere();
+               if(matrix && i->bounding_sphere)
+               {
+                       float depth = dot(*matrix*i->bounding_sphere->get_center()-camera_pos, look_dir);
+                       float size = i->bounding_sphere->get_radius()*2/max(depth, near_clip);
+                       i->occluder = (size>frustum_h*occluder_min_size);
+               }
+               else
+                       // If the size can't be calculated, treat it as occluder
+                       i->occluder = true;
+
+               if(i->occluder)
+                       renderer.render(*i->renderable, tag);
+       }
+
+       // Move all objects within the frustum to the beginning of the array
+       for(OccludedArray::iterator i=occluded_cache.begin(), j=i+renderables.size()-1; i!=j; )
+       {
+               if(i->in_frustum)
+                       ++i;
+               else if(j->in_frustum)
+                       swap(*i, *j);
+               else
+                       --j;
+       }
+
+       {
+               Renderer::Push push(renderer);
+               renderer.set_shader_program(&bounding_shader);
+
+               glColorMask(false, false, false, false);
+               glDepthMask(false);
+
+               // Fire off the occlusion queries
+               for(OccludedArray::const_iterator i=occluded_cache.begin(); (i!=occluded_cache.end() && i->in_frustum); ++i)
+                       if(!i->occluder)
+                       {
+                               glBeginQuery(GL_ANY_SAMPLES_PASSED, i->query);
+                               Renderer::Push push2(renderer);
+                               renderer.transform(Matrix(*i->renderable->get_matrix())
+                                       .translate(i->bounding_sphere->get_center())
+                                       .scale(i->bounding_sphere->get_radius()));
+                               bounding_mesh.draw(renderer);
+                               glEndQuery(GL_ANY_SAMPLES_PASSED);
+                       }
+
+               glColorMask(true, true, true, true);
+               glDepthMask(true);
+       }
+
+       // Render anything that has a chance of being visible
+       for(OccludedArray::const_iterator i=occluded_cache.begin(); (i!=occluded_cache.end() && i->in_frustum); ++i)
+               if(!i->occluder)
+               {
+                       unsigned any_passed = 0;
+                       glGetQueryObjectuiv(i->query, GL_QUERY_RESULT, &any_passed);
+                       if(any_passed)
+                               renderer.render(*i->renderable, tag);
+               }
+}
+
+
+OccludedScene::OccludedRenderable::OccludedRenderable():
+       renderable(0),
+       bounding_sphere(0),
+       in_frustum(false),
+       occluder(false),
+       query(0)
+{ }
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/render/occludedscene.h b/source/render/occludedscene.h
new file mode 100644 (file)
index 0000000..2898bb3
--- /dev/null
@@ -0,0 +1,61 @@
+#ifndef MSP_GL_OCCLUDEDSCENE_H_
+#define MSP_GL_OCCLUDEDSCENE_H_
+
+#include <set>
+#include <vector>
+#include "mesh.h"
+#include "program.h"
+#include "scene.h"
+
+namespace Msp {
+namespace GL {
+
+/**
+A scene that performs occlusion queries on renderables to skip those that are
+entirely occluded by others.
+*/
+class OccludedScene: public Scene
+{
+private:
+       struct OccludedRenderable
+       {
+               Renderable *renderable;
+               const Geometry::BoundingSphere<float, 3> *bounding_sphere;
+               bool in_frustum;
+               bool occluder;
+               unsigned query;
+
+               OccludedRenderable();
+       };
+
+       typedef std::set<Renderable *> RenderableSet;
+       typedef std::vector<OccludedRenderable> OccludedArray;
+
+       Mesh bounding_mesh;
+       Program bounding_shader;
+       RenderableSet renderables;
+       float occluder_min_size;
+       mutable OccludedArray occluded_cache;
+       mutable bool cache_dirty;
+
+public:
+       OccludedScene();
+       ~OccludedScene();
+
+       virtual void add(Renderable &);
+       virtual void remove(Renderable &);
+
+private:
+       void populate_cache() const;
+
+public:
+       virtual void setup_frame(Renderer &);
+       virtual void finish_frame();
+
+       virtual void render(Renderer &, const Tag & = Tag()) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/render/offscreenview.cpp b/source/render/offscreenview.cpp
new file mode 100644 (file)
index 0000000..6d0d88b
--- /dev/null
@@ -0,0 +1,16 @@
+#include "offscreenview.h"
+#include "rendertarget.h"
+
+namespace Msp {
+namespace GL {
+
+OffscreenView::OffscreenView(Framebuffer &t):
+       View(t)
+{ }
+
+OffscreenView::OffscreenView(RenderTarget &t):
+       View(t.get_framebuffer())
+{ }
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/render/offscreenview.h b/source/render/offscreenview.h
new file mode 100644 (file)
index 0000000..3de47f1
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef MSP_GL_OFFSCREENVIEW_H_
+#define MSP_GL_OFFSCREENVIEW_H_
+
+#include "view.h"
+
+namespace Msp {
+namespace GL {
+
+class Framebuffer;
+class RenderTarget;
+
+class OffscreenView: public View
+{
+public:
+       OffscreenView(Framebuffer &);
+       OffscreenView(RenderTarget &);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/render/orderedscene.cpp b/source/render/orderedscene.cpp
new file mode 100644 (file)
index 0000000..ab1ba45
--- /dev/null
@@ -0,0 +1,66 @@
+#include <algorithm>
+#include "orderedscene.h"
+#include "renderer.h"
+
+namespace Msp {
+namespace GL {
+
+void OrderedScene::add(Renderable &r)
+{
+       renderables.push_back(&r);
+}
+
+void OrderedScene::remove(Renderable &r)
+{
+       RenderableList::iterator end = std::remove(renderables.begin(), renderables.end(), &r);
+       renderables.erase(end, renderables.end());
+}
+
+void OrderedScene::prepend(Renderable &r)
+{
+       renderables.push_front(&r);
+}
+
+void OrderedScene::insert(unsigned index, Renderable &r)
+{
+       RenderableList::iterator i = renderables.begin();
+       for(; (i!=renderables.end() && index); ++i, --index) ;
+       renderables.insert(i, &r);
+}
+
+void OrderedScene::insert_after(Renderable &after, Renderable &r)
+{
+       RenderableList::iterator i = renderables.begin();
+       for(; (i!=renderables.end() && *i!=&after); ++i) ;
+       renderables.insert(i, &r);
+}
+
+void OrderedScene::setup_frame(Renderer &renderer)
+{
+       for(RenderableList::const_iterator i=renderables.begin(); i!=renderables.end(); ++i)
+               (*i)->setup_frame(renderer);
+}
+
+void OrderedScene::finish_frame()
+{
+       for(RenderableList::const_iterator i=renderables.begin(); i!=renderables.end(); ++i)
+               (*i)->finish_frame();
+}
+
+void OrderedScene::render(Renderer &renderer, const Tag &tag) const
+{
+       if(setup_frustum(renderer))
+       {
+               for(RenderableList::const_iterator i=renderables.begin(); i!=renderables.end(); ++i)
+                       if(!frustum_cull(**i))
+                               renderer.render(**i, tag);
+       }
+       else
+       {
+               for(RenderableList::const_iterator i=renderables.begin(); i!=renderables.end(); ++i)
+                       renderer.render(**i, tag);
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/render/orderedscene.h b/source/render/orderedscene.h
new file mode 100644 (file)
index 0000000..53f7b00
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef MSP_GL_ORDEREDSCENE_H_
+#define MSP_GL_ORDEREDSCENE_H_
+
+#include <list>
+#include "scene.h"
+
+namespace Msp {
+namespace GL {
+
+/**
+A scene that renders its contents in a specific order.  Inserting Renderables
+in the middle and removing them are O(N) operations.
+*/
+class OrderedScene: public Scene
+{
+private:
+       typedef std::list<Renderable *> RenderableList;
+
+       RenderableList renderables;
+
+public:
+       virtual void add(Renderable &);
+       virtual void remove(Renderable &);
+       void prepend(Renderable &);
+       void insert(unsigned, Renderable &);
+       void insert_after(Renderable &, Renderable &);
+
+       virtual void setup_frame(Renderer &);
+       virtual void finish_frame();
+
+       using Scene::render;
+       virtual void render(Renderer &, const Tag & = Tag()) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/render/pipeline.cpp b/source/render/pipeline.cpp
new file mode 100644 (file)
index 0000000..a7f4130
--- /dev/null
@@ -0,0 +1,287 @@
+#include <msp/core/maputils.h>
+#include "blend.h"
+#include "camera.h"
+#include "framebuffer.h"
+#include "lighting.h"
+#include "pipeline.h"
+#include "postprocessor.h"
+#include "renderbuffer.h"
+#include "renderer.h"
+#include "tests.h"
+#include "texture2d.h"
+#include "view.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Pipeline::Pipeline(unsigned w, unsigned h, bool d)
+{
+       init(w, h);
+       hdr = d;
+}
+
+Pipeline::Pipeline(const View &view)
+{
+       init(view.get_width(), view.get_height());
+}
+
+Pipeline::Pipeline(const Framebuffer &fbo)
+{
+       init(fbo.get_width(), fbo.get_height());
+}
+
+void Pipeline::init(unsigned w, unsigned h)
+{
+       camera = 0;
+       width = w;
+       height = h;
+       hdr = false;
+       alpha = false;
+       samples = 0;
+       target_ms = 0;
+       target[0] = 0;
+       target[1] = 0;
+}
+
+Pipeline::~Pipeline()
+{
+       delete target[0];
+       delete target[1];
+       delete target_ms;
+}
+
+void Pipeline::set_hdr(bool h)
+{
+       if(h==hdr)
+               return;
+
+       bool old_hdr = hdr;
+       hdr = h;
+       try
+       {
+               create_targets(2);
+       }
+       catch(...)
+       {
+               hdr = old_hdr;
+               throw;
+       }
+}
+
+void Pipeline::set_alpha(bool a)
+{
+       if(a==alpha)
+               return;
+
+       bool old_alpha = alpha;
+       alpha = a;
+       try
+       {
+               create_targets(2);
+       }
+       catch(...)
+       {
+               alpha = old_alpha;
+               throw;
+       }
+}
+
+void Pipeline::set_multisample(unsigned s)
+{
+       if(s==samples)
+               return;
+
+       unsigned old_samples = samples;
+       samples = s;
+       try
+       {
+               create_targets(1);
+       }
+       catch(...)
+       {
+               samples = old_samples;
+               throw;
+       }
+}
+
+Pipeline::Pass &Pipeline::add_pass(const Tag &tag, Renderable &r)
+{
+       passes.push_back(Pass(tag, &r));
+       return passes.back();
+}
+
+void Pipeline::add_postprocessor(PostProcessor &pp)
+{
+       add_postprocessor(&pp, true);
+}
+
+void Pipeline::add_postprocessor_owned(PostProcessor *pp)
+{
+       add_postprocessor(pp, false);
+}
+
+void Pipeline::add_postprocessor(PostProcessor *pp, bool keep)
+{
+       postproc.push_back(pp);
+       if(keep)
+               postproc.back().keep();
+       try
+       {
+               create_targets(0);
+       }
+       catch(...)
+       {
+               postproc.pop_back();
+               throw;
+       }
+}
+
+void Pipeline::setup_frame(Renderer &renderer)
+{
+       for(PassList::const_iterator i=passes.begin(); i!=passes.end(); ++i)
+               if(Renderable *renderable = i->get_renderable())
+                       renderable->setup_frame(renderer);
+       for(vector<Slot>::const_iterator i=renderables.begin(); i!=renderables.end(); ++i)
+               i->renderable->setup_frame(renderer);
+}
+
+void Pipeline::finish_frame()
+{
+       for(PassList::const_iterator i=passes.begin(); i!=passes.end(); ++i)
+               if(Renderable *renderable = i->get_renderable())
+                       renderable->finish_frame();
+       for(vector<Slot>::const_iterator i=renderables.begin(); i!=renderables.end(); ++i)
+               i->renderable->finish_frame();
+}
+
+void Pipeline::render(Renderer &renderer, const Tag &tag) const
+{
+       if(tag.id)
+               return;
+
+       const Framebuffer *out_fbo = Framebuffer::current();
+       // These are no-ops but will ensure the related state gets restored
+       BindRestore restore_fbo(out_fbo);
+       BindRestore restore_depth_test(DepthTest::current());
+       BindRestore restore_blend(Blend::current());
+
+       if(target[0])
+       {
+               Framebuffer &fbo = (samples ? target_ms : target[0])->get_framebuffer();
+               fbo.bind();
+               fbo.clear();
+       }
+
+       for(PassList::const_iterator i=passes.begin(); i!=passes.end(); ++i)
+       {
+               if(const DepthTest *dt = i->get_depth_test())
+                       dt->bind();
+               else
+                       DepthTest::unbind();
+
+               if(const Blend *b = i->get_blend())
+                       b->bind();
+               else
+                       Blend::unbind();
+
+               renderer.set_lighting(i->get_lighting());
+               renderer.set_clipping(i->get_clipping());
+
+               if(const Renderable *renderable = i->get_renderable())
+                       renderer.render(*renderable, i->get_tag());
+
+               for(vector<Slot>::const_iterator j=renderables.begin(); j!=renderables.end(); ++j)
+                       if(j->passes.empty() || j->passes.count(i->get_tag()))
+                               renderer.render(*j->renderable, i->get_tag());
+       }
+
+       if(target[0])
+       {
+               DepthTest::unbind();
+               Blend::unbind();
+
+               if(samples)
+                       target[0]->blit_from(*target_ms);
+
+               for(unsigned i=0; i<postproc.size(); ++i)
+               {
+                       unsigned j = i%2;
+                       if(i+1<postproc.size())
+                               target[1-j]->get_framebuffer().bind();
+                       else
+                               out_fbo->bind();
+                       const Texture2D &color = target[j]->get_target_texture(RENDER_COLOR);
+                       const Texture2D &depth = target[j]->get_target_texture(RENDER_DEPTH);
+                       postproc[i]->render(renderer, color, depth);
+               }
+       }
+}
+
+void Pipeline::create_targets(unsigned recreate)
+{
+       if(recreate>=2)
+       {
+               delete target[0];
+               delete target[1];
+               target[0] = 0;
+               target[1] = 0;
+       }
+       if(recreate>=1)
+       {
+               delete target_ms;
+               target_ms = 0;
+       }
+
+       PixelFormat color_pf = (hdr ? (alpha ? RGBA16F : RGB16F) : (alpha ? RGBA8 : RGB8));
+       RenderTargetFormat fmt = (RENDER_COLOR,color_pf, RENDER_DEPTH);
+       if(!postproc.empty() || samples)
+       {
+               if(!target[0])
+                       target[0] = new RenderTarget(width, height, fmt);
+               if(!target[1] && postproc.size()>1)
+                       target[1] = new RenderTarget(width, height, fmt);
+       }
+
+       if(!target_ms && samples)
+               target_ms = new RenderTarget(width, height, samples, fmt);
+}
+
+
+Pipeline::Pass::Pass(const Tag &t, Renderable *r):
+       tag(t),
+       lighting(0),
+       depth_test(0),
+       blend(0),
+       clipping(0),
+       renderable(r)
+{ }
+
+void Pipeline::Pass::set_lighting(const Lighting *l)
+{
+       lighting = l;
+}
+
+void Pipeline::Pass::set_depth_test(const DepthTest *d)
+{
+       depth_test = d;
+}
+
+void Pipeline::Pass::set_blend(const Blend *b)
+{
+       blend = b;
+}
+
+void Pipeline::Pass::set_clipping(const Clipping *c)
+{
+       clipping =c;
+}
+
+
+Pipeline::Slot::Slot(Renderable *r):
+       renderable(r)
+{ }
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/render/pipeline.h b/source/render/pipeline.h
new file mode 100644 (file)
index 0000000..dd80ef3
--- /dev/null
@@ -0,0 +1,140 @@
+#ifndef MSP_GL_PIPELINE_H_
+#define MSP_GL_PIPELINE_H_
+
+#include <map>
+#include <set>
+#include "framebuffer.h"
+#include "renderable.h"
+#include "renderbuffer.h"
+#include "rendertarget.h"
+#include "texture2d.h"
+
+namespace Msp {
+namespace GL {
+
+class Blend;
+class Camera;
+class Clipping;
+class DepthTest;
+class Lighting;
+class PostProcessor;
+class View;
+
+/**
+Top-level content class.  Typically a Pipeline is used as the content
+Renderable for a View or effects such as ShadowMap or EnvironmentMap.
+
+A Pipeline contains a sequence of passes.  Each pass has a Renderable along
+with Lighting, Clipping, DepthTest and Blend states.  Scenes can be used to
+organize Renderables within a pass.
+
+PostProcessors can be applied after all of the passes in the Pipeline have been
+rendered.  Framebuffer objects are automatically used to pass render results to
+the PostProcessors.  High dynamic range and multisample rendering can be
+requested for increased quality.
+*/
+class Pipeline: public Renderable
+{
+public:
+       class Pass
+       {
+       private:
+               Tag tag;
+               const Lighting *lighting;
+               const DepthTest *depth_test;
+               const Blend *blend;
+               const Clipping *clipping;
+               Renderable *renderable;
+
+       public:
+               Pass(const Tag &, Renderable *);
+
+               const Tag &get_tag() const { return tag; }
+
+               void set_lighting(const Lighting *);
+               void set_depth_test(const DepthTest *);
+               void set_blend(const Blend *);
+               void set_clipping(const Clipping *);
+               const Lighting *get_lighting() const { return lighting; }
+               const DepthTest *get_depth_test() const { return depth_test; }
+               const Blend *get_blend() const { return blend; }
+               const Clipping *get_clipping() const { return clipping; }
+               Renderable *get_renderable() const { return renderable; }
+       };
+
+private:
+       struct Slot
+       {
+               Renderable *renderable;
+               std::set<Tag> passes;
+
+               Slot(Renderable *);
+       };
+
+       typedef std::list<Pass> PassList;
+
+       PassList passes;
+       const Camera *camera;
+       std::vector<Slot> renderables;
+       std::vector<RefPtr<PostProcessor> > postproc;
+       unsigned width;
+       unsigned height;
+       bool hdr;
+       bool alpha;
+       unsigned samples;
+       RenderTarget *target[2];
+       RenderTarget *target_ms;
+
+public:
+       Pipeline(unsigned, unsigned, bool = false);
+       Pipeline(const View &);
+       Pipeline(const Framebuffer &);
+private:
+       void init(unsigned, unsigned);
+public:
+       ~Pipeline();
+
+       /* Sets high dynamic range mode.  Requires floating-point texture support.
+       A ColorCurve postprocessor is recommended for full benefit. */
+       void set_hdr(bool);
+
+       /* Enable or disable alpha channel.  When enabled, all render targets are
+       created with an RGBA pixel format instead of RGB. */
+       void set_alpha(bool);
+
+       void set_multisample(unsigned);
+
+       unsigned get_width() const { return width; }
+       unsigned get_height() const { return height; }
+       bool get_hdr() const { return hdr; }
+       unsigned get_multisample() const { return samples; }
+
+       /** Adds a pass to the pipeline.  It's permissible to add the same
+       Renderable multiple times. */
+       Pass &add_pass(const Tag &, Renderable &);
+
+       /** Adds a postprocessor to the pipeline. */
+       void add_postprocessor(PostProcessor &);
+
+       /** Adds a postprocessor to the pipeline, transferring ownership.  The
+       postprocessor will be deleted together with with pipeline.  It is also
+       deleted if this call throws an exception. */
+       void add_postprocessor_owned(PostProcessor *);
+
+private:
+       void add_postprocessor(PostProcessor *, bool);
+
+public:
+       virtual void setup_frame(Renderer &);
+       virtual void finish_frame();
+
+       virtual void render(Renderer &, const Tag &tag = Tag()) const;
+
+private:
+       void create_targets(unsigned);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/render/placeable.cpp b/source/render/placeable.cpp
new file mode 100644 (file)
index 0000000..c7ea792
--- /dev/null
@@ -0,0 +1,12 @@
+#include "placeable.h"
+
+namespace Msp {
+namespace GL {
+
+void Placeable::set_matrix(const Matrix &m)
+{
+       matrix = m;
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/render/placeable.h b/source/render/placeable.h
new file mode 100644 (file)
index 0000000..35999a0
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef MSP_GL_PLACEABLE_H_
+#define MSP_GL_PLACEABLE_H_
+
+#include "matrix.h"
+#include "renderable.h"
+
+namespace Msp {
+namespace GL {
+
+/**
+A base class for things that can be positioned and oriented in 3D space.
+*/
+class Placeable
+{
+protected:
+       Matrix matrix;
+
+       Placeable() { }
+public:
+       virtual ~Placeable() { }
+
+       virtual void set_matrix(const Matrix &);
+
+       /** Returns the Placeable's matrix.  This function returns a pointer for
+       compatibility with Renderable.  The returned pointer is never null. */
+       virtual const Matrix *get_matrix() const { return &matrix; }
+};
+
+
+class PlacedRenderable: public Renderable, public Placeable
+{
+protected:
+       PlacedRenderable() { }
+
+public:
+       /* Reimplement to clear ambiguity between Renderable and Placeable.  This
+       overrides both base classes' implementations. */
+       virtual const Matrix *get_matrix() const { return &matrix; }
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/render/programdata.cpp b/source/render/programdata.cpp
new file mode 100644 (file)
index 0000000..5f124a9
--- /dev/null
@@ -0,0 +1,962 @@
+#include <msp/core/maputils.h>
+#include <msp/debug/demangle.h>
+#include <msp/gl/extensions/arb_direct_state_access.h>
+#include <msp/io/print.h>
+#include "buffer.h"
+#include "color.h"
+#include "error.h"
+#include "matrix.h"
+#include "program.h"
+#include "programdata.h"
+#include "uniform.h"
+#include "uniformblock.h"
+#include "vector.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+ProgramData::ProgramData(const Program *p):
+       tied_program(p),
+       last_block(0),
+       buffer(0),
+       dirty(0)
+{ }
+
+// Blocks are intentionally left uncopied
+ProgramData::ProgramData(const ProgramData &other):
+       tied_program(other.tied_program),
+       uniforms(other.uniforms),
+       last_block(0),
+       buffer(0),
+       dirty(0)
+{
+       for(vector<NamedUniform>::iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
+               i->value = i->value->clone();
+}
+
+ProgramData::ProgramData(const ProgramData &other, const Program *p):
+       tied_program(p),
+       last_block(0),
+       buffer(0),
+       dirty(0)
+{
+       if(tied_program)
+       {
+               for(vector<NamedUniform>::const_iterator i=other.uniforms.begin(); i!=other.uniforms.end(); ++i)
+                       tied_program->get_uniform_info(i->name);
+       }
+
+       uniforms = other.uniforms;
+       for(vector<NamedUniform>::iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
+               i->value = i->value->clone();
+}
+
+ProgramData &ProgramData::operator=(const ProgramData &other)
+{
+       tied_program = other.tied_program;
+
+       uniforms = other.uniforms;
+       for(vector<NamedUniform>::iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
+               i->value = i->value->clone();
+
+       for(BlockMap::iterator i=blocks.begin(); i!=blocks.end(); ++i)
+               delete i->second.block;
+       programs.clear();
+
+       last_block = 0;
+       buffer = 0;
+       dirty = 0;
+
+       return *this;
+}
+
+ProgramData::~ProgramData()
+{
+       for(vector<NamedUniform>::iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
+               delete i->value;
+       for(BlockMap::iterator i=blocks.begin(); i!=blocks.end(); ++i)
+       {
+               if(i->second.indices.type_flag==0xFE)
+                       delete i->second.indices.dynamic.values;
+               delete i->second.block;
+       }
+       delete buffer;
+}
+
+void ProgramData::uniform(const string &name, Uniform *uni)
+{
+       try
+       {
+               if(!validate_name(name))
+               {
+                       delete uni;
+                       return;
+               }
+       }
+       catch(...)
+       {
+               delete uni;
+               throw;
+       }
+
+       int i = find_uniform_index(name);
+       if(i<0)
+               return add_uniform(name, uni);
+
+       uniforms[i].replace_value(uni);
+       dirty |= 1<<i;
+}
+
+template<typename T, typename V>
+void ProgramData::uniform(const string &name, V value)
+{
+       if(!validate_name(name))
+               return;
+
+       int i = find_uniform_index(name);
+       if(i<0)
+               return add_uniform(name, new T(value));
+
+       if(T *uni = dynamic_cast<T *>(uniforms[i].value))
+               uni->set(value);
+       else
+               uniforms[i].replace_value(new T(value));
+
+       dirty |= 1<<i;
+}
+
+template<typename T, typename V>
+void ProgramData::uniform_array(const string &name, unsigned n, V value)
+{
+       if(!validate_name(name))
+               return;
+
+       int i = find_uniform_index(name);
+       if(i<0)
+               return add_uniform(name, new UniformArray<T>(n, value));
+
+       UniformArray<T> *uni = dynamic_cast<UniformArray<T> *>(uniforms[i].value);
+       if(uni && n==uni->size())
+               uni->set(value);
+       else
+               uniforms[i].replace_value(new UniformArray<T>(n, value));
+
+       dirty |= 1<<i;
+}
+
+bool ProgramData::validate_name(const string &name) const
+{
+#ifdef DEBUG
+       try
+#endif
+       {
+               if(tied_program)
+                       tied_program->get_uniform_info(name);
+               else if(name[name.size()-1]==']')
+                       throw invalid_argument("ProgramData::uniform");
+               return true;
+       }
+#ifdef DEBUG
+       catch(const exception &e)
+       {
+               IO::print(IO::cerr, "Error while setting uniform %s: %s: %s\n", name, Debug::demangle(typeid(e).name()), e.what());
+               return false;
+       }
+#endif
+}
+
+void ProgramData::add_uniform(const string &name, Uniform *uni)
+{
+       if(uniforms.size()>=MASK_BITS)
+       {
+               delete uni;
+               throw too_many_uniforms(name);
+       }
+
+       vector<NamedUniform>::iterator j = lower_bound(uniforms.begin(), uniforms.end(), name, uniform_name_compare);
+
+       NamedUniform nu;
+       nu.name = name;
+       nu.value = uni;
+       uniforms.insert(j, nu);
+
+       dirty = ALL_ONES;
+}
+
+void ProgramData::uniform(const string &name, const Uniform &u)
+{
+       uniform(name, u.clone());
+}
+
+void ProgramData::uniform(const string &name, int v)
+{
+       uniform<Uniform1i>(name, v);
+}
+
+void ProgramData::uniform(const string &name, float v)
+{
+       uniform<Uniform1f>(name, v);
+}
+
+void ProgramData::uniform(const string &name, int v0, int v1)
+{
+       int va[2] = { v0, v1 };
+       uniform2(name, va);
+}
+
+void ProgramData::uniform(const string &name, float v0, float v1)
+{
+       float va[2] = { v0, v1 };
+       uniform2(name, va);
+}
+
+void ProgramData::uniform2(const string &name, const int *v)
+{
+       uniform<Uniform2i>(name, v);
+}
+
+void ProgramData::uniform2(const string &name, const float *v)
+{
+       uniform<Uniform2f>(name, v);
+}
+
+void ProgramData::uniform(const string &name, int v0, int v1, int v2)
+{
+       int va[3] = { v0, v1, v2 };
+       uniform3(name, va);
+}
+
+void ProgramData::uniform(const string &name, float v0, float v1, float v2)
+{
+       float va[3] = { v0, v1, v2 };
+       uniform3(name, va);
+}
+
+void ProgramData::uniform(const string &name, const Vector3 &v)
+{
+       uniform(name, v.x, v.y, v.z);
+}
+
+void ProgramData::uniform3(const string &name, const int *v)
+{
+       uniform<Uniform3i>(name, v);
+}
+
+void ProgramData::uniform3(const string &name, const float *v)
+{
+       uniform<Uniform3f>(name, v);
+}
+
+void ProgramData::uniform(const string &name, int v0, int v1, int v2, int v3)
+{
+       int va[4] = { v0, v1, v2, v3 };
+       uniform4(name, va);
+}
+
+void ProgramData::uniform(const string &name, float v0, float v1, float v2, float v3)
+{
+       float va[4] = { v0, v1, v2, v3 };
+       uniform4(name, va);
+}
+
+void ProgramData::uniform(const string &name, const Vector4 &v)
+{
+       uniform(name, v.x, v.y, v.z, v.w);
+}
+
+void ProgramData::uniform(const string &name, const Color &c)
+{
+       uniform(name, c.r, c.g, c.b, c.a);
+}
+
+void ProgramData::uniform4(const string &name, const int *v)
+{
+       uniform<Uniform4i>(name, v);
+}
+
+void ProgramData::uniform4(const string &name, const float *v)
+{
+       uniform<Uniform4f>(name, v);
+}
+
+void ProgramData::uniform(const string &name, const LinAl::Matrix<float, 2, 2> &m)
+{
+       uniform_matrix2(name, &m(0, 0));
+}
+
+void ProgramData::uniform_matrix2(const string &name, const float *v)
+{
+       uniform<UniformMatrix2x2f>(name, v);
+}
+
+void ProgramData::uniform(const string &name, const LinAl::Matrix<float, 2, 3> &m)
+{
+       uniform_matrix3x2(name, &m(0, 0));
+}
+
+void ProgramData::uniform_matrix3x2(const string &name, const float *v)
+{
+       uniform<UniformMatrix3x2f>(name, v);
+}
+
+void ProgramData::uniform(const string &name, const LinAl::Matrix<float, 2, 4> &m)
+{
+       uniform_matrix4x2(name, &m(0, 0));
+}
+
+void ProgramData::uniform_matrix4x2(const string &name, const float *v)
+{
+       uniform<UniformMatrix4x2f>(name, v);
+}
+
+void ProgramData::uniform(const string &name, const LinAl::Matrix<float, 3, 2> &m)
+{
+       uniform_matrix2x3(name, &m(0, 0));
+}
+
+void ProgramData::uniform_matrix2x3(const string &name, const float *v)
+{
+       uniform<UniformMatrix2x3f>(name, v);
+}
+
+void ProgramData::uniform(const string &name, const LinAl::Matrix<float, 3, 3> &m)
+{
+       uniform_matrix3(name, &m(0, 0));
+}
+
+void ProgramData::uniform_matrix3(const string &name, const float *v)
+{
+       uniform<UniformMatrix3x3f>(name, v);
+}
+
+void ProgramData::uniform(const string &name, const LinAl::Matrix<float, 3, 4> &m)
+{
+       uniform_matrix4x3(name, &m(0, 0));
+}
+
+void ProgramData::uniform_matrix4x3(const string &name, const float *v)
+{
+       uniform<UniformMatrix4x3f>(name, v);
+}
+
+void ProgramData::uniform(const string &name, const LinAl::Matrix<float, 4, 2> &m)
+{
+       uniform_matrix2x4(name, &m(0, 0));
+}
+
+void ProgramData::uniform_matrix2x4(const string &name, const float *v)
+{
+       uniform<UniformMatrix2x4f>(name, v);
+}
+
+void ProgramData::uniform(const string &name, const LinAl::Matrix<float, 4, 3> &m)
+{
+       uniform_matrix3x4(name, &m(0, 0));
+}
+
+void ProgramData::uniform_matrix3x4(const string &name, const float *v)
+{
+       uniform<UniformMatrix3x4f>(name, v);
+}
+
+void ProgramData::uniform(const string &name, const Matrix &m)
+{
+       uniform_matrix4(name, m.data());
+}
+
+void ProgramData::uniform_matrix4(const string &name, const float *v)
+{
+       uniform<UniformMatrix4x4f>(name, v);
+}
+
+void ProgramData::uniform1_array(const string &name, unsigned n, const int *v)
+{
+       uniform_array<Uniform1i>(name, n, v);
+}
+
+void ProgramData::uniform1_array(const string &name, unsigned n, const float *v)
+{
+       uniform_array<Uniform1f>(name, n, v);
+}
+
+void ProgramData::uniform2_array(const string &name, unsigned n, const int *v)
+{
+       uniform_array<Uniform2i>(name, n, v);
+}
+
+void ProgramData::uniform2_array(const string &name, unsigned n, const float *v)
+{
+       uniform_array<Uniform2f>(name, n, v);
+}
+
+void ProgramData::uniform3_array(const string &name, unsigned n, const int *v)
+{
+       uniform_array<Uniform3i>(name, n, v);
+}
+
+void ProgramData::uniform3_array(const string &name, unsigned n, const float *v)
+{
+       uniform_array<Uniform3f>(name, n, v);
+}
+
+void ProgramData::uniform4_array(const string &name, unsigned n, const int *v)
+{
+       uniform_array<Uniform4i>(name, n, v);
+}
+
+void ProgramData::uniform4_array(const string &name, unsigned n, const float *v)
+{
+       uniform_array<Uniform4f>(name, n, v);
+}
+
+void ProgramData::uniform_matrix2_array(const string &name, unsigned n, const float *v)
+{
+       uniform_array<UniformMatrix2x2f>(name, n, v);
+}
+
+void ProgramData::uniform_matrix3x2_array(const string &name, unsigned n, const float *v)
+{
+       uniform_array<UniformMatrix3x2f>(name, n, v);
+}
+
+void ProgramData::uniform_matrix4x2_array(const string &name, unsigned n, const float *v)
+{
+       uniform_array<UniformMatrix4x2f>(name, n, v);
+}
+
+void ProgramData::uniform_matrix2x3_array(const string &name, unsigned n, const float *v)
+{
+       uniform_array<UniformMatrix2x3f>(name, n, v);
+}
+
+void ProgramData::uniform_matrix3_array(const string &name, unsigned n, const float *v)
+{
+       uniform_array<UniformMatrix3x3f>(name, n, v);
+}
+
+void ProgramData::uniform_matrix4x3_array(const string &name, unsigned n, const float *v)
+{
+       uniform_array<UniformMatrix4x3f>(name, n, v);
+}
+
+void ProgramData::uniform_matrix2x4_array(const string &name, unsigned n, const float *v)
+{
+       uniform_array<UniformMatrix2x4f>(name, n, v);
+}
+
+void ProgramData::uniform_matrix3x4_array(const string &name, unsigned n, const float *v)
+{
+       uniform_array<UniformMatrix3x4f>(name, n, v);
+}
+
+void ProgramData::uniform_matrix4_array(const string &name, unsigned n, const float *v)
+{
+       uniform_array<UniformMatrix4x4f>(name, n, v);
+}
+
+void ProgramData::remove_uniform(const string &name)
+{
+       vector<NamedUniform>::const_iterator i = lower_bound(uniforms.begin(), uniforms.end(), name, uniform_name_compare);
+       if(i==uniforms.end() || i->name!=name)
+               return;
+
+       delete i->value;
+       uniforms.erase(i);
+
+       dirty = ALL_ONES;
+}
+
+vector<string> ProgramData::get_uniform_names() const
+{
+       vector<string> names;
+       names.reserve(uniforms.size());
+       for(vector<NamedUniform>::const_iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
+               names.push_back(i->name);
+       return names;
+}
+
+const Uniform &ProgramData::get_uniform(const string &name) const
+{
+       int i = find_uniform_index(name);
+       if(i<0)
+               throw key_error(name);
+       return *uniforms[i].value;
+}
+
+const Uniform *ProgramData::find_uniform(const string &name) const
+{
+       int i = find_uniform_index(name);
+       return (i>=0 ? uniforms[i].value : 0);
+}
+
+bool ProgramData::uniform_name_compare(const NamedUniform &nu, const string &name)
+{
+       return nu.name<name;
+}
+
+int ProgramData::find_uniform_index(const string &name) const
+{
+       vector<NamedUniform>::const_iterator i = lower_bound(uniforms.begin(), uniforms.end(), name, uniform_name_compare);
+       return ((i!=uniforms.end() && i->name==name) ? i-uniforms.begin() : -1);
+}
+
+void ProgramData::update_block_uniform_indices(SharedBlock &block, const Program::UniformBlockInfo &info) const
+{
+       UInt8 *indices = block.indices.values;
+       if(info.uniforms.size()>16)
+       {
+               if(block.indices.type_flag==0xFD)
+               {
+                       block.indices.dynamic.values = new UInt8[info.uniforms.size()];
+                       block.indices.type_flag = 0xFE;
+               }
+               indices = block.indices.dynamic.values;
+       }
+
+       block.used = 0;
+       for(unsigned i=0; i<info.uniforms.size(); ++i)
+       {
+               int j = find_uniform_index(info.uniforms[i]->name);
+               if(j>=0)
+               {
+                       indices[i] = j;
+                       if(static_cast<unsigned>(j)<MASK_BITS)
+                               block.used |= 1<<j;
+               }
+               else
+                       indices[i] = 0xFF;
+       }
+
+       block.dirty = block.used;
+}
+
+void ProgramData::update_block(SharedBlock &block, const Program::UniformBlockInfo &info) const
+{
+       const UInt8 *indices = block.get_uniform_indices();
+       for(unsigned i=0; i<info.uniforms.size(); ++i)
+               if(indices[i]!=0xFF)
+                       block.block->attach(*info.uniforms[i], *uniforms[indices[i]].value);
+}
+
+ProgramData::SharedBlock *ProgramData::get_shared_block(const Program::UniformBlockInfo &info) const
+{
+       BlockMap::iterator i = blocks.find(info.layout_hash);
+       if(i==blocks.end())
+       {
+               bool any_found = false;
+               bool all_found = true;
+               for(vector<const Program::UniformInfo *>::const_iterator j=info.uniforms.begin(); j!=info.uniforms.end(); ++j)
+               {
+                       if(find_uniform_index((*j)->name)>=0)
+                               any_found = true;
+                       else
+                               all_found = false;
+               }
+
+               if(!any_found)
+                       return 0;
+               else if(!all_found && info.bind_point>=0)
+               {
+#ifdef DEBUG
+                       IO::print(IO::cerr, "Warning: not all uniforms for block %s are present\n", info.name);
+#else
+                       throw incomplete_uniform_block(info.name);
+#endif
+               }
+
+               UniformBlock *block;
+               if(info.bind_point>=0)
+               {
+                       if(!buffer)
+                               buffer = new Buffer(UNIFORM_BUFFER);
+
+                       block = new UniformBlock(info.data_size);
+                       block->use_buffer(buffer, last_block);
+                       last_block = block;
+               }
+               else
+                       block = new UniformBlock;
+
+               i = blocks.insert(BlockMap::value_type(info.layout_hash, SharedBlock(block))).first;
+               update_block_uniform_indices(i->second, info);
+       }
+
+       return &i->second;
+}
+
+void ProgramData::apply() const
+{
+       const Program *prog = Program::current();
+       if(!prog)
+               throw invalid_operation("ProgramData::apply");
+
+       Program::LayoutHash layout = prog->get_uniform_layout_hash();
+       ProgramUniforms &pu = programs[layout];
+
+       Mask force_dirty = (dirty==ALL_ONES ? ALL_ONES : 0U);
+       Mask affected = (dirty&pu.used) | force_dirty;
+       if(affected|pu.dirty)
+       {
+               /* If the global dirty flag affects this program, add it to per-program
+               dirty flags and clear the global flag.  A previously unseen program will
+               cause this to happen if there's any dirty uniforms. */
+               if(affected)
+               {
+                       for(BlockMap::iterator i=blocks.begin(); i!=blocks.end(); ++i)
+                               i->second.dirty |= (dirty&i->second.used) | force_dirty;
+                       for(ProgramMap::iterator i=programs.begin(); i!=programs.end(); ++i)
+                               i->second.dirty |= (dirty&i->second.used) | force_dirty;
+                       dirty = 0;
+               }
+
+               const Program::UniformBlockMap &prog_blocks = prog->get_uniform_blocks();
+
+               UniformBlock *old_last_block = last_block;
+               if(pu.dirty==ALL_ONES)
+               {
+                       /* The set of uniforms has changed since this program was last used.
+                       Regenerate the list of uniform blocks. */
+                       pu.blocks.clear();
+                       pu.blocks.reserve(prog_blocks.size());
+
+                       pu.used = 0;
+                       for(Program::UniformBlockMap::const_iterator i=prog_blocks.begin(); i!=prog_blocks.end(); ++i)
+                       {
+                               SharedBlock *shared = get_shared_block(i->second);
+                               if(shared)
+                               {
+                                       if(shared->dirty==ALL_ONES)
+                                               update_block_uniform_indices(*shared, i->second);
+                                       pu.used |= shared->used;
+                               }
+
+                               pu.blocks.push_back(ProgramBlock(i->second.bind_point, shared));
+                       }
+               }
+
+               // Update the contents of all dirty blocks.
+               bool buffered_blocks_updated = false;
+               std::vector<ProgramBlock>::iterator j = pu.blocks.begin();
+               for(Program::UniformBlockMap::const_iterator i=prog_blocks.begin(); i!=prog_blocks.end(); ++i, ++j)
+               {
+                       if(!j->shared || !j->shared->dirty)
+                               continue;
+
+                       update_block(*j->shared, i->second);
+                       j->shared->dirty = 0;
+                       buffered_blocks_updated |= (j->bind_point>=0);
+               }
+
+               pu.dirty = 0;
+
+               /* If any blocks stored in the buffer were updated, bind the buffer here
+               to avoid state thrashing. */
+               if(buffered_blocks_updated && !ARB_direct_state_access)
+                       buffer->bind();
+
+               if(last_block!=old_last_block)
+               {
+                       unsigned required_size = last_block->get_required_buffer_size();
+                       if(last_block->get_required_buffer_size()>buffer->get_size())
+                       {
+                               if(buffer->get_size()>0)
+                               {
+                                       delete buffer;
+                                       buffer = new Buffer(UNIFORM_BUFFER);
+                                       last_block->change_buffer(buffer);
+                               }
+
+                               buffer->storage(required_size);
+                       }
+               }
+       }
+
+       for(vector<ProgramBlock>::iterator i=pu.blocks.begin(); i!=pu.blocks.end(); ++i)
+               if(i->block)
+                       i->block->apply(i->bind_point);
+}
+
+
+ProgramData::NamedUniform::NamedUniform():
+       value(0)
+{ }
+
+void ProgramData::NamedUniform::replace_value(Uniform *v)
+{
+       /* UniformBlock does not copy the uniforms, so existing default blocks
+       will be left with stale pointers.  This is not a problem as long as no
+       one stores pointers to the blocks and expects them to stay valid. */
+       delete value;
+       value = v;
+}
+
+
+ProgramData::SharedBlock::SharedBlock(UniformBlock *b):
+       used(0),
+       dirty(0),
+       block(b)
+{
+       indices.type_flag = 0xFD;
+}
+
+const UInt8 *ProgramData::SharedBlock::get_uniform_indices() const
+{
+       return (indices.type_flag==0xFE ? indices.dynamic.values : indices.values);
+}
+
+
+ProgramData::ProgramBlock::ProgramBlock():
+       bind_point(-1),
+       block(0),
+       shared(0)
+{ }
+
+ProgramData::ProgramBlock::ProgramBlock(int p, SharedBlock *b):
+       bind_point(p),
+       block((b && b->used) ? b->block : 0),
+       shared(b)
+{ }
+
+
+ProgramData::ProgramUniforms::ProgramUniforms():
+       used(ALL_ONES),
+       dirty(ALL_ONES)
+{ }
+
+
+ProgramData::Loader::Loader(ProgramData &pd):
+       DataFile::ObjectLoader<ProgramData>(pd)
+{
+       add("uniform", &Loader::uniform1i);
+       add("uniform1i", &Loader::uniform1i);
+       add("uniform", &Loader::uniform1f);
+       add("uniform1f", &Loader::uniform1f);
+       add("uniform", &Loader::uniform2i);
+       add("uniform2i", &Loader::uniform2i);
+       add("uniform", &Loader::uniform2f);
+       add("uniform2f", &Loader::uniform2f);
+       add("uniform", &Loader::uniform3i);
+       add("uniform3i", &Loader::uniform3i);
+       add("uniform", &Loader::uniform3f);
+       add("uniform3f", &Loader::uniform3f);
+       add("uniform", &Loader::uniform4i);
+       add("uniform4i", &Loader::uniform4i);
+       add("uniform", &Loader::uniform4f);
+       add("uniform4f", &Loader::uniform4f);
+       add("uniform1i_array", &Loader::uniform1i_array);
+       add("uniform1f_array", &Loader::uniform1f_array);
+       add("uniform2f_array", &Loader::uniform2f_array);
+       add("uniform3f_array", &Loader::uniform3f_array);
+       add("uniform4f_array", &Loader::uniform4f_array);
+       add("uniform_array", &Loader::uniform_array);
+}
+
+void ProgramData::Loader::uniform1i(const string &n, int v)
+{
+       obj.uniform(n, v);
+}
+
+void ProgramData::Loader::uniform1f(const string &n, float v)
+{
+       obj.uniform(n, v);
+}
+
+void ProgramData::Loader::uniform2i(const string &n, int v0, int v1)
+{
+       obj.uniform(n, v0, v1);
+}
+
+void ProgramData::Loader::uniform2f(const string &n, float v0, float v1)
+{
+       obj.uniform(n, v0, v1);
+}
+
+void ProgramData::Loader::uniform3i(const string &n, int v0, int v1, int v2)
+{
+       obj.uniform(n, v0, v1, v2);
+}
+
+void ProgramData::Loader::uniform3f(const string &n, float v0, float v1, float v2)
+{
+       obj.uniform(n, v0, v1, v2);
+}
+
+void ProgramData::Loader::uniform4i(const string &n, int v0, int v1, int v2, int v3)
+{
+       obj.uniform(n, v0, v1, v2, v3);
+}
+
+void ProgramData::Loader::uniform4f(const string &n, float v0, float v1, float v2, float v3)
+{
+       obj.uniform(n, v0, v1, v2, v3);
+}
+
+void ProgramData::Loader::uniform_array_(const string &n, DataType t, unsigned e)
+{
+       ArrayLoader ldr(t, e);
+       load_sub_with(ldr);
+       unsigned size = ldr.get_size();
+       if(!size)
+               throw logic_error("empty uniform array");
+
+       DataType type = ldr.get_data_type();
+       unsigned elem_size = ldr.get_element_size();
+       if(type==INT)
+       {
+               const int *data = reinterpret_cast<const int *>(ldr.get_data());
+               if(elem_size==1)
+                       obj.uniform1_array(n, size, data);
+               else if(elem_size==2)
+                       obj.uniform2_array(n, size, data);
+               else if(elem_size==3)
+                       obj.uniform3_array(n, size, data);
+               else if(elem_size==4)
+                       obj.uniform4_array(n, size, data);
+               else
+                       throw logic_error("unsupported combination of array type and element size");
+       }
+       else if(type==FLOAT)
+       {
+               const float *data = reinterpret_cast<const float *>(ldr.get_data());
+               if(elem_size==1)
+                       obj.uniform1_array(n, size, data);
+               else if(elem_size==2)
+                       obj.uniform2_array(n, size, data);
+               else if(elem_size==3)
+                       obj.uniform3_array(n, size, data);
+               else if(elem_size==4)
+                       obj.uniform4_array(n, size, data);
+               else
+                       throw logic_error("unsupported combination of array type and element size");
+       }
+       else
+               throw logic_error("unsupported array type");
+}
+
+void ProgramData::Loader::uniform1i_array(const string &n)
+{
+       uniform_array_(n, INT, 1);
+}
+
+void ProgramData::Loader::uniform1f_array(const string &n)
+{
+       uniform_array_(n, FLOAT, 1);
+}
+
+void ProgramData::Loader::uniform2i_array(const string &n)
+{
+       uniform_array_(n, INT, 2);
+}
+
+void ProgramData::Loader::uniform2f_array(const string &n)
+{
+       uniform_array_(n, FLOAT, 2);
+}
+
+void ProgramData::Loader::uniform3i_array(const string &n)
+{
+       uniform_array_(n, INT, 3);
+}
+
+void ProgramData::Loader::uniform3f_array(const string &n)
+{
+       uniform_array_(n, FLOAT, 3);
+}
+
+void ProgramData::Loader::uniform4i_array(const string &n)
+{
+       uniform_array_(n, INT, 4);
+}
+
+void ProgramData::Loader::uniform4f_array(const string &n)
+{
+       uniform_array_(n, FLOAT, 4);
+}
+
+void ProgramData::Loader::uniform_array(const string &n)
+{
+       uniform_array_(n, static_cast<DataType>(0), 0);
+}
+
+
+ProgramData::ArrayLoader::ArrayLoader(DataType t, unsigned e):
+       type(t),
+       element_size(e)
+{
+       add("uniform", &ArrayLoader::uniform1i);
+       add("uniform1i", &ArrayLoader::uniform1i);
+       add("uniform", &ArrayLoader::uniform1f);
+       add("uniform1f", &ArrayLoader::uniform1f);
+       add("uniform", &ArrayLoader::uniform2f);
+       add("uniform2f", &ArrayLoader::uniform2f);
+       add("uniform", &ArrayLoader::uniform3f);
+       add("uniform3f", &ArrayLoader::uniform3f);
+       add("uniform", &ArrayLoader::uniform4f);
+       add("uniform4f", &ArrayLoader::uniform4f);
+}
+
+void ProgramData::ArrayLoader::uniform(DataType t, unsigned e, const void *v)
+{
+       if(element_size && (t!=type || e!=element_size))
+               throw logic_error("heterogeneous array contents");
+
+       if(!element_size)
+       {
+               type = t;
+               element_size = e;
+       }
+
+       const char *cv = reinterpret_cast<const char *>(v);
+       data.insert(data.end(), cv, cv+element_size*4);
+}
+
+void ProgramData::ArrayLoader::uniform1i(int v)
+{
+       uniform(INT, 1, &v);
+}
+
+void ProgramData::ArrayLoader::uniform1f(float v)
+{
+       uniform(FLOAT, 1, &v);
+}
+
+void ProgramData::ArrayLoader::uniform2i(int v0, int v1)
+{
+       int va[2] = { v0, v1 };
+       uniform(INT, 2, va);
+}
+
+void ProgramData::ArrayLoader::uniform2f(float v0, float v1)
+{
+       float va[2] = { v0, v1 };
+       uniform(FLOAT, 2, va);
+}
+
+void ProgramData::ArrayLoader::uniform3i(int v0, int v1, int v2)
+{
+       int va[3] = { v0, v1, v2 };
+       uniform(INT, 3, va);
+}
+
+void ProgramData::ArrayLoader::uniform3f(float v0, float v1, float v2)
+{
+       float va[3] = { v0, v1, v2 };
+       uniform(FLOAT, 3, va);
+}
+
+void ProgramData::ArrayLoader::uniform4i(int v0, int v1, int v2, int v3)
+{
+       int va[4] = { v0, v1, v2, v3 };
+       uniform(INT, 4, va);
+}
+
+void ProgramData::ArrayLoader::uniform4f(float v0, float v1, float v2, float v3)
+{
+       float va[4] = { v0, v1, v2, v3 };
+       uniform(FLOAT, 4, va);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/render/programdata.h b/source/render/programdata.h
new file mode 100644 (file)
index 0000000..e96a73a
--- /dev/null
@@ -0,0 +1,254 @@
+#ifndef MSP_GL_PROGRAMDATA_H_
+#define MSP_GL_PROGRAMDATA_H_
+
+#include <map>
+#include <stdexcept>
+#include <msp/datafile/objectloader.h>
+#include "datatype.h"
+#include "matrix.h"
+#include "program.h"
+#include "vector.h"
+
+namespace Msp {
+namespace GL {
+
+class too_many_uniforms: public std::runtime_error
+{
+public:
+       too_many_uniforms(const std::string &w): std::runtime_error(w) { }
+       virtual ~too_many_uniforms() throw() { }
+};
+
+class Buffer;
+class Uniform;
+class UniformBlock;
+struct Color;
+
+/**
+Stores uniform variables for shader programs.  The uniforms are stored in a
+program-independent way, and UniformBlocks are created to match the uniform
+layouts of different programs.  If multiple programs have the same layout, the
+same block is used for them.
+
+The class is optimized for an access pattern where the set of uniforms and
+programs stays constants, with only the values changing.
+*/
+class ProgramData
+{
+public:
+       class Loader: public DataFile::ObjectLoader<ProgramData>
+       {
+       public:
+               Loader(ProgramData &);
+       private:
+               void uniform1i(const std::string &, int);
+               void uniform1f(const std::string &, float);
+               void uniform2i(const std::string &, int, int);
+               void uniform2f(const std::string &, float, float);
+               void uniform3i(const std::string &, int, int, int);
+               void uniform3f(const std::string &, float, float, float);
+               void uniform4i(const std::string &, int, int, int, int);
+               void uniform4f(const std::string &, float, float, float, float);
+               void uniform_array_(const std::string &, DataType, unsigned);
+               void uniform1i_array(const std::string &);
+               void uniform1f_array(const std::string &);
+               void uniform2i_array(const std::string &);
+               void uniform2f_array(const std::string &);
+               void uniform3i_array(const std::string &);
+               void uniform3f_array(const std::string &);
+               void uniform4i_array(const std::string &);
+               void uniform4f_array(const std::string &);
+               void uniform_array(const std::string &);
+       };
+
+private:
+       class ArrayLoader: public DataFile::Loader
+       {
+       private:
+               DataType type;
+               unsigned element_size;
+               std::vector<char> data;
+
+       public:
+               ArrayLoader(DataType, unsigned);
+
+               DataType get_data_type() const { return type; }
+               unsigned get_element_size() const { return element_size; }
+               const void *get_data() const { return &data[0]; }
+               unsigned get_size() const { return data.size()/(4*element_size); }
+
+       private:
+               void uniform(DataType, unsigned, const void *);
+               void uniform1i(int);
+               void uniform1f(float);
+               void uniform2i(int, int);
+               void uniform2f(float, float);
+               void uniform3i(int, int, int);
+               void uniform3f(float, float, float);
+               void uniform4i(int, int, int, int);
+               void uniform4f(float, float, float, float);
+       };
+
+       typedef unsigned Mask;
+
+       enum
+       {
+               MASK_BITS = sizeof(Mask)*8,
+               ALL_ONES = static_cast<Mask>(-1)
+       };
+
+       struct NamedUniform
+       {
+               std::string name;
+               Uniform *value;
+
+               NamedUniform();
+
+               bool compare_name(const std::string &, unsigned) const;
+               void replace_value(Uniform *);
+       };
+
+       struct SharedBlock
+       {
+               Mask used;
+               Mask dirty;
+               UniformBlock *block;
+               union
+               {
+                       UInt8 type_flag;
+                       UInt8 values[16];
+                       struct
+                       {
+                               UInt8 type_flag;
+                               UInt8 *values;
+                       } dynamic;
+               } indices;
+
+               SharedBlock(UniformBlock *);
+
+               const UInt8 *get_uniform_indices() const;
+       };
+
+       struct ProgramBlock
+       {
+               int bind_point;
+               UniformBlock *block;
+               SharedBlock *shared;
+
+               ProgramBlock();
+               ProgramBlock(int, SharedBlock *);
+       };
+
+       struct ProgramUniforms
+       {
+               std::vector<ProgramBlock> blocks;
+               Mask used;
+               Mask dirty;
+
+               ProgramUniforms();
+       };
+
+       typedef std::map<Program::LayoutHash, SharedBlock> BlockMap;
+       typedef std::map<Program::LayoutHash, ProgramUniforms> ProgramMap;
+
+       // XXX All these mutables are a bit silly, but I'm out of better ideas
+       const Program *tied_program;
+       std::vector<NamedUniform> uniforms;
+       mutable BlockMap blocks;
+       mutable ProgramMap programs;
+       mutable UniformBlock *last_block;
+       mutable Buffer *buffer;
+       mutable unsigned dirty;
+
+public:
+       ProgramData(const Program * = 0);
+       ProgramData(const ProgramData &);
+       ProgramData(const ProgramData &, const Program *);
+       ProgramData &operator=(const ProgramData &);
+       ~ProgramData();
+
+private:
+       void uniform(const std::string &, Uniform *);
+       template<typename T, typename V>
+       void uniform(const std::string &, V);
+       template<typename T, typename V>
+       void uniform_array(const std::string &, unsigned, V);
+       bool validate_name(const std::string &) const;
+       void add_uniform(const std::string &, Uniform *);
+public:
+       void uniform(const std::string &, const Uniform &);
+       void uniform(const std::string &, int);
+       void uniform(const std::string &, float);
+       void uniform(const std::string &, int, int);
+       void uniform(const std::string &, float, float);
+       void uniform2(const std::string &, const int *);
+       void uniform2(const std::string &, const float *);
+       void uniform(const std::string &, int, int, int);
+       void uniform(const std::string &, float, float, float);
+       void uniform(const std::string &, const Vector3 &);
+       void uniform3(const std::string &, const int *);
+       void uniform3(const std::string &, const float *);
+       void uniform(const std::string &, int, int, int, int);
+       void uniform(const std::string &, float, float, float, float);
+       void uniform(const std::string &, const Vector4 &);
+       void uniform(const std::string &, const Color &);
+       void uniform4(const std::string &, const int *);
+       void uniform4(const std::string &, const float *);
+       void uniform(const std::string &, const LinAl::Matrix<float, 2, 2> &);
+       void uniform_matrix2(const std::string &, const float *);
+       void uniform(const std::string &, const LinAl::Matrix<float, 2, 3> &);
+       void uniform_matrix3x2(const std::string &, const float *);
+       void uniform(const std::string &, const LinAl::Matrix<float, 2, 4> &);
+       void uniform_matrix4x2(const std::string &, const float *);
+       void uniform(const std::string &, const LinAl::Matrix<float, 3, 2> &);
+       void uniform_matrix2x3(const std::string &, const float *);
+       void uniform(const std::string &, const LinAl::Matrix<float, 3, 3> &);
+       void uniform_matrix3(const std::string &, const float *);
+       void uniform(const std::string &, const LinAl::Matrix<float, 3, 4> &);
+       void uniform_matrix4x3(const std::string &, const float *);
+       void uniform(const std::string &, const LinAl::Matrix<float, 4, 2> &);
+       void uniform_matrix2x4(const std::string &, const float *);
+       void uniform(const std::string &, const LinAl::Matrix<float, 4, 3> &);
+       void uniform_matrix3x4(const std::string &, const float *);
+       void uniform(const std::string &, const Matrix &);
+       void uniform_matrix4(const std::string &, const float *);
+       void uniform1_array(const std::string &, unsigned, const int *);
+       void uniform1_array(const std::string &, unsigned, const float *);
+       void uniform2_array(const std::string &, unsigned, const int *);
+       void uniform2_array(const std::string &, unsigned, const float *);
+       void uniform3_array(const std::string &, unsigned, const int *);
+       void uniform3_array(const std::string &, unsigned, const float *);
+       void uniform4_array(const std::string &, unsigned, const int *);
+       void uniform4_array(const std::string &, unsigned, const float *);
+       void uniform_matrix2_array(const std::string &, unsigned, const float *);
+       void uniform_matrix3x2_array(const std::string &, unsigned, const float *);
+       void uniform_matrix4x2_array(const std::string &, unsigned, const float *);
+       void uniform_matrix2x3_array(const std::string &, unsigned, const float *);
+       void uniform_matrix3_array(const std::string &, unsigned, const float *);
+       void uniform_matrix4x3_array(const std::string &, unsigned, const float *);
+       void uniform_matrix2x4_array(const std::string &, unsigned, const float *);
+       void uniform_matrix3x4_array(const std::string &, unsigned, const float *);
+       void uniform_matrix4_array(const std::string &, unsigned, const float *);
+       void remove_uniform(const std::string &);
+
+       std::vector<std::string> get_uniform_names() const;
+       const Uniform &get_uniform(const std::string &) const;
+       const Uniform *find_uniform(const std::string &) const;
+
+private:
+       static bool uniform_name_compare(const NamedUniform &, const std::string &);
+       int find_uniform_index(const std::string &) const;
+       void update_block_uniform_indices(SharedBlock &, const Program::UniformBlockInfo &) const;
+       void update_block(SharedBlock &, const Program::UniformBlockInfo &) const;
+       SharedBlock *get_shared_block(const Program::UniformBlockInfo &) const;
+
+public:
+       /** Applies uniform blocks for the currently bound program, creating them
+       if needed. */
+       void apply() const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/render/renderable.h b/source/render/renderable.h
new file mode 100644 (file)
index 0000000..80fe172
--- /dev/null
@@ -0,0 +1,64 @@
+#ifndef MSP_GL_RENDERABLE_H_
+#define MSP_GL_RENDERABLE_H_
+
+#include <string>
+#include <msp/core/inttypes.h>
+#include <msp/geometry/boundingsphere.h>
+#include "tag.h"
+
+namespace Msp {
+namespace GL {
+
+class Matrix;
+class Renderer;
+
+/**
+Base class for renderable objects.  Rendering is performed with the help of a
+Renderer object.
+
+The render method takes a Tag to identify a render pass.  It can be used with
+a Technique to select alternative rendering methods, such as simplified shaders
+for a depth-only shadow pass.
+
+The setup_frame and finish_frame methods provide a mechanism for performing
+once-per-frame operations.  This is most useful for effects, which may need to
+do auxiliary rendering.  With complex rendering hierarchies, these methods may
+be called multiple times for one frame, but it's guaranteed that no rendering
+will occur before a setup_frame call or after a finish_frame call.
+*/
+class Renderable
+{
+protected:
+       Renderable() { }
+public:
+       virtual ~Renderable() { }
+
+       /** Returns a key used for grouping Renderables in an InstanceScene.  The
+       returned value is treated as opaque. */
+       virtual IntPtr get_instance_key() const { return 0; }
+
+       /** Returns the model matrix of the Renderable.  Null is returned if no such
+       matrix exists.  The matrix should be in world space for some effects to work
+       correctly. */
+       virtual const Matrix *get_matrix() const { return 0; }
+
+       /** Returns a bounding sphere that completely encloses the Renderable.  The
+       bounding sphere is expressed in the renderable's coordinates.  Null is
+       returned if the bounding sphere cannot be determined. */
+       virtual const Geometry::BoundingSphere<float, 3> *get_bounding_sphere() const { return 0; }
+
+       /** Called when starting to render a new frame. */
+       virtual void setup_frame(Renderer &) { }
+
+       /** Called when a complete frame has been rendered. */
+       virtual void finish_frame() { }
+
+       /** Renders the Renderable.  Implementors should take care to return the
+       renderer to the state it was in, for example by using Renderer::Push. */
+       virtual void render(Renderer &, const Tag & = Tag()) const = 0;
+};
+
+} // namespace Msp
+} // namespace GL
+
+#endif
diff --git a/source/render/renderer.cpp b/source/render/renderer.cpp
new file mode 100644 (file)
index 0000000..416a0fc
--- /dev/null
@@ -0,0 +1,374 @@
+#include "batch.h"
+#include "buffer.h"
+#include "camera.h"
+#include "clipping.h"
+#include "error.h"
+#include "lighting.h"
+#include "material.h"
+#include "program.h"
+#include "programdata.h"
+#include "renderable.h"
+#include "renderer.h"
+#include "sampler.h"
+#include "texture.h"
+#include "texturing.h"
+#include "texunit.h"
+#include "vertexarray.h"
+#include "vertexsetup.h"
+#include "windingtest.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Renderer::Renderer():
+       default_camera(0)
+{
+       init();
+}
+
+Renderer::Renderer(const Camera *c):
+       default_camera(c)
+{
+       init();
+
+       if(c)
+               set_camera(*c);
+}
+
+void Renderer::init()
+{
+       state_stack.reserve(16);
+       state_stack.push_back(State());
+       shdata_stack.reserve(32);
+       state = &state_stack.back();
+
+       standard_shdata.uniform("projection_matrix", Matrix());
+       standard_shdata.uniform("eye_world_matrix", Matrix());
+}
+
+Renderer::~Renderer()
+{
+       end();
+}
+
+void Renderer::set_camera(const Camera &c)
+{
+       state->camera = &c;
+       standard_shdata.uniform("projection_matrix", state->camera->get_projection_matrix());
+       standard_shdata.uniform("eye_world_matrix", state->camera->get_view_matrix());
+       changed |= STANDARD_SHDATA;
+       set_matrix(state->camera->get_view_matrix());
+}
+
+void Renderer::set_matrix(const Matrix &matrix)
+{
+       state->modelview_matrix = matrix;
+       changed |= MATRIX;
+}
+
+void Renderer::transform(const Matrix &matrix)
+{
+       state->modelview_matrix *= matrix;
+       changed |= MATRIX;
+}
+
+void Renderer::set_texture(const Texture *t, const Sampler *s)
+{
+       state->texture = t;
+       state->sampler = s;
+       state->texturing = 0;
+}
+
+void Renderer::set_texturing(const Texturing *t)
+{
+       state->texturing = t;
+       state->texture = 0;
+       state->sampler = 0;
+}
+
+unsigned Renderer::allocate_effect_texunit()
+{
+       return --state->lowest_effect_texunit;
+}
+
+void Renderer::set_material(const Material *m)
+{
+       state->material = m;
+       changed |= MATERIAL_SHDATA;
+}
+
+void Renderer::set_lighting(const Lighting *l)
+{
+       state->lighting = l;
+       state->lighting_matrix = state->modelview_matrix;
+       if(l)
+       {
+               l->update_shader_data(standard_shdata, state->lighting_matrix);
+               changed |= STANDARD_SHDATA;
+       }
+}
+
+void Renderer::set_clipping(const Clipping *c)
+{
+       state->clipping = c;
+       state->clipping_matrix = state->modelview_matrix;
+       if(c)
+       {
+               c->update_shader_data(standard_shdata, state->clipping_matrix);
+               changed |= STANDARD_SHDATA;
+       }
+}
+
+void Renderer::set_shader_program(const Program *p, const ProgramData *d)
+{
+       state->shprog = p;
+       if(p && d)
+               add_shader_data(*d);
+}
+
+void Renderer::add_shader_data(const ProgramData &d)
+{
+       if(state->shdata_count<shdata_stack.size() && shdata_stack[state->shdata_count]==&d)
+               ++state->shdata_count;
+       else
+       {
+               flush_shader_data();
+               shdata_stack.push_back(&d);
+               state->shdata_count = shdata_stack.size();
+               changed |= SHADER_DATA;
+       }
+}
+
+void Renderer::flush_shader_data()
+{
+       if(shdata_stack.size()>state->shdata_count)
+               shdata_stack.erase(shdata_stack.begin()+state->shdata_count, shdata_stack.end());
+}
+
+void Renderer::set_vertex_setup(const VertexSetup *vs)
+{
+       state->vertex_setup = vs;
+}
+
+void Renderer::set_winding_test(const WindingTest *w)
+{
+       state->winding_test = w;
+}
+
+void Renderer::set_reverse_winding(bool r)
+{
+       state->reverse_winding = r;
+}
+
+void Renderer::set_object_lod_bias(unsigned b)
+{
+       state->object_lod_bias = b;
+}
+
+void Renderer::push_state()
+{
+       state_stack.push_back(state_stack.back());
+       state = &state_stack.back();
+}
+
+void Renderer::pop_state()
+{
+       if(state_stack.size()==1)
+               throw stack_underflow("Renderer::pop_state");
+
+       const Camera *old_camera = state->camera;
+       const Lighting *old_lighting = state->lighting;
+       const Clipping *old_clipping = state->clipping;
+       state_stack.pop_back();
+       state = &state_stack.back();
+       changed |= MATRIX;
+       bool camera_changed = (state->camera!=old_camera);
+       if(camera_changed)
+       {
+               if(state->camera)
+               {
+                       standard_shdata.uniform("projection_matrix", state->camera->get_projection_matrix());
+                       standard_shdata.uniform("eye_world_matrix", state->camera->get_view_matrix());
+               }
+               else
+               {
+                       standard_shdata.uniform("projection_matrix", Matrix());
+                       standard_shdata.uniform("eye_world_matrix", Matrix());
+               }
+               changed |= STANDARD_SHDATA;
+       }
+       /* This actually should compare the relevant matrices rather than check for
+       camera, but in practice lighting and clipping is set right after the camera
+       and a boolean check is much faster than matrix comparison. */
+       if(state->lighting!=old_lighting || camera_changed)
+       {
+               if(state->lighting)
+               {
+                       state->lighting->update_shader_data(standard_shdata, state->lighting_matrix);
+                       changed |= STANDARD_SHDATA;
+               }
+       }
+       if(state->clipping!=old_clipping || camera_changed)
+       {
+               if(state->clipping)
+               {
+                       state->clipping->update_shader_data(standard_shdata, state->clipping_matrix);
+                       changed |= STANDARD_SHDATA;
+               }
+       }
+}
+
+void Renderer::end()
+{
+       if(state_stack.size()>1)
+               throw invalid_operation("Renderer::end");
+
+       *state = State();
+       if(default_camera)
+               set_camera(*default_camera);
+       else
+               standard_shdata.uniform("projection_matrix", Matrix());
+       shdata_stack.clear();
+       excluded.clear();
+
+       Texturing::unbind();
+       Texture::unbind_from(0);
+       Clipping::unbind();
+       Program::unbind();
+       VertexSetup::unbind();
+       Buffer::unbind_from(ELEMENT_ARRAY_BUFFER);
+       WindingTest::unbind();
+}
+
+void Renderer::exclude(const Renderable &renderable)
+{
+       excluded.insert(&renderable);
+}
+
+void Renderer::include(const Renderable &renderable)
+{
+       excluded.erase(&renderable);
+}
+
+void Renderer::render(const Renderable &renderable, const Tag &tag)
+{
+       if(!excluded.count(&renderable))
+               renderable.render(*this, tag);
+}
+
+void Renderer::draw(const Batch &batch)
+{
+       apply_state();
+
+       batch.draw();
+}
+
+void Renderer::draw_instanced(const Batch &batch, unsigned count)
+{
+       apply_state();
+
+       batch.draw_instanced(count);
+}
+
+void Renderer::apply_state()
+{
+       if(!state->shprog)
+               throw invalid_operation("Renderer::apply_state");
+
+       /* We (mostly) let the objects themselves figure out if the binding has
+       changed */
+
+       if(state->texturing)
+               state->texturing->bind();
+       else
+       {
+               Texturing::unbind();
+               if(state->texture)
+               {
+                       if(state->sampler)
+                               state->sampler->bind_to(0);
+                       else
+                               Sampler::unbind_from(0);
+                       state->texture->bind_to(0);
+               }
+               else
+                       Texture::unbind_from(0);
+       }
+
+       if(state->clipping)
+               state->clipping->bind();
+       else
+               Clipping::unbind();
+
+       bool shprog_changed = (state->shprog!=Program::current());
+       state->shprog->bind();
+
+       if(changed&MATRIX)
+       {
+               standard_shdata.uniform("eye_obj_matrix", state->modelview_matrix);
+               LinAl::SquareMatrix<float, 3> nm = state->modelview_matrix.block<3, 3>(0, 0);
+               nm = transpose(invert(nm));
+               standard_shdata.uniform_matrix3("eye_obj_normal_matrix", &nm(0, 0));
+               changed = (changed&~MATRIX)|STANDARD_SHDATA;
+       }
+
+       if(state->material && ((changed&MATERIAL_SHDATA) || shprog_changed))
+       {
+               state->material->get_shader_data().apply();
+               changed &= ~MATERIAL_SHDATA;
+       }
+
+       if((changed&STANDARD_SHDATA) || shprog_changed)
+       {
+               standard_shdata.apply();
+               changed &= ~STANDARD_SHDATA;
+       }
+
+       bool extra_shdata = (shdata_stack.size()>state->shdata_count);
+
+       if((changed&SHADER_DATA) || shprog_changed || extra_shdata)
+       {
+               if(extra_shdata)
+                       shdata_stack.erase(shdata_stack.begin()+state->shdata_count, shdata_stack.end());
+               for(vector<const ProgramData *>::const_iterator i=shdata_stack.begin(); i!=shdata_stack.end(); ++i)
+                       (*i)->apply();
+               changed &= ~SHADER_DATA;
+       }
+
+       if(state->vertex_setup)
+               state->vertex_setup->bind();
+       else
+               VertexSetup::unbind();
+
+       if(state->winding_test)
+       {
+               if(state->reverse_winding)
+                       state->winding_test->get_reverse().bind();
+               else
+                       state->winding_test->bind();
+       }
+       else
+               WindingTest::unbind();
+}
+
+
+Renderer::State::State():
+       camera(0),
+       texture(0),
+       texturing(0),
+       lowest_effect_texunit(TexUnit::get_n_units()),
+       material(0),
+       lighting(0),
+       clipping(0),
+       shprog(0),
+       shdata_count(0),
+       vertex_setup(0),
+       winding_test(0),
+       reverse_winding(false),
+       object_lod_bias(0)
+{ }
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/render/renderer.h b/source/render/renderer.h
new file mode 100644 (file)
index 0000000..2854bc8
--- /dev/null
@@ -0,0 +1,181 @@
+#ifndef MSP_GL_RENDERER_H_
+#define MSP_GL_RENDERER_H_
+
+#include <set>
+#include <vector>
+#include "matrix.h"
+#include "programdata.h"
+#include "tag.h"
+
+namespace Msp {
+namespace GL {
+
+class Batch;
+class Buffer;
+class Camera;
+class Clipping;
+class Material;
+class Mesh;
+class Lighting;
+class Program;
+class Renderable;
+class Sampler;
+class Texture;
+class Texturing;
+class VertexSetup;
+class WindingTest;
+
+/**
+A class for supervising the rendering process.  While many Renderables (in
+particular, Objects and Scenes) can by rendered without a Renderer, using one
+will often be more efficient.  This is especially true for ObjectInstances.
+
+The Renderer works by deferring GL state changes until something is actually
+being drawn.  This avoids many unnecessary GL calls if consecutive renderables
+use the same resources.
+
+A state stack is provided to help with state scoping.  Typically a Renderable
+will push the current state on entry, set whatever state it requires, render
+itself, and pop the state when it's done.  An RAII helper class is provided for
+the push/pop operation.
+*/
+class Renderer
+{
+public:
+       class Push
+       {
+       private:
+               Renderer &renderer;
+
+       public:
+               Push(Renderer &r): renderer(r) { renderer.push_state(); }
+               ~Push() { renderer.pop_state(); }
+       };
+
+       class Exclude
+       {
+       private:
+               Renderer &renderer;
+               const Renderable &renderable;
+
+       public:
+               Exclude(Renderer &r, const Renderable &e): renderer(r), renderable(e) { renderer.exclude(renderable); }
+               ~Exclude() { renderer.include(renderable); }
+       };
+
+private:
+       struct State
+       {
+               const Camera *camera;
+               Matrix modelview_matrix;
+               const Texture *texture;
+               const Sampler *sampler;
+               const Texturing *texturing;
+               unsigned lowest_effect_texunit;
+               const Material *material;
+               const Lighting *lighting;
+               Matrix lighting_matrix;
+               const Clipping *clipping;
+               Matrix clipping_matrix;
+               const Program *shprog;
+               unsigned shdata_count;
+               const VertexSetup *vertex_setup;
+               const WindingTest *winding_test;
+               bool reverse_winding;
+               unsigned object_lod_bias;
+
+               State();
+       };
+
+       enum ChangeMask
+       {
+               MATRIX = 2,
+               SHADER_DATA = 16,
+               MATERIAL_SHDATA = 32,
+               STANDARD_SHDATA = 64
+       };
+
+       const Camera *default_camera;
+       unsigned char changed;
+       std::vector<State> state_stack;
+       State *state;
+       ProgramData standard_shdata;
+       std::vector<const ProgramData *> shdata_stack;
+       std::set<const Renderable *> excluded;
+
+public:
+       Renderer();
+       DEPRECATED Renderer(const Camera *);
+private:
+       void init();
+public:
+       ~Renderer();
+
+       /** Sets the camera to render from.  The modelview matrix is reset to the
+       camera's view matrix. */
+       void set_camera(const Camera &);
+
+       const Camera *get_camera() const { return state->camera; }
+
+       /** Replaces the Renderer's modelview matrix. */
+       void set_matrix(const Matrix &);
+
+       /** Applies a transform to the Renderer's modelview matrix. */
+       void transform(const Matrix &);
+
+       /** Returns the current modelview matrix. */
+       const Matrix &get_matrix() const { return state->modelview_matrix; }
+
+       void set_texture(const Texture *, const Sampler * = 0);
+       void set_texturing(const Texturing *);
+       unsigned allocate_effect_texunit();
+       void set_material(const Material *);
+
+       void set_lighting(const Lighting *);
+       void set_clipping(const Clipping *);
+
+       /** Sets the shader program to use.  An initial set of data can be set as
+       well, with the same semantics as add_shader_data. */
+       void set_shader_program(const Program *prog, const ProgramData *data = 0);
+
+       /** Adds another set of data to be use with shader programs.  The data is
+       independent of any shader program changes and remains in effect until the
+       Renderer state is popped. */
+       void add_shader_data(const ProgramData &data);
+
+       void flush_shader_data();
+
+       void set_vertex_setup(const VertexSetup *);
+       void set_winding_test(const WindingTest *);
+       void set_reverse_winding(bool);
+
+       void set_object_lod_bias(unsigned);
+       unsigned get_object_lod_bias() const { return state->object_lod_bias; }
+
+       /** Saves the current state so it can be restored later. */
+       void push_state();
+
+       /** Restores a previously saved state.  Must be matched with an earlier
+       push_state call. */
+       void pop_state();
+
+       /** Unbinds all objects and resets related state.  There must be no unpopped
+       state in the stack.  The Renderer remains valid and may be reused for
+       further rendering. */
+       void end();
+
+       void exclude(const Renderable &);
+       void include(const Renderable &);
+
+       void render(const Renderable &, const Tag & = Tag());
+       void draw(const Batch &);
+       void draw_instanced(const Batch &, unsigned);
+
+private:
+       void apply_state();
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/render/rendertarget.cpp b/source/render/rendertarget.cpp
new file mode 100644 (file)
index 0000000..1349944
--- /dev/null
@@ -0,0 +1,197 @@
+#include <msp/core/maputils.h>
+#include "error.h"
+#include "renderbuffer.h"
+#include "rendertarget.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+RenderTargetFormat::RenderTargetFormat():
+       count(0)
+{ }
+
+RenderTargetFormat::RenderTargetFormat(RenderOutput o):
+       count(1)
+{
+       outputs[0] = o;
+}
+
+RenderTargetFormat RenderTargetFormat::operator,(RenderOutput o) const
+{
+       if(count>=MAX_OUTPUTS)
+               throw invalid_operation("RenderTargetFormat::operator,");
+
+       RenderTargetFormat result = *this;
+       result.outputs[result.count++] = o;
+
+       return result;
+}
+
+RenderTargetFormat RenderTargetFormat::operator,(PixelFormat f) const
+{
+       if(!count)
+               throw invalid_operation("RenderTargetFormat::operator,");
+
+       PixelComponents comp = get_components(f);
+       unsigned size = get_component_size(f);
+       unsigned char out = outputs[count-1];
+       if(get_output_type(out)>=get_output_type(RENDER_DEPTH))
+       {
+               if(comp!=DEPTH_COMPONENT)
+                       throw invalid_argument("RenderTargetFormat::operator,");
+               if(size>1)
+                       --size;
+               if(get_component_type(f)==UNSIGNED_INT)
+                       --size;
+       }
+       else
+       {
+               if(comp!=RED && comp!=RG && comp!=RGB && comp!=RGBA)
+                       throw invalid_argument("RenderTargetformat::operator,");
+               if(size>3)
+                       --size;
+       }
+
+       out = (out&~15) | (size<<2) | (get_component_count(f)-1);
+       RenderTargetFormat result = *this;
+       result.outputs[result.count-1] = out;
+
+       return result;
+}
+
+int RenderTargetFormat::index(RenderOutput o) const
+{
+       unsigned type = get_output_type(o);
+       unsigned i = 0;
+       for(const unsigned char *j=begin(); j!=end(); ++j, ++i)
+               if(get_output_type(*j)==type)
+                       return i;
+       return -1;
+}
+
+
+PixelFormat get_output_pixelformat(unsigned char o)
+{
+       PixelComponents comp;
+       DataType type;
+       if(get_output_type(o)>=get_output_type(RENDER_DEPTH))
+       {
+               static DataType types[4] = { UNSIGNED_SHORT, UNSIGNED_SHORT, UNSIGNED_INT, FLOAT };
+               comp = DEPTH_COMPONENT;
+               type = types[(o>>2)&3];
+       }
+       else
+       {
+               static PixelComponents components[4] = { RED, RG, RGB, RGBA };
+               static DataType types[4] = { UNSIGNED_BYTE, UNSIGNED_SHORT, HALF_FLOAT, FLOAT };
+               comp = components[o&3];
+               type = types[(o>>2)&3];
+       }
+
+       return make_pixelformat(comp, type);
+}
+
+
+RenderTarget::RenderTarget(unsigned w, unsigned h, RenderOutput o)
+{
+       init(w, h, 0, o);
+}
+
+RenderTarget::RenderTarget(unsigned w, unsigned h, const RenderTargetFormat &f)
+{
+       init(w, h, 0, f);
+}
+
+RenderTarget::RenderTarget(unsigned w, unsigned h, unsigned s, const RenderTargetFormat &f)
+{
+       init(w, h, s, f);
+}
+
+void RenderTarget::init(unsigned w, unsigned h, unsigned s, const RenderTargetFormat &f)
+{
+       width = w;
+       height = h;
+       samples = s;
+       format = f;
+
+       for(const unsigned char *i=format.begin(); i!=format.end(); ++i)
+       {
+               unsigned type = get_output_type(*i);
+               FramebufferAttachment att;
+               if(type>=get_output_type(RENDER_DEPTH))
+                       att = DEPTH_ATTACHMENT;
+               else
+                       att = static_cast<FramebufferAttachment>(COLOR_ATTACHMENT0+type);
+
+               PixelFormat pf = get_output_pixelformat(*i);
+
+               TargetBuffer tgt;
+               if(samples)
+               {
+                       tgt.buffer = new Renderbuffer;
+                       tgt.buffer->storage_multisample(samples, pf, width, height);
+                       fbo.attach(att, *tgt.buffer);
+               }
+               else
+               {
+                       tgt.texture = new Texture2D;
+                       tgt.texture->storage(pf, width, height, 1);
+                       Sampler &sampler = tgt.texture->get_default_sampler();
+                       sampler.set_filter(NEAREST);
+                       sampler.set_wrap(CLAMP_TO_EDGE);
+                       fbo.attach(att, *tgt.texture);
+               }
+               buffers.push_back(tgt);
+       }
+
+       fbo.require_complete();
+}
+
+RenderTarget::~RenderTarget()
+{
+       for(vector<TargetBuffer>::iterator i=buffers.begin(); i!=buffers.end(); ++i)
+       {
+               if(samples)
+                       delete i->buffer;
+               else
+                       delete i->texture;
+       }
+}
+
+void RenderTarget::set_texture_filter(TextureFilter filt)
+{
+       if(!samples)
+       {
+               for(vector<TargetBuffer>::iterator i=buffers.begin(); i!=buffers.end(); ++i)
+                       i->texture->get_default_sampler().set_filter(filt);
+       }
+}
+
+const Texture2D &RenderTarget::get_target_texture(unsigned i) const
+{
+       if(i>=buffers.size())
+               throw out_of_range("RenderTarget::get_target_texture");
+       if(samples)
+               throw invalid_operation("RenderTarget::get_target_texture");
+
+       return *buffers[i].texture;
+}
+
+const Texture2D &RenderTarget::get_target_texture(RenderOutput o) const
+{
+       int index = format.index(o);
+       if(index<0)
+               throw key_error(o);
+
+       return get_target_texture(index);
+}
+
+void RenderTarget::blit_from(const RenderTarget &other)
+{
+       fbo.blit_from(other.fbo, COLOR_BUFFER_BIT|DEPTH_BUFFER_BIT, false);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/render/rendertarget.h b/source/render/rendertarget.h
new file mode 100644 (file)
index 0000000..7d796a3
--- /dev/null
@@ -0,0 +1,89 @@
+#ifndef RENDERTARGET_H_
+#define RENDERTARGET_H_
+
+#include "framebuffer.h"
+#include "texture2d.h"
+
+namespace Msp {
+namespace GL {
+
+enum RenderOutput
+{
+       RENDER_COLOR = 0|3,
+       RENDER_DEPTH = 192|12
+};
+
+class RenderTargetFormat
+{
+private:
+       enum { MAX_OUTPUTS = 7 };
+
+       unsigned char count;
+       unsigned char outputs[MAX_OUTPUTS];
+
+public:
+       RenderTargetFormat();
+       RenderTargetFormat(RenderOutput);
+
+       RenderTargetFormat operator,(RenderOutput) const;
+       RenderTargetFormat operator,(PixelFormat) const;
+
+       bool empty() const { return !count; }
+       const unsigned char *begin() const { return outputs; }
+       const unsigned char *end() const { return outputs+count; }
+       int index(RenderOutput) const;
+};
+
+inline RenderTargetFormat operator,(RenderOutput o1, RenderOutput o2)
+{ return (RenderTargetFormat(o1), o2); }
+
+inline RenderTargetFormat operator,(RenderOutput o, PixelFormat f)
+{ return (RenderTargetFormat(o), f); }
+
+inline unsigned get_output_type(unsigned char o)
+{ return o>>4; }
+
+PixelFormat get_output_pixelformat(unsigned char);
+
+
+class RenderTarget
+{
+private:
+       union TargetBuffer
+       {
+               Texture2D *texture;
+               Renderbuffer *buffer;
+       };
+
+       unsigned width;
+       unsigned height;
+       unsigned samples;
+       RenderTargetFormat format;
+       std::vector<TargetBuffer> buffers;
+       Framebuffer fbo;
+
+public:
+       RenderTarget(unsigned, unsigned, RenderOutput);
+       RenderTarget(unsigned, unsigned, const RenderTargetFormat & = (RENDER_COLOR, RENDER_DEPTH));
+       RenderTarget(unsigned, unsigned, unsigned, const RenderTargetFormat & = (RENDER_COLOR, RENDER_DEPTH));
+private:
+       RenderTarget(const RenderTarget &);
+       RenderTarget &operator=(const RenderTarget &);
+       void init(unsigned, unsigned, unsigned, const RenderTargetFormat &);
+public:
+       ~RenderTarget();
+
+       unsigned get_width() const { return width; }
+       unsigned get_height() const { return height; }
+       const RenderTargetFormat &get_format() const { return format; }
+       Framebuffer &get_framebuffer() { return fbo; }
+       void set_texture_filter(TextureFilter);
+       const Texture2D &get_target_texture(unsigned) const;
+       const Texture2D &get_target_texture(RenderOutput) const;
+       void blit_from(const RenderTarget &);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/render/scene.cpp b/source/render/scene.cpp
new file mode 100644 (file)
index 0000000..e6c62e8
--- /dev/null
@@ -0,0 +1,113 @@
+#include <msp/datafile/collection.h>
+#include "animatedobject.h"
+#include "camera.h"
+#include "renderer.h"
+#include "scene.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Scene::~Scene()
+{
+       for(vector<Renderable *>::iterator i=owned_data.begin(); i!=owned_data.end(); ++i)
+               delete *i;
+}
+
+bool Scene::setup_frustum(const Renderer &renderer) const
+{
+       const Camera *camera = renderer.get_camera();
+       if(!camera)
+               return false;
+
+       culling_matrix = renderer.get_matrix();
+
+       if(camera->is_orthographic())
+       {
+               float h = camera->get_orthographic_height();
+               frustum_edges[0] = Vector4(0, 1, 0, -h);
+               frustum_edges[1] = Vector4(0, -1, 0, -h);
+
+               float w = camera->get_orthographic_width();
+               frustum_edges[2] = Vector4(1, 0, 0, -w);
+               frustum_edges[3] = Vector4(-1, 0, 0, -w);
+       }
+       else
+       {
+               float y = tan(camera->get_field_of_view()/2.0f);
+               float s = sqrt(y*y+1);
+               frustum_edges[0] = Vector4(0, 1/s, y/s, 0);
+               frustum_edges[1] = Vector4(0, -1/s, y/s, 0);
+
+               float x = y*camera->get_aspect_ratio();
+               s = sqrt(x*x+1);
+               frustum_edges[2] = Vector4(1/s, 0, x/s, 0);
+               frustum_edges[3] = Vector4(-1/s, 0, x/s, 0);
+       }
+
+       frustum_edges[4] = Vector4(0, 0, -1, -camera->get_far_clip());
+       frustum_edges[5] = Vector4(0, 0, 1, camera->get_near_clip());
+
+       return true;
+}
+
+bool Scene::frustum_cull(const Renderable &renderable) const
+{
+       const Matrix *matrix = renderable.get_matrix();
+       const Geometry::BoundingSphere<float, 3> *bsphere = renderable.get_bounding_sphere();
+       if(!matrix || !bsphere)
+               return false;
+
+       Vector4 center = culling_matrix*(*matrix*compose(bsphere->get_center(), 1.0f));
+       Vector3 x_axis = (matrix->column(0)*bsphere->get_radius()).slice<3>(0);
+       float radius_sq = inner_product(x_axis, x_axis);
+
+       for(unsigned i=0; i<6; ++i)
+       {
+               float distance = inner_product(center, frustum_edges[i]);
+               if(distance>0 && distance*distance>radius_sq)
+                       return true;
+       }
+
+       return false;
+}
+
+
+Scene::Loader::Loader(Scene &s, Collection &c):
+       DataFile::CollectionObjectLoader<Scene>(s, &c),
+       content(0)
+{
+       init();
+}
+
+Scene::Loader::Loader(Scene &s, Collection &c, ContentMap &m):
+       DataFile::CollectionObjectLoader<Scene>(s, &c),
+       content(&m)
+{
+       init();
+}
+
+void Scene::Loader::init()
+{
+       add("object", &Loader::object);
+       add("object", &Loader::object_tagged);
+}
+
+void Scene::Loader::object(const string &n)
+{
+       object_tagged(n, string());
+}
+
+void Scene::Loader::object_tagged(const string &n, const string &t)
+{
+       RefPtr<AnimatedObject> anob = new AnimatedObject(get_collection().get<GL::Object>(n));
+       load_sub(*anob);
+       obj.add(*anob);
+       if(content && !t.empty())
+               (*content)[t] = anob.get();
+       obj.owned_data.push_back(anob.release());
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/render/scene.h b/source/render/scene.h
new file mode 100644 (file)
index 0000000..639a467
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef MSP_GL_SCENE_H_
+#define MSP_GL_SCENE_H_
+
+#include <vector>
+#include <msp/datafile/objectloader.h>
+#include "matrix.h"
+#include "renderable.h"
+#include "vector.h"
+
+namespace Msp {
+namespace GL {
+
+/**
+Scenes are containers for other Renderables.  This is a base class that can't
+be instantiated.  Examples of available Scene types are SimpleScene,
+InstancedScene and OrderedScene.
+*/
+class Scene: public Renderable
+{
+public:
+       class Loader: public DataFile::CollectionObjectLoader<Scene>
+       {
+       public:
+               typedef std::map<std::string, Renderable *> ContentMap;
+
+       private:
+               ContentMap *content;
+
+       public:
+               Loader(Scene &, Collection &);
+               Loader(Scene &, Collection &, ContentMap &);
+       private:
+               void init();
+
+               void object(const std::string &);
+               void object_tagged(const std::string &, const std::string &);
+       };
+
+protected:
+       // XXX If a loaded renderable is removed from the scene it needs to be removed from here as well
+       std::vector<Renderable *> owned_data;
+       mutable Matrix culling_matrix;
+       mutable Vector4 frustum_edges[6];
+
+       Scene() { }
+private:
+       Scene(const Scene &);
+       Scene &operator=(const Scene &);
+public:
+       virtual ~Scene();
+
+       virtual void add(Renderable &) = 0;
+       virtual void remove(Renderable &) = 0;
+
+protected:
+       bool setup_frustum(const Renderer &) const;
+       bool frustum_cull(const Renderable &) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/render/simplescene.cpp b/source/render/simplescene.cpp
new file mode 100644 (file)
index 0000000..d1fd3c1
--- /dev/null
@@ -0,0 +1,59 @@
+#include "renderer.h"
+#include "simplescene.h"
+
+namespace Msp {
+namespace GL {
+
+void SimpleScene::add(Renderable &r)
+{
+       // Add to cache as well if the cache is valid
+       if(renderables.insert(&r).second && !cache.empty())
+               cache.push_back(&r);
+}
+
+void SimpleScene::remove(Renderable &r)
+{
+       renderables.erase(&r);
+       cache.clear();
+}
+
+void SimpleScene::populate_cache() const
+{
+       if(cache.empty() && !renderables.empty())
+       {
+               cache.reserve(renderables.size());
+               cache.insert(cache.end(), renderables.begin(), renderables.end());
+       }
+}
+
+void SimpleScene::setup_frame(Renderer &renderer)
+{
+       populate_cache();
+       for(RenderableArray::const_iterator i=cache.begin(); i!=cache.end(); ++i)
+               (*i)->setup_frame(renderer);
+}
+
+void SimpleScene::finish_frame()
+{
+       for(RenderableArray::const_iterator i=cache.begin(); i!=cache.end(); ++i)
+               (*i)->finish_frame();
+}
+
+void SimpleScene::render(Renderer &renderer, const Tag &tag) const
+{
+       populate_cache();
+       if(setup_frustum(renderer))
+       {
+               for(RenderableArray::const_iterator i=cache.begin(); i!=cache.end(); ++i)
+                       if(!frustum_cull(**i))
+                               renderer.render(**i, tag);
+       }
+       else
+       {
+               for(RenderableArray::const_iterator i=cache.begin(); i!=cache.end(); ++i)
+                       renderer.render(**i, tag);
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/render/simplescene.h b/source/render/simplescene.h
new file mode 100644 (file)
index 0000000..e56aeb0
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef MSP_GL_SIMPLESCENE_H_
+#define MSP_GL_SIMPLESCENE_H_
+
+#include <set>
+#include "scene.h"
+
+namespace Msp {
+namespace GL {
+
+/**
+A simple yet efficient scene.  Rendering order is unspecified.
+*/
+class SimpleScene: public Scene
+{
+private:
+       typedef std::set<Renderable *> RenderableSet;
+       typedef std::vector<Renderable *> RenderableArray;
+
+       RenderableSet renderables;
+       mutable RenderableArray cache;
+
+public:
+       virtual void add(Renderable &);
+       virtual void remove(Renderable &);
+
+private:
+       void populate_cache() const;
+
+public:
+       virtual void setup_frame(Renderer &);
+       virtual void finish_frame();
+
+       virtual void render(Renderer &, const Tag & = Tag()) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/render/slot.cpp b/source/render/slot.cpp
new file mode 100644 (file)
index 0000000..07dabf0
--- /dev/null
@@ -0,0 +1,45 @@
+#include <msp/gl/renderer.h>
+#include "slot.h"
+
+namespace Msp {
+namespace GL {
+
+Slot::Slot():
+       renderable(0)
+{ }
+
+void Slot::set(Renderable *r)
+{
+       renderable = r;
+}
+
+const Matrix *Slot::get_matrix() const
+{
+       return renderable ? renderable->get_matrix() : 0;
+}
+
+const Geometry::BoundingSphere<float, 3> *Slot::get_bounding_sphere() const
+{
+       return renderable ? renderable->get_bounding_sphere() : 0;
+}
+
+void Slot::setup_frame(Renderer &renderer)
+{
+       if(renderable)
+               renderable->setup_frame(renderer);
+}
+
+void Slot::finish_frame()
+{
+       if(renderable)
+               renderable->finish_frame();
+}
+
+void Slot::render(Renderer &renderer, const Tag &tag) const
+{
+       if(renderable)
+               renderer.render(*renderable, tag);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/render/slot.h b/source/render/slot.h
new file mode 100644 (file)
index 0000000..ad86301
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef MSP_GL_SLOT_H_
+#define MSP_GL_SLOT_H_
+
+#include "renderable.h"
+
+namespace Msp {
+namespace GL {
+
+/**
+A container for a single renderable.  Can be used if a part of the scene graph
+needs to be switched without affecting the rest.
+*/
+class Slot: public Renderable
+{
+private:
+       Renderable *renderable;
+
+public:
+       Slot();
+
+       void set(Renderable *);
+       Renderable *get() const { return renderable; }
+
+       virtual const Matrix *get_matrix() const;
+       virtual const Geometry::BoundingSphere<float, 3> *get_bounding_sphere() const;
+       virtual void setup_frame(Renderer &);
+       virtual void finish_frame();
+       virtual void render(Renderer &, const Tag &) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/render/tag.cpp b/source/render/tag.cpp
new file mode 100644 (file)
index 0000000..c5824ae
--- /dev/null
@@ -0,0 +1,17 @@
+#include <cstring>
+#include <msp/core/hash.h>
+#include "tag.h"
+
+namespace Msp {
+namespace GL {
+
+Tag::Tag(const char *s):
+       id((s && *s) ? hash32(s, strlen(s)) : 0)
+{ }
+
+Tag::Tag(const std::string &s):
+       id(s.empty() ? 0 : hash32(s))
+{ }
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/render/tag.h b/source/render/tag.h
new file mode 100644 (file)
index 0000000..28b4fa0
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef MSP_GL_TAG_H_
+#define MSP_GL_TAG_H_
+
+#include <string>
+
+namespace Msp {
+namespace GL {
+
+/**
+Provides transparent string-to-hash conversion for faster comparison.  An empty
+string is guaranteed to have an id of 0.
+*/
+struct Tag
+{
+       unsigned id;
+
+       Tag(): id(0) { }
+       Tag(const char *);
+       Tag(const std::string &s);
+
+       bool operator<(const Tag &t) const { return id<t.id; }
+       bool operator==(const Tag &t) const { return id==t.id; }
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/render/text.cpp b/source/render/text.cpp
new file mode 100644 (file)
index 0000000..b408bbe
--- /dev/null
@@ -0,0 +1,99 @@
+#include "meshbuilder.h"
+#include "text.h"
+#include "texture2d.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Text::Text(const Font &f, const Technique *tech):
+       font(f),
+       mesh((TEXCOORD2, VERTEX2)),
+       horz_align(0.0f),
+       vert_offset(0.0f),
+       width(0.0f)
+{
+       object.set_mesh(&mesh);
+       if(tech)
+               set_technique(tech);
+}
+
+void Text::set_technique(const Technique *tech)
+{
+       if(tech)
+       {
+               technique = *tech;
+               technique.replace_texture("diffuse_map", font.get_texture());
+               object.set_technique(&technique);
+       }
+       else
+               object.set_technique(0);
+}
+
+void Text::set_text(const string &text, StringCodec::Decoder &dec)
+{
+       clear();
+       width = font.get_string_width(text, dec);
+       MeshBuilder bld(mesh);
+       bld.transform(Matrix::translation(Vector3(-horz_align*width, vert_offset, 0.0f)));
+       font.build_string(text, dec, bld);
+}
+
+void Text::clear()
+{
+       mesh.clear();
+       width = 0;
+}
+
+void Text::set_alignment(HorizontalAlign ha, VerticalAlign va)
+{
+       float h;
+       switch(ha)
+       {
+       case LEFT: h = 0.0f; break;
+       case CENTER: h = 0.5f; break;
+       case RIGHT: h = 1.0f; break;
+       default: throw invalid_argument("Text::set_alignment");
+       }
+
+       float v;
+       switch(va)
+       {
+       case DESCENT: v = -font.get_descent(); break;
+       case BASELINE: v = 0.0f; break;
+       case MIDLINE: v = font.get_cap_height()/2; break;
+       case ASCENT: v = font.get_ascent(); break;
+       default: throw invalid_argument("Text::set_alignment");
+       }
+
+       set_alignment(h, v);
+}
+
+void Text::set_alignment(float h, float v)
+{
+       if(h==horz_align && -v==vert_offset)
+               return;
+
+       float horz_adjust = (horz_align-h)*width;
+       float vert_adjust = -v-vert_offset;
+       horz_align = h;
+       vert_offset = -v;
+
+       unsigned pos_offset = mesh.get_vertices().get_format().offset(VERTEX2);
+       unsigned n_vertices = mesh.get_n_vertices();
+       for(unsigned i=0; i<n_vertices; ++i)
+       {
+               float *pos = mesh.modify_vertex(i)+pos_offset;
+               pos[0] += horz_adjust;
+               pos[1] += vert_adjust;
+       }
+}
+
+void Text::render(Renderer &renderer, const Tag &tag) const
+{
+       object.render(renderer, tag);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/render/text.h b/source/render/text.h
new file mode 100644 (file)
index 0000000..40c9b85
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef MSP_GL_TEXT_H_
+#define MSP_GL_TEXT_H_
+
+#include "font.h"
+#include "mesh.h"
+#include "object.h"
+#include "technique.h"
+
+namespace Msp {
+namespace GL {
+
+/**
+Creates an object consisting of the visual representation of a string.  Can be
+used with an ObjectInstance to further customize the appearance.
+*/
+class Text: public Renderable
+{
+public:
+       enum HorizontalAlign
+       {
+               LEFT,
+               CENTER,
+               RIGHT
+       };
+
+       enum VerticalAlign
+       {
+               DESCENT,
+               BASELINE,
+               MIDLINE,
+               ASCENT
+       };
+
+private:
+       const Font &font;
+       Mesh mesh;
+       Object object;
+       Technique technique;
+       float horz_align;
+       float vert_offset;
+       float width;
+
+public:
+       Text(const Font &, const Technique * = 0);
+
+       const Mesh *get_mesh() const { return &mesh; }
+
+       /** Sets technique to render with.  It should have a texture slot named
+       "diffuse_map", which will be replaced with the font's texture. */
+       void set_technique(const Technique *);
+
+       const Technique *get_technique() const { return object.get_technique(); }
+
+       /// Sets the string to be displayed.
+       void set_text(const std::string &, StringCodec::Decoder &);
+
+       template<typename C>
+       void set_text(const std::string &t)
+       {
+               typename C::Decoder dec;
+               set_text(t, dec);
+       }
+
+       void set_text(const std::string &t)
+       { set_text<StringCodec::Utf8>(t); }
+
+       /// Clears the object's contents.
+       void clear();
+
+       /// Sets horizontal and vertical alignment with predefined anchors.
+       void set_alignment(HorizontalAlign, VerticalAlign = BASELINE);
+
+       /** Sets horizontal and vertical alignment.  0.0 means left or baseline,
+       1.0 means right or top. */
+       void set_alignment(float, float = 0.0f);
+
+       float get_width() const { return width; }
+
+       virtual void render(Renderer &, const Tag & = Tag()) const;
+
+       operator const Object &() const { return object; }
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/render/texturing.cpp b/source/render/texturing.cpp
new file mode 100644 (file)
index 0000000..ab5d39b
--- /dev/null
@@ -0,0 +1,166 @@
+#include <msp/core/hash.h>
+#include "texture.h"
+#include "texturing.h"
+#include "texunit.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+Texturing::~Texturing()
+{
+       if(current()==this)
+               unbind();
+}
+
+int Texturing::find_free_unit(const string &name_hint) const
+{
+       unsigned max_unit = TexUnit::get_n_units();
+       // Leave some space for effect textures
+       max_unit -= min(max_unit/4, 8U);
+       unsigned initial_unit = (name_hint.empty() ? 0 : hash32(name_hint)%max_unit);
+       unsigned unit = initial_unit;
+       while(get_attached_texture(unit) || get_attached_sampler(unit))
+       {
+               unit = (unit+1)%max_unit;
+               if(unit==initial_unit)
+                       return -1;
+       }
+
+       return unit;
+}
+
+void Texturing::attach(unsigned attch, const Texture &tex, const Sampler *samp)
+{
+       set_attachment(attch, &tex, samp);
+}
+
+void Texturing::attach(unsigned attch, const Sampler &samp)
+{
+       set_attachment(attch, 0, &samp);
+}
+
+void Texturing::detach(unsigned attch)
+{
+       set_attachment(attch, 0, 0);
+}
+
+void Texturing::set_attachment(unsigned unit, const Texture *tex, const Sampler *samp)
+{
+       if(unit>=TexUnit::get_n_units())
+               throw out_of_range("Texturing::set_attachment");
+
+       if(tex || samp)
+       {
+               vector<Attachment>::iterator i;
+               for(i=attachments.begin(); (i!=attachments.end() && i->unit<=unit); ++i)
+                       if(i->unit==unit)
+                       {
+                               i->texture = tex;
+                               i->sampler = samp;
+                               if(current()==this)
+                                       bind_attachment(*i);
+                               return;
+                       }
+
+               attachments.insert(i, Attachment(unit, tex, samp));
+               if(current()==this)
+                       tex->bind_to(unit);
+       }
+       else
+       {
+               for(vector<Attachment>::iterator i=attachments.begin(); (i!=attachments.end() && i->unit<=unit); ++i)
+                       if(i->unit==unit)
+                       {
+                               attachments.erase(i);
+                               if(current()==this)
+                                       unbind_attachment(unit);
+                               return;
+                       }
+       }
+}
+
+const Texture *Texturing::get_attached_texture(unsigned unit) const
+{
+       for(vector<Attachment>::const_iterator i=attachments.begin(); (i!=attachments.end() && i->unit<=unit); ++i)
+               if(i->unit==unit)
+                       return i->texture;
+       return 0;
+}
+
+const Sampler *Texturing::get_attached_sampler(unsigned unit) const
+{
+       for(vector<Attachment>::const_iterator i=attachments.begin(); (i!=attachments.end() && i->unit<=unit); ++i)
+               if(i->unit==unit)
+                       return i->sampler;
+       return 0;
+}
+
+void Texturing::bind() const
+{
+       const Texturing *old = current();
+       if(set_current(this))
+       {
+               if(old)
+               {
+                       vector<Attachment>::const_iterator i = attachments.begin();
+                       vector<Attachment>::const_iterator j = old->attachments.begin();
+                       while(i!=attachments.end() || j!=old->attachments.end())
+                       {
+                               if(i!=attachments.end() && (j==old->attachments.end() || i->unit<=j->unit))
+                               {
+                                       bind_attachment(*i);
+                                       if(j!=old->attachments.end() && j->unit==i->unit)
+                                               ++j;
+                                       ++i;
+                               }
+                               else
+                               {
+                                       unbind_attachment(j->unit);
+                                       ++j;
+                               }
+                       }
+               }
+               else
+               {
+                       for(vector<Attachment>::const_iterator i=attachments.begin(); i!=attachments.end(); ++i)
+                               bind_attachment(*i);
+               }
+       }
+}
+
+void Texturing::bind_attachment(const Attachment &attch) const
+{
+       if(attch.sampler)
+               attch.sampler->bind_to(attch.unit);
+       else
+               Sampler::unbind_from(attch.unit);
+       attch.texture->bind_to(attch.unit);
+}
+
+void Texturing::unbind()
+{
+       const Texturing *old = current();
+       if(set_current(0))
+       {
+               for(vector<Attachment>::const_iterator i=old->attachments.begin(); i!=old->attachments.end(); ++i)
+                       unbind_attachment(i->unit);
+       }
+}
+
+void Texturing::unbind_attachment(unsigned unit)
+{
+       Texture::unbind_from(unit);
+       Sampler::unbind_from(unit);
+}
+
+
+Texturing::Attachment::Attachment(unsigned u, const Texture *t, const Sampler *s):
+       unit(u),
+       texture(t),
+       sampler(s)
+{ }
+
+} // namespace GL
+} // namespace Msp;
diff --git a/source/render/texturing.h b/source/render/texturing.h
new file mode 100644 (file)
index 0000000..0da3eae
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef MSP_GL_TEXTURING_H_
+#define MSP_GL_TEXTURING_H_
+
+#include <vector>
+#include "bindable.h"
+
+namespace Msp {
+namespace GL {
+
+class Texture;
+
+class Texturing: public Bindable<Texturing>
+{
+private:
+       struct Attachment
+       {
+               unsigned unit;
+               const Texture *texture;
+               const Sampler *sampler;
+
+               Attachment(unsigned, const Texture *, const Sampler *);
+       };
+
+       std::vector<Attachment> attachments;
+
+public:
+       ~Texturing();
+
+       int find_free_unit(const std::string & = std::string()) const;
+       void attach(unsigned, const Texture &, const Sampler * = 0);
+       void attach(unsigned, const Sampler &);
+       void detach(unsigned);
+private:
+       void set_attachment(unsigned, const Texture *, const Sampler *);
+public:
+       const Texture *get_attached_texture(unsigned) const;
+       const Sampler *get_attached_sampler(unsigned) const;
+
+       void bind() const;
+
+       static void unbind();
+
+private:
+       void bind_attachment(const Attachment &) const;
+       static void unbind_attachment(unsigned);
+};
+
+} // namespace GL
+} // namespace Msp;
+
+#endif
diff --git a/source/render/view.cpp b/source/render/view.cpp
new file mode 100644 (file)
index 0000000..d0d3542
--- /dev/null
@@ -0,0 +1,57 @@
+#include "camera.h"
+#include "renderable.h"
+#include "renderer.h"
+#include "view.h"
+
+namespace Msp {
+namespace GL {
+
+View::View(Framebuffer &t):
+       target(t),
+       camera(0),
+       content(0),
+       internal_renderer(0)
+{ }
+
+View::~View()
+{
+       delete internal_renderer;
+}
+
+void View::set_camera(Camera *c)
+{
+       camera = c;
+       if(camera)
+               camera->set_aspect_ratio(get_aspect_ratio());
+}
+
+void View::set_content(Renderable *r)
+{
+       content = r;
+}
+
+void View::render()
+{
+       if(!internal_renderer)
+               internal_renderer = new Renderer;
+       render(*internal_renderer);
+}
+
+void View::render(Renderer &renderer)
+{
+       Bind bind_fbo(target);
+       target.clear();
+       if(content)
+       {
+               Renderer::Push push(renderer);
+               if(camera)
+                       renderer.set_camera(*camera);
+               content->setup_frame(renderer);
+               content->render(renderer);
+               content->finish_frame();
+               renderer.flush_shader_data();
+       }
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/render/view.h b/source/render/view.h
new file mode 100644 (file)
index 0000000..df623c3
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef MSP_GL_VIEW_H_
+#define MSP_GL_VIEW_H_
+
+#include <list>
+#include "framebuffer.h"
+
+namespace Msp {
+namespace GL {
+
+class Camera;
+class Renderable;
+class Renderer;
+
+/**
+Manages the presentation of rendering results on the screen.
+*/
+class View: public sigc::trackable
+{
+protected:
+       Framebuffer &target;
+       Camera *camera;
+       Renderable *content;
+       Renderer *internal_renderer;
+
+       View(Framebuffer &);
+public:
+       virtual ~View();
+
+       virtual unsigned get_width() const { return target.get_width(); }
+       virtual unsigned get_height() const { return target.get_height(); }
+       float get_aspect_ratio() const { return static_cast<float>(get_width())/get_height(); }
+
+       void set_camera(Camera *);
+       void set_content(Renderable *);
+
+       virtual void render();
+       virtual void render(Renderer &);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/render/windowview.cpp b/source/render/windowview.cpp
new file mode 100644 (file)
index 0000000..2cce075
--- /dev/null
@@ -0,0 +1,33 @@
+#include "camera.h"
+#include "windowview.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+WindowView::WindowView(Graphics::Window &w, Graphics::GLContext &c):
+       View(Framebuffer::system()),
+       window(w),
+       context(c)
+{
+       window.signal_resize.connect(sigc::mem_fun(this, &WindowView::window_resized));
+       window_resized(window.get_width(), window.get_height());
+}
+
+void WindowView::render(Renderer &renderer)
+{
+       View::render(renderer);
+       context.swap_buffers();
+}
+
+void WindowView::window_resized(unsigned w, unsigned h)
+{
+       target.viewport(0, 0, w, h);
+       float aspect = static_cast<float>(w)/h;
+       if(camera)
+               camera->set_aspect_ratio(aspect);
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/render/windowview.h b/source/render/windowview.h
new file mode 100644 (file)
index 0000000..8b6f143
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef MSP_GL_WINDOWVIEW_H_
+#define MSP_GL_WINDOWVIEW_H_
+
+#include <sigc++/trackable.h>
+#include <msp/graphics/glcontext.h>
+#include <msp/graphics/window.h>
+#include "view.h"
+
+namespace Msp {
+namespace GL {
+
+class WindowView: public View
+{
+private:
+       Graphics::Window &window;
+       Graphics::GLContext &context;
+
+public:
+       WindowView(Graphics::Window &, Graphics::GLContext &);
+
+       Graphics::Window &get_window() { return window; }
+       Graphics::GLContext &get_context() { return context; }
+       virtual unsigned get_width() const { return window.get_width(); }
+       virtual unsigned get_height() const { return window.get_height(); }
+
+       using View::render;
+       virtual void render(Renderer &);
+
+private:
+       void window_resized(unsigned, unsigned);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/render/zsortedscene.cpp b/source/render/zsortedscene.cpp
new file mode 100644 (file)
index 0000000..46b14db
--- /dev/null
@@ -0,0 +1,132 @@
+#include "camera.h"
+#include "renderer.h"
+#include "zsortedscene.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+ZSortedScene::ZSortedScene():
+       order(BACK_TO_FRONT),
+       reference(FURTHEST)
+{ }
+
+void ZSortedScene::add(Renderable &r)
+{
+       if(renderables.insert(&r).second && !sorted_cache.empty())
+               sorted_cache.push_back(&r);
+}
+
+void ZSortedScene::remove(Renderable &r)
+{
+       renderables.erase(&r);
+       sorted_cache.clear();
+}
+
+void ZSortedScene::set_order(SortOrder o)
+{
+       order = o;
+}
+
+void ZSortedScene::set_reference(DepthReference r)
+{
+       reference = r;
+}
+
+void ZSortedScene::populate_cache() const
+{
+       if(sorted_cache.empty() && !renderables.empty())
+       {
+               sorted_cache.reserve(renderables.size());
+               sorted_cache.insert(sorted_cache.end(), renderables.begin(), renderables.end());
+       }
+}
+
+void ZSortedScene::setup_frame(Renderer &renderer)
+{
+       populate_cache();
+       for(SortedArray::const_iterator i=sorted_cache.begin(); i!=sorted_cache.end(); ++i)
+               i->renderable->setup_frame(renderer);
+}
+
+void ZSortedScene::finish_frame()
+{
+       for(SortedArray::const_iterator i=sorted_cache.begin(); i!=sorted_cache.end(); ++i)
+               i->renderable->finish_frame();
+}
+
+void ZSortedScene::render(Renderer &renderer, const Tag &tag) const
+{
+       if(renderables.empty())
+               return;
+
+       populate_cache();
+
+       const Camera *camera = renderer.get_camera();
+       if(!camera)
+       {
+               for(SortedArray::const_iterator i=sorted_cache.begin(); i!=sorted_cache.end(); ++i)
+                       renderer.render(*i->renderable, tag);
+               return;
+       }
+
+       const Vector3 &camera_pos = camera->get_position();
+       const Vector3 &look_dir = camera->get_look_direction();
+       float radius_factor = reference-1.0f;
+       float sign = 1.0f-order*2.0f;
+
+       bool use_frustum = setup_frustum(renderer);
+       for(SortedArray::iterator i=sorted_cache.begin(); i!=sorted_cache.end(); ++i)
+       {
+               i->in_frustum = (!use_frustum || !frustum_cull(*i->renderable));
+               if(!i->in_frustum)
+                       continue;
+
+               if(const Matrix *matrix = i->renderable->get_matrix())
+               {
+                       if(const Geometry::BoundingSphere<float, 3> *bsphere = i->renderable->get_bounding_sphere())
+                               i->depth = dot(*matrix*bsphere->get_center()-camera_pos, look_dir)+bsphere->get_radius()*radius_factor;
+                       else
+                               i->depth = dot(*matrix*Vector3()-camera_pos, look_dir);
+                       i->depth *= sign;
+               }
+               else
+                       i->depth = 0;
+       }
+
+       for(SortedArray::iterator i=sorted_cache.begin(), j=i; i!=sorted_cache.end(); ++i)
+               if(i->in_frustum)
+               {
+                       if(i!=j)
+                               swap(*i, *j);
+
+                       if(j!=sorted_cache.begin() && *j<*(j-1))
+                       {
+                               SortedRenderable sr = *j;
+                               SortedArray::iterator k = j-1;
+                               *j = *k;
+                               while(k!=sorted_cache.begin() && sr<*(k-1))
+                               {
+                                       *k = *(k-1);
+                                       --k;
+                               }
+                               *k = sr;
+                       }
+
+                       ++j;
+               }
+
+       for(SortedArray::const_iterator i=sorted_cache.begin(); (i!=sorted_cache.end() && i->in_frustum); ++i)
+               renderer.render(*i->renderable, tag);
+}
+
+
+ZSortedScene::SortedRenderable::SortedRenderable(Renderable *r):
+       renderable(r),
+       in_frustum(false),
+       depth(0.0f)
+{ }
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/render/zsortedscene.h b/source/render/zsortedscene.h
new file mode 100644 (file)
index 0000000..cb21061
--- /dev/null
@@ -0,0 +1,75 @@
+#ifndef MSP_GL_ZSORTEDSCENE_H_
+#define MSP_GL_ZSORTEDSCENE_H_
+
+#include <set>
+#include <vector>
+#include "scene.h"
+
+namespace Msp {
+namespace GL {
+
+enum SortOrder
+{
+       FRONT_TO_BACK,
+       BACK_TO_FRONT
+};
+
+enum DepthReference
+{
+       CLOSEST,
+       CENTER,
+       FURTHEST
+};
+
+/**
+Sorts renderables by their distance from the camera before rendering.  Requires
+renderables to have a matrix.
+*/
+class ZSortedScene: public Scene
+{
+private:
+       struct SortedRenderable
+       {
+               Renderable *renderable;
+               bool in_frustum;
+               float depth;
+
+               SortedRenderable(Renderable *);
+
+               bool operator<(const SortedRenderable &o) const { return depth<o.depth; }
+       };
+
+       typedef std::set<Renderable *> RenderableSet;
+       typedef std::vector<SortedRenderable> SortedArray;
+
+       RenderableSet renderables;
+       SortOrder order;
+       DepthReference reference;
+       mutable SortedArray sorted_cache;
+
+public:
+       ZSortedScene();
+
+       virtual void add(Renderable &);
+       virtual void remove(Renderable &);
+
+       /// Sets the sort order.  Default is back to front.
+       void set_order(SortOrder);
+
+       /// Sets the reference point for sorting.  Default is furthest from camera.
+       void set_reference(DepthReference);
+
+private:
+       void populate_cache() const;
+
+public:
+       virtual void setup_frame(Renderer &);
+       virtual void finish_frame();
+
+       virtual void render(Renderer &, const Tag & = Tag()) const;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/renderable.h b/source/renderable.h
deleted file mode 100644 (file)
index 80fe172..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-#ifndef MSP_GL_RENDERABLE_H_
-#define MSP_GL_RENDERABLE_H_
-
-#include <string>
-#include <msp/core/inttypes.h>
-#include <msp/geometry/boundingsphere.h>
-#include "tag.h"
-
-namespace Msp {
-namespace GL {
-
-class Matrix;
-class Renderer;
-
-/**
-Base class for renderable objects.  Rendering is performed with the help of a
-Renderer object.
-
-The render method takes a Tag to identify a render pass.  It can be used with
-a Technique to select alternative rendering methods, such as simplified shaders
-for a depth-only shadow pass.
-
-The setup_frame and finish_frame methods provide a mechanism for performing
-once-per-frame operations.  This is most useful for effects, which may need to
-do auxiliary rendering.  With complex rendering hierarchies, these methods may
-be called multiple times for one frame, but it's guaranteed that no rendering
-will occur before a setup_frame call or after a finish_frame call.
-*/
-class Renderable
-{
-protected:
-       Renderable() { }
-public:
-       virtual ~Renderable() { }
-
-       /** Returns a key used for grouping Renderables in an InstanceScene.  The
-       returned value is treated as opaque. */
-       virtual IntPtr get_instance_key() const { return 0; }
-
-       /** Returns the model matrix of the Renderable.  Null is returned if no such
-       matrix exists.  The matrix should be in world space for some effects to work
-       correctly. */
-       virtual const Matrix *get_matrix() const { return 0; }
-
-       /** Returns a bounding sphere that completely encloses the Renderable.  The
-       bounding sphere is expressed in the renderable's coordinates.  Null is
-       returned if the bounding sphere cannot be determined. */
-       virtual const Geometry::BoundingSphere<float, 3> *get_bounding_sphere() const { return 0; }
-
-       /** Called when starting to render a new frame. */
-       virtual void setup_frame(Renderer &) { }
-
-       /** Called when a complete frame has been rendered. */
-       virtual void finish_frame() { }
-
-       /** Renders the Renderable.  Implementors should take care to return the
-       renderer to the state it was in, for example by using Renderer::Push. */
-       virtual void render(Renderer &, const Tag & = Tag()) const = 0;
-};
-
-} // namespace Msp
-} // namespace GL
-
-#endif
diff --git a/source/renderbuffer.cpp b/source/renderbuffer.cpp
deleted file mode 100644 (file)
index e86a97a..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include <msp/gl/extensions/ext_framebuffer_multisample.h>
-#include <msp/gl/extensions/ext_framebuffer_object.h>
-#include "misc.h"
-#include "renderbuffer.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Renderbuffer::Renderbuffer()
-{
-       static Require _req(EXT_framebuffer_object);
-
-       if(ARB_direct_state_access)
-               glCreateRenderbuffers(1, &id);
-       else
-               glGenRenderbuffers(1, &id);
-}
-
-Renderbuffer::~Renderbuffer()
-{
-       glDeleteRenderbuffers(1, &id);
-}
-
-void Renderbuffer::storage(PixelFormat fmt, unsigned wd, unsigned ht)
-{
-       require_pixelformat(fmt);
-       width = wd;
-       height = ht;
-       if(ARB_direct_state_access)
-               glNamedRenderbufferStorage(id, fmt, width, height);
-       else
-       {
-               BindRestore _bind(this);
-               glRenderbufferStorage(GL_RENDERBUFFER, fmt, width, height);
-       }
-}
-
-unsigned Renderbuffer::get_max_samples()
-{
-       static unsigned max_samples = (EXT_framebuffer_multisample ? get_i(GL_MAX_SAMPLES) : 0);
-       return max_samples;
-}
-
-void Renderbuffer::storage_multisample(unsigned samples, PixelFormat fmt, unsigned wd, unsigned ht)
-{
-       if(!samples)
-               return storage(fmt, wd, ht);
-
-       static Require _req(EXT_framebuffer_multisample);
-       if(samples>get_max_samples())
-               throw out_of_range("Renderbuffer::storage_multisample");
-
-       require_pixelformat(fmt);
-
-       width = wd;
-       height = ht;
-       if(ARB_direct_state_access)
-               glNamedRenderbufferStorageMultisample(id, samples, fmt, width, height);
-       else
-       {
-               BindRestore _bind(this);
-               glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, fmt, width, height);
-       }
-}
-
-void Renderbuffer::bind() const
-{
-       if(set_current(this))
-               glBindRenderbuffer(GL_RENDERBUFFER, id);
-}
-
-void Renderbuffer::unbind()
-{
-       if(set_current(0))
-               glBindRenderbuffer(GL_RENDERBUFFER, 0);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/renderbuffer.h b/source/renderbuffer.h
deleted file mode 100644 (file)
index afc6ed2..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-#ifndef MSP_GL_RENDERBUFFER_H_
-#define MSP_GL_RENDERBUFFER_H_
-
-#include "bindable.h"
-#include "pixelformat.h"
-
-namespace Msp {
-namespace GL {
-
-/**
-A Renderbuffer contains a single renderable image.  It can be attached to a
-Framebuffer to provide a logical buffer that is required to render the scene
-correctly but that is not needed as a texture later.  Renderbuffers also
-provide a capability for multisampling, which is not available in textures.
-
-Requires the GL_EXT_framebuffer_object extension.  Multisample renderbuffers
-additionally require the GL_EXT_framebuffer_multisample extension.
-*/
-class Renderbuffer: public Bindable<Renderbuffer>
-{
-private:
-       unsigned id;
-       unsigned width;
-       unsigned height;
-
-public:
-       Renderbuffer();
-       ~Renderbuffer();
-
-       unsigned get_id() const { return id; }
-       unsigned get_width() const { return width; }
-       unsigned get_height() const { return height; }
-
-       /** Allocates storage for the renderbuffer. */
-       void storage(PixelFormat fmt, unsigned wd, unsigned ht);
-
-       /** Returns the maximum supported sample count for multisampling.  If
-       multisampling is not supported, returns 0. */
-       static unsigned get_max_samples();
-
-       /** Allocates multisample storage for the renderbuffer.  All attachments in
-       a framebuffer must have the same number of samples.  To transfer the
-       contents to a texture for furter processing, use the framebuffer blit
-       functions.*/
-       void storage_multisample(unsigned samples, PixelFormat fmt, unsigned wd, unsigned ht);
-
-       void bind() const;
-
-       static void unbind();
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/renderer.cpp b/source/renderer.cpp
deleted file mode 100644 (file)
index 416a0fc..0000000
+++ /dev/null
@@ -1,374 +0,0 @@
-#include "batch.h"
-#include "buffer.h"
-#include "camera.h"
-#include "clipping.h"
-#include "error.h"
-#include "lighting.h"
-#include "material.h"
-#include "program.h"
-#include "programdata.h"
-#include "renderable.h"
-#include "renderer.h"
-#include "sampler.h"
-#include "texture.h"
-#include "texturing.h"
-#include "texunit.h"
-#include "vertexarray.h"
-#include "vertexsetup.h"
-#include "windingtest.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Renderer::Renderer():
-       default_camera(0)
-{
-       init();
-}
-
-Renderer::Renderer(const Camera *c):
-       default_camera(c)
-{
-       init();
-
-       if(c)
-               set_camera(*c);
-}
-
-void Renderer::init()
-{
-       state_stack.reserve(16);
-       state_stack.push_back(State());
-       shdata_stack.reserve(32);
-       state = &state_stack.back();
-
-       standard_shdata.uniform("projection_matrix", Matrix());
-       standard_shdata.uniform("eye_world_matrix", Matrix());
-}
-
-Renderer::~Renderer()
-{
-       end();
-}
-
-void Renderer::set_camera(const Camera &c)
-{
-       state->camera = &c;
-       standard_shdata.uniform("projection_matrix", state->camera->get_projection_matrix());
-       standard_shdata.uniform("eye_world_matrix", state->camera->get_view_matrix());
-       changed |= STANDARD_SHDATA;
-       set_matrix(state->camera->get_view_matrix());
-}
-
-void Renderer::set_matrix(const Matrix &matrix)
-{
-       state->modelview_matrix = matrix;
-       changed |= MATRIX;
-}
-
-void Renderer::transform(const Matrix &matrix)
-{
-       state->modelview_matrix *= matrix;
-       changed |= MATRIX;
-}
-
-void Renderer::set_texture(const Texture *t, const Sampler *s)
-{
-       state->texture = t;
-       state->sampler = s;
-       state->texturing = 0;
-}
-
-void Renderer::set_texturing(const Texturing *t)
-{
-       state->texturing = t;
-       state->texture = 0;
-       state->sampler = 0;
-}
-
-unsigned Renderer::allocate_effect_texunit()
-{
-       return --state->lowest_effect_texunit;
-}
-
-void Renderer::set_material(const Material *m)
-{
-       state->material = m;
-       changed |= MATERIAL_SHDATA;
-}
-
-void Renderer::set_lighting(const Lighting *l)
-{
-       state->lighting = l;
-       state->lighting_matrix = state->modelview_matrix;
-       if(l)
-       {
-               l->update_shader_data(standard_shdata, state->lighting_matrix);
-               changed |= STANDARD_SHDATA;
-       }
-}
-
-void Renderer::set_clipping(const Clipping *c)
-{
-       state->clipping = c;
-       state->clipping_matrix = state->modelview_matrix;
-       if(c)
-       {
-               c->update_shader_data(standard_shdata, state->clipping_matrix);
-               changed |= STANDARD_SHDATA;
-       }
-}
-
-void Renderer::set_shader_program(const Program *p, const ProgramData *d)
-{
-       state->shprog = p;
-       if(p && d)
-               add_shader_data(*d);
-}
-
-void Renderer::add_shader_data(const ProgramData &d)
-{
-       if(state->shdata_count<shdata_stack.size() && shdata_stack[state->shdata_count]==&d)
-               ++state->shdata_count;
-       else
-       {
-               flush_shader_data();
-               shdata_stack.push_back(&d);
-               state->shdata_count = shdata_stack.size();
-               changed |= SHADER_DATA;
-       }
-}
-
-void Renderer::flush_shader_data()
-{
-       if(shdata_stack.size()>state->shdata_count)
-               shdata_stack.erase(shdata_stack.begin()+state->shdata_count, shdata_stack.end());
-}
-
-void Renderer::set_vertex_setup(const VertexSetup *vs)
-{
-       state->vertex_setup = vs;
-}
-
-void Renderer::set_winding_test(const WindingTest *w)
-{
-       state->winding_test = w;
-}
-
-void Renderer::set_reverse_winding(bool r)
-{
-       state->reverse_winding = r;
-}
-
-void Renderer::set_object_lod_bias(unsigned b)
-{
-       state->object_lod_bias = b;
-}
-
-void Renderer::push_state()
-{
-       state_stack.push_back(state_stack.back());
-       state = &state_stack.back();
-}
-
-void Renderer::pop_state()
-{
-       if(state_stack.size()==1)
-               throw stack_underflow("Renderer::pop_state");
-
-       const Camera *old_camera = state->camera;
-       const Lighting *old_lighting = state->lighting;
-       const Clipping *old_clipping = state->clipping;
-       state_stack.pop_back();
-       state = &state_stack.back();
-       changed |= MATRIX;
-       bool camera_changed = (state->camera!=old_camera);
-       if(camera_changed)
-       {
-               if(state->camera)
-               {
-                       standard_shdata.uniform("projection_matrix", state->camera->get_projection_matrix());
-                       standard_shdata.uniform("eye_world_matrix", state->camera->get_view_matrix());
-               }
-               else
-               {
-                       standard_shdata.uniform("projection_matrix", Matrix());
-                       standard_shdata.uniform("eye_world_matrix", Matrix());
-               }
-               changed |= STANDARD_SHDATA;
-       }
-       /* This actually should compare the relevant matrices rather than check for
-       camera, but in practice lighting and clipping is set right after the camera
-       and a boolean check is much faster than matrix comparison. */
-       if(state->lighting!=old_lighting || camera_changed)
-       {
-               if(state->lighting)
-               {
-                       state->lighting->update_shader_data(standard_shdata, state->lighting_matrix);
-                       changed |= STANDARD_SHDATA;
-               }
-       }
-       if(state->clipping!=old_clipping || camera_changed)
-       {
-               if(state->clipping)
-               {
-                       state->clipping->update_shader_data(standard_shdata, state->clipping_matrix);
-                       changed |= STANDARD_SHDATA;
-               }
-       }
-}
-
-void Renderer::end()
-{
-       if(state_stack.size()>1)
-               throw invalid_operation("Renderer::end");
-
-       *state = State();
-       if(default_camera)
-               set_camera(*default_camera);
-       else
-               standard_shdata.uniform("projection_matrix", Matrix());
-       shdata_stack.clear();
-       excluded.clear();
-
-       Texturing::unbind();
-       Texture::unbind_from(0);
-       Clipping::unbind();
-       Program::unbind();
-       VertexSetup::unbind();
-       Buffer::unbind_from(ELEMENT_ARRAY_BUFFER);
-       WindingTest::unbind();
-}
-
-void Renderer::exclude(const Renderable &renderable)
-{
-       excluded.insert(&renderable);
-}
-
-void Renderer::include(const Renderable &renderable)
-{
-       excluded.erase(&renderable);
-}
-
-void Renderer::render(const Renderable &renderable, const Tag &tag)
-{
-       if(!excluded.count(&renderable))
-               renderable.render(*this, tag);
-}
-
-void Renderer::draw(const Batch &batch)
-{
-       apply_state();
-
-       batch.draw();
-}
-
-void Renderer::draw_instanced(const Batch &batch, unsigned count)
-{
-       apply_state();
-
-       batch.draw_instanced(count);
-}
-
-void Renderer::apply_state()
-{
-       if(!state->shprog)
-               throw invalid_operation("Renderer::apply_state");
-
-       /* We (mostly) let the objects themselves figure out if the binding has
-       changed */
-
-       if(state->texturing)
-               state->texturing->bind();
-       else
-       {
-               Texturing::unbind();
-               if(state->texture)
-               {
-                       if(state->sampler)
-                               state->sampler->bind_to(0);
-                       else
-                               Sampler::unbind_from(0);
-                       state->texture->bind_to(0);
-               }
-               else
-                       Texture::unbind_from(0);
-       }
-
-       if(state->clipping)
-               state->clipping->bind();
-       else
-               Clipping::unbind();
-
-       bool shprog_changed = (state->shprog!=Program::current());
-       state->shprog->bind();
-
-       if(changed&MATRIX)
-       {
-               standard_shdata.uniform("eye_obj_matrix", state->modelview_matrix);
-               LinAl::SquareMatrix<float, 3> nm = state->modelview_matrix.block<3, 3>(0, 0);
-               nm = transpose(invert(nm));
-               standard_shdata.uniform_matrix3("eye_obj_normal_matrix", &nm(0, 0));
-               changed = (changed&~MATRIX)|STANDARD_SHDATA;
-       }
-
-       if(state->material && ((changed&MATERIAL_SHDATA) || shprog_changed))
-       {
-               state->material->get_shader_data().apply();
-               changed &= ~MATERIAL_SHDATA;
-       }
-
-       if((changed&STANDARD_SHDATA) || shprog_changed)
-       {
-               standard_shdata.apply();
-               changed &= ~STANDARD_SHDATA;
-       }
-
-       bool extra_shdata = (shdata_stack.size()>state->shdata_count);
-
-       if((changed&SHADER_DATA) || shprog_changed || extra_shdata)
-       {
-               if(extra_shdata)
-                       shdata_stack.erase(shdata_stack.begin()+state->shdata_count, shdata_stack.end());
-               for(vector<const ProgramData *>::const_iterator i=shdata_stack.begin(); i!=shdata_stack.end(); ++i)
-                       (*i)->apply();
-               changed &= ~SHADER_DATA;
-       }
-
-       if(state->vertex_setup)
-               state->vertex_setup->bind();
-       else
-               VertexSetup::unbind();
-
-       if(state->winding_test)
-       {
-               if(state->reverse_winding)
-                       state->winding_test->get_reverse().bind();
-               else
-                       state->winding_test->bind();
-       }
-       else
-               WindingTest::unbind();
-}
-
-
-Renderer::State::State():
-       camera(0),
-       texture(0),
-       texturing(0),
-       lowest_effect_texunit(TexUnit::get_n_units()),
-       material(0),
-       lighting(0),
-       clipping(0),
-       shprog(0),
-       shdata_count(0),
-       vertex_setup(0),
-       winding_test(0),
-       reverse_winding(false),
-       object_lod_bias(0)
-{ }
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/renderer.h b/source/renderer.h
deleted file mode 100644 (file)
index 2854bc8..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-#ifndef MSP_GL_RENDERER_H_
-#define MSP_GL_RENDERER_H_
-
-#include <set>
-#include <vector>
-#include "matrix.h"
-#include "programdata.h"
-#include "tag.h"
-
-namespace Msp {
-namespace GL {
-
-class Batch;
-class Buffer;
-class Camera;
-class Clipping;
-class Material;
-class Mesh;
-class Lighting;
-class Program;
-class Renderable;
-class Sampler;
-class Texture;
-class Texturing;
-class VertexSetup;
-class WindingTest;
-
-/**
-A class for supervising the rendering process.  While many Renderables (in
-particular, Objects and Scenes) can by rendered without a Renderer, using one
-will often be more efficient.  This is especially true for ObjectInstances.
-
-The Renderer works by deferring GL state changes until something is actually
-being drawn.  This avoids many unnecessary GL calls if consecutive renderables
-use the same resources.
-
-A state stack is provided to help with state scoping.  Typically a Renderable
-will push the current state on entry, set whatever state it requires, render
-itself, and pop the state when it's done.  An RAII helper class is provided for
-the push/pop operation.
-*/
-class Renderer
-{
-public:
-       class Push
-       {
-       private:
-               Renderer &renderer;
-
-       public:
-               Push(Renderer &r): renderer(r) { renderer.push_state(); }
-               ~Push() { renderer.pop_state(); }
-       };
-
-       class Exclude
-       {
-       private:
-               Renderer &renderer;
-               const Renderable &renderable;
-
-       public:
-               Exclude(Renderer &r, const Renderable &e): renderer(r), renderable(e) { renderer.exclude(renderable); }
-               ~Exclude() { renderer.include(renderable); }
-       };
-
-private:
-       struct State
-       {
-               const Camera *camera;
-               Matrix modelview_matrix;
-               const Texture *texture;
-               const Sampler *sampler;
-               const Texturing *texturing;
-               unsigned lowest_effect_texunit;
-               const Material *material;
-               const Lighting *lighting;
-               Matrix lighting_matrix;
-               const Clipping *clipping;
-               Matrix clipping_matrix;
-               const Program *shprog;
-               unsigned shdata_count;
-               const VertexSetup *vertex_setup;
-               const WindingTest *winding_test;
-               bool reverse_winding;
-               unsigned object_lod_bias;
-
-               State();
-       };
-
-       enum ChangeMask
-       {
-               MATRIX = 2,
-               SHADER_DATA = 16,
-               MATERIAL_SHDATA = 32,
-               STANDARD_SHDATA = 64
-       };
-
-       const Camera *default_camera;
-       unsigned char changed;
-       std::vector<State> state_stack;
-       State *state;
-       ProgramData standard_shdata;
-       std::vector<const ProgramData *> shdata_stack;
-       std::set<const Renderable *> excluded;
-
-public:
-       Renderer();
-       DEPRECATED Renderer(const Camera *);
-private:
-       void init();
-public:
-       ~Renderer();
-
-       /** Sets the camera to render from.  The modelview matrix is reset to the
-       camera's view matrix. */
-       void set_camera(const Camera &);
-
-       const Camera *get_camera() const { return state->camera; }
-
-       /** Replaces the Renderer's modelview matrix. */
-       void set_matrix(const Matrix &);
-
-       /** Applies a transform to the Renderer's modelview matrix. */
-       void transform(const Matrix &);
-
-       /** Returns the current modelview matrix. */
-       const Matrix &get_matrix() const { return state->modelview_matrix; }
-
-       void set_texture(const Texture *, const Sampler * = 0);
-       void set_texturing(const Texturing *);
-       unsigned allocate_effect_texunit();
-       void set_material(const Material *);
-
-       void set_lighting(const Lighting *);
-       void set_clipping(const Clipping *);
-
-       /** Sets the shader program to use.  An initial set of data can be set as
-       well, with the same semantics as add_shader_data. */
-       void set_shader_program(const Program *prog, const ProgramData *data = 0);
-
-       /** Adds another set of data to be use with shader programs.  The data is
-       independent of any shader program changes and remains in effect until the
-       Renderer state is popped. */
-       void add_shader_data(const ProgramData &data);
-
-       void flush_shader_data();
-
-       void set_vertex_setup(const VertexSetup *);
-       void set_winding_test(const WindingTest *);
-       void set_reverse_winding(bool);
-
-       void set_object_lod_bias(unsigned);
-       unsigned get_object_lod_bias() const { return state->object_lod_bias; }
-
-       /** Saves the current state so it can be restored later. */
-       void push_state();
-
-       /** Restores a previously saved state.  Must be matched with an earlier
-       push_state call. */
-       void pop_state();
-
-       /** Unbinds all objects and resets related state.  There must be no unpopped
-       state in the stack.  The Renderer remains valid and may be reused for
-       further rendering. */
-       void end();
-
-       void exclude(const Renderable &);
-       void include(const Renderable &);
-
-       void render(const Renderable &, const Tag & = Tag());
-       void draw(const Batch &);
-       void draw_instanced(const Batch &, unsigned);
-
-private:
-       void apply_state();
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/renderpass.cpp b/source/renderpass.cpp
deleted file mode 100644 (file)
index 835e78a..0000000
+++ /dev/null
@@ -1,276 +0,0 @@
-#include <msp/datafile/collection.h>
-#include <msp/strings/format.h>
-#include "error.h"
-#include "material.h"
-#include "renderpass.h"
-#include "program.h"
-#include "programdata.h"
-#include "renderer.h"
-#include "texture.h"
-#include "texture2d.h"
-#include "texturing.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-RenderPass::RenderPass():
-       shprog(0),
-       shprog_from_material(false),
-       shdata(0),
-       material(0),
-       texturing(0),
-       back_faces(false)
-{ }
-
-RenderPass::RenderPass(const RenderPass &other):
-       shprog(other.shprog),
-       shprog_from_material(other.shprog_from_material),
-       shdata(other.shdata),
-       uniform_slots(other.uniform_slots),
-       material(other.material),
-       material_slot(other.material_slot),
-       texturing(other.texturing ? new Texturing(*other.texturing) : 0),
-       tex_names(other.tex_names),
-       back_faces(other.back_faces)
-{ }
-
-RenderPass &RenderPass::operator=(const RenderPass &other)
-{
-       shprog = other.shprog;
-       shprog_from_material = other.shprog_from_material;
-       shdata = other.shdata;
-       uniform_slots = other.uniform_slots;
-       material = other.material;
-       material_slot = other.material_slot;
-       texturing = other.texturing ? new Texturing(*other.texturing) : 0;
-       tex_names = other.tex_names;
-       back_faces = other.back_faces;
-       return *this;
-}
-
-RenderPass::~RenderPass()
-{
-       delete texturing;
-}
-
-void RenderPass::finalize_material(DataFile::Collection *coll)
-{
-       maybe_create_material_shader(coll);
-       ensure_private_shader_data();
-
-       if(!texturing)
-               texturing = new Texturing;
-       material->attach_textures_to(*texturing, *shdata);
-}
-
-void RenderPass::maybe_create_material_shader(DataFile::Collection *coll)
-{
-       if(shprog && !shprog_from_material)
-               return;
-
-       if(coll)
-       {
-               shprog = material->create_compatible_shader(*coll);
-               shprog.keep();
-       }
-       else
-               shprog = material->create_compatible_shader();
-
-       if(shdata)
-               shdata = new ProgramData(*shdata, shprog.get());
-
-       shprog_from_material = true;
-}
-
-void RenderPass::ensure_private_shader_data()
-{
-       if(!shprog)
-               throw invalid_operation("RenderPass::ensure_private_shader_data");
-
-       if(!shdata)
-               shdata = new ProgramData(shprog.get());
-       else if(shdata.refcount()>1)
-               shdata = new ProgramData(*shdata);
-}
-
-void RenderPass::set_shader_program(const Program *prog, const ProgramData *data)
-{
-       shprog = prog;
-       shprog.keep();
-       shprog_from_material = false;
-       shdata = (data ? new ProgramData(*data) : 0);
-       if(material)
-               finalize_material(0);
-}
-
-const string &RenderPass::get_slotted_uniform_name(const string &slot) const
-{
-       map<string, string>::const_iterator i = uniform_slots.find(slot);
-       if(i==uniform_slots.end())
-       {
-               static string empty;
-               return empty;
-       }
-       return i->second;
-}
-
-void RenderPass::set_material(const Material *mat)
-{
-       material = mat;
-       material.keep();
-       finalize_material(0);
-}
-
-void RenderPass::set_texture(unsigned index, const Texture *tex)
-{
-       if(!texturing)
-               texturing = new Texturing;
-
-       texturing->attach(index, *tex, texturing->get_attached_sampler(index));
-}
-
-int RenderPass::get_texture_index(const string &n) const
-{
-       map<string, unsigned>::const_iterator i = tex_names.find(n);
-       if(i==tex_names.end())
-               return -1;
-       return i->second;
-}
-
-void RenderPass::apply(Renderer &renderer) const
-{
-       renderer.set_texturing(texturing);
-       renderer.set_material(material.get());
-       renderer.set_shader_program(shprog.get(), shdata.get());
-       renderer.set_reverse_winding(back_faces);
-}
-
-
-RenderPass::Loader::Loader(RenderPass &p):
-       DataFile::CollectionObjectLoader<RenderPass>(p, 0)
-{
-       init();
-}
-
-RenderPass::Loader::Loader(RenderPass &p, Collection &c):
-       DataFile::CollectionObjectLoader<RenderPass>(p, &c)
-{
-       init();
-}
-
-void RenderPass::Loader::init()
-{
-       add("shader",   &Loader::shader);
-       add("material", &Loader::material_inline);
-       add("material", &Loader::material);
-       add("material_slot", &RenderPass::material_slot);
-       add("back_faces",&RenderPass::back_faces);
-       add("texunit",  &Loader::texunit);
-       add("texunit",  &Loader::texunit_auto);
-       add("texunit",  &Loader::texunit_named);
-       add("uniforms", &Loader::uniforms);
-       add("uniform_slot", &Loader::uniform_slot);
-       add("uniform_slot", &Loader::uniform_slot2);
-}
-
-void RenderPass::Loader::material_inline()
-{
-       Material::GenericLoader ldr(coll);
-       load_sub_with(ldr);
-       obj.material = ldr.get_material();
-       obj.finalize_material(coll);
-}
-
-void RenderPass::Loader::material(const string &name)
-{
-       obj.material = &get_collection().get<Material>(name);
-       obj.material.keep();
-       obj.finalize_material(coll);
-}
-
-void RenderPass::Loader::shader(const string &n)
-{
-       obj.shprog = &get_collection().get<Program>(n);
-       obj.shprog.keep();
-       obj.shprog_from_material = false;
-       if(obj.shdata)
-               obj.shdata = new ProgramData(*obj.shdata, obj.shprog.get());
-       if(obj.material)
-               obj.finalize_material(coll);
-}
-
-void RenderPass::Loader::texunit(unsigned i)
-{
-       if(!obj.texturing)
-               obj.texturing = new Texturing;
-       TextureLoader ldr(*obj.texturing, i, coll);
-       load_sub_with(ldr);
-}
-
-void RenderPass::Loader::texunit_auto(const string &n)
-{
-       if(!obj.texturing)
-               obj.texturing = new Texturing;
-       int i = obj.texturing->find_free_unit(n);
-       if(i<0)
-               throw runtime_error("no free texunit");
-       texunit_named(i, n);
-}
-
-void RenderPass::Loader::texunit_named(unsigned i, const string &n)
-{
-       texunit(i);
-       obj.tex_names[n] = i;
-       obj.ensure_private_shader_data();
-       obj.shdata->uniform(n, static_cast<int>(i));
-}
-
-void RenderPass::Loader::uniforms()
-{
-       obj.ensure_private_shader_data();
-       load_sub(*obj.shdata);
-}
-
-void RenderPass::Loader::uniform_slot(const string &name)
-{
-       uniform_slot2(name, name);
-}
-
-void RenderPass::Loader::uniform_slot2(const string &name, const string &slot)
-{
-       obj.uniform_slots[slot] = name;
-}
-
-
-RenderPass::TextureLoader::TextureLoader(Texturing &t, unsigned i, Collection *c):
-       DataFile::CollectionObjectLoader<Texturing>(t, c),
-       index(i),
-       tex(0),
-       samp(0)
-{
-       add("sampler",   &TextureLoader::sampler);
-       add("texture",   &TextureLoader::texture);
-}
-
-void RenderPass::TextureLoader::finish()
-{
-       if(tex)
-               obj.attach(index, *tex, samp);
-       else if(samp)
-               obj.attach(index, *samp);
-}
-
-void RenderPass::TextureLoader::sampler(const string &name)
-{
-       samp = &get_collection().get<Sampler>(name);
-}
-
-void RenderPass::TextureLoader::texture(const string &name)
-{
-       tex = &get_collection().get<Texture>(name);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/renderpass.h b/source/renderpass.h
deleted file mode 100644 (file)
index 9e70437..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-#ifndef MSP_GL_RENDERPASS_H_
-#define MSP_GL_RENDERPASS_H_
-
-#include <msp/core/refptr.h>
-#include <msp/datafile/objectloader.h>
-
-namespace Msp {
-namespace GL {
-
-class Material;
-class Program;
-class ProgramData;
-class Renderer;
-class Sampler;
-class Texture;
-class Texturing;
-
-/**
-Encapsulates the data that determines the appearance of a rendered surface.
-This includes shader and data for it, material and texturing.
-*/
-class RenderPass
-{
-public:
-       class Loader: public DataFile::CollectionObjectLoader<RenderPass>
-       {
-       public:
-               Loader(RenderPass &);
-               Loader(RenderPass &, Collection &);
-
-       private:
-               void init();
-
-               void material_inline();
-               void material(const std::string &);
-               void shader(const std::string &);
-               void texunit(unsigned);
-               void texunit_auto(const std::string &);
-               void texunit_named(unsigned, const std::string &);
-               void uniforms();
-               void uniform_slot(const std::string &);
-               void uniform_slot2(const std::string &, const std::string &);
-       };
-
-private:
-       struct TextureLoader: public DataFile::CollectionObjectLoader<Texturing>
-       {
-       private:
-               unsigned index;
-               const Texture *tex;
-               const Sampler *samp;
-
-       public:
-               TextureLoader(Texturing &, unsigned, Collection *);
-       private:
-               virtual void finish();
-
-               void sampler(const std::string &);
-               void texture(const std::string &);
-       };
-
-       RefPtr<const Program> shprog;
-       bool shprog_from_material;
-       RefPtr<ProgramData> shdata;
-       std::map<std::string, std::string> uniform_slots;
-       RefPtr<const Material> material;
-       std::string material_slot;
-       Texturing *texturing;
-       std::map<std::string, unsigned> tex_names;
-       bool back_faces;
-
-public:
-       RenderPass();
-       RenderPass(const RenderPass &);
-       RenderPass &operator=(const RenderPass &);
-       ~RenderPass();
-
-private:
-       void finalize_material(DataFile::Collection *);
-       void maybe_create_material_shader(DataFile::Collection *);
-       void ensure_private_shader_data();
-
-public:
-       void set_shader_program(const Program *, const ProgramData *);
-       const Program *get_shader_program() const { return shprog.get(); }
-       const ProgramData *get_shader_data() const { return shdata.get(); }
-       const std::string &get_slotted_uniform_name(const std::string &) const;
-       void set_material(const Material *);
-       const Material *get_material() const { return material.get(); }
-       const std::string &get_material_slot_name() const { return material_slot; }
-       void set_texture(unsigned, const Texture *);
-       const Texturing *get_texturing() const { return texturing; }
-       int get_texture_index(const std::string &) const;
-       void set_back_faces(bool);
-       bool get_back_faces() const { return back_faces; }
-
-       void apply(Renderer &) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/rendertarget.cpp b/source/rendertarget.cpp
deleted file mode 100644 (file)
index 1349944..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-#include <msp/core/maputils.h>
-#include "error.h"
-#include "renderbuffer.h"
-#include "rendertarget.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-RenderTargetFormat::RenderTargetFormat():
-       count(0)
-{ }
-
-RenderTargetFormat::RenderTargetFormat(RenderOutput o):
-       count(1)
-{
-       outputs[0] = o;
-}
-
-RenderTargetFormat RenderTargetFormat::operator,(RenderOutput o) const
-{
-       if(count>=MAX_OUTPUTS)
-               throw invalid_operation("RenderTargetFormat::operator,");
-
-       RenderTargetFormat result = *this;
-       result.outputs[result.count++] = o;
-
-       return result;
-}
-
-RenderTargetFormat RenderTargetFormat::operator,(PixelFormat f) const
-{
-       if(!count)
-               throw invalid_operation("RenderTargetFormat::operator,");
-
-       PixelComponents comp = get_components(f);
-       unsigned size = get_component_size(f);
-       unsigned char out = outputs[count-1];
-       if(get_output_type(out)>=get_output_type(RENDER_DEPTH))
-       {
-               if(comp!=DEPTH_COMPONENT)
-                       throw invalid_argument("RenderTargetFormat::operator,");
-               if(size>1)
-                       --size;
-               if(get_component_type(f)==UNSIGNED_INT)
-                       --size;
-       }
-       else
-       {
-               if(comp!=RED && comp!=RG && comp!=RGB && comp!=RGBA)
-                       throw invalid_argument("RenderTargetformat::operator,");
-               if(size>3)
-                       --size;
-       }
-
-       out = (out&~15) | (size<<2) | (get_component_count(f)-1);
-       RenderTargetFormat result = *this;
-       result.outputs[result.count-1] = out;
-
-       return result;
-}
-
-int RenderTargetFormat::index(RenderOutput o) const
-{
-       unsigned type = get_output_type(o);
-       unsigned i = 0;
-       for(const unsigned char *j=begin(); j!=end(); ++j, ++i)
-               if(get_output_type(*j)==type)
-                       return i;
-       return -1;
-}
-
-
-PixelFormat get_output_pixelformat(unsigned char o)
-{
-       PixelComponents comp;
-       DataType type;
-       if(get_output_type(o)>=get_output_type(RENDER_DEPTH))
-       {
-               static DataType types[4] = { UNSIGNED_SHORT, UNSIGNED_SHORT, UNSIGNED_INT, FLOAT };
-               comp = DEPTH_COMPONENT;
-               type = types[(o>>2)&3];
-       }
-       else
-       {
-               static PixelComponents components[4] = { RED, RG, RGB, RGBA };
-               static DataType types[4] = { UNSIGNED_BYTE, UNSIGNED_SHORT, HALF_FLOAT, FLOAT };
-               comp = components[o&3];
-               type = types[(o>>2)&3];
-       }
-
-       return make_pixelformat(comp, type);
-}
-
-
-RenderTarget::RenderTarget(unsigned w, unsigned h, RenderOutput o)
-{
-       init(w, h, 0, o);
-}
-
-RenderTarget::RenderTarget(unsigned w, unsigned h, const RenderTargetFormat &f)
-{
-       init(w, h, 0, f);
-}
-
-RenderTarget::RenderTarget(unsigned w, unsigned h, unsigned s, const RenderTargetFormat &f)
-{
-       init(w, h, s, f);
-}
-
-void RenderTarget::init(unsigned w, unsigned h, unsigned s, const RenderTargetFormat &f)
-{
-       width = w;
-       height = h;
-       samples = s;
-       format = f;
-
-       for(const unsigned char *i=format.begin(); i!=format.end(); ++i)
-       {
-               unsigned type = get_output_type(*i);
-               FramebufferAttachment att;
-               if(type>=get_output_type(RENDER_DEPTH))
-                       att = DEPTH_ATTACHMENT;
-               else
-                       att = static_cast<FramebufferAttachment>(COLOR_ATTACHMENT0+type);
-
-               PixelFormat pf = get_output_pixelformat(*i);
-
-               TargetBuffer tgt;
-               if(samples)
-               {
-                       tgt.buffer = new Renderbuffer;
-                       tgt.buffer->storage_multisample(samples, pf, width, height);
-                       fbo.attach(att, *tgt.buffer);
-               }
-               else
-               {
-                       tgt.texture = new Texture2D;
-                       tgt.texture->storage(pf, width, height, 1);
-                       Sampler &sampler = tgt.texture->get_default_sampler();
-                       sampler.set_filter(NEAREST);
-                       sampler.set_wrap(CLAMP_TO_EDGE);
-                       fbo.attach(att, *tgt.texture);
-               }
-               buffers.push_back(tgt);
-       }
-
-       fbo.require_complete();
-}
-
-RenderTarget::~RenderTarget()
-{
-       for(vector<TargetBuffer>::iterator i=buffers.begin(); i!=buffers.end(); ++i)
-       {
-               if(samples)
-                       delete i->buffer;
-               else
-                       delete i->texture;
-       }
-}
-
-void RenderTarget::set_texture_filter(TextureFilter filt)
-{
-       if(!samples)
-       {
-               for(vector<TargetBuffer>::iterator i=buffers.begin(); i!=buffers.end(); ++i)
-                       i->texture->get_default_sampler().set_filter(filt);
-       }
-}
-
-const Texture2D &RenderTarget::get_target_texture(unsigned i) const
-{
-       if(i>=buffers.size())
-               throw out_of_range("RenderTarget::get_target_texture");
-       if(samples)
-               throw invalid_operation("RenderTarget::get_target_texture");
-
-       return *buffers[i].texture;
-}
-
-const Texture2D &RenderTarget::get_target_texture(RenderOutput o) const
-{
-       int index = format.index(o);
-       if(index<0)
-               throw key_error(o);
-
-       return get_target_texture(index);
-}
-
-void RenderTarget::blit_from(const RenderTarget &other)
-{
-       fbo.blit_from(other.fbo, COLOR_BUFFER_BIT|DEPTH_BUFFER_BIT, false);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/rendertarget.h b/source/rendertarget.h
deleted file mode 100644 (file)
index 7d796a3..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-#ifndef RENDERTARGET_H_
-#define RENDERTARGET_H_
-
-#include "framebuffer.h"
-#include "texture2d.h"
-
-namespace Msp {
-namespace GL {
-
-enum RenderOutput
-{
-       RENDER_COLOR = 0|3,
-       RENDER_DEPTH = 192|12
-};
-
-class RenderTargetFormat
-{
-private:
-       enum { MAX_OUTPUTS = 7 };
-
-       unsigned char count;
-       unsigned char outputs[MAX_OUTPUTS];
-
-public:
-       RenderTargetFormat();
-       RenderTargetFormat(RenderOutput);
-
-       RenderTargetFormat operator,(RenderOutput) const;
-       RenderTargetFormat operator,(PixelFormat) const;
-
-       bool empty() const { return !count; }
-       const unsigned char *begin() const { return outputs; }
-       const unsigned char *end() const { return outputs+count; }
-       int index(RenderOutput) const;
-};
-
-inline RenderTargetFormat operator,(RenderOutput o1, RenderOutput o2)
-{ return (RenderTargetFormat(o1), o2); }
-
-inline RenderTargetFormat operator,(RenderOutput o, PixelFormat f)
-{ return (RenderTargetFormat(o), f); }
-
-inline unsigned get_output_type(unsigned char o)
-{ return o>>4; }
-
-PixelFormat get_output_pixelformat(unsigned char);
-
-
-class RenderTarget
-{
-private:
-       union TargetBuffer
-       {
-               Texture2D *texture;
-               Renderbuffer *buffer;
-       };
-
-       unsigned width;
-       unsigned height;
-       unsigned samples;
-       RenderTargetFormat format;
-       std::vector<TargetBuffer> buffers;
-       Framebuffer fbo;
-
-public:
-       RenderTarget(unsigned, unsigned, RenderOutput);
-       RenderTarget(unsigned, unsigned, const RenderTargetFormat & = (RENDER_COLOR, RENDER_DEPTH));
-       RenderTarget(unsigned, unsigned, unsigned, const RenderTargetFormat & = (RENDER_COLOR, RENDER_DEPTH));
-private:
-       RenderTarget(const RenderTarget &);
-       RenderTarget &operator=(const RenderTarget &);
-       void init(unsigned, unsigned, unsigned, const RenderTargetFormat &);
-public:
-       ~RenderTarget();
-
-       unsigned get_width() const { return width; }
-       unsigned get_height() const { return height; }
-       const RenderTargetFormat &get_format() const { return format; }
-       Framebuffer &get_framebuffer() { return fbo; }
-       void set_texture_filter(TextureFilter);
-       const Texture2D &get_target_texture(unsigned) const;
-       const Texture2D &get_target_texture(RenderOutput) const;
-       void blit_from(const RenderTarget &);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/resource.cpp b/source/resource.cpp
deleted file mode 100644 (file)
index 4be6ee2..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#include "resource.h"
-#include "resourcemanager.h"
-
-namespace Msp {
-namespace GL {
-
-Resource::Resource():
-       manager(0)
-{ }
-
-Resource::~Resource()
-{
-       if(manager)
-               manager->remove_resource(*this);
-}
-
-void Resource::set_manager(ResourceManager *m)
-{
-       if(manager)
-               manager->remove_resource(*this);
-       manager = m;
-       if(manager)
-       {
-               manager->add_resource(*this);
-               manager_data = manager->get_data_for_resource(*this);
-       }
-       else
-               manager_data = 0;
-}
-
-bool Resource::is_loaded() const
-{
-       return manager ? manager->is_resource_loaded(*this) : true;
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/resource.h b/source/resource.h
deleted file mode 100644 (file)
index 571d1ea..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef MSP_GL_RESOURCE_H_
-#define MSP_GL_RESOURCE_H_
-
-#include <msp/core/inttypes.h>
-#include <msp/io/seekable.h>
-
-namespace Msp {
-namespace GL {
-
-class ResourceManager;
-class Resources;
-
-class Resource
-{
-public:
-       class AsyncLoader
-       {
-       protected:
-               AsyncLoader() { }
-       public:
-               virtual ~AsyncLoader() { }
-
-               virtual bool needs_sync() const = 0;
-               virtual bool process() = 0;
-       };
-
-protected:
-       ResourceManager *manager;
-       void *manager_data;
-
-       Resource();
-public:
-       virtual ~Resource();
-
-       void set_manager(ResourceManager *);
-       ResourceManager *get_manager() const { return manager; }
-       void *get_manager_data() const { return manager_data; }
-       virtual int get_load_priority() const { return 0; }
-       virtual AsyncLoader *load(IO::Seekable &, const Resources * = 0) = 0;
-       virtual bool is_loaded() const;
-
-       /** Returns the amount of graphics memory used by this resource.  The
-       returned value must not change while the resource is loaded. */
-       virtual UInt64 get_data_size() const = 0;
-
-       virtual void unload() = 0;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/resourcemanager.cpp b/source/resourcemanager.cpp
deleted file mode 100644 (file)
index 30f7501..0000000
+++ /dev/null
@@ -1,550 +0,0 @@
-#include <algorithm>
-#include <typeinfo>
-#include <msp/debug/demangle.h>
-#include <msp/strings/format.h>
-#include <msp/time/utils.h>
-#include "resourcemanager.h"
-#include "resources.h"
-#include "resourceobserver.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-resource_load_error::resource_load_error(const string &name, const string &err):
-       runtime_error(format("%s: %s", name, err))
-{ }
-
-resource_load_error::resource_load_error(const string &name, const exception &exc):
-       runtime_error(format("%s: %s: %s", name, Debug::demangle(typeid(exc).name()), exc.what()))
-{ }
-
-
-ResourceManager::ResourceManager():
-       policy(LOAD_ON_DEMAND),
-       async_loads(true),
-       total_data_size(0),
-       size_limit(0),
-       frame(0),
-       min_retain_frames(30),
-       max_retain_frames(0),
-       next_unload(0)
-{ }
-
-ResourceManager::~ResourceManager()
-{
-       thread.terminate();
-
-       while(!resources.empty())
-               resources.begin()->second.resource->set_manager(0);
-}
-
-void ResourceManager::set_loading_policy(LoadingPolicy p)
-{
-       policy = p;
-}
-
-void ResourceManager::set_async_loads(bool a)
-{
-       async_loads = a;
-}
-
-void ResourceManager::set_size_limit(UInt64 s)
-{
-       size_limit = s;
-}
-
-void ResourceManager::set_min_retain_frames(unsigned f)
-{
-       min_retain_frames = f;
-}
-
-void ResourceManager::set_max_retain_frames(unsigned f)
-{
-       max_retain_frames = f;
-}
-
-void ResourceManager::add_resource(Resource &r)
-{
-       MutexLock lock(map_mutex);
-       insert_unique(resources, &r, ManagedResource(r));
-}
-
-const ResourceManager::ManagedResource &ResourceManager::get_managed_resource(const Resource &r) const
-{
-       MutexLock lock(map_mutex);
-       return get_item(resources, &r);
-}
-
-ResourceManager::ManagedResource &ResourceManager::get_managed_resource(const Resource &r)
-{
-       MutexLock lock(map_mutex);
-       return get_item(resources, &r);
-}
-
-void ResourceManager::set_resource_location(Resource &r, DataFile::Collection &c, const string &n)
-{
-       set_resource_location(r, ResourceLocation(c, n));
-}
-
-void ResourceManager::set_resource_location(Resource &r, const ResourceLocation &l)
-{
-       {
-               MutexLock lock(map_mutex);
-               ManagedResource &managed = get_item(resources, &r);
-               managed.location = l;
-       }
-
-       if(policy==LOAD_IMMEDIATELY)
-               load_resource(r);
-}
-
-const ResourceManager::ResourceLocation *ResourceManager::get_resource_location(const Resource &r) const
-{
-       const ManagedResource &managed = get_managed_resource(r);
-       return managed.location.collection ? &managed.location : 0;
-}
-
-void ResourceManager::load_resource(Resource &r)
-{
-       ManagedResource &managed = get_managed_resource(r);
-       if(!managed.location.collection)
-               throw runtime_error("no location");
-
-       if(managed.state!=ManagedResource::NOT_LOADED)
-               return;
-
-       if(async_loads)
-       {
-               managed.state = ManagedResource::LOAD_QUEUED;
-               LoadQueue::iterator i;
-               for(i=queue.begin(); (i!=queue.end() && (*i)->load_priority>=managed.load_priority); ++i) ;
-               queue.insert(i, &managed);
-       }
-       else
-       {
-               managed.start_loading();
-               while(!managed.loader->process()) ;
-               managed.finish_loading(true);
-               total_data_size += managed.data_size;
-       }
-}
-
-bool ResourceManager::is_resource_loaded(const Resource &r) const
-{
-       ManagedResource *managed = reinterpret_cast<ManagedResource *>(r.get_manager_data());
-       return managed ? managed->state==ManagedResource::LOADED : false;
-}
-
-void ResourceManager::resource_used(const Resource &r)
-{
-       ManagedResource *managed = reinterpret_cast<ManagedResource *>(r.get_manager_data());
-       if(!managed)
-               return;
-       if(managed->state==ManagedResource::NOT_LOADED && policy!=LOAD_MANUALLY)
-               load_resource(*managed->resource);
-
-       managed->last_used = frame;
-       if(max_retain_frames && !next_unload)
-               next_unload = frame+max_retain_frames+1;
-}
-
-void ResourceManager::remove_resource(Resource &r)
-{
-       ManagedResource &managed = get_managed_resource(r);
-       ManagedResource::State state = managed.state;
-       if(state==ManagedResource::LOAD_QUEUED)
-       {
-               LoadQueue::iterator i = find(queue.begin(), queue.end(), &managed);
-               if(i!=queue.end())
-                       queue.erase(i);
-       }
-       else if(state>ManagedResource::LOAD_QUEUED && state<ManagedResource::LOADED)
-               thread.remove_resource(managed);
-
-       for(vector<ResourceObserver *>::const_iterator i=managed.observers.begin(); i!=managed.observers.end(); ++i)
-               (*i)->resource_removed(r);
-
-       MutexLock lock(map_mutex);
-       remove_existing(resources, &r);
-}
-
-void ResourceManager::observe_resource(const Resource &r, ResourceObserver &w)
-{
-       get_managed_resource(r).add_observer(w);
-}
-
-void ResourceManager::unobserve_resource(const Resource &r, ResourceObserver &w)
-{
-       get_managed_resource(r).remove_observer(w);
-}
-
-void ResourceManager::tick()
-{
-       ++frame;
-
-       bool do_unload = (frame>=next_unload);
-       if(thread.sync())
-       {
-               total_data_size += thread.get_and_reset_loaded_data_size();
-               do_unload = true;
-       }
-
-       if(thread.needs_work() && !queue.empty())
-               dispatch_work();
-
-       if(do_unload)
-       {
-               MutexLock lock(map_mutex);
-               if(max_retain_frames && frame>=next_unload)
-               {
-                       unload_by_age();
-
-                       next_unload = frame;
-                       for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
-                               if(i->second.state==ManagedResource::LOADED)
-                                       next_unload = min(next_unload, i->second.last_used);
-                       next_unload = (next_unload<frame ? next_unload+max_retain_frames : 0);
-               }
-
-               if(size_limit)
-                       unload_by_size();
-       }
-}
-
-void ResourceManager::dispatch_work()
-{
-       queue.sort(age_order);
-
-       if(queue.front()->last_used+min_retain_frames<frame)
-       {
-               for(LoadQueue::iterator i=queue.begin(); i!=queue.end(); ++i)
-                       (*i)->state = ManagedResource::NOT_LOADED;
-               queue.clear();
-               return;
-       }
-
-       while(thread.needs_work() && !queue.empty())
-       {
-               ManagedResource *managed = queue.front();
-               queue.pop_front();
-               thread.add_resource(*managed);
-       }
-}
-
-void ResourceManager::unload_by_age()
-{
-       unsigned unload_limit = frame-max_retain_frames;
-
-       for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
-               if(i->second.state==ManagedResource::LOADED && i->second.last_used<unload_limit)
-               {
-                       i->second.unload();
-                       total_data_size -= i->second.data_size;
-               }
-}
-
-void ResourceManager::unload_by_size()
-{
-       unsigned unload_limit = frame-min_retain_frames;
-
-       while(total_data_size>size_limit)
-       {
-               ManagedResource *best = 0;
-               UInt64 best_impact = 0;
-               for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
-                       if(i->second.state==ManagedResource::LOADED && i->second.last_used<unload_limit)
-                       {
-                               UInt64 impact = i->second.data_size*(frame-i->second.last_used);
-                               if(!best || impact>best_impact)
-                               {
-                                       best = &i->second;
-                                       best_impact = impact;
-                               }
-                       }
-
-               if(!best)
-                       break;
-
-               best->unload();
-               total_data_size -= best->data_size;
-       }
-}
-
-bool ResourceManager::age_order(ManagedResource *mr1, ManagedResource *mr2)
-{
-       return mr1->last_used>mr2->last_used;
-}
-
-
-ResourceManager::ResourceLocation::ResourceLocation():
-       collection(0)
-{ }
-
-ResourceManager::ResourceLocation::ResourceLocation(DataFile::Collection &c, const string &n):
-       collection(&c),
-       name(n)
-{ }
-
-
-ResourceManager::ManagedResource::ManagedResource(Resource &r):
-       resource(&r),
-       load_priority(r.get_load_priority()),
-       io(0),
-       loader(0),
-       state(NOT_LOADED),
-       last_used(0),
-       data_size(0)
-{ }
-
-void ResourceManager::ManagedResource::start_loading()
-{
-       io = location.collection->open_raw(location.name);
-       if(!io)
-               throw resource_load_error(location.name, "open failed");
-
-       const Resources *res = dynamic_cast<Resources *>(location.collection);
-       loader = resource->load(*io, res);
-       if(!loader)
-       {
-               delete io;
-               io = 0;
-               throw logic_error("no loader created");
-       }
-       state = LOADING;
-}
-
-bool ResourceManager::ManagedResource::process(bool sync)
-{
-       while(state!=LOAD_FINISHED && loader->needs_sync()==sync)
-               if(loader->process())
-                       state = LOAD_FINISHED;
-
-       return state==LOAD_FINISHED;
-}
-
-void ResourceManager::ManagedResource::finish_loading(bool successful)
-{
-       delete loader;
-       loader = 0;
-       delete io;
-       io = 0;
-
-       if(successful)
-       {
-               state = LOADED;
-               data_size = resource->get_data_size();
-
-               for(vector<ResourceObserver *>::const_iterator i=observers.begin(); i!=observers.end(); ++i)
-                       (*i)->resource_loaded(*resource);
-       }
-       else
-       {
-               resource->unload();
-               state = NOT_LOADED;
-       }
-}
-
-void ResourceManager::ManagedResource::finish_loading()
-{
-       finish_loading(state==LOAD_FINISHED);
-}
-
-void ResourceManager::ManagedResource::unload()
-{
-       resource->unload();
-       state = NOT_LOADED;
-
-       for(vector<ResourceObserver *>::const_iterator i=observers.begin(); i!=observers.end(); ++i)
-               (*i)->resource_unloaded(*resource);
-}
-
-void ResourceManager::ManagedResource::add_observer(ResourceObserver &w)
-{
-       if(find(observers.begin(), observers.end(), &w)==observers.end())
-               observers.push_back(&w);
-}
-
-void ResourceManager::ManagedResource::remove_observer(ResourceObserver &w)
-{
-       vector<ResourceObserver *>::iterator end = remove(observers.begin(), observers.end(), &w);
-       if(end!=observers.end())
-               observers.erase(end, observers.end());
-}
-
-
-ResourceManager::LoadingThread::LoadingThread():
-       Thread("ResourceManager"),
-       sem(1),
-       capacity(2),
-       size(0),
-       loaded_data_size(0),
-       done(false)
-{
-       launch();
-}
-
-void ResourceManager::LoadingThread::main()
-{
-       bool wait_for_work = false;
-       while(!done)
-       {
-               if(wait_for_work)
-                       sem.wait();
-
-               if(ManagedResource *managed = front(async_queue))
-               {
-                       try
-                       {
-                               managed->process(false);
-                       }
-                       catch(const exception &e)
-                       {
-                               MutexLock lock(queue_mutex);
-                               error_queue.push_back(resource_load_error(managed->location.name, e));
-                               managed->state = ManagedResource::LOAD_ERROR;
-                       }
-
-                       MutexLock lock(queue_mutex);
-                       sync_queue.splice(sync_queue.end(), async_queue, async_queue.begin());
-                       wait_for_work = async_queue.empty();
-               }
-               else
-                       wait_for_work = true;
-       }
-}
-
-ResourceManager::ManagedResource *ResourceManager::LoadingThread::front(LoadQueue &que)
-{
-       MutexLock lock(queue_mutex);
-       if(que.empty())
-               return 0;
-
-       return que.front();
-}
-
-void ResourceManager::LoadingThread::add_resource(ManagedResource &r)
-{
-       r.start_loading();
-
-       MutexLock lock(queue_mutex);
-       if(r.loader->needs_sync())
-               sync_queue.push_back(&r);
-       else
-       {
-               bool was_empty = async_queue.empty();
-               async_queue.push_back(&r);
-               if(was_empty)
-                       sem.signal();
-       }
-
-       ++size;
-}
-
-void ResourceManager::LoadingThread::remove_resource(ManagedResource &r)
-{
-       while(!try_remove_resource(r))
-               Time::sleep(Time::msec);
-
-       r.finish_loading();
-       if(r.state==ManagedResource::LOADED)
-       {
-               MutexLock lock(data_size_mutex);
-               loaded_data_size += r.data_size;
-       }
-}
-
-bool ResourceManager::LoadingThread::try_remove_resource(ManagedResource &r)
-{
-       MutexLock lock(queue_mutex);
-
-       LoadQueue::iterator i = find(async_queue.begin(), async_queue.end(), &r);
-       if(i==async_queue.end())
-       {
-               i = find(sync_queue.begin(), sync_queue.end(), &r);
-               if(i!=sync_queue.end())
-               {
-                       sync_queue.erase(i);
-                       --size;
-               }
-       }
-       else if(i==async_queue.begin())
-               return false;
-       else
-       {
-               async_queue.erase(i);
-               --size;
-       }
-
-       return true;
-}
-
-bool ResourceManager::LoadingThread::sync()
-{
-       {
-               MutexLock lock(queue_mutex);
-
-               if(!error_queue.empty())
-               {
-                       resource_load_error err = error_queue.front();
-                       error_queue.pop_front();
-                       throw err;
-               }
-
-               unsigned async_size = async_queue.size();
-               if(async_size==0 && size==capacity)
-                       ++capacity;
-               else if(async_size>2 && capacity>2)
-                       --capacity;
-       }
-
-       bool any_finished = false;
-       while(ManagedResource *managed = front(sync_queue))
-       {
-               if(managed->state==ManagedResource::LOAD_ERROR || managed->process(true))
-               {
-                       managed->finish_loading();
-                       if(managed->state==ManagedResource::LOADED)
-                       {
-                               MutexLock lock(data_size_mutex);
-                               loaded_data_size += managed->data_size;
-                       }
-                       any_finished = true;
-                       --size;
-
-                       MutexLock lock(queue_mutex);
-                       sync_queue.pop_front();
-               }
-               else
-               {
-                       MutexLock lock(queue_mutex);
-                       bool was_empty = async_queue.empty();
-                       async_queue.splice(async_queue.end(), sync_queue, sync_queue.begin());
-                       if(was_empty)
-                               sem.signal();
-               }
-       }
-
-       return any_finished;
-}
-
-UInt64 ResourceManager::LoadingThread::get_and_reset_loaded_data_size()
-{
-       MutexLock lock(data_size_mutex);
-       UInt64 result = loaded_data_size;
-       loaded_data_size = 0;
-       return result;
-}
-
-void ResourceManager::LoadingThread::terminate()
-{
-       done = true;
-       sem.signal();
-       join();
-       async_queue.clear();
-       sync_queue.clear();
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/resourcemanager.h b/source/resourcemanager.h
deleted file mode 100644 (file)
index cf34a82..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-#ifndef MSP_GL_RESOURCEMANAGER_H_
-#define MSP_GL_RESOURCEMANAGER_H_
-
-#include <msp/core/inttypes.h>
-#include <msp/core/mutex.h>
-#include <msp/core/semaphore.h>
-#include <msp/core/thread.h>
-#include <msp/datafile/collection.h>
-#include "resource.h"
-
-namespace Msp {
-namespace GL {
-
-class ResourceObserver;
-
-class resource_load_error: public std::runtime_error
-{
-public:
-       resource_load_error(const std::string &, const std::string &);
-       resource_load_error(const std::string &, const std::exception &);
-       virtual ~resource_load_error() throw() { }
-};
-
-
-class ResourceManager
-{
-public:
-       enum LoadingPolicy
-       {
-               LOAD_IMMEDIATELY,
-               LOAD_ON_DEMAND,
-               LOAD_MANUALLY
-       };
-
-       struct ResourceLocation
-       {
-               DataFile::Collection *collection;
-               std::string name;
-
-               ResourceLocation();
-               ResourceLocation(DataFile::Collection &, const std::string &);
-       };
-
-private:
-       struct ManagedResource
-       {
-               enum State
-               {
-                       NOT_LOADED,
-                       LOAD_QUEUED,
-                       LOADING,
-                       LOAD_FINISHED,
-                       LOADED,
-                       LOAD_ERROR
-               };
-
-               Resource *resource;
-               ResourceLocation location;
-               bool load_priority;
-               IO::Seekable *io;
-               Resource::AsyncLoader *loader;
-               State state;
-               unsigned last_used;
-               UInt64 data_size;
-               std::vector<ResourceObserver *> observers;
-
-               ManagedResource(Resource &);
-
-               void start_loading();
-               bool process(bool);
-               void finish_loading(bool);
-               void finish_loading();
-               void unload();
-
-               void add_observer(ResourceObserver &);
-               void remove_observer(ResourceObserver &);
-       };
-
-       typedef std::list<ManagedResource *> LoadQueue;
-
-       class LoadingThread: public Thread
-       {
-       private:
-               Semaphore sem;
-               Mutex queue_mutex;
-               LoadQueue async_queue;
-               LoadQueue sync_queue;
-               unsigned capacity;
-               unsigned size;
-               std::list<resource_load_error> error_queue;
-               Mutex data_size_mutex;
-               UInt64 loaded_data_size;
-               volatile bool done;
-
-       public:
-               LoadingThread();
-
-       private:
-               virtual void main();
-
-               ManagedResource *front(LoadQueue &);
-
-       public:
-               void add_resource(ManagedResource &);
-               void remove_resource(ManagedResource &);
-       private:
-               bool try_remove_resource(ManagedResource &);
-       public:
-               bool sync();
-               bool needs_work() const { return size<capacity; }
-               UInt64 get_and_reset_loaded_data_size();
-
-               void terminate();
-       };
-
-       typedef std::map<const Resource *, ManagedResource> ResourceMap;
-
-       LoadingPolicy policy;
-       bool async_loads;
-       mutable Mutex map_mutex;
-       ResourceMap resources;
-       LoadQueue queue;
-       UInt64 total_data_size;
-       UInt64 size_limit;
-       unsigned frame;
-       unsigned min_retain_frames;
-       unsigned max_retain_frames;
-       unsigned next_unload;
-       LoadingThread thread;
-
-public:
-       ResourceManager();
-       ~ResourceManager();
-
-       void set_loading_policy(LoadingPolicy);
-       void set_async_loads(bool);
-       void set_size_limit(UInt64);
-       void set_min_retain_frames(unsigned);
-       void set_max_retain_frames(unsigned);
-
-       void add_resource(Resource &);
-private:
-       const ManagedResource &get_managed_resource(const Resource &) const;
-       ManagedResource &get_managed_resource(const Resource &);
-public:
-       void *get_data_for_resource(const Resource &r) { return &get_managed_resource(r); }
-       void set_resource_location(Resource &, DataFile::Collection &, const std::string &);
-       void set_resource_location(Resource &, const ResourceLocation &);
-       const ResourceLocation *get_resource_location(const Resource &) const;
-       void load_resource(Resource &);
-       bool is_resource_loaded(const Resource &) const;
-       void resource_used(const Resource &);
-       void remove_resource(Resource &);
-
-       void observe_resource(const Resource &, ResourceObserver &);
-       void unobserve_resource(const Resource &, ResourceObserver &);
-
-       void tick();
-private:
-       void dispatch_work();
-       void unload_by_age();
-       void unload_by_size();
-public:
-       UInt64 get_total_data_size() const { return total_data_size; }
-
-private:
-       static bool age_order(ManagedResource *, ManagedResource *);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/resourceobserver.h b/source/resourceobserver.h
deleted file mode 100644 (file)
index d1313d3..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef MSP_GL_RESOURCEOBSERVER_H_
-#define MSP_GL_RESOURCEOBSERVER_H_
-
-namespace Msp {
-namespace GL {
-
-class Resource;
-
-class ResourceObserver
-{
-protected:
-       ResourceObserver() { }
-public:
-       virtual ~ResourceObserver() { }
-
-       virtual void resource_loaded(Resource &) { }
-       virtual void resource_unloaded(Resource &) { }
-       virtual void resource_removed(Resource &) { }
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/resources.cpp b/source/resources.cpp
deleted file mode 100644 (file)
index d52dbe3..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-#include <msp/datafile/builtinsource.h>
-#include <msp/fs/utils.h>
-#include "animation.h"
-#include "armature.h"
-#include "camera.h"
-#include "font.h"
-#include "keyframe.h"
-#include "lighting.h"
-#include "material.h"
-#include "mesh.h"
-#include "object.h"
-#include "pipelinetemplate.h"
-#include "pose.h"
-#include "program.h"
-#include "programcompiler.h"
-#include "resourcemanager.h"
-#include "resources.h"
-#include "sampler.h"
-#include "technique.h"
-#include "texture1d.h"
-#include "texture2d.h"
-#include "texture2darray.h"
-#include "texturecube.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-void init_shaderlib(DataFile::BuiltinSource &);
-
-Resources::Resources():
-       default_tex_filter(Texture::can_generate_mipmap() ? LINEAR_MIPMAP_LINEAR : LINEAR),
-       default_tex_anisotropy(1.0f),
-       srgb_conversion(false),
-       resource_manager(0)
-{
-       add_type<Animation>().suffix(".anim").keyword("animation");
-       add_type<Armature>().suffix(".arma").keyword("armature");
-       add_type<Camera>().keyword("camera");
-       add_type<Font>().keyword("font");
-       add_type<KeyFrame>().suffix(".kframe").keyword("keyframe");
-       add_type<Lighting>().suffix(".lightn").keyword("lighting");
-       add_type<Material>().suffix(".mat").creator(&Resources::create_material);
-       add_type<Mesh>().keyword("mesh").creator(&Resources::create_mesh);
-       add_type<Object>().keyword("object");
-       add_type<PipelineTemplate>().suffix(".pipe").keyword("pipeline");
-       add_type<Pose>().keyword("pose");
-       add_type<Program>().keyword("shader").suffix(".glsl").creator(&Resources::create_program);
-       add_type<Sampler>().suffix(".samp").keyword("sampler");
-       add_type<Technique>().suffix(".tech").keyword("technique");
-       add_type<Texture1D>().base<Texture>().suffix(".tex1d").keyword("texture1d");
-       add_type<Texture2D>().base<Texture>().suffix(".tex2d").suffix(".png").suffix(".jpg").keyword("texture2d").creator(&Resources::create_texture2d);
-       add_type<Texture3D>().base<Texture>().suffix(".tex3d").keyword("texture3d");
-       add_type<TextureCube>().base<Texture>().suffix(".texcb").keyword("texture_cube");
-       add_type<Texture2DArray>().base<Texture>().suffix(".tex2da").keyword("texture2d_array");
-
-       add_source(get_builtins());
-}
-
-const DataFile::CollectionSource &Resources::get_builtins()
-{
-       static DataFile::BuiltinSource builtins;
-       bool init_done = false;
-
-       if(!init_done)
-       {
-               init_shaderlib(builtins);
-               init_done = true;
-       }
-
-       return builtins;
-}
-
-void Resources::set_default_texture_filter(TextureFilter tf)
-{
-       default_tex_filter = tf;
-}
-
-void Resources::set_default_texture_anisotropy(float a)
-{
-       default_tex_anisotropy = a;
-}
-
-void Resources::set_srgb_conversion(bool c)
-{
-       srgb_conversion = c;
-}
-
-void Resources::set_resource_manager(ResourceManager *m)
-{
-       resource_manager = m;
-}
-
-Material *Resources::create_material(const string &name)
-{
-       if(RefPtr<IO::Seekable> io = open_raw(name))
-       {
-               DataFile::Parser parser(*io, name);
-               Material::GenericLoader ldr(this);
-               ldr.load(parser);
-               return ldr.get_material();
-       }
-
-       return 0;
-}
-
-Mesh *Resources::create_mesh(const string &name)
-{
-       if(!resource_manager)
-               return 0;
-
-       if(RefPtr<IO::Seekable> io = open_raw(name))
-       {
-               RefPtr<Mesh> mesh = new Mesh(resource_manager);
-               resource_manager->set_resource_location(*mesh, *this, name);
-               return mesh.release();
-       }
-
-       return 0;
-}
-
-Texture2D *Resources::create_texture2d(const string &name)
-{
-       string ext = FS::extpart(name);
-       if(ext==".tex2d" && !resource_manager)
-               return 0;
-
-       if(RefPtr<IO::Seekable> io = open_raw(name))
-       {
-               Graphics::Image image;
-               if(!resource_manager)
-                       image.load_io(*io);
-
-               RefPtr<Texture2D> tex = new Texture2D(resource_manager);
-
-               if(ext==".tex2d")
-               {
-                       DataFile::Parser parser(*io, name);
-                       Texture2D::Loader ldr(*tex, *this);
-                       ldr.load(parser);
-               }
-               else
-               {
-                       Sampler &samp = tex->get_default_sampler();
-                       if(is_mipmapped(default_tex_filter))
-                       {
-                               tex->set_auto_generate_mipmap(true);
-                               samp.set_mag_filter(LINEAR);
-                       }
-                       else
-                               samp.set_mag_filter(default_tex_filter);
-                       samp.set_min_filter(default_tex_filter);
-                       samp.set_max_anisotropy(default_tex_anisotropy);
-               }
-
-               if(resource_manager)
-                       resource_manager->set_resource_location(*tex, *this, name);
-               else
-                       tex->image(image);
-               return tex.release();
-       }
-
-       return 0;
-}
-
-Program *Resources::create_program(const string &name)
-{
-       string ext = FS::extpart(name);
-       if(ext==".shader")
-               return 0;
-
-       if(RefPtr<IO::Seekable> io = open_raw(name))
-       {
-               ProgramCompiler compiler;
-               compiler.compile(*io, this, name);
-               RefPtr<Program> program = new Program;
-               compiler.add_shaders(*program);
-               program->link();
-               return program.release();
-       }
-
-       return 0;
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/resources.h b/source/resources.h
deleted file mode 100644 (file)
index 6f04a26..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef MSP_GL_RESOURCES_H_
-#define MSP_GL_RESOURCES_H_
-
-#include <msp/datafile/collection.h>
-#include "texture.h"
-
-namespace Msp {
-namespace GL {
-
-class Material;
-class Mesh;
-class Program;
-class ResourceManager;
-class Texture2D;
-
-/**
-A collection class for GL resources.  Most useful as a base class for an
-application-specific collection.
-*/
-class Resources: virtual public DataFile::Collection
-{
-private:
-       TextureFilter default_tex_filter;
-       float default_tex_anisotropy;
-       bool srgb_conversion;
-       ResourceManager *resource_manager;
-
-public:
-       Resources();
-
-       static const DataFile::CollectionSource &get_builtins();
-
-       void set_default_texture_filter(TextureFilter);
-       void set_default_texture_anisotropy(float);
-
-       /** Enables or disables sRGB conversion.  If enabled, textures and material
-       colors are converted from sRGB to linear color space when loaded. */
-       DEPRECATED void set_srgb_conversion(bool);
-
-       DEPRECATED bool get_srgb_conversion() const { return srgb_conversion; }
-
-       void set_resource_manager(ResourceManager *);
-
-protected:
-       Material *create_material(const std::string &);
-       Mesh *create_mesh(const std::string &);
-       Texture2D *create_texture2d(const std::string &);
-       Program *create_program(const std::string &);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/resources/resource.cpp b/source/resources/resource.cpp
new file mode 100644 (file)
index 0000000..4be6ee2
--- /dev/null
@@ -0,0 +1,37 @@
+#include "resource.h"
+#include "resourcemanager.h"
+
+namespace Msp {
+namespace GL {
+
+Resource::Resource():
+       manager(0)
+{ }
+
+Resource::~Resource()
+{
+       if(manager)
+               manager->remove_resource(*this);
+}
+
+void Resource::set_manager(ResourceManager *m)
+{
+       if(manager)
+               manager->remove_resource(*this);
+       manager = m;
+       if(manager)
+       {
+               manager->add_resource(*this);
+               manager_data = manager->get_data_for_resource(*this);
+       }
+       else
+               manager_data = 0;
+}
+
+bool Resource::is_loaded() const
+{
+       return manager ? manager->is_resource_loaded(*this) : true;
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/resources/resource.h b/source/resources/resource.h
new file mode 100644 (file)
index 0000000..571d1ea
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef MSP_GL_RESOURCE_H_
+#define MSP_GL_RESOURCE_H_
+
+#include <msp/core/inttypes.h>
+#include <msp/io/seekable.h>
+
+namespace Msp {
+namespace GL {
+
+class ResourceManager;
+class Resources;
+
+class Resource
+{
+public:
+       class AsyncLoader
+       {
+       protected:
+               AsyncLoader() { }
+       public:
+               virtual ~AsyncLoader() { }
+
+               virtual bool needs_sync() const = 0;
+               virtual bool process() = 0;
+       };
+
+protected:
+       ResourceManager *manager;
+       void *manager_data;
+
+       Resource();
+public:
+       virtual ~Resource();
+
+       void set_manager(ResourceManager *);
+       ResourceManager *get_manager() const { return manager; }
+       void *get_manager_data() const { return manager_data; }
+       virtual int get_load_priority() const { return 0; }
+       virtual AsyncLoader *load(IO::Seekable &, const Resources * = 0) = 0;
+       virtual bool is_loaded() const;
+
+       /** Returns the amount of graphics memory used by this resource.  The
+       returned value must not change while the resource is loaded. */
+       virtual UInt64 get_data_size() const = 0;
+
+       virtual void unload() = 0;
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/resources/resourcemanager.cpp b/source/resources/resourcemanager.cpp
new file mode 100644 (file)
index 0000000..30f7501
--- /dev/null
@@ -0,0 +1,550 @@
+#include <algorithm>
+#include <typeinfo>
+#include <msp/debug/demangle.h>
+#include <msp/strings/format.h>
+#include <msp/time/utils.h>
+#include "resourcemanager.h"
+#include "resources.h"
+#include "resourceobserver.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+resource_load_error::resource_load_error(const string &name, const string &err):
+       runtime_error(format("%s: %s", name, err))
+{ }
+
+resource_load_error::resource_load_error(const string &name, const exception &exc):
+       runtime_error(format("%s: %s: %s", name, Debug::demangle(typeid(exc).name()), exc.what()))
+{ }
+
+
+ResourceManager::ResourceManager():
+       policy(LOAD_ON_DEMAND),
+       async_loads(true),
+       total_data_size(0),
+       size_limit(0),
+       frame(0),
+       min_retain_frames(30),
+       max_retain_frames(0),
+       next_unload(0)
+{ }
+
+ResourceManager::~ResourceManager()
+{
+       thread.terminate();
+
+       while(!resources.empty())
+               resources.begin()->second.resource->set_manager(0);
+}
+
+void ResourceManager::set_loading_policy(LoadingPolicy p)
+{
+       policy = p;
+}
+
+void ResourceManager::set_async_loads(bool a)
+{
+       async_loads = a;
+}
+
+void ResourceManager::set_size_limit(UInt64 s)
+{
+       size_limit = s;
+}
+
+void ResourceManager::set_min_retain_frames(unsigned f)
+{
+       min_retain_frames = f;
+}
+
+void ResourceManager::set_max_retain_frames(unsigned f)
+{
+       max_retain_frames = f;
+}
+
+void ResourceManager::add_resource(Resource &r)
+{
+       MutexLock lock(map_mutex);
+       insert_unique(resources, &r, ManagedResource(r));
+}
+
+const ResourceManager::ManagedResource &ResourceManager::get_managed_resource(const Resource &r) const
+{
+       MutexLock lock(map_mutex);
+       return get_item(resources, &r);
+}
+
+ResourceManager::ManagedResource &ResourceManager::get_managed_resource(const Resource &r)
+{
+       MutexLock lock(map_mutex);
+       return get_item(resources, &r);
+}
+
+void ResourceManager::set_resource_location(Resource &r, DataFile::Collection &c, const string &n)
+{
+       set_resource_location(r, ResourceLocation(c, n));
+}
+
+void ResourceManager::set_resource_location(Resource &r, const ResourceLocation &l)
+{
+       {
+               MutexLock lock(map_mutex);
+               ManagedResource &managed = get_item(resources, &r);
+               managed.location = l;
+       }
+
+       if(policy==LOAD_IMMEDIATELY)
+               load_resource(r);
+}
+
+const ResourceManager::ResourceLocation *ResourceManager::get_resource_location(const Resource &r) const
+{
+       const ManagedResource &managed = get_managed_resource(r);
+       return managed.location.collection ? &managed.location : 0;
+}
+
+void ResourceManager::load_resource(Resource &r)
+{
+       ManagedResource &managed = get_managed_resource(r);
+       if(!managed.location.collection)
+               throw runtime_error("no location");
+
+       if(managed.state!=ManagedResource::NOT_LOADED)
+               return;
+
+       if(async_loads)
+       {
+               managed.state = ManagedResource::LOAD_QUEUED;
+               LoadQueue::iterator i;
+               for(i=queue.begin(); (i!=queue.end() && (*i)->load_priority>=managed.load_priority); ++i) ;
+               queue.insert(i, &managed);
+       }
+       else
+       {
+               managed.start_loading();
+               while(!managed.loader->process()) ;
+               managed.finish_loading(true);
+               total_data_size += managed.data_size;
+       }
+}
+
+bool ResourceManager::is_resource_loaded(const Resource &r) const
+{
+       ManagedResource *managed = reinterpret_cast<ManagedResource *>(r.get_manager_data());
+       return managed ? managed->state==ManagedResource::LOADED : false;
+}
+
+void ResourceManager::resource_used(const Resource &r)
+{
+       ManagedResource *managed = reinterpret_cast<ManagedResource *>(r.get_manager_data());
+       if(!managed)
+               return;
+       if(managed->state==ManagedResource::NOT_LOADED && policy!=LOAD_MANUALLY)
+               load_resource(*managed->resource);
+
+       managed->last_used = frame;
+       if(max_retain_frames && !next_unload)
+               next_unload = frame+max_retain_frames+1;
+}
+
+void ResourceManager::remove_resource(Resource &r)
+{
+       ManagedResource &managed = get_managed_resource(r);
+       ManagedResource::State state = managed.state;
+       if(state==ManagedResource::LOAD_QUEUED)
+       {
+               LoadQueue::iterator i = find(queue.begin(), queue.end(), &managed);
+               if(i!=queue.end())
+                       queue.erase(i);
+       }
+       else if(state>ManagedResource::LOAD_QUEUED && state<ManagedResource::LOADED)
+               thread.remove_resource(managed);
+
+       for(vector<ResourceObserver *>::const_iterator i=managed.observers.begin(); i!=managed.observers.end(); ++i)
+               (*i)->resource_removed(r);
+
+       MutexLock lock(map_mutex);
+       remove_existing(resources, &r);
+}
+
+void ResourceManager::observe_resource(const Resource &r, ResourceObserver &w)
+{
+       get_managed_resource(r).add_observer(w);
+}
+
+void ResourceManager::unobserve_resource(const Resource &r, ResourceObserver &w)
+{
+       get_managed_resource(r).remove_observer(w);
+}
+
+void ResourceManager::tick()
+{
+       ++frame;
+
+       bool do_unload = (frame>=next_unload);
+       if(thread.sync())
+       {
+               total_data_size += thread.get_and_reset_loaded_data_size();
+               do_unload = true;
+       }
+
+       if(thread.needs_work() && !queue.empty())
+               dispatch_work();
+
+       if(do_unload)
+       {
+               MutexLock lock(map_mutex);
+               if(max_retain_frames && frame>=next_unload)
+               {
+                       unload_by_age();
+
+                       next_unload = frame;
+                       for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
+                               if(i->second.state==ManagedResource::LOADED)
+                                       next_unload = min(next_unload, i->second.last_used);
+                       next_unload = (next_unload<frame ? next_unload+max_retain_frames : 0);
+               }
+
+               if(size_limit)
+                       unload_by_size();
+       }
+}
+
+void ResourceManager::dispatch_work()
+{
+       queue.sort(age_order);
+
+       if(queue.front()->last_used+min_retain_frames<frame)
+       {
+               for(LoadQueue::iterator i=queue.begin(); i!=queue.end(); ++i)
+                       (*i)->state = ManagedResource::NOT_LOADED;
+               queue.clear();
+               return;
+       }
+
+       while(thread.needs_work() && !queue.empty())
+       {
+               ManagedResource *managed = queue.front();
+               queue.pop_front();
+               thread.add_resource(*managed);
+       }
+}
+
+void ResourceManager::unload_by_age()
+{
+       unsigned unload_limit = frame-max_retain_frames;
+
+       for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
+               if(i->second.state==ManagedResource::LOADED && i->second.last_used<unload_limit)
+               {
+                       i->second.unload();
+                       total_data_size -= i->second.data_size;
+               }
+}
+
+void ResourceManager::unload_by_size()
+{
+       unsigned unload_limit = frame-min_retain_frames;
+
+       while(total_data_size>size_limit)
+       {
+               ManagedResource *best = 0;
+               UInt64 best_impact = 0;
+               for(ResourceMap::iterator i=resources.begin(); i!=resources.end(); ++i)
+                       if(i->second.state==ManagedResource::LOADED && i->second.last_used<unload_limit)
+                       {
+                               UInt64 impact = i->second.data_size*(frame-i->second.last_used);
+                               if(!best || impact>best_impact)
+                               {
+                                       best = &i->second;
+                                       best_impact = impact;
+                               }
+                       }
+
+               if(!best)
+                       break;
+
+               best->unload();
+               total_data_size -= best->data_size;
+       }
+}
+
+bool ResourceManager::age_order(ManagedResource *mr1, ManagedResource *mr2)
+{
+       return mr1->last_used>mr2->last_used;
+}
+
+
+ResourceManager::ResourceLocation::ResourceLocation():
+       collection(0)
+{ }
+
+ResourceManager::ResourceLocation::ResourceLocation(DataFile::Collection &c, const string &n):
+       collection(&c),
+       name(n)
+{ }
+
+
+ResourceManager::ManagedResource::ManagedResource(Resource &r):
+       resource(&r),
+       load_priority(r.get_load_priority()),
+       io(0),
+       loader(0),
+       state(NOT_LOADED),
+       last_used(0),
+       data_size(0)
+{ }
+
+void ResourceManager::ManagedResource::start_loading()
+{
+       io = location.collection->open_raw(location.name);
+       if(!io)
+               throw resource_load_error(location.name, "open failed");
+
+       const Resources *res = dynamic_cast<Resources *>(location.collection);
+       loader = resource->load(*io, res);
+       if(!loader)
+       {
+               delete io;
+               io = 0;
+               throw logic_error("no loader created");
+       }
+       state = LOADING;
+}
+
+bool ResourceManager::ManagedResource::process(bool sync)
+{
+       while(state!=LOAD_FINISHED && loader->needs_sync()==sync)
+               if(loader->process())
+                       state = LOAD_FINISHED;
+
+       return state==LOAD_FINISHED;
+}
+
+void ResourceManager::ManagedResource::finish_loading(bool successful)
+{
+       delete loader;
+       loader = 0;
+       delete io;
+       io = 0;
+
+       if(successful)
+       {
+               state = LOADED;
+               data_size = resource->get_data_size();
+
+               for(vector<ResourceObserver *>::const_iterator i=observers.begin(); i!=observers.end(); ++i)
+                       (*i)->resource_loaded(*resource);
+       }
+       else
+       {
+               resource->unload();
+               state = NOT_LOADED;
+       }
+}
+
+void ResourceManager::ManagedResource::finish_loading()
+{
+       finish_loading(state==LOAD_FINISHED);
+}
+
+void ResourceManager::ManagedResource::unload()
+{
+       resource->unload();
+       state = NOT_LOADED;
+
+       for(vector<ResourceObserver *>::const_iterator i=observers.begin(); i!=observers.end(); ++i)
+               (*i)->resource_unloaded(*resource);
+}
+
+void ResourceManager::ManagedResource::add_observer(ResourceObserver &w)
+{
+       if(find(observers.begin(), observers.end(), &w)==observers.end())
+               observers.push_back(&w);
+}
+
+void ResourceManager::ManagedResource::remove_observer(ResourceObserver &w)
+{
+       vector<ResourceObserver *>::iterator end = remove(observers.begin(), observers.end(), &w);
+       if(end!=observers.end())
+               observers.erase(end, observers.end());
+}
+
+
+ResourceManager::LoadingThread::LoadingThread():
+       Thread("ResourceManager"),
+       sem(1),
+       capacity(2),
+       size(0),
+       loaded_data_size(0),
+       done(false)
+{
+       launch();
+}
+
+void ResourceManager::LoadingThread::main()
+{
+       bool wait_for_work = false;
+       while(!done)
+       {
+               if(wait_for_work)
+                       sem.wait();
+
+               if(ManagedResource *managed = front(async_queue))
+               {
+                       try
+                       {
+                               managed->process(false);
+                       }
+                       catch(const exception &e)
+                       {
+                               MutexLock lock(queue_mutex);
+                               error_queue.push_back(resource_load_error(managed->location.name, e));
+                               managed->state = ManagedResource::LOAD_ERROR;
+                       }
+
+                       MutexLock lock(queue_mutex);
+                       sync_queue.splice(sync_queue.end(), async_queue, async_queue.begin());
+                       wait_for_work = async_queue.empty();
+               }
+               else
+                       wait_for_work = true;
+       }
+}
+
+ResourceManager::ManagedResource *ResourceManager::LoadingThread::front(LoadQueue &que)
+{
+       MutexLock lock(queue_mutex);
+       if(que.empty())
+               return 0;
+
+       return que.front();
+}
+
+void ResourceManager::LoadingThread::add_resource(ManagedResource &r)
+{
+       r.start_loading();
+
+       MutexLock lock(queue_mutex);
+       if(r.loader->needs_sync())
+               sync_queue.push_back(&r);
+       else
+       {
+               bool was_empty = async_queue.empty();
+               async_queue.push_back(&r);
+               if(was_empty)
+                       sem.signal();
+       }
+
+       ++size;
+}
+
+void ResourceManager::LoadingThread::remove_resource(ManagedResource &r)
+{
+       while(!try_remove_resource(r))
+               Time::sleep(Time::msec);
+
+       r.finish_loading();
+       if(r.state==ManagedResource::LOADED)
+       {
+               MutexLock lock(data_size_mutex);
+               loaded_data_size += r.data_size;
+       }
+}
+
+bool ResourceManager::LoadingThread::try_remove_resource(ManagedResource &r)
+{
+       MutexLock lock(queue_mutex);
+
+       LoadQueue::iterator i = find(async_queue.begin(), async_queue.end(), &r);
+       if(i==async_queue.end())
+       {
+               i = find(sync_queue.begin(), sync_queue.end(), &r);
+               if(i!=sync_queue.end())
+               {
+                       sync_queue.erase(i);
+                       --size;
+               }
+       }
+       else if(i==async_queue.begin())
+               return false;
+       else
+       {
+               async_queue.erase(i);
+               --size;
+       }
+
+       return true;
+}
+
+bool ResourceManager::LoadingThread::sync()
+{
+       {
+               MutexLock lock(queue_mutex);
+
+               if(!error_queue.empty())
+               {
+                       resource_load_error err = error_queue.front();
+                       error_queue.pop_front();
+                       throw err;
+               }
+
+               unsigned async_size = async_queue.size();
+               if(async_size==0 && size==capacity)
+                       ++capacity;
+               else if(async_size>2 && capacity>2)
+                       --capacity;
+       }
+
+       bool any_finished = false;
+       while(ManagedResource *managed = front(sync_queue))
+       {
+               if(managed->state==ManagedResource::LOAD_ERROR || managed->process(true))
+               {
+                       managed->finish_loading();
+                       if(managed->state==ManagedResource::LOADED)
+                       {
+                               MutexLock lock(data_size_mutex);
+                               loaded_data_size += managed->data_size;
+                       }
+                       any_finished = true;
+                       --size;
+
+                       MutexLock lock(queue_mutex);
+                       sync_queue.pop_front();
+               }
+               else
+               {
+                       MutexLock lock(queue_mutex);
+                       bool was_empty = async_queue.empty();
+                       async_queue.splice(async_queue.end(), sync_queue, sync_queue.begin());
+                       if(was_empty)
+                               sem.signal();
+               }
+       }
+
+       return any_finished;
+}
+
+UInt64 ResourceManager::LoadingThread::get_and_reset_loaded_data_size()
+{
+       MutexLock lock(data_size_mutex);
+       UInt64 result = loaded_data_size;
+       loaded_data_size = 0;
+       return result;
+}
+
+void ResourceManager::LoadingThread::terminate()
+{
+       done = true;
+       sem.signal();
+       join();
+       async_queue.clear();
+       sync_queue.clear();
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/resources/resourcemanager.h b/source/resources/resourcemanager.h
new file mode 100644 (file)
index 0000000..cf34a82
--- /dev/null
@@ -0,0 +1,173 @@
+#ifndef MSP_GL_RESOURCEMANAGER_H_
+#define MSP_GL_RESOURCEMANAGER_H_
+
+#include <msp/core/inttypes.h>
+#include <msp/core/mutex.h>
+#include <msp/core/semaphore.h>
+#include <msp/core/thread.h>
+#include <msp/datafile/collection.h>
+#include "resource.h"
+
+namespace Msp {
+namespace GL {
+
+class ResourceObserver;
+
+class resource_load_error: public std::runtime_error
+{
+public:
+       resource_load_error(const std::string &, const std::string &);
+       resource_load_error(const std::string &, const std::exception &);
+       virtual ~resource_load_error() throw() { }
+};
+
+
+class ResourceManager
+{
+public:
+       enum LoadingPolicy
+       {
+               LOAD_IMMEDIATELY,
+               LOAD_ON_DEMAND,
+               LOAD_MANUALLY
+       };
+
+       struct ResourceLocation
+       {
+               DataFile::Collection *collection;
+               std::string name;
+
+               ResourceLocation();
+               ResourceLocation(DataFile::Collection &, const std::string &);
+       };
+
+private:
+       struct ManagedResource
+       {
+               enum State
+               {
+                       NOT_LOADED,
+                       LOAD_QUEUED,
+                       LOADING,
+                       LOAD_FINISHED,
+                       LOADED,
+                       LOAD_ERROR
+               };
+
+               Resource *resource;
+               ResourceLocation location;
+               bool load_priority;
+               IO::Seekable *io;
+               Resource::AsyncLoader *loader;
+               State state;
+               unsigned last_used;
+               UInt64 data_size;
+               std::vector<ResourceObserver *> observers;
+
+               ManagedResource(Resource &);
+
+               void start_loading();
+               bool process(bool);
+               void finish_loading(bool);
+               void finish_loading();
+               void unload();
+
+               void add_observer(ResourceObserver &);
+               void remove_observer(ResourceObserver &);
+       };
+
+       typedef std::list<ManagedResource *> LoadQueue;
+
+       class LoadingThread: public Thread
+       {
+       private:
+               Semaphore sem;
+               Mutex queue_mutex;
+               LoadQueue async_queue;
+               LoadQueue sync_queue;
+               unsigned capacity;
+               unsigned size;
+               std::list<resource_load_error> error_queue;
+               Mutex data_size_mutex;
+               UInt64 loaded_data_size;
+               volatile bool done;
+
+       public:
+               LoadingThread();
+
+       private:
+               virtual void main();
+
+               ManagedResource *front(LoadQueue &);
+
+       public:
+               void add_resource(ManagedResource &);
+               void remove_resource(ManagedResource &);
+       private:
+               bool try_remove_resource(ManagedResource &);
+       public:
+               bool sync();
+               bool needs_work() const { return size<capacity; }
+               UInt64 get_and_reset_loaded_data_size();
+
+               void terminate();
+       };
+
+       typedef std::map<const Resource *, ManagedResource> ResourceMap;
+
+       LoadingPolicy policy;
+       bool async_loads;
+       mutable Mutex map_mutex;
+       ResourceMap resources;
+       LoadQueue queue;
+       UInt64 total_data_size;
+       UInt64 size_limit;
+       unsigned frame;
+       unsigned min_retain_frames;
+       unsigned max_retain_frames;
+       unsigned next_unload;
+       LoadingThread thread;
+
+public:
+       ResourceManager();
+       ~ResourceManager();
+
+       void set_loading_policy(LoadingPolicy);
+       void set_async_loads(bool);
+       void set_size_limit(UInt64);
+       void set_min_retain_frames(unsigned);
+       void set_max_retain_frames(unsigned);
+
+       void add_resource(Resource &);
+private:
+       const ManagedResource &get_managed_resource(const Resource &) const;
+       ManagedResource &get_managed_resource(const Resource &);
+public:
+       void *get_data_for_resource(const Resource &r) { return &get_managed_resource(r); }
+       void set_resource_location(Resource &, DataFile::Collection &, const std::string &);
+       void set_resource_location(Resource &, const ResourceLocation &);
+       const ResourceLocation *get_resource_location(const Resource &) const;
+       void load_resource(Resource &);
+       bool is_resource_loaded(const Resource &) const;
+       void resource_used(const Resource &);
+       void remove_resource(Resource &);
+
+       void observe_resource(const Resource &, ResourceObserver &);
+       void unobserve_resource(const Resource &, ResourceObserver &);
+
+       void tick();
+private:
+       void dispatch_work();
+       void unload_by_age();
+       void unload_by_size();
+public:
+       UInt64 get_total_data_size() const { return total_data_size; }
+
+private:
+       static bool age_order(ManagedResource *, ManagedResource *);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/resources/resourceobserver.h b/source/resources/resourceobserver.h
new file mode 100644 (file)
index 0000000..d1313d3
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef MSP_GL_RESOURCEOBSERVER_H_
+#define MSP_GL_RESOURCEOBSERVER_H_
+
+namespace Msp {
+namespace GL {
+
+class Resource;
+
+class ResourceObserver
+{
+protected:
+       ResourceObserver() { }
+public:
+       virtual ~ResourceObserver() { }
+
+       virtual void resource_loaded(Resource &) { }
+       virtual void resource_unloaded(Resource &) { }
+       virtual void resource_removed(Resource &) { }
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/resources/resources.cpp b/source/resources/resources.cpp
new file mode 100644 (file)
index 0000000..d52dbe3
--- /dev/null
@@ -0,0 +1,187 @@
+#include <msp/datafile/builtinsource.h>
+#include <msp/fs/utils.h>
+#include "animation.h"
+#include "armature.h"
+#include "camera.h"
+#include "font.h"
+#include "keyframe.h"
+#include "lighting.h"
+#include "material.h"
+#include "mesh.h"
+#include "object.h"
+#include "pipelinetemplate.h"
+#include "pose.h"
+#include "program.h"
+#include "programcompiler.h"
+#include "resourcemanager.h"
+#include "resources.h"
+#include "sampler.h"
+#include "technique.h"
+#include "texture1d.h"
+#include "texture2d.h"
+#include "texture2darray.h"
+#include "texturecube.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+void init_shaderlib(DataFile::BuiltinSource &);
+
+Resources::Resources():
+       default_tex_filter(Texture::can_generate_mipmap() ? LINEAR_MIPMAP_LINEAR : LINEAR),
+       default_tex_anisotropy(1.0f),
+       srgb_conversion(false),
+       resource_manager(0)
+{
+       add_type<Animation>().suffix(".anim").keyword("animation");
+       add_type<Armature>().suffix(".arma").keyword("armature");
+       add_type<Camera>().keyword("camera");
+       add_type<Font>().keyword("font");
+       add_type<KeyFrame>().suffix(".kframe").keyword("keyframe");
+       add_type<Lighting>().suffix(".lightn").keyword("lighting");
+       add_type<Material>().suffix(".mat").creator(&Resources::create_material);
+       add_type<Mesh>().keyword("mesh").creator(&Resources::create_mesh);
+       add_type<Object>().keyword("object");
+       add_type<PipelineTemplate>().suffix(".pipe").keyword("pipeline");
+       add_type<Pose>().keyword("pose");
+       add_type<Program>().keyword("shader").suffix(".glsl").creator(&Resources::create_program);
+       add_type<Sampler>().suffix(".samp").keyword("sampler");
+       add_type<Technique>().suffix(".tech").keyword("technique");
+       add_type<Texture1D>().base<Texture>().suffix(".tex1d").keyword("texture1d");
+       add_type<Texture2D>().base<Texture>().suffix(".tex2d").suffix(".png").suffix(".jpg").keyword("texture2d").creator(&Resources::create_texture2d);
+       add_type<Texture3D>().base<Texture>().suffix(".tex3d").keyword("texture3d");
+       add_type<TextureCube>().base<Texture>().suffix(".texcb").keyword("texture_cube");
+       add_type<Texture2DArray>().base<Texture>().suffix(".tex2da").keyword("texture2d_array");
+
+       add_source(get_builtins());
+}
+
+const DataFile::CollectionSource &Resources::get_builtins()
+{
+       static DataFile::BuiltinSource builtins;
+       bool init_done = false;
+
+       if(!init_done)
+       {
+               init_shaderlib(builtins);
+               init_done = true;
+       }
+
+       return builtins;
+}
+
+void Resources::set_default_texture_filter(TextureFilter tf)
+{
+       default_tex_filter = tf;
+}
+
+void Resources::set_default_texture_anisotropy(float a)
+{
+       default_tex_anisotropy = a;
+}
+
+void Resources::set_srgb_conversion(bool c)
+{
+       srgb_conversion = c;
+}
+
+void Resources::set_resource_manager(ResourceManager *m)
+{
+       resource_manager = m;
+}
+
+Material *Resources::create_material(const string &name)
+{
+       if(RefPtr<IO::Seekable> io = open_raw(name))
+       {
+               DataFile::Parser parser(*io, name);
+               Material::GenericLoader ldr(this);
+               ldr.load(parser);
+               return ldr.get_material();
+       }
+
+       return 0;
+}
+
+Mesh *Resources::create_mesh(const string &name)
+{
+       if(!resource_manager)
+               return 0;
+
+       if(RefPtr<IO::Seekable> io = open_raw(name))
+       {
+               RefPtr<Mesh> mesh = new Mesh(resource_manager);
+               resource_manager->set_resource_location(*mesh, *this, name);
+               return mesh.release();
+       }
+
+       return 0;
+}
+
+Texture2D *Resources::create_texture2d(const string &name)
+{
+       string ext = FS::extpart(name);
+       if(ext==".tex2d" && !resource_manager)
+               return 0;
+
+       if(RefPtr<IO::Seekable> io = open_raw(name))
+       {
+               Graphics::Image image;
+               if(!resource_manager)
+                       image.load_io(*io);
+
+               RefPtr<Texture2D> tex = new Texture2D(resource_manager);
+
+               if(ext==".tex2d")
+               {
+                       DataFile::Parser parser(*io, name);
+                       Texture2D::Loader ldr(*tex, *this);
+                       ldr.load(parser);
+               }
+               else
+               {
+                       Sampler &samp = tex->get_default_sampler();
+                       if(is_mipmapped(default_tex_filter))
+                       {
+                               tex->set_auto_generate_mipmap(true);
+                               samp.set_mag_filter(LINEAR);
+                       }
+                       else
+                               samp.set_mag_filter(default_tex_filter);
+                       samp.set_min_filter(default_tex_filter);
+                       samp.set_max_anisotropy(default_tex_anisotropy);
+               }
+
+               if(resource_manager)
+                       resource_manager->set_resource_location(*tex, *this, name);
+               else
+                       tex->image(image);
+               return tex.release();
+       }
+
+       return 0;
+}
+
+Program *Resources::create_program(const string &name)
+{
+       string ext = FS::extpart(name);
+       if(ext==".shader")
+               return 0;
+
+       if(RefPtr<IO::Seekable> io = open_raw(name))
+       {
+               ProgramCompiler compiler;
+               compiler.compile(*io, this, name);
+               RefPtr<Program> program = new Program;
+               compiler.add_shaders(*program);
+               program->link();
+               return program.release();
+       }
+
+       return 0;
+}
+
+} // namespace GL
+} // namespace Msp
diff --git a/source/resources/resources.h b/source/resources/resources.h
new file mode 100644 (file)
index 0000000..6f04a26
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef MSP_GL_RESOURCES_H_
+#define MSP_GL_RESOURCES_H_
+
+#include <msp/datafile/collection.h>
+#include "texture.h"
+
+namespace Msp {
+namespace GL {
+
+class Material;
+class Mesh;
+class Program;
+class ResourceManager;
+class Texture2D;
+
+/**
+A collection class for GL resources.  Most useful as a base class for an
+application-specific collection.
+*/
+class Resources: virtual public DataFile::Collection
+{
+private:
+       TextureFilter default_tex_filter;
+       float default_tex_anisotropy;
+       bool srgb_conversion;
+       ResourceManager *resource_manager;
+
+public:
+       Resources();
+
+       static const DataFile::CollectionSource &get_builtins();
+
+       void set_default_texture_filter(TextureFilter);
+       void set_default_texture_anisotropy(float);
+
+       /** Enables or disables sRGB conversion.  If enabled, textures and material
+       colors are converted from sRGB to linear color space when loaded. */
+       DEPRECATED void set_srgb_conversion(bool);
+
+       DEPRECATED bool get_srgb_conversion() const { return srgb_conversion; }
+
+       void set_resource_manager(ResourceManager *);
+
+protected:
+       Material *create_material(const std::string &);
+       Mesh *create_mesh(const std::string &);
+       Texture2D *create_texture2d(const std::string &);
+       Program *create_program(const std::string &);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
diff --git a/source/sampler.cpp b/source/sampler.cpp
deleted file mode 100644 (file)
index 5375d17..0000000
+++ /dev/null
@@ -1,332 +0,0 @@
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include <msp/gl/extensions/arb_sampler_objects.h>
-#include <msp/gl/extensions/arb_shadow.h>
-#include <msp/gl/extensions/ext_texture_filter_anisotropic.h>
-#include <msp/gl/extensions/ext_texture3d.h>
-#include <msp/strings/format.h>
-#include "error.h"
-#include "sampler.h"
-#include "texture.h"
-#include "texunit.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Sampler::Sampler():
-       owner(0)
-{
-       init();
-
-       Require _req(ARB_sampler_objects);
-       if(ARB_direct_state_access)
-               glCreateSamplers(1, &id);
-       else
-               glGenSamplers(1, &id);
-}
-
-Sampler::Sampler(const Texture &tex):
-       id(0),
-       owner(&tex)
-{
-       if(this!=&tex.get_default_sampler())
-               throw invalid_argument("Sampler::Sampler");
-
-       init();
-}
-
-void Sampler::init()
-{
-       min_filter = NEAREST_MIPMAP_LINEAR;
-       mag_filter = LINEAR;
-       max_anisotropy = 1.0f;
-       wrap_s = REPEAT;
-       wrap_t = REPEAT;
-       wrap_r = REPEAT;
-       compare = false;
-       cmp_func = LEQUAL;
-       dirty_params = 0;
-}
-
-void Sampler::update_parameter(int mask) const
-{
-       if(owner)
-       {
-               if(!owner->get_id())
-               {
-                       dirty_params |= mask;
-                       return;
-               }
-
-               if(!ARB_direct_state_access && TexUnit::current().get_texture()!=owner)
-               {
-                       TexUnit *unit = TexUnit::find_unit(owner);
-                       if(!unit)
-                       {
-                               dirty_params |= mask;
-                               return;
-                       }
-
-                       unit->bind();
-               }
-       }
-
-       if(mask&MIN_FILTER)
-               set_parameter_i(GL_TEXTURE_MIN_FILTER, min_filter);
-       if(mask&MAG_FILTER)
-               set_parameter_i(GL_TEXTURE_MAG_FILTER, mag_filter);
-       if(mask&MAX_ANISOTROPY)
-               set_parameter_f(GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy);
-       if(mask&WRAP_S)
-               set_parameter_i(GL_TEXTURE_WRAP_S, wrap_s);
-       if(mask&WRAP_T)
-               set_parameter_i(GL_TEXTURE_WRAP_T, wrap_t);
-       if(mask&WRAP_R)
-               set_parameter_i(GL_TEXTURE_WRAP_R, wrap_r);
-       if(mask&COMPARE)
-       {
-               set_parameter_i(GL_TEXTURE_COMPARE_MODE, (compare ? GL_COMPARE_R_TO_TEXTURE : GL_NONE));
-               if(compare)
-                       set_parameter_i(GL_TEXTURE_COMPARE_FUNC, cmp_func);
-       }
-}
-
-void Sampler::set_parameter_i(unsigned param, int value) const
-{
-       if(id)
-               glSamplerParameteri(id, param, value);
-       else if(ARB_direct_state_access)
-               glTextureParameteri(owner->get_id(), param, value);
-       else
-               glTexParameteri(owner->get_target(), param, value);
-}
-
-void Sampler::set_parameter_f(unsigned param, float value) const
-{
-       if(id)
-               glSamplerParameterf(id, param, value);
-       else if(ARB_direct_state_access)
-               glTextureParameterf(owner->get_id(), param, value);
-       else
-               glTexParameterf(owner->get_target(), param, value);
-}
-
-void Sampler::set_min_filter(TextureFilter f)
-{
-       min_filter = f;
-       update_parameter(MIN_FILTER);
-}
-
-void Sampler::set_mag_filter(TextureFilter f)
-{
-       mag_filter = f;
-       update_parameter(MAG_FILTER);
-}
-
-void Sampler::set_filter(TextureFilter f)
-{
-       set_min_filter(f);
-       set_mag_filter(f==NEAREST ? NEAREST : LINEAR);
-}
-
-void Sampler::set_max_anisotropy(float a)
-{
-       if(a<1.0f)
-               throw invalid_argument("Sampler::set_max_anisotropy");
-       else if(a>1.0f)
-               static Require _req(EXT_texture_filter_anisotropic);
-       max_anisotropy = a;
-       if(EXT_texture_filter_anisotropic)
-               update_parameter(MAX_ANISOTROPY);
-}
-
-void Sampler::set_wrap(TextureWrap w)
-{
-       set_wrap_s(w);
-       set_wrap_t(w);
-       if(EXT_texture3D)
-               set_wrap_r(w);
-}
-
-void Sampler::set_wrap_s(TextureWrap w)
-{
-       wrap_s = w;
-       update_parameter(WRAP_S);
-}
-
-void Sampler::set_wrap_t(TextureWrap w)
-{
-       wrap_t = w;
-       update_parameter(WRAP_T);
-}
-
-void Sampler::set_wrap_r(TextureWrap w)
-{
-       static Require _req(EXT_texture3D);
-       wrap_r = w;
-       update_parameter(WRAP_R);
-}
-
-void Sampler::disable_compare()
-{
-       compare = false;
-       update_parameter(COMPARE);
-}
-
-void Sampler::set_compare(Predicate f)
-{
-       static Require _req(ARB_shadow);
-       compare = true;
-       cmp_func = f;
-       update_parameter(COMPARE);
-}
-
-void Sampler::bind_to(unsigned i) const
-{
-       TexUnit &unit = TexUnit::get_unit(i);
-       if(owner && owner!=unit.get_texture())
-               throw invalid_operation("Sampler::bind_to");
-
-       const Sampler *cur = unit.get_sampler();
-       if(unit.set_sampler(this))
-       {
-               if(!owner || (cur && cur->id))
-                       glBindSampler(i, id);
-
-               if(dirty_params)
-               {
-                       update_parameter(dirty_params);
-                       dirty_params = 0;
-               }
-       }
-}
-
-const Sampler *Sampler::current(unsigned i)
-{
-       return TexUnit::get_unit(i).get_sampler();
-}
-
-void Sampler::unbind_from(unsigned i)
-{
-       TexUnit &unit = TexUnit::get_unit(i);
-       const Sampler *cur = unit.get_sampler();
-       if(unit.set_sampler(0) && cur->id)
-               glBindSampler(i, 0);
-}
-
-void Sampler::unload()
-{
-       if(owner && !owner->get_id())
-       {
-               if(min_filter!=NEAREST_MIPMAP_LINEAR)
-                       dirty_params |= MIN_FILTER;
-               if(mag_filter!=LINEAR)
-                       dirty_params |= MAG_FILTER;
-               if(max_anisotropy!=1.0f)
-                       dirty_params |= MAX_ANISOTROPY;
-               if(wrap_s!=REPEAT)
-                       dirty_params |= WRAP_S;
-               if(wrap_t!=REPEAT)
-                       dirty_params |= WRAP_T;
-               if(wrap_r!=REPEAT)
-                       dirty_params |= WRAP_R;
-               if(compare || cmp_func!=LEQUAL)
-                       dirty_params |= COMPARE;
-       }
-}
-
-
-Sampler::Loader::Loader(Sampler &s):
-       DataFile::ObjectLoader<Sampler>(s)
-{
-       add("filter", &Loader::filter);
-       add("mag_filter", &Loader::mag_filter);
-       add("max_anisotropy", &Loader::max_anisotropy);
-       add("min_filter", &Loader::min_filter);
-       add("wrap", &Loader::wrap);
-       add("wrap_r", &Loader::wrap_r);
-       add("wrap_s", &Loader::wrap_s);
-       add("wrap_t", &Loader::wrap_t);
-}
-
-void Sampler::Loader::filter(TextureFilter f)
-{
-       obj.set_filter(f);
-}
-
-void Sampler::Loader::mag_filter(TextureFilter f)
-{
-       obj.set_mag_filter(f);
-}
-
-void Sampler::Loader::max_anisotropy(float a)
-{
-       obj.set_max_anisotropy(a);
-}
-
-void Sampler::Loader::min_filter(TextureFilter f)
-{
-       obj.set_min_filter(f);
-}
-
-void Sampler::Loader::wrap(TextureWrap w)
-{
-       obj.set_wrap(w);
-}
-
-void Sampler::Loader::wrap_r(TextureWrap w)
-{
-       obj.set_wrap_r(w);
-}
-
-void Sampler::Loader::wrap_s(TextureWrap w)
-{
-       obj.set_wrap_s(w);
-}
-
-void Sampler::Loader::wrap_t(TextureWrap w)
-{
-       obj.set_wrap_t(w);
-}
-
-
-bool is_mipmapped(TextureFilter filter)
-{
-       return (filter==NEAREST_MIPMAP_NEAREST || filter==NEAREST_MIPMAP_LINEAR ||
-               filter==LINEAR_MIPMAP_NEAREST || filter==LINEAR_MIPMAP_LINEAR);
-}
-
-void operator>>(const LexicalConverter &c, TextureFilter &tf)
-{
-       if(c.get()=="NEAREST")
-               tf = NEAREST;
-       else if(c.get()=="LINEAR")
-               tf = LINEAR;
-       else if(c.get()=="NEAREST_MIPMAP_NEAREST")
-               tf = NEAREST_MIPMAP_NEAREST;
-       else if(c.get()=="NEAREST_MIPMAP_LINEAR")
-               tf = NEAREST_MIPMAP_LINEAR;
-       else if(c.get()=="LINEAR_MIPMAP_NEAREST")
-               tf = LINEAR_MIPMAP_NEAREST;
-       else if(c.get()=="LINEAR_MIPMAP_LINEAR")
-               tf = LINEAR_MIPMAP_LINEAR;
-       else
-               throw lexical_error(format("conversion of '%s' to TextureFilter", c.get()));
-}
-
-void operator>>(const LexicalConverter &c, TextureWrap &tw)
-{
-       if(c.get()=="REPEAT")
-               tw = REPEAT;
-       else if(c.get()=="CLAMP_TO_EDGE")
-               tw = CLAMP_TO_EDGE;
-       else if(c.get()=="MIRRORED_REPEAT")
-               tw = MIRRORED_REPEAT;
-       else
-               throw lexical_error(format("conversion of '%s' to TextureWrap", c.get()));
-}
-
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/sampler.h b/source/sampler.h
deleted file mode 100644 (file)
index e1e9bec..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-#ifndef MSP_GL_SAMPLER_H_
-#define MSP_GL_SAMPLER_H_
-
-#include <msp/datafile/objectloader.h>
-#include "gl.h"
-#include "predicate.h"
-
-namespace Msp {
-namespace GL {
-
-enum TextureFilter
-{
-       /// No filtering
-       NEAREST = GL_NEAREST,
-
-       /// Bilinear filtering
-       LINEAR = GL_LINEAR,
-
-       /// Mipmapping without filtering
-       NEAREST_MIPMAP_NEAREST = GL_NEAREST_MIPMAP_NEAREST,
-
-       /// Linear filtering between two mipmap levels
-       NEAREST_MIPMAP_LINEAR = GL_NEAREST_MIPMAP_LINEAR,
-
-       /// Bilinear filtering on the closest mipmap level
-       LINEAR_MIPMAP_NEAREST = GL_LINEAR_MIPMAP_NEAREST,
-
-       /// Trilinear filtering between two mipmap levels
-       LINEAR_MIPMAP_LINEAR = GL_LINEAR_MIPMAP_LINEAR
-};
-
-
-enum TextureWrap
-{
-       /// Tile the texture infinitely
-       REPEAT = GL_REPEAT,
-
-       /// Extend the texels at the edge of the texture to infinity
-       CLAMP_TO_EDGE = GL_CLAMP_TO_EDGE,
-
-       /// Tile the texture, with every other repetition mirrored
-       MIRRORED_REPEAT = GL_MIRRORED_REPEAT
-};
-
-class Texture;
-
-
-/**
-Samplers are used to access texture data in shaders.  To use a sampler with a
-texture, bind it to the same texture unit.  Each texture has a default sampler
-which is used if no external sampler is bound.
-
-A texture is generally rendered at a size that's either smaller or larger than
-its native size, so that the texture coordinates do not exactly correspond to
-the texels of the texture.  The kind of filtering used, if any, is determined
-by the minification and magnification filter parameters.  The default is LINEAR
-for magnification and NEAREST_MIPMAP_LINEAR for minification.
-
-If texture coordinates fall outside of the principal range of the texture,
-wrapping is applied.  The default for all directions is REPEAT.
-*/
-class Sampler
-{
-public:
-       class Loader: public DataFile::ObjectLoader<Sampler>
-       {
-       public:
-               Loader(Sampler &);
-       private:
-               void init();
-
-               void filter(TextureFilter);
-               void mag_filter(TextureFilter);
-               void max_anisotropy(float);
-               void min_filter(TextureFilter);
-               void wrap(TextureWrap);
-               void wrap_r(TextureWrap);
-               void wrap_s(TextureWrap);
-               void wrap_t(TextureWrap);
-       };
-
-private:
-       enum ParameterMask
-       {
-               MIN_FILTER = 1,
-               MAG_FILTER = 2,
-               MAX_ANISOTROPY = 4,
-               WRAP_S = 8,
-               WRAP_T = 16,
-               WRAP_R = 32,
-               COMPARE = 64
-       };
-
-       unsigned id;
-       const Texture *owner;
-       TextureFilter min_filter;
-       TextureFilter mag_filter;
-       float max_anisotropy;
-       TextureWrap wrap_s;
-       TextureWrap wrap_t;
-       TextureWrap wrap_r;
-       bool compare;
-       Predicate cmp_func;
-       mutable int dirty_params;
-
-public:
-       Sampler();
-       Sampler(const Texture &);
-private:
-       void init();
-
-       void update_parameter(int) const;
-       void set_parameter_i(unsigned, int) const;
-       void set_parameter_f(unsigned, float) const;
-
-public:
-       void set_min_filter(TextureFilter);
-       void set_mag_filter(TextureFilter);
-
-       /** Sets filter for both minification and magnification.  If a mipmapping
-       filter is specified, LINEAR is used for magnification. */
-       void set_filter(TextureFilter);
-
-       TextureFilter get_min_filter() const { return min_filter; }
-       TextureFilter get_mag_filter() const { return mag_filter; }
-
-       void set_max_anisotropy(float);
-       float get_max_anisotropy() const { return max_anisotropy; }
-
-       /** Sets the wrapping mode for all coordinates. */
-       void set_wrap(TextureWrap);
-
-       void set_wrap_s(TextureWrap);
-       void set_wrap_t(TextureWrap);
-       void set_wrap_r(TextureWrap);
-
-       /** Disables depth comparison. */
-       void disable_compare();
-
-       /** Enables depth comparison and sets the compare function.  Only has an
-       effect when used with a depth texture.  When depth comparison is enabled,
-       the third component of the texture coordinate is compared against the texel
-       value, and the result is returned as the texture sample.*/
-       void set_compare(Predicate);
-
-       bool is_compare_enabled() const { return compare; }
-       Predicate get_compare_function() const { return cmp_func; }
-
-       void bind() const { bind_to(0); }
-       void bind_to(unsigned) const;
-
-       static const Sampler *current(unsigned = 0);
-       static void unbind() { unbind_from(0); }
-       static void unbind_from(unsigned);
-
-       void unload();
-};
-
-
-bool is_mipmapped(TextureFilter);
-
-void operator>>(const LexicalConverter &, TextureFilter &);
-void operator>>(const LexicalConverter &, TextureWrap &);
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/scene.cpp b/source/scene.cpp
deleted file mode 100644 (file)
index e6c62e8..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-#include <msp/datafile/collection.h>
-#include "animatedobject.h"
-#include "camera.h"
-#include "renderer.h"
-#include "scene.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Scene::~Scene()
-{
-       for(vector<Renderable *>::iterator i=owned_data.begin(); i!=owned_data.end(); ++i)
-               delete *i;
-}
-
-bool Scene::setup_frustum(const Renderer &renderer) const
-{
-       const Camera *camera = renderer.get_camera();
-       if(!camera)
-               return false;
-
-       culling_matrix = renderer.get_matrix();
-
-       if(camera->is_orthographic())
-       {
-               float h = camera->get_orthographic_height();
-               frustum_edges[0] = Vector4(0, 1, 0, -h);
-               frustum_edges[1] = Vector4(0, -1, 0, -h);
-
-               float w = camera->get_orthographic_width();
-               frustum_edges[2] = Vector4(1, 0, 0, -w);
-               frustum_edges[3] = Vector4(-1, 0, 0, -w);
-       }
-       else
-       {
-               float y = tan(camera->get_field_of_view()/2.0f);
-               float s = sqrt(y*y+1);
-               frustum_edges[0] = Vector4(0, 1/s, y/s, 0);
-               frustum_edges[1] = Vector4(0, -1/s, y/s, 0);
-
-               float x = y*camera->get_aspect_ratio();
-               s = sqrt(x*x+1);
-               frustum_edges[2] = Vector4(1/s, 0, x/s, 0);
-               frustum_edges[3] = Vector4(-1/s, 0, x/s, 0);
-       }
-
-       frustum_edges[4] = Vector4(0, 0, -1, -camera->get_far_clip());
-       frustum_edges[5] = Vector4(0, 0, 1, camera->get_near_clip());
-
-       return true;
-}
-
-bool Scene::frustum_cull(const Renderable &renderable) const
-{
-       const Matrix *matrix = renderable.get_matrix();
-       const Geometry::BoundingSphere<float, 3> *bsphere = renderable.get_bounding_sphere();
-       if(!matrix || !bsphere)
-               return false;
-
-       Vector4 center = culling_matrix*(*matrix*compose(bsphere->get_center(), 1.0f));
-       Vector3 x_axis = (matrix->column(0)*bsphere->get_radius()).slice<3>(0);
-       float radius_sq = inner_product(x_axis, x_axis);
-
-       for(unsigned i=0; i<6; ++i)
-       {
-               float distance = inner_product(center, frustum_edges[i]);
-               if(distance>0 && distance*distance>radius_sq)
-                       return true;
-       }
-
-       return false;
-}
-
-
-Scene::Loader::Loader(Scene &s, Collection &c):
-       DataFile::CollectionObjectLoader<Scene>(s, &c),
-       content(0)
-{
-       init();
-}
-
-Scene::Loader::Loader(Scene &s, Collection &c, ContentMap &m):
-       DataFile::CollectionObjectLoader<Scene>(s, &c),
-       content(&m)
-{
-       init();
-}
-
-void Scene::Loader::init()
-{
-       add("object", &Loader::object);
-       add("object", &Loader::object_tagged);
-}
-
-void Scene::Loader::object(const string &n)
-{
-       object_tagged(n, string());
-}
-
-void Scene::Loader::object_tagged(const string &n, const string &t)
-{
-       RefPtr<AnimatedObject> anob = new AnimatedObject(get_collection().get<GL::Object>(n));
-       load_sub(*anob);
-       obj.add(*anob);
-       if(content && !t.empty())
-               (*content)[t] = anob.get();
-       obj.owned_data.push_back(anob.release());
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/scene.h b/source/scene.h
deleted file mode 100644 (file)
index 639a467..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-#ifndef MSP_GL_SCENE_H_
-#define MSP_GL_SCENE_H_
-
-#include <vector>
-#include <msp/datafile/objectloader.h>
-#include "matrix.h"
-#include "renderable.h"
-#include "vector.h"
-
-namespace Msp {
-namespace GL {
-
-/**
-Scenes are containers for other Renderables.  This is a base class that can't
-be instantiated.  Examples of available Scene types are SimpleScene,
-InstancedScene and OrderedScene.
-*/
-class Scene: public Renderable
-{
-public:
-       class Loader: public DataFile::CollectionObjectLoader<Scene>
-       {
-       public:
-               typedef std::map<std::string, Renderable *> ContentMap;
-
-       private:
-               ContentMap *content;
-
-       public:
-               Loader(Scene &, Collection &);
-               Loader(Scene &, Collection &, ContentMap &);
-       private:
-               void init();
-
-               void object(const std::string &);
-               void object_tagged(const std::string &, const std::string &);
-       };
-
-protected:
-       // XXX If a loaded renderable is removed from the scene it needs to be removed from here as well
-       std::vector<Renderable *> owned_data;
-       mutable Matrix culling_matrix;
-       mutable Vector4 frustum_edges[6];
-
-       Scene() { }
-private:
-       Scene(const Scene &);
-       Scene &operator=(const Scene &);
-public:
-       virtual ~Scene();
-
-       virtual void add(Renderable &) = 0;
-       virtual void remove(Renderable &) = 0;
-
-protected:
-       bool setup_frustum(const Renderer &) const;
-       bool frustum_cull(const Renderable &) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/shader.cpp b/source/shader.cpp
deleted file mode 100644 (file)
index 02dcc0d..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-#include <msp/gl/extensions/arb_fragment_shader.h>
-#include <msp/gl/extensions/arb_geometry_shader4.h>
-#include <msp/gl/extensions/arb_shader_objects.h>
-#include <msp/gl/extensions/arb_vertex_shader.h>
-#include <msp/io/print.h>
-#include "error.h"
-#include "misc.h"
-#include "shader.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Shader::Shader(GLenum t)
-{
-       init(t);
-}
-
-Shader::Shader(GLenum t, const string &src)
-{
-       init(t);
-
-       source(src);
-       compile();
-}
-
-void Shader::init(GLenum t)
-{
-       static Require _req_base(ARB_shader_objects);
-       compiled = false;
-
-       if(t==GL_FRAGMENT_SHADER)
-               static Require _req(ARB_fragment_shader);
-       else if(t==GL_VERTEX_SHADER)
-               static Require _req(ARB_vertex_shader);
-       else if(t==GL_GEOMETRY_SHADER)
-               static Require _req(ARB_geometry_shader4);
-
-       id = glCreateShader(t);
-}
-
-Shader::~Shader()
-{
-       glDeleteShader(id);
-}
-
-void Shader::source(unsigned count, const char **str, const int *len)
-{
-       glShaderSource(id, count, str, len);
-}
-
-void Shader::source(const string &str)
-{
-       source(str.data(), str.size());
-}
-
-void Shader::source(const char *str, int len)
-{
-       source(1, &str, &len);
-}
-
-void Shader::compile()
-{
-       glCompileShader(id);
-       compiled = get_shader_i(id, GL_COMPILE_STATUS);
-       if(!compiled)
-               throw compile_error(get_info_log());
-
-#ifdef DEBUG
-       string info_log = get_info_log();
-       if(!info_log.empty())
-               IO::print("Shader compile info log:\n%s", info_log);
-#endif
-}
-
-string Shader::get_info_log() const
-{
-       GLsizei len = get_shader_i(id, GL_INFO_LOG_LENGTH);
-       string log(len+1, 0);
-       glGetShaderInfoLog(id, len+1, &len, &log[0]);
-       log.erase(len);
-       return log;
-}
-
-
-VertexShader::VertexShader():
-       Shader(GL_VERTEX_SHADER)
-{ }
-
-VertexShader::VertexShader(const string &src):
-       Shader(GL_VERTEX_SHADER, src)
-{ }
-
-
-FragmentShader::FragmentShader():
-       Shader(GL_FRAGMENT_SHADER)
-{ }
-
-FragmentShader::FragmentShader(const string &src):
-       Shader(GL_FRAGMENT_SHADER, src)
-{ }
-
-
-GeometryShader::GeometryShader():
-       Shader(GL_GEOMETRY_SHADER)
-{ }
-
-GeometryShader::GeometryShader(const string &src):
-       Shader(GL_GEOMETRY_SHADER, src)
-{ }
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/shader.h b/source/shader.h
deleted file mode 100644 (file)
index bab11a7..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-#ifndef MSP_GL_SHADER_H_
-#define MSP_GL_SHADER_H_
-
-#include <string>
-#include "gl.h"
-
-namespace Msp {
-namespace GL {
-
-/**
-A single shader stage.  Shaders must be attached to a Program to be used.
-
-This class can't be instantiated directly.  Use one of the VertexShader and
-FragmentShader classes to create Shaders.
-*/
-class Shader
-{
-private:
-       unsigned id;
-       bool compiled;
-
-protected:
-       Shader(GLenum t);
-       Shader(GLenum t, const std::string &);
-private:
-       void init(GLenum);
-public:
-       virtual ~Shader();
-
-       void source(unsigned count, const char **str, const int *len);
-       void source(const std::string &str);
-       void source(const char *str, int len);
-       void compile();
-       unsigned get_id() const { return id; }
-       bool is_compiled() const { return compiled; }
-       std::string get_info_log() const;
-};
-
-
-class VertexShader: public Shader
-{
-public:
-       VertexShader();
-       VertexShader(const std::string &);
-};
-
-
-class FragmentShader: public Shader
-{
-public:
-       FragmentShader();
-       FragmentShader(const std::string &);
-};
-
-
-class GeometryShader: public Shader
-{
-public:
-       GeometryShader();
-       GeometryShader(const std::string &);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/shadowmap.cpp b/source/shadowmap.cpp
deleted file mode 100644 (file)
index eaf6e13..0000000
+++ /dev/null
@@ -1,126 +0,0 @@
-#include <cmath>
-#include <cstdlib>
-#include "camera.h"
-#include "light.h"
-#include "renderer.h"
-#include "scene.h"
-#include "shadowmap.h"
-#include "tests.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-WeakPtr<Sampler> ShadowMap::shadow_sampler;
-
-ShadowMap::ShadowMap(unsigned s, Renderable &r, const Light &l):
-       Effect(r),
-       size(s),
-       light(l),
-       radius(1),
-       depth_bias(4),
-       rendered(false)
-{
-       sampler = shadow_sampler;
-       if(!sampler)
-       {
-               sampler = new Sampler;
-               sampler->set_filter(LINEAR);
-               sampler->set_compare(LEQUAL);
-               sampler->set_wrap(CLAMP_TO_EDGE);
-               shadow_sampler = sampler;
-       }
-       depth_buf.storage(DEPTH_COMPONENT32F, size, size, 1);
-       fbo.attach(DEPTH_ATTACHMENT, depth_buf, 0);
-       fbo.require_complete();
-
-       set_darkness(0.7);
-}
-
-void ShadowMap::set_target(const Vector3 &t, float r)
-{
-       target = t;
-       radius = r;
-}
-
-void ShadowMap::set_darkness(float d)
-{
-       if(d<0.0f || d>1.0f)
-               throw invalid_argument("ShadowMap::set_darkness");
-
-       shdata.uniform("shadow_darkness", d);
-}
-
-void ShadowMap::set_depth_bias(float b)
-{
-       if(b<0.0f)
-               throw invalid_argument("ShadowMap::set_depth_bias");
-
-       depth_bias = b;
-}
-
-void ShadowMap::setup_frame(Renderer &renderer)
-{
-       if(rendered)
-               return;
-
-       rendered = true;
-       renderable.setup_frame(renderer);
-
-       Camera camera;
-       camera.set_object_matrix(*light.get_matrix());
-       camera.set_position(target);
-       // TODO support point and spot lights with a frustum projection.
-       // Omnidirectional lights also need a cube shadow map.
-       camera.set_orthographic(radius*2, radius*2);
-       camera.set_depth_clip(-radius, radius);
-
-       shadow_matrix = camera.get_object_matrix();
-       shadow_matrix.scale(radius*2, radius*2, -radius*2);
-       shadow_matrix.translate(-0.5, -0.5, depth_bias/size-0.5);
-       shadow_matrix.invert();
-
-       BindRestore bind_fbo(fbo);
-       Bind bind_depth(DepthTest::lequal());
-       fbo.clear(DEPTH_BUFFER_BIT);
-
-       Renderer::Push push(renderer);
-       renderer.set_camera(camera);
-
-       renderer.render(renderable, "shadow");
-}
-
-void ShadowMap::finish_frame()
-{
-       renderable.finish_frame();
-       rendered = false;
-}
-
-void ShadowMap::render(Renderer &renderer, const Tag &tag) const
-{
-       if(!enabled_passes.count(tag))
-               return renderer.render(renderable, tag);
-
-       Renderer::Push _push_rend(renderer);
-
-       unsigned unit = renderer.allocate_effect_texunit();
-       int iunit = unit;
-       shdata.uniform("shadow_map", iunit);
-
-       Bind _bind_sampler(*sampler, unit);
-       Bind _bind_depth(depth_buf, unit);
-
-       if(const Camera *camera = renderer.get_camera())
-               /* Multiply by camera's object matrix to form a matrix that transforms
-               from eye space to shadow space. */
-               shdata.uniform("shd_eye_matrix", shadow_matrix*camera->get_object_matrix());
-       else
-               shdata.uniform("shd_eye_matrix", shadow_matrix);
-
-       renderer.add_shader_data(shdata);
-       renderer.render(renderable, tag);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/shadowmap.h b/source/shadowmap.h
deleted file mode 100644 (file)
index e142fc8..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-#ifndef MSP_GL_SHADOWMAP_H_
-#define MSP_GL_SHADOWMAP_H_
-
-#include "effect.h"
-#include "framebuffer.h"
-#include "programdata.h"
-#include "texture2d.h"
-#include "vector.h"
-
-namespace Msp {
-namespace GL {
-
-class Light;
-class Scene;
-
-/**
-Creates shadows on a Scene through a shadow map texture.  In the preparation
-phase, the scene is rendered to a depth texture from the point of view of the
-lightsource.  This texture is then used in the rendering phase together with
-texture coordinate generation to determine whether each fragment is lit.
-*/
-class ShadowMap: public Effect
-{
-private:
-       unsigned size;
-       const Light &light;
-       Framebuffer fbo;
-       Matrix shadow_matrix;
-       Texture2D depth_buf;
-       RefPtr<Sampler> sampler;
-       Vector3 target;
-       float radius;
-       float depth_bias;
-       mutable ProgramData shdata;
-       bool rendered;
-
-       static WeakPtr<Sampler> shadow_sampler;
-
-public:
-       ShadowMap(unsigned, Renderable &, const Light &);
-
-       /** Sets the ShadowMap target point and radius.  The transformation matrix is
-       computed so that a sphere with the specified parameters will be completely
-       covered by the ShadowMap. */
-       void set_target(const Vector3 &, float);
-
-       /** Sets the darkness of shadows.  Must be in the range between 0.0 and 1.0,
-       inclusive.  Only usable with shaders, and provided through the
-       shadow_darkness uniform. */
-       void set_darkness(float);
-
-       /** Sets a distance beyond objects from which the shadow starts.  Expressed
-       in pixel-sized units.  Must be positive; values less than 1.0 are not
-       recommended.  Larger values produce less depth artifacts, but may prevent
-       thin objects from casting shadows on nearby sufraces. */
-       void set_depth_bias(float);
-
-       const Texture2D &get_depth_texture() const { return depth_buf; }
-       const Matrix &get_shadow_matrix() const { return shadow_matrix; }
-
-       virtual void setup_frame(Renderer &);
-       virtual void finish_frame();
-
-       virtual void render(Renderer &, const Tag & = Tag()) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/simplescene.cpp b/source/simplescene.cpp
deleted file mode 100644 (file)
index d1fd3c1..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-#include "renderer.h"
-#include "simplescene.h"
-
-namespace Msp {
-namespace GL {
-
-void SimpleScene::add(Renderable &r)
-{
-       // Add to cache as well if the cache is valid
-       if(renderables.insert(&r).second && !cache.empty())
-               cache.push_back(&r);
-}
-
-void SimpleScene::remove(Renderable &r)
-{
-       renderables.erase(&r);
-       cache.clear();
-}
-
-void SimpleScene::populate_cache() const
-{
-       if(cache.empty() && !renderables.empty())
-       {
-               cache.reserve(renderables.size());
-               cache.insert(cache.end(), renderables.begin(), renderables.end());
-       }
-}
-
-void SimpleScene::setup_frame(Renderer &renderer)
-{
-       populate_cache();
-       for(RenderableArray::const_iterator i=cache.begin(); i!=cache.end(); ++i)
-               (*i)->setup_frame(renderer);
-}
-
-void SimpleScene::finish_frame()
-{
-       for(RenderableArray::const_iterator i=cache.begin(); i!=cache.end(); ++i)
-               (*i)->finish_frame();
-}
-
-void SimpleScene::render(Renderer &renderer, const Tag &tag) const
-{
-       populate_cache();
-       if(setup_frustum(renderer))
-       {
-               for(RenderableArray::const_iterator i=cache.begin(); i!=cache.end(); ++i)
-                       if(!frustum_cull(**i))
-                               renderer.render(**i, tag);
-       }
-       else
-       {
-               for(RenderableArray::const_iterator i=cache.begin(); i!=cache.end(); ++i)
-                       renderer.render(**i, tag);
-       }
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/simplescene.h b/source/simplescene.h
deleted file mode 100644 (file)
index e56aeb0..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-#ifndef MSP_GL_SIMPLESCENE_H_
-#define MSP_GL_SIMPLESCENE_H_
-
-#include <set>
-#include "scene.h"
-
-namespace Msp {
-namespace GL {
-
-/**
-A simple yet efficient scene.  Rendering order is unspecified.
-*/
-class SimpleScene: public Scene
-{
-private:
-       typedef std::set<Renderable *> RenderableSet;
-       typedef std::vector<Renderable *> RenderableArray;
-
-       RenderableSet renderables;
-       mutable RenderableArray cache;
-
-public:
-       virtual void add(Renderable &);
-       virtual void remove(Renderable &);
-
-private:
-       void populate_cache() const;
-
-public:
-       virtual void setup_frame(Renderer &);
-       virtual void finish_frame();
-
-       virtual void render(Renderer &, const Tag & = Tag()) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/slot.cpp b/source/slot.cpp
deleted file mode 100644 (file)
index 07dabf0..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#include <msp/gl/renderer.h>
-#include "slot.h"
-
-namespace Msp {
-namespace GL {
-
-Slot::Slot():
-       renderable(0)
-{ }
-
-void Slot::set(Renderable *r)
-{
-       renderable = r;
-}
-
-const Matrix *Slot::get_matrix() const
-{
-       return renderable ? renderable->get_matrix() : 0;
-}
-
-const Geometry::BoundingSphere<float, 3> *Slot::get_bounding_sphere() const
-{
-       return renderable ? renderable->get_bounding_sphere() : 0;
-}
-
-void Slot::setup_frame(Renderer &renderer)
-{
-       if(renderable)
-               renderable->setup_frame(renderer);
-}
-
-void Slot::finish_frame()
-{
-       if(renderable)
-               renderable->finish_frame();
-}
-
-void Slot::render(Renderer &renderer, const Tag &tag) const
-{
-       if(renderable)
-               renderer.render(*renderable, tag);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/slot.h b/source/slot.h
deleted file mode 100644 (file)
index ad86301..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef MSP_GL_SLOT_H_
-#define MSP_GL_SLOT_H_
-
-#include "renderable.h"
-
-namespace Msp {
-namespace GL {
-
-/**
-A container for a single renderable.  Can be used if a part of the scene graph
-needs to be switched without affecting the rest.
-*/
-class Slot: public Renderable
-{
-private:
-       Renderable *renderable;
-
-public:
-       Slot();
-
-       void set(Renderable *);
-       Renderable *get() const { return renderable; }
-
-       virtual const Matrix *get_matrix() const;
-       virtual const Geometry::BoundingSphere<float, 3> *get_bounding_sphere() const;
-       virtual void setup_frame(Renderer &);
-       virtual void finish_frame();
-       virtual void render(Renderer &, const Tag &) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/sphere.cpp b/source/sphere.cpp
deleted file mode 100644 (file)
index dd27fad..0000000
+++ /dev/null
@@ -1,261 +0,0 @@
-#define _USE_MATH_DEFINES
-#include <cmath>
-#include <vector>
-#include "primitivebuilder.h"
-#include "sphere.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-UvSphereBuilder::UvSphereBuilder(float r, unsigned s, unsigned n):
-       radius(r),
-       segments(s),
-       rings(n)
-{
-       if(segments<3)
-               segments = 3;
-
-       if(rings==0)
-               rings = (segments+1)/2;
-       else if(rings<2)
-               rings = 2;
-}
-
-void UvSphereBuilder::build(PrimitiveBuilder &builder) const
-{
-       float u_scale = 1.0f/segments;
-       float v_scale = 1.0f/rings;
-       adjust_texture_scale(u_scale, v_scale, radius*M_PI*2, radius*M_PI);
-
-       for(unsigned i=0; i<=rings; ++i)
-       {
-               float av = i*M_PI/rings-M_PI/2;
-               float cv = cos(av);
-               float sv = sin(av);
-
-               for(unsigned j=0; j<=segments; ++j)
-               {
-                       float au = j*M_PI*2/segments;
-                       float cu = cos(au);
-                       float su = sin(au);
-
-                       builder.normal(cv*cu, cv*su, sv);
-                       builder.texcoord(j*u_scale, i*v_scale);
-                       if(generate_tbn)
-                       {
-                               builder.tangent(-su, cu, 0);
-                               builder.binormal(-sv*cu, -sv*su, cv);
-                       }
-                       builder.vertex(cv*cu*radius, cv*su*radius, sv*radius);
-               }
-       }
-
-       builder.begin(TRIANGLES);
-       for(unsigned j=0; j<segments; ++j)
-       {
-               builder.element(j);
-               builder.element(segments+2+j);
-               builder.element(segments+1+j);
-       }
-       unsigned top = (rings+1)*(segments+1)-1;
-       for(unsigned j=0; j<segments; ++j)
-       {
-               builder.element(top-j);
-               builder.element(top-(segments+2+j));
-               builder.element(top-(segments+1+j));
-       }
-       builder.end();
-
-       for(unsigned i=1; i<rings; ++i)
-       {
-               builder.begin(TRIANGLE_STRIP);
-               unsigned base = i*(segments+1);
-               for(unsigned j=0; j<=segments; ++j)
-               {
-                       builder.element(base+segments+1+j);
-                       builder.element(base+j);
-               }
-               builder.end();
-       }
-}
-
-
-// https://en.wikipedia.org/wiki/Regular_icosahedron#Cartesian_coordinates
-float IcoSphereBuilder::base_vertices[12*3] =
-{
-        0.0f,       -0.5257311f, -0.8506508f,
-        0.0f,        0.5257311f, -0.8506508f,
-        0.0f,       -0.5257311f,  0.8506508f,
-        0.0f,        0.5257311f,  0.8506508f,
-       -0.8506508f,  0.0f,       -0.5257311f,
-       -0.8506508f,  0.0f,        0.5257311f,
-        0.8506508f,  0.0f,       -0.5257311f,
-        0.8506508f,  0.0f,        0.5257311f,
-       -0.5257311f, -0.8506508f,  0.0f,
-        0.5257311f, -0.8506508f,  0.0f,
-       -0.5257311f,  0.8506508f,  0.0f,
-        0.5257311f,  0.8506508f,  0.0f
-};
-
-unsigned IcoSphereBuilder::base_triangles[20*3] =
-{
-       0, 1, 6,
-       1, 0, 4,
-       2, 3, 5,
-       3, 2, 7,
-       4, 5, 10,
-       5, 4, 8,
-       6, 7, 9,
-       7, 6, 11,
-       8, 9, 2,
-       9, 8, 0,
-       10, 11, 1,
-       11, 10, 3,
-       0, 8, 4,
-       0, 6, 9,
-       1, 4, 10,
-       1, 11, 6,
-       2, 5, 8,
-       2, 9, 7,
-       3, 10, 5,
-       3, 7, 11
-};
-
-unsigned IcoSphereBuilder::base_edges[30*2] = { 0, 0 };
-unsigned IcoSphereBuilder::base_tri_edges[20*3] = { 0, 0, 0 };
-
-IcoSphereBuilder::IcoSphereBuilder(float r, unsigned s):
-       radius(r),
-       subdivision(s)
-{
-       if(base_edges[0]==base_edges[1])
-               initialize_edges();
-}
-
-void IcoSphereBuilder::initialize_edges()
-{
-       vector<int> edge_map(12*12, -1);
-       unsigned next_edge = 0;
-       for(unsigned i=0; i<20; ++i)
-               for(unsigned j=0; j<3; ++j)
-               {
-                       unsigned v1 = base_triangles[i*3+j];
-                       unsigned v2 = base_triangles[i*3+(j+1)%3];
-                       int e = edge_map[v1*12+v2];
-                       if(e<0)
-                       {
-                               e = next_edge++;
-                               base_edges[e*2] = v1;
-                               base_edges[e*2+1] = v2;
-                               // The other triangle using this edge will have the vertices swapped
-                               edge_map[v2*12+v1] = e|32;
-                       }
-                       base_tri_edges[i*3+j] = e;
-               }
-}
-
-void IcoSphereBuilder::build(PrimitiveBuilder &bld) const
-{
-       for(unsigned i=0; i<12; ++i)
-       {
-               const float *v = base_vertices+i*3;
-               bld.normal(v[0], v[1], v[2]);
-               bld.vertex(v[0]*radius, v[1]*radius, v[2]*radius);
-       }
-
-       if(subdivision>1)
-       {
-               vector<Vector3> edge_subdiv(30*(subdivision+1));
-               for(unsigned i=0; i<30; ++i)
-               {
-                       Vector3 v1(base_vertices+base_edges[i*2]*3);
-                       Vector3 v2(base_vertices+base_edges[i*2+1]*3);
-                       for(unsigned j=1; j<subdivision; ++j)
-                       {
-                               float t = static_cast<float>(j)/subdivision;
-                               Vector3 v = v1*(1.0f-t)+v2*t;
-                               edge_subdiv[i*(subdivision-1)+j-1] = v;
-                               v.normalize();
-                               bld.normal(v);
-                               bld.vertex(v*radius);
-                       }
-               }
-
-               for(unsigned i=0; i<20; ++i)
-                       for(unsigned j=1; j<subdivision; ++j)
-                       {
-                               unsigned e = base_tri_edges[i*3];
-                               Vector3 v1 = edge_subdiv[edge_vertex(e, subdivision-j)];
-                               e = base_tri_edges[i*3+1];
-                               Vector3 v2 = edge_subdiv[edge_vertex(e, j)];
-                               for(unsigned k=1; k<j; ++k)
-                               {
-                                       float t = static_cast<float>(k)/j;
-                                       Vector3 v = normalize(v1*(1.0f-t)+v2*t);
-                                       bld.normal(v);
-                                       bld.vertex(v*radius);
-                               }
-                       }
-
-               for(unsigned i=0; i<20; ++i)
-               {
-                       unsigned mid = 12+30*(subdivision-1)+i*(subdivision-1)*(subdivision-2)/2;
-                       for(unsigned j=1; j<=subdivision; ++j)
-                       {
-                               bld.begin(TRIANGLE_STRIP);
-                               if(j==subdivision)
-                                       bld.element(base_triangles[i*3]);
-                               else
-                                       bld.element(12+edge_vertex(base_tri_edges[i*3], subdivision-j));
-
-                               if(j==1)
-                               {
-                                       bld.element(base_triangles[i*3+1]);
-                                       bld.element(12+edge_vertex(base_tri_edges[i*3+1], j));
-                               }
-                               else
-                               {
-                                       bld.element(12+edge_vertex(base_tri_edges[i*3], subdivision-j+1));
-
-                                       if(j==subdivision)
-                                               bld.element(12+edge_vertex(base_tri_edges[i*3+2], subdivision-1));
-                                       else
-                                               bld.element(mid+(j-1)*(j-2)/2);
-
-                                       for(unsigned k=2; k<j; ++k)
-                                       {
-                                               bld.element(mid+(j-2)*(j-3)/2+k-2);
-                                               if(j==subdivision)
-                                                       bld.element(12+edge_vertex(base_tri_edges[i*3+2], subdivision-k));
-                                               else
-                                                       bld.element(mid+(j-1)*(j-2)/2+k-1);
-                                       }
-
-                                       bld.element(12+edge_vertex(base_tri_edges[i*3+1], j-1));
-                                       if(j==subdivision)
-                                               bld.element(base_triangles[i*3+2]);
-                                       else
-                                               bld.element(12+edge_vertex(base_tri_edges[i*3+1], j));
-                               }
-                               bld.end();
-                       }
-               }
-       }
-       else
-       {
-               bld.begin(TRIANGLES);
-               for(unsigned i=0; i<20*3; ++i)
-                       bld.element(base_triangles[i]);
-               bld.end();
-       }
-}
-
-unsigned IcoSphereBuilder::edge_vertex(unsigned e, unsigned i) const
-{
-       return (e&31)*(subdivision-1)+((e&32)?subdivision-i:i)-1;
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/sphere.h b/source/sphere.h
deleted file mode 100644 (file)
index b20ff54..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-#ifndef MSP_GL_SPHERE_H_
-#define MSP_GL_SPHERE_H_
-
-#include "geometrybuilder.h"
-
-namespace Msp {
-namespace GL {
-
-class UvSphereBuilder: public GeometryBuilder
-{
-private:
-       float radius;
-       unsigned segments;
-       unsigned rings;
-
-public:
-       UvSphereBuilder(float, unsigned, unsigned = 0);
-
-       using GeometryBuilder::build;
-       virtual void build(PrimitiveBuilder &) const;
-};
-
-
-class IcoSphereBuilder: public GeometryBuilder
-{
-private:
-       float radius;
-       unsigned subdivision;
-
-       static float base_vertices[];
-       static unsigned base_triangles[];
-       static unsigned base_edges[];
-       static unsigned base_tri_edges[];
-
-public:
-       IcoSphereBuilder(float, unsigned);
-private:
-       static void initialize_edges();
-
-public:
-       using GeometryBuilder::build;
-       virtual void build(PrimitiveBuilder &) const;
-
-private:
-       unsigned edge_vertex(unsigned, unsigned) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/stencil.cpp b/source/stencil.cpp
deleted file mode 100644 (file)
index 9de8b1f..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#include "stencil.h"
-
-namespace Msp {
-namespace GL {
-
-void stencil_func(Predicate func, int ref, unsigned mask)
-{
-       glStencilFunc(func, ref, mask);
-}
-
-void stencil_op(StencilOp sfail, StencilOp dfail, StencilOp dpass)
-{
-       glStencilOp(sfail, dfail, dpass);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/stencil.h b/source/stencil.h
deleted file mode 100644 (file)
index 02f4ed3..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#ifndef MSP_GL_STENCIL_H_
-#define MSP_GL_STENCIL_H_
-
-#include "gl.h"
-#include "predicate.h"
-
-namespace Msp {
-namespace GL {
-
-enum
-{
-       STENCIL_TEST = GL_STENCIL_TEST
-};
-
-enum StencilOp
-{
-       KEEP      = GL_KEEP,
-       SET_ZERO  = GL_ZERO,
-       REPLACE   = GL_REPLACE,
-       INCR      = GL_INCR,
-       DECR      = GL_DECR,
-       INVERT    = GL_INVERT,
-       INCR_WRAP = GL_INCR_WRAP,
-       DECR_WRAP = GL_DECR_WRAP
-};
-
-void stencil_func(Predicate func, int ref, unsigned mask);
-void stencil_op(StencilOp sfail, StencilOp dfail, StencilOp dpass);
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/tag.cpp b/source/tag.cpp
deleted file mode 100644 (file)
index c5824ae..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#include <cstring>
-#include <msp/core/hash.h>
-#include "tag.h"
-
-namespace Msp {
-namespace GL {
-
-Tag::Tag(const char *s):
-       id((s && *s) ? hash32(s, strlen(s)) : 0)
-{ }
-
-Tag::Tag(const std::string &s):
-       id(s.empty() ? 0 : hash32(s))
-{ }
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/tag.h b/source/tag.h
deleted file mode 100644 (file)
index 28b4fa0..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef MSP_GL_TAG_H_
-#define MSP_GL_TAG_H_
-
-#include <string>
-
-namespace Msp {
-namespace GL {
-
-/**
-Provides transparent string-to-hash conversion for faster comparison.  An empty
-string is guaranteed to have an id of 0.
-*/
-struct Tag
-{
-       unsigned id;
-
-       Tag(): id(0) { }
-       Tag(const char *);
-       Tag(const std::string &s);
-
-       bool operator<(const Tag &t) const { return id<t.id; }
-       bool operator==(const Tag &t) const { return id==t.id; }
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/technique.cpp b/source/technique.cpp
deleted file mode 100644 (file)
index 09799ac..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-#include <msp/core/refptr.h>
-#include <msp/datafile/collection.h>
-#include <msp/strings/format.h>
-#include "material.h"
-#include "program.h"
-#include "programdata.h"
-#include "tag.h"
-#include "technique.h"
-#include "texture.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-RenderPass &Technique::add_pass(const Tag &tag)
-{
-       return insert_unique(passes, tag, RenderPass())->second;
-}
-
-bool Technique::has_pass(const Tag &tag) const
-{
-       return passes.count(tag);
-}
-
-const RenderPass &Technique::get_pass(const Tag &tag) const
-{
-       return get_item(passes, tag);
-}
-
-const RenderPass *Technique::find_pass(const Tag &tag) const
-{
-       PassMap::const_iterator i = passes.find(tag);
-       return (i!=passes.end() ? &i->second : 0);
-}
-
-bool Technique::replace_texture(const string &slot, const Texture &tex)
-{
-       bool replaced = false;
-       for(PassMap::iterator i=passes.begin(); i!=passes.end(); ++i)
-       {
-               int index = i->second.get_texture_index(slot);
-               if(index>=0)
-               {
-                       i->second.set_texture(index, &tex);
-                       replaced = true;
-               }
-       }
-
-       return replaced;
-}
-
-bool Technique::replace_material(const string &slot, const Material &mat)
-{
-       bool replaced = false;
-       for(PassMap::iterator i=passes.begin(); i!=passes.end(); ++i)
-       {
-               const string &pass_slot = i->second.get_material_slot_name();
-               if(!pass_slot.empty() && pass_slot==slot)
-               {
-                       i->second.set_material(&mat);
-                       replaced = true;
-               }
-       }
-
-       return replaced;
-}
-
-bool Technique::replace_uniforms(const ProgramData &shdata)
-{
-       bool replaced = false;
-       const vector<string> &uniform_names = shdata.get_uniform_names();
-       for(PassMap::iterator i=passes.begin(); i!=passes.end(); ++i)
-       {
-               RefPtr<ProgramData> new_shdata;
-               for(vector<string>::const_iterator j=uniform_names.begin(); j!=uniform_names.end(); ++j)
-               {
-                       const string &name = i->second.get_slotted_uniform_name(*j);
-                       if(name.empty())
-                               continue;
-
-                       if(!new_shdata)
-                               new_shdata = new ProgramData(*i->second.get_shader_data());
-
-                       new_shdata->uniform(name, shdata.get_uniform(*j));
-                       replaced = true;
-               }
-
-               if(new_shdata)
-                       i->second.set_shader_program(i->second.get_shader_program(), new_shdata.get());
-       }
-
-       return replaced;
-}
-
-bool Technique::has_shaders() const
-{
-       for(PassMap::const_iterator i=passes.begin(); i!=passes.end(); ++i)
-               if(i->second.get_shader_program())
-                       return true;
-       return false;
-}
-
-
-Technique::Loader::Loader(Technique &t):
-       DataFile::CollectionObjectLoader<Technique>(t, 0)
-{
-       init();
-}
-
-Technique::Loader::Loader(Technique &t, Collection &c):
-       DataFile::CollectionObjectLoader<Technique>(t, &c)
-{
-       init();
-}
-
-void Technique::Loader::init()
-{
-       add("inherit", &Loader::inherit);
-       add("pass", &Loader::pass);
-}
-
-void Technique::Loader::inherit(const string &n)
-{
-       obj.passes = get_collection().get<Technique>(n).get_passes();
-       InheritLoader ldr(obj, get_collection());
-       load_sub_with(ldr);
-}
-
-void Technique::Loader::pass(const string &n)
-{
-       RenderPass p;
-       if(coll)
-               load_sub(p, get_collection());
-       else
-               load_sub(p);
-
-       if(!p.get_shader_program())
-               throw logic_error("no shader program in pass");
-
-       insert_unique(obj.passes, n, p);
-}
-
-
-Technique::InheritLoader::InheritLoader(Technique &t, Collection &c):
-       DataFile::CollectionObjectLoader<Technique>(t, &c)
-{
-       add("material", &InheritLoader::material);
-       add("texture", &InheritLoader::texture);
-       add("uniforms", &InheritLoader::uniforms);
-}
-
-void Technique::InheritLoader::material(const string &slot, const string &name)
-{
-       const Material &mat = get_collection().get<Material>(name);
-       if(obj.replace_material(slot, mat))
-               return;
-
-       // For backwards compatibility
-       RenderPass &pass = get_item(obj.passes, slot);
-       if(const Material *base_mat = pass.get_material())
-       {
-               for(PassMap::iterator i=obj.passes.begin(); i!=obj.passes.end(); ++i)
-                       if(i->second.get_material()==base_mat)
-                               i->second.set_material(&mat);
-       }
-       else
-               pass.set_material(&mat);
-}
-
-void Technique::InheritLoader::texture(const string &slot, const string &name)
-{
-       if(!obj.replace_texture(slot, get_collection().get<Texture>(name)))
-               throw key_error(slot);
-}
-
-void Technique::InheritLoader::uniforms()
-{
-       ProgramData shdata;
-       load_sub(shdata);
-       obj.replace_uniforms(shdata);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/technique.h b/source/technique.h
deleted file mode 100644 (file)
index 762b98f..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-#ifndef MSP_GL_TECHNIQUE_H_
-#define MSP_GL_TECHNIQUE_H_
-
-#include "renderpass.h"
-#include "tag.h"
-
-namespace Msp {
-namespace GL {
-
-class Material;
-class Texture;
-
-/**
-Ties multiple tagged render passes together.
-*/
-class Technique
-{
-public:
-       class Loader: public Msp::DataFile::CollectionObjectLoader<Technique>
-       {
-       public:
-               Loader(Technique &);
-               Loader(Technique &, Collection &);
-
-       private:
-               void init();
-               void inherit(const std::string &);
-               void pass(const std::string &);
-       };
-
-private:
-       class InheritLoader: public Msp::DataFile::CollectionObjectLoader<Technique>
-       {
-       public:
-               InheritLoader(Technique &, Collection &);
-
-       private:
-               void material(const std::string &, const std::string &);
-               void texture(const std::string &, const std::string &);
-               void uniforms();
-       };
-
-public:
-       typedef std::map<Tag, RenderPass> PassMap;
-
-private:
-       PassMap passes;
-
-public:
-       RenderPass &add_pass(const Tag &);
-       bool has_pass(const Tag &) const;
-       const RenderPass &get_pass(const Tag &) const;
-       const RenderPass *find_pass(const Tag &) const;
-       const PassMap &get_passes() const { return passes; }
-       bool replace_texture(const std::string &, const Texture &);
-       bool replace_material(const std::string &, const Material &);
-       bool replace_uniforms(const ProgramData &);
-       bool has_shaders() const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/tests.cpp b/source/tests.cpp
deleted file mode 100644 (file)
index 8e24bb5..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-#include "tests.h"
-
-namespace Msp {
-namespace GL {
-
-DepthTest::DepthTest():
-       write(true),
-       pred(LESS)
-{ }
-
-DepthTest::DepthTest(Predicate p, bool w):
-       write(w),
-       pred(p)
-{ }
-
-void DepthTest::bind() const
-{
-       if(set_current(this))
-       {
-               glEnable(GL_DEPTH_TEST);
-               glDepthFunc(pred);
-               glDepthMask(write);
-       }
-}
-
-const DepthTest &DepthTest::lequal()
-{
-       static DepthTest test(LEQUAL);
-       return test;
-}
-
-void DepthTest::unbind()
-{
-       if(set_current(0))
-       {
-               glDisable(GL_DEPTH_TEST);
-               // Allow glClear(GL_DEPTH_BUFFER_BIT) to work
-               glDepthMask(true);
-       }
-}
-
-
-ScissorTest::ScissorTest():
-       left(0),
-       bottom(0),
-       width(1),
-       height(1)
-{ }
-
-ScissorTest::ScissorTest(int l, int b, unsigned w, unsigned h):
-       left(l),
-       bottom(b),
-       width(w),
-       height(h)
-{ }
-
-void ScissorTest::bind() const
-{
-       if(set_current(this))
-       {
-               glEnable(GL_SCISSOR_TEST);
-               glScissor(left, bottom, width, height);
-       }
-}
-
-void ScissorTest::unbind()
-{
-       if(set_current(0))
-               glDisable(GL_SCISSOR_TEST);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/tests.h b/source/tests.h
deleted file mode 100644 (file)
index f7d7295..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-#ifndef MSP_GL_TESTS_H_
-#define MSP_GL_TESTS_H_
-
-#include "bindable.h"
-#include "gl.h"
-#include "predicate.h"
-
-namespace Msp {
-namespace GL {
-
-/**
-Tests incoming fragment depth values against the depth buffer.  If the test
-fails, the fragment is discarded.
-*/
-class DepthTest: public Bindable<DepthTest>
-{
-private:
-       bool write;
-       Predicate pred;
-
-public:
-       DepthTest();
-       DepthTest(Predicate, bool = true);
-
-       void bind() const;
-
-       static const DepthTest &lequal();
-       static void unbind();
-};
-
-
-/**
-Tests fragment coordinates against a rectangle.  Any fragments outside the
-rectangle are discarded.
-*/
-class ScissorTest: public Bindable<ScissorTest>
-{
-private:
-       int left;
-       int bottom;
-       unsigned width;
-       unsigned height;
-
-public:
-       ScissorTest();
-       ScissorTest(int, int, unsigned, unsigned);
-
-       void bind() const;
-
-       static void unbind();
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/text.cpp b/source/text.cpp
deleted file mode 100644 (file)
index b408bbe..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-#include "meshbuilder.h"
-#include "text.h"
-#include "texture2d.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Text::Text(const Font &f, const Technique *tech):
-       font(f),
-       mesh((TEXCOORD2, VERTEX2)),
-       horz_align(0.0f),
-       vert_offset(0.0f),
-       width(0.0f)
-{
-       object.set_mesh(&mesh);
-       if(tech)
-               set_technique(tech);
-}
-
-void Text::set_technique(const Technique *tech)
-{
-       if(tech)
-       {
-               technique = *tech;
-               technique.replace_texture("diffuse_map", font.get_texture());
-               object.set_technique(&technique);
-       }
-       else
-               object.set_technique(0);
-}
-
-void Text::set_text(const string &text, StringCodec::Decoder &dec)
-{
-       clear();
-       width = font.get_string_width(text, dec);
-       MeshBuilder bld(mesh);
-       bld.transform(Matrix::translation(Vector3(-horz_align*width, vert_offset, 0.0f)));
-       font.build_string(text, dec, bld);
-}
-
-void Text::clear()
-{
-       mesh.clear();
-       width = 0;
-}
-
-void Text::set_alignment(HorizontalAlign ha, VerticalAlign va)
-{
-       float h;
-       switch(ha)
-       {
-       case LEFT: h = 0.0f; break;
-       case CENTER: h = 0.5f; break;
-       case RIGHT: h = 1.0f; break;
-       default: throw invalid_argument("Text::set_alignment");
-       }
-
-       float v;
-       switch(va)
-       {
-       case DESCENT: v = -font.get_descent(); break;
-       case BASELINE: v = 0.0f; break;
-       case MIDLINE: v = font.get_cap_height()/2; break;
-       case ASCENT: v = font.get_ascent(); break;
-       default: throw invalid_argument("Text::set_alignment");
-       }
-
-       set_alignment(h, v);
-}
-
-void Text::set_alignment(float h, float v)
-{
-       if(h==horz_align && -v==vert_offset)
-               return;
-
-       float horz_adjust = (horz_align-h)*width;
-       float vert_adjust = -v-vert_offset;
-       horz_align = h;
-       vert_offset = -v;
-
-       unsigned pos_offset = mesh.get_vertices().get_format().offset(VERTEX2);
-       unsigned n_vertices = mesh.get_n_vertices();
-       for(unsigned i=0; i<n_vertices; ++i)
-       {
-               float *pos = mesh.modify_vertex(i)+pos_offset;
-               pos[0] += horz_adjust;
-               pos[1] += vert_adjust;
-       }
-}
-
-void Text::render(Renderer &renderer, const Tag &tag) const
-{
-       object.render(renderer, tag);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/text.h b/source/text.h
deleted file mode 100644 (file)
index 40c9b85..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-#ifndef MSP_GL_TEXT_H_
-#define MSP_GL_TEXT_H_
-
-#include "font.h"
-#include "mesh.h"
-#include "object.h"
-#include "technique.h"
-
-namespace Msp {
-namespace GL {
-
-/**
-Creates an object consisting of the visual representation of a string.  Can be
-used with an ObjectInstance to further customize the appearance.
-*/
-class Text: public Renderable
-{
-public:
-       enum HorizontalAlign
-       {
-               LEFT,
-               CENTER,
-               RIGHT
-       };
-
-       enum VerticalAlign
-       {
-               DESCENT,
-               BASELINE,
-               MIDLINE,
-               ASCENT
-       };
-
-private:
-       const Font &font;
-       Mesh mesh;
-       Object object;
-       Technique technique;
-       float horz_align;
-       float vert_offset;
-       float width;
-
-public:
-       Text(const Font &, const Technique * = 0);
-
-       const Mesh *get_mesh() const { return &mesh; }
-
-       /** Sets technique to render with.  It should have a texture slot named
-       "diffuse_map", which will be replaced with the font's texture. */
-       void set_technique(const Technique *);
-
-       const Technique *get_technique() const { return object.get_technique(); }
-
-       /// Sets the string to be displayed.
-       void set_text(const std::string &, StringCodec::Decoder &);
-
-       template<typename C>
-       void set_text(const std::string &t)
-       {
-               typename C::Decoder dec;
-               set_text(t, dec);
-       }
-
-       void set_text(const std::string &t)
-       { set_text<StringCodec::Utf8>(t); }
-
-       /// Clears the object's contents.
-       void clear();
-
-       /// Sets horizontal and vertical alignment with predefined anchors.
-       void set_alignment(HorizontalAlign, VerticalAlign = BASELINE);
-
-       /** Sets horizontal and vertical alignment.  0.0 means left or baseline,
-       1.0 means right or top. */
-       void set_alignment(float, float = 0.0f);
-
-       float get_width() const { return width; }
-
-       virtual void render(Renderer &, const Tag & = Tag()) const;
-
-       operator const Object &() const { return object; }
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/texture.cpp b/source/texture.cpp
deleted file mode 100644 (file)
index f6594ab..0000000
+++ /dev/null
@@ -1,410 +0,0 @@
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include <msp/gl/extensions/arb_texture_swizzle.h>
-#include <msp/gl/extensions/ext_framebuffer_object.h>
-#include <msp/io/memory.h>
-#include "bindable.h"
-#include "error.h"
-#include "resourcemanager.h"
-#include "resources.h"
-#include "texture.h"
-#include "texunit.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-int Texture::swizzle_orders[] =
-{
-       GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA,
-       GL_RED, GL_RED, GL_RED, GL_ONE,
-       GL_RED, GL_RED, GL_RED, GL_GREEN,
-       GL_BLUE, GL_GREEN, GL_RED, GL_ALPHA
-};
-
-Texture::Texture(GLenum t, ResourceManager *m):
-       id(0),
-       target(t),
-       format(RGB8),
-       storage_fmt(RGB8),
-       swizzle(NO_SWIZZLE),
-       use_srgb_format(false),
-       auto_gen_mipmap(false),
-       default_sampler(*this)
-{
-       if(m)
-               set_manager(m);
-       else if(ARB_direct_state_access)
-               glCreateTextures(target, 1, &id);
-       else
-               glGenTextures(1, &id);
-}
-
-Texture::~Texture()
-{
-       while(TexUnit *unit = TexUnit::find_unit(this))
-               unbind_from(unit->get_index());
-
-       if(id)
-               glDeleteTextures(1, &id);
-}
-
-void Texture::set_format(PixelFormat fmt)
-{
-       PixelComponents comp = get_components(fmt);
-       PixelComponents st_comp = comp;
-       FormatSwizzle swiz = NO_SWIZZLE;
-       switch(comp)
-       {
-       case LUMINANCE:
-               st_comp = RED;
-               swiz = R_TO_LUMINANCE;
-               break;
-       case LUMINANCE_ALPHA:
-               st_comp = RG;
-               swiz = RG_TO_LUMINANCE_ALPHA;
-               break;
-       case BGR:
-               st_comp = RGB;
-               swiz = RGB_TO_BGR;
-               break;
-       case BGRA:
-               st_comp = RGBA;
-               swiz = RGB_TO_BGR;
-               break;
-       default:;
-       }
-
-       PixelFormat st_fmt = make_pixelformat(st_comp, get_component_type(fmt));
-       require_pixelformat(st_fmt);
-       if(swiz!=NO_SWIZZLE)
-               static Require _req(ARB_texture_swizzle);
-
-       format = fmt;
-       storage_fmt = st_fmt;
-       swizzle = swiz;
-}
-
-void Texture::apply_swizzle()
-{
-       if(swizzle==NO_SWIZZLE)
-               return;
-
-       if(get_gl_api()==OPENGL_ES2)
-       {
-               set_parameter_i(GL_TEXTURE_SWIZZLE_R, swizzle_orders[swizzle*4]);
-               set_parameter_i(GL_TEXTURE_SWIZZLE_G, swizzle_orders[swizzle*4+1]);
-               set_parameter_i(GL_TEXTURE_SWIZZLE_B, swizzle_orders[swizzle*4+2]);
-               set_parameter_i(GL_TEXTURE_SWIZZLE_A, swizzle_orders[swizzle*4+3]);
-       }
-       else
-       {
-               if(ARB_direct_state_access)
-                       glTextureParameteriv(id, GL_TEXTURE_SWIZZLE_RGBA, swizzle_orders+swizzle*4);
-               else
-                       glTexParameteriv(target, GL_TEXTURE_SWIZZLE_RGBA, swizzle_orders+swizzle*4);
-       }
-}
-
-void Texture::set_parameter_i(GLenum param, int value) const
-{
-       if(ARB_direct_state_access)
-               glTextureParameteri(id, param, value);
-       else
-               glTexParameteri(target, param, value);
-}
-
-void Texture::set_min_filter(TextureFilter f)
-{
-       default_sampler.set_min_filter(f);
-}
-
-void Texture::set_mag_filter(TextureFilter f)
-{
-       default_sampler.set_mag_filter(f);
-}
-
-void Texture::set_filter(TextureFilter f)
-{
-       default_sampler.set_filter(f);
-}
-
-void Texture::set_max_anisotropy(float a)
-{
-       default_sampler.set_max_anisotropy(a);
-}
-
-void Texture::set_wrap(TextureWrap w)
-{
-       default_sampler.set_wrap(w);
-}
-
-void Texture::set_wrap_s(TextureWrap w)
-{
-       default_sampler.set_wrap_s(w);
-}
-
-void Texture::set_wrap_t(TextureWrap w)
-{
-       default_sampler.set_wrap_t(w);
-}
-
-void Texture::set_wrap_r(TextureWrap w)
-{
-       default_sampler.set_wrap_r(w);
-}
-
-bool Texture::can_generate_mipmap()
-{
-       return EXT_framebuffer_object;
-}
-
-void Texture::generate_mipmap()
-{
-       // glGenerateMipmap is defined here
-       static Require _req(EXT_framebuffer_object);
-
-       if(ARB_direct_state_access)
-               glGenerateTextureMipmap(id);
-       else
-       {
-               BindRestore _bind(this);
-               glGenerateMipmap(target);
-       }
-}
-
-void Texture::set_auto_generate_mipmap(bool gm)
-{
-       if(gm)
-               static Require _req(EXT_framebuffer_object);
-
-       auto_gen_mipmap = gm;
-}
-
-void Texture::set_compare_enabled(bool c)
-{
-       if(c)
-               default_sampler.set_compare(default_sampler.get_compare_function());
-       else
-               default_sampler.disable_compare();
-}
-
-void Texture::set_compare_func(Predicate f)
-{
-       default_sampler.set_compare(f);
-}
-
-void Texture::load_image(const string &fn, bool)
-{
-       load_image(fn, 0U);
-}
-
-void Texture::load_image(const string &fn, unsigned lv)
-{
-       Graphics::Image img;
-       img.load_file(fn);
-
-       image(img, lv);
-}
-
-void Texture::image(const Graphics::Image &img, bool)
-{
-       image(img, 0U);
-}
-
-void Texture::bind_to(unsigned i) const
-{
-       if(!id)
-       {
-               if(manager)
-                       manager->resource_used(*this);
-               if(!id)
-               {
-                       unbind_from(i);
-                       return;
-               }
-       }
-
-       TexUnit &unit = TexUnit::get_unit(i);
-       const Texture *cur = unit.get_texture();
-       if(unit.set_texture(this))
-       {
-               if(manager)
-                       manager->resource_used(*this);
-
-               if(ARB_direct_state_access)
-                       glBindTextureUnit(i, id);
-               else
-               {
-                       unit.bind();
-                       glBindTexture(target, id);
-               }
-
-               if(!unit.get_sampler() || unit.get_sampler()==&cur->default_sampler)
-                       default_sampler.bind_to(i);
-       }
-}
-
-const Texture *Texture::current(unsigned i)
-{
-       return TexUnit::get_unit(i).get_texture();
-}
-
-void Texture::unbind_from(unsigned i)
-{
-       TexUnit &unit = TexUnit::get_unit(i);
-       const Texture *cur = unit.get_texture();
-       if(unit.set_texture(0))
-       {
-               if(ARB_direct_state_access)
-                       glBindTextureUnit(i, 0);
-               else
-               {
-                       unit.bind();
-                       glBindTexture(cur->target, 0);
-               }
-
-               if(unit.get_sampler()==&cur->default_sampler)
-                       Sampler::unbind_from(i);
-       }
-}
-
-
-Texture::Loader::Loader(Texture &t):
-       DataFile::CollectionObjectLoader<Texture>(t, 0)
-{
-       init();
-}
-
-Texture::Loader::Loader(Texture &t, Collection &c):
-       DataFile::CollectionObjectLoader<Texture>(t, &c)
-{
-       init();
-}
-
-void Texture::Loader::init()
-{
-       levels = 0;
-
-       add("external_image", &Loader::external_image);
-       add("external_image_srgb", &Loader::external_image);
-       add("filter", &Loader::filter);
-       add("generate_mipmap", &Loader::generate_mipmap);
-       add("image_data", &Loader::image_data);
-       add("mag_filter", &Loader::mag_filter);
-       add("max_anisotropy", &Loader::max_anisotropy);
-       add("min_filter", &Loader::min_filter);
-       add("mipmap_levels", &Loader::mipmap_levels);
-       add("sampler",    &Loader::sampler);
-       add("wrap",       &Loader::wrap);
-       add("wrap_r",     &Loader::wrap_r);
-       add("wrap_s",     &Loader::wrap_s);
-       add("wrap_t",     &Loader::wrap_t);
-}
-
-unsigned Texture::Loader::get_levels() const
-{
-       return (is_mipmapped(obj.default_sampler.get_min_filter()) ? levels : 1);
-}
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-void Texture::Loader::load_external_image(Graphics::Image &img, const std::string &fn)
-{
-       RefPtr<IO::Seekable> io = get_collection().open_raw(fn);
-       if(!io)
-               throw IO::file_not_found(fn);
-       img.load_io(*io);
-}
-
-void Texture::Loader::external_image(const string &fn)
-{
-       obj.use_srgb_format = false;
-       external_image_common(fn);
-}
-
-void Texture::Loader::external_image_srgb(const string &fn)
-{
-       obj.use_srgb_format = true;
-       external_image_common(fn);
-}
-
-void Texture::Loader::external_image_common(const string &fn)
-{
-       if(obj.manager)
-               obj.manager->set_resource_location(obj, get_collection(), fn);
-       else
-       {
-               Graphics::Image img;
-               load_external_image(img, fn);
-               obj.image(img, get_levels());
-       }
-}
-
-void Texture::Loader::filter(TextureFilter f)
-{
-       obj.set_filter(f);
-}
-
-void Texture::Loader::generate_mipmap(bool gm)
-{
-       obj.set_auto_generate_mipmap(gm);
-}
-
-void Texture::Loader::image_data(const string &data)
-{
-       Graphics::Image img;
-       IO::Memory mem(data.data(), data.size());
-       img.load_io(mem);
-
-       obj.image(img, get_levels());
-}
-
-void Texture::Loader::mag_filter(TextureFilter f)
-{
-       obj.set_mag_filter(f);
-}
-
-void Texture::Loader::max_anisotropy(float a)
-{
-       obj.set_max_anisotropy(a);
-}
-
-void Texture::Loader::min_filter(TextureFilter f)
-{
-       obj.set_min_filter(f);
-}
-
-void Texture::Loader::mipmap_levels(unsigned l)
-{
-       levels = l;
-}
-
-void Texture::Loader::sampler()
-{
-       load_sub(obj.default_sampler);
-}
-
-void Texture::Loader::wrap(TextureWrap w)
-{
-       obj.set_wrap(w);
-}
-
-void Texture::Loader::wrap_r(TextureWrap w)
-{
-       obj.set_wrap_r(w);
-}
-
-void Texture::Loader::wrap_s(TextureWrap w)
-{
-       obj.set_wrap_s(w);
-}
-
-void Texture::Loader::wrap_t(TextureWrap w)
-{
-       obj.set_wrap_t(w);
-}
-#pragma GCC diagnostic pop
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/texture.h b/source/texture.h
deleted file mode 100644 (file)
index c4e7203..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-#ifndef MSP_GL_TEXTURE_H_
-#define MSP_GL_TEXTURE_H_
-
-#include <msp/core/attributes.h>
-#include <msp/datafile/objectloader.h>
-#include <msp/graphics/image.h>
-#include "datatype.h"
-#include "gl.h"
-#include "pixelformat.h"
-#include "predicate.h"
-#include "sampler.h"
-#include "resource.h"
-
-namespace Msp {
-namespace GL {
-
-/**
-Base class for textures.  This class only defines operations common for all
-texture types and is not instantiable.  For specifying images for textures,
-see one of the dimensioned texture classes.
-
-A texture can consinst of a stack of images, called a mipmap.  The dimensions
-of each mipmap level are half that of the previous level.  The mipmap stack
-can be used for texture minification; see the Sampler class for details.
-*/
-class Texture: public Resource
-{
-protected:
-       class Loader: public DataFile::CollectionObjectLoader<Texture>
-       {
-       protected:
-               unsigned levels;
-
-       public:
-               Loader(Texture &);
-               Loader(Texture &, Collection &);
-       private:
-               void init();
-
-               unsigned get_levels() const;
-       protected:
-               void load_external_image(Graphics::Image &, const std::string &);
-
-       private:
-               void external_image(const std::string &);
-               void external_image_srgb(const std::string &);
-               void external_image_common(const std::string &);
-               void filter(TextureFilter);
-               void generate_mipmap(bool);
-               void image_data(const std::string &);
-               void mag_filter(TextureFilter);
-               void max_anisotropy(float);
-               void min_filter(TextureFilter);
-               void mipmap_levels(unsigned);
-               void sampler();
-               void wrap(TextureWrap);
-               void wrap_r(TextureWrap);
-               void wrap_s(TextureWrap);
-               void wrap_t(TextureWrap);
-       };
-
-       enum ParameterMask
-       {
-               FORMAT_SWIZZLE = 512
-       };
-
-       enum FormatSwizzle
-       {
-               NO_SWIZZLE,
-               R_TO_LUMINANCE,
-               RG_TO_LUMINANCE_ALPHA,
-               RGB_TO_BGR
-       };
-
-       unsigned id;
-       GLenum target;
-       PixelFormat format;
-       PixelFormat storage_fmt;
-       FormatSwizzle swizzle;
-       bool use_srgb_format;
-       bool auto_gen_mipmap;
-       Sampler default_sampler;
-
-       static int swizzle_orders[];
-
-       Texture(GLenum, ResourceManager * = 0);
-       Texture(const Texture &);
-       Texture &operator=(const Texture &);
-public:
-       ~Texture();
-
-protected:
-       void set_format(PixelFormat);
-       void apply_swizzle();
-       void set_parameter_i(GLenum, int) const;
-
-public:
-       Sampler &get_default_sampler() { return default_sampler; }
-       const Sampler &get_default_sampler() const { return default_sampler; }
-
-       DEPRECATED void set_min_filter(TextureFilter);
-       DEPRECATED void set_mag_filter(TextureFilter);
-
-       /** Sets filter for both minification and magnification.  Since mipmapping
-       is not applicable to magnification, LINEAR is used instead. */
-       DEPRECATED void set_filter(TextureFilter);
-
-       DEPRECATED void set_mipmap_levels(unsigned) { }
-
-       DEPRECATED void set_max_anisotropy(float);
-
-       /** Sets the wrapping mode for all coordinates. */
-       DEPRECATED void set_wrap(TextureWrap);
-
-       DEPRECATED void set_wrap_s(TextureWrap);
-       DEPRECATED void set_wrap_t(TextureWrap);
-       DEPRECATED void set_wrap_r(TextureWrap);
-
-       static bool can_generate_mipmap();
-
-       void generate_mipmap();
-
-       /** Sets automatic mipmap generation.  If enabled, mipmaps are generated
-       when a texture image is uploaded. */
-       void set_auto_generate_mipmap(bool);
-
-       /// Deprecated.  Use set_auto_generate_mipmap instead.
-       DEPRECATED void set_generate_mipmap(bool g) { set_auto_generate_mipmap(g); }
-
-       /** Sets depth texture comparison.  Has no effect on other formats.  When
-       comparison is enabled, the third component of the texture coordinate is
-       compared against the texel value, and the result is returned as the texture
-       sample. */
-       DEPRECATED void set_compare_enabled(bool);
-
-       /** Sets the function to use for depth comparison. */
-       DEPRECATED void set_compare_func(Predicate);
-
-       /// Loads a Graphics::Image from a file and uploads it to the texture.
-       virtual void load_image(const std::string &, unsigned = 0);
-
-       DEPRECATED void load_image(const std::string &, bool srgb);
-
-       /** Uploads an image to the texture.  If storage has not been defined, it
-       will be set to match the image.  Otherwise the image must be compatible
-       with the defined storage.  Semantics depend on the type of texture.  */
-       virtual void image(const Graphics::Image &, unsigned = 0) = 0;
-
-       DEPRECATED void image(const Graphics::Image &, bool srgb);
-
-       GLenum get_target() const { return target; }
-       unsigned get_id() const { return id; }
-
-       void bind() const { bind_to(0); }
-       void bind_to(unsigned) const;
-
-       static const Texture *current(unsigned = 0);
-       static void unbind() { unbind_from(0); }
-       static void unbind_from(unsigned);
-
-       virtual UInt64 get_data_size() const { return 0; }
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/texture1d.cpp b/source/texture1d.cpp
deleted file mode 100644 (file)
index 742857b..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-#include <msp/core/raii.h>
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include <msp/gl/extensions/arb_texture_storage.h>
-#include <msp/gl/extensions/msp_texture1d.h>
-#include "bindable.h"
-#include "error.h"
-#include "texture1d.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Texture1D::Texture1D():
-       Texture(GL_TEXTURE_1D),
-       width(0),
-       allocated(0)
-{
-       static Require _req(MSP_texture1D);
-}
-
-void Texture1D::storage(PixelFormat fmt, unsigned wd, unsigned lv)
-{
-       if(width>0)
-               throw invalid_operation("Texture1D::storage");
-       if(wd==0)
-               throw invalid_argument("Texture1D::storage");
-
-       set_format(fmt);
-       width = wd;
-       levels = get_n_levels();
-       if(lv)
-               levels = min(levels, lv);
-}
-
-void Texture1D::allocate(unsigned level)
-{
-       if(width==0)
-               throw invalid_operation("Texture1D::allocate");
-       if(level>=levels)
-               throw invalid_argument("Texture1D::allocate");
-       if(allocated&(1<<level))
-               return;
-
-       if(ARB_texture_storage)
-       {
-               Conditional<BindRestore> _bind(!ARB_direct_state_access, this);
-               if(ARB_direct_state_access)
-                       glTextureStorage1D(id, levels, storage_fmt, width);
-               else
-                       glTexStorage1D(target, levels, storage_fmt, width);
-               apply_swizzle();
-               allocated |= (1<<levels)-1;
-       }
-       else
-               image(level, 0);
-}
-
-void Texture1D::image(unsigned level, const void *data)
-{
-       if(width==0)
-               throw invalid_operation("Texture1D::image");
-
-       unsigned w = get_level_size(level);
-
-       if(ARB_texture_storage)
-               return sub_image(level, 0, w, data);
-
-       BindRestore _bind(this);
-
-       if(!allocated)
-       {
-               glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
-               apply_swizzle();
-       }
-
-       PixelComponents comp = get_components(storage_fmt);
-       DataType type = get_component_type(storage_fmt);
-       glTexImage1D(target, level, storage_fmt, w, 0, comp, type, data);
-
-       allocated |= 1<<level;
-       if(auto_gen_mipmap && level==0)
-       {
-               generate_mipmap();
-               allocated |= (1<<levels)-1;
-       }
-}
-
-void Texture1D::image(unsigned level, PixelComponents comp, DataType type, const void *data)
-{
-       if(comp!=get_components(format) || type!=get_component_type(format))
-               throw incompatible_data("Texture1D::image");
-       image(level, data);
-}
-
-void Texture1D::sub_image(unsigned level, int x, unsigned wd, const void *data)
-{
-       if(width==0)
-               throw invalid_operation("Texture3D::image");
-
-       Conditional<BindRestore> _bind(!ARB_direct_state_access, this);
-       allocate(level);
-
-       PixelComponents comp = get_components(storage_fmt);
-       DataType type = get_component_type(storage_fmt);
-       if(ARB_direct_state_access)
-               glTextureSubImage1D(id, level, x, wd, comp, type, data);
-       else
-               glTexSubImage1D(target, level, x, wd, comp, type, data);
-
-       if(auto_gen_mipmap && level==0)
-               generate_mipmap();
-}
-
-void Texture1D::sub_image(unsigned level, int x, unsigned wd, PixelComponents comp, DataType type, const void *data)
-{
-       if(comp!=get_components(format) || type!=get_component_type(format))
-               throw incompatible_data("Texture1D::sub_image");
-       sub_image(level, x, wd, data);
-}
-
-void Texture1D::image(const Graphics::Image &img, unsigned lv)
-{
-       if(img.get_height()!=1)
-               throw incompatible_data("Texture1D::image");
-
-       unsigned w = img.get_width();
-       PixelFormat fmt = pixelformat_from_image(img);
-       if(width==0)
-               storage(make_pixelformat(get_components(fmt), get_component_type(fmt), use_srgb_format), w, lv);
-       else if(w!=width)
-               throw incompatible_data("Texture1D::image");
-
-       image(0, img.get_pixels());
-}
-
-unsigned Texture1D::get_n_levels() const
-{
-       unsigned n = 0;
-       for(unsigned s=width; s; s>>=1, ++n) ;
-       return n;
-}
-
-unsigned Texture1D::get_level_size(unsigned level) const
-{
-       return width>>level;
-}
-
-UInt64 Texture1D::get_data_size() const
-{
-       return id ? width*get_pixel_size(storage_fmt) : 0;
-}
-
-
-Texture1D::Loader::Loader(Texture1D &t):
-       DataFile::DerivedObjectLoader<Texture1D, Texture::Loader>(t)
-{
-       init();
-}
-
-Texture1D::Loader::Loader(Texture1D &t, Collection &c):
-       DataFile::DerivedObjectLoader<Texture1D, Texture::Loader>(t, c)
-{
-       init();
-}
-
-void Texture1D::Loader::init()
-{
-       add("raw_data", &Loader::raw_data);
-       add("storage", &Loader::storage);
-       add("storage", &Loader::storage_levels);
-}
-
-void Texture1D::Loader::raw_data(const string &data)
-{
-       obj.image(0, data.data());
-}
-
-void Texture1D::Loader::storage(PixelFormat fmt, unsigned w)
-{
-       obj.storage(fmt, w);
-}
-
-void Texture1D::Loader::storage_levels(PixelFormat fmt, unsigned w, unsigned l)
-{
-       obj.storage(fmt, w, l);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/texture1d.h b/source/texture1d.h
deleted file mode 100644 (file)
index b14ff91..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-#ifndef MSP_GL_TEXTURE1D_H_
-#define MSP_GL_TEXTURE1D_H_
-
-#include "texture.h"
-
-namespace Msp {
-namespace GL {
-
-class Texture1D: public Texture
-{
-public:
-       class Loader: public DataFile::DerivedObjectLoader<Texture1D, Texture::Loader>
-       {
-       public:
-               Loader(Texture1D &);
-               Loader(Texture1D &, Collection &);
-       private:
-               void init();
-
-               void raw_data(const std::string &);
-               void storage(PixelFormat, unsigned);
-               void storage_levels(PixelFormat, unsigned, unsigned);
-       };
-
-private:
-       unsigned width;
-       unsigned levels;
-       unsigned allocated;
-
-public:
-       Texture1D();
-
-       void storage(PixelFormat, unsigned, unsigned = 0);
-
-       DEPRECATED void storage(PixelComponents c, unsigned w, unsigned l = 0)
-       { storage(make_pixelformat(c, UNSIGNED_BYTE), w, l); }
-
-       void allocate(unsigned);
-       void image(unsigned, const void *);
-       DEPRECATED void image(unsigned, PixelComponents, DataType, const void *);
-       void sub_image(unsigned, int, unsigned, const void *);
-       DEPRECATED void sub_image(unsigned, int, unsigned, PixelComponents, DataType, const void *);
-       virtual void image(const Graphics::Image &, unsigned = 0);
-       using Texture::image;
-       unsigned get_width() const { return width; }
-
-private:
-       unsigned get_n_levels() const;
-       unsigned get_level_size(unsigned) const;
-
-public:
-       virtual AsyncLoader *load(IO::Seekable &, const Resources * = 0) { return 0; }
-       virtual UInt64 get_data_size() const;
-       virtual void unload() { }
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/texture2d.cpp b/source/texture2d.cpp
deleted file mode 100644 (file)
index 0e76128..0000000
+++ /dev/null
@@ -1,307 +0,0 @@
-#include <msp/core/raii.h>
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include <msp/gl/extensions/arb_texture_storage.h>
-#include <msp/graphics/imageloader.h>
-#include "bindable.h"
-#include "buffer.h"
-#include "error.h"
-#include "pixelstore.h"
-#include "resources.h"
-#include "texture2d.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-class Texture2D::AsyncLoader: public Resource::AsyncLoader
-{
-private:
-       Texture2D &texture;
-       IO::Seekable &io;
-       Buffer pixel_buffer;
-       char *mapped_address;
-       Graphics::Image image;
-       Graphics::ImageLoader *img_loader;
-       unsigned n_bytes;
-       int phase;
-
-public:
-       AsyncLoader(Texture2D &, IO::Seekable &);
-       ~AsyncLoader();
-
-       virtual bool needs_sync() const;
-       virtual bool process();
-};
-
-
-Texture2D::Texture2D(ResourceManager *m):
-       Texture(GL_TEXTURE_2D, m),
-       width(0),
-       height(0),
-       allocated(0)
-{ }
-
-Texture2D::~Texture2D()
-{
-       set_manager(0);
-}
-
-void Texture2D::storage(PixelFormat fmt, unsigned wd, unsigned ht, unsigned lv)
-{
-       if(width>0)
-               throw invalid_operation("Texture2D::storage");
-       if(wd==0 || ht==0)
-               throw invalid_argument("Texture2D::storage");
-
-       set_format(fmt);
-       width = wd;
-       height = ht;
-       levels = get_n_levels();
-       if(lv>0)
-               levels = min(levels, lv);
-}
-
-void Texture2D::allocate(unsigned level)
-{
-       if(width==0 || height==0)
-               throw invalid_operation("Texture2D::allocate");
-       if(level>=levels)
-               throw invalid_argument("Texture2D::allocate");
-       if(allocated&(1<<level))
-               return;
-
-       if(ARB_texture_storage)
-       {
-               Conditional<BindRestore> _bind(!ARB_direct_state_access, this);
-               if(ARB_direct_state_access)
-                       glTextureStorage2D(id, levels, storage_fmt, width, height);
-               else
-                       glTexStorage2D(target, levels, storage_fmt, width, height);
-               apply_swizzle();
-               allocated |= (1<<levels)-1;
-       }
-       else
-               image(level, 0);
-}
-
-void Texture2D::image(unsigned level, const void *data)
-{
-       if(width==0 || height==0)
-               throw invalid_operation("Texture2D::image");
-
-       unsigned w = width;
-       unsigned h = height;
-       get_level_size(level, w, h);
-
-       if(ARB_texture_storage)
-               return sub_image(level, 0, 0, w, h, data);
-
-       BindRestore _bind(this);
-
-       if(!allocated)
-       {
-               glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
-               apply_swizzle();
-       }
-
-       PixelComponents comp = get_components(storage_fmt);
-       DataType type = get_component_type(storage_fmt);
-       glTexImage2D(target, level, storage_fmt, w, h, 0, comp, type, data);
-
-       allocated |= 1<<level;
-       if(auto_gen_mipmap && level==0)
-       {
-               generate_mipmap();
-               allocated |= (1<<levels)-1;
-       }
-}
-
-void Texture2D::image(unsigned level, PixelComponents comp, DataType type, const void *data)
-{
-       if(comp!=get_components(format) || type!=get_component_type(format))
-               throw incompatible_data("Texture2D::image");
-       image(level, data);
-}
-
-void Texture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, const void *data)
-{
-       if(width==0 || height==0)
-               throw invalid_operation("Texture2D::sub_image");
-
-       Conditional<BindRestore> _bind(!ARB_direct_state_access, this);
-       allocate(level);
-
-       PixelComponents comp = get_components(storage_fmt);
-       DataType type = get_component_type(storage_fmt);
-       if(ARB_direct_state_access)
-               glTextureSubImage2D(id, level, x, y, wd, ht, comp, type, data);
-       else
-               glTexSubImage2D(target, level, x, y, wd, ht, comp, type, data);
-
-       if(auto_gen_mipmap && level==0)
-               generate_mipmap();
-}
-
-void Texture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, PixelComponents comp, DataType type, const void *data)
-{
-       if(comp!=get_components(format) || type!=get_component_type(format))
-               throw incompatible_data("Texture2D::sub_image");
-       sub_image(level, x, y, wd, ht, data);
-}
-
-void Texture2D::image(const Graphics::Image &img, unsigned lv)
-{
-       image(img, lv, false);
-}
-
-void Texture2D::image(const Graphics::Image &img, unsigned lv, bool from_buffer)
-{
-       unsigned w = img.get_width();
-       unsigned h = img.get_height();
-       PixelFormat fmt = pixelformat_from_image(img);
-       if(width==0)
-               storage(make_pixelformat(get_components(fmt), get_component_type(fmt), use_srgb_format), w, h, lv);
-       else if(w!=width || h!=height || (lv && lv!=levels))
-               throw incompatible_data("Texture2D::image");
-
-       PixelStore pstore = PixelStore::from_image(img);
-       BindRestore _bind_ps(pstore);
-
-       image(0, from_buffer ? 0 : img.get_pixels());
-}
-
-unsigned Texture2D::get_n_levels() const
-{
-       unsigned n = 0;
-       for(unsigned s=max(width, height); s; s>>=1, ++n) ;
-       return n;
-}
-
-void Texture2D::get_level_size(unsigned level, unsigned &w, unsigned &h) const
-{
-       w >>= level;
-       h >>= level;
-
-       if(!w && h)
-               w = 1;
-       else if(!h && w)
-               h = 1;
-}
-
-Resource::AsyncLoader *Texture2D::load(IO::Seekable &io, const Resources *)
-{
-       AsyncLoader *ldr = new AsyncLoader(*this, io);
-       return ldr;
-}
-
-UInt64 Texture2D::get_data_size() const
-{
-       return id ? width*height*get_pixel_size(format) : 0;
-}
-
-void Texture2D::unload()
-{
-       glDeleteTextures(1, &id);
-       id = 0;
-       allocated = 0;
-       default_sampler.unload();
-}
-
-
-Texture2D::Loader::Loader(Texture2D &t):
-       DataFile::DerivedObjectLoader<Texture2D, Texture::Loader>(t)
-{
-       init();
-}
-
-Texture2D::Loader::Loader(Texture2D &t, Collection &c):
-       DataFile::DerivedObjectLoader<Texture2D, Texture::Loader>(t, c)
-{
-       init();
-}
-
-void Texture2D::Loader::init()
-{
-       add("raw_data", &Loader::raw_data);
-       add("storage", &Loader::storage);
-       add("storage", &Loader::storage_levels);
-}
-
-void Texture2D::Loader::raw_data(const string &data)
-{
-       obj.image(0, data.data());
-}
-
-void Texture2D::Loader::storage(PixelFormat fmt, unsigned w, unsigned h)
-{
-       obj.storage(fmt, w, h);
-}
-
-void Texture2D::Loader::storage_levels(PixelFormat fmt, unsigned w, unsigned h, unsigned l)
-{
-       obj.storage(fmt, w, h, l);
-}
-
-
-Texture2D::AsyncLoader::AsyncLoader(Texture2D &t, IO::Seekable &i):
-       texture(t),
-       io(i),
-       pixel_buffer(PIXEL_UNPACK_BUFFER),
-       mapped_address(0),
-       img_loader(Graphics::ImageLoader::open_io(io)),
-       phase(0)
-{ }
-
-Texture2D::AsyncLoader::~AsyncLoader()
-{
-       if(mapped_address)
-               pixel_buffer.unmap();
-       delete img_loader;
-}
-
-bool Texture2D::AsyncLoader::needs_sync() const
-{
-       return phase%2;
-}
-
-bool Texture2D::AsyncLoader::process()
-{
-       if(phase==0)
-       {
-               image.load_headers(*img_loader);
-               n_bytes = image.get_stride()*image.get_height();
-       }
-       else if(phase==1)
-       {
-               pixel_buffer.storage(n_bytes);
-               mapped_address = reinterpret_cast<char *>(pixel_buffer.map());
-       }
-       else if(phase==2)
-               image.load_into(*img_loader, mapped_address);
-       else if(phase==3)
-       {
-               Bind _bind_buf(pixel_buffer, PIXEL_UNPACK_BUFFER);
-               mapped_address = 0;
-               if(!pixel_buffer.unmap())
-               {
-                       phase = 1;
-                       return false;
-               }
-
-               if(!texture.id)
-               {
-                       if(ARB_direct_state_access)
-                               glCreateTextures(texture.target, 1, &texture.id);
-                       else
-                               glGenTextures(1, &texture.id);
-               }
-               texture.image(image, 0, true);
-       }
-
-       ++phase;
-       return phase>3;
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/texture2d.h b/source/texture2d.h
deleted file mode 100644 (file)
index 7553b6b..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-#ifndef MSP_GL_TEXTURE2D_H_
-#define MSP_GL_TEXTURE2D_H_
-
-#include <string>
-#include <msp/graphics/image.h>
-#include "texture.h"
-
-namespace Msp {
-namespace GL {
-
-/**
-Two-dimensional texture.  Consists of an array of texels in the shape of a
-rectangle.  Texture coordinate have a range of [0, 1].  Coordinates outside of
-this range are subject to wrapping.  This is the most common type of texture.
-*/
-class Texture2D: public Texture
-{
-public:
-       class Loader: public Msp::DataFile::DerivedObjectLoader<Texture2D, Texture::Loader>
-       {
-       public:
-               Loader(Texture2D &);
-               Loader(Texture2D &, Collection &);
-       private:
-               void init();
-
-               void raw_data(const std::string &);
-               void storage(PixelFormat, unsigned, unsigned);
-               void storage_levels(PixelFormat, unsigned, unsigned, unsigned);
-       };
-
-private:
-       class AsyncLoader;
-
-       unsigned width;
-       unsigned height;
-       unsigned levels;
-       unsigned allocated;
-
-public:
-       Texture2D(ResourceManager * = 0);
-       virtual ~Texture2D();
-
-       /** Defines storage structure for the texture.  If lv is zero, the number
-       of mipmap levels is automatically determined from storage dimensions.
-
-       Must be called before an image can be uploaded.  Once storage is defined,
-       it can't be changed. */
-       void storage(PixelFormat fmt, unsigned wd, unsigned ht, unsigned lv = 0);
-
-       DEPRECATED void storage(PixelComponents cm, unsigned wd, unsigned ht, unsigned lv = 0)
-       { storage(make_pixelformat(cm, UNSIGNED_BYTE), wd, ht, lv); }
-
-       /** Allocates storage for the texture.  The contents are initially
-       undefined.  If storage has already been allocated, does nothing. */
-       void allocate(unsigned level);
-
-       /** Updates the contents of the entire texture.  Storage must be defined
-       beforehand.  The image data must have dimensions and format matching the
-       defined storage. */
-       virtual void image(unsigned level, const void *data);
-
-       DEPRECATED void image(unsigned level, PixelComponents fmt, DataType type, const void *data);
-
-       /** Updates a rectangular region of the texture.  Storage must be defined
-       beforehand.  The image data must be in a format mathing the defined storage
-       and the update region must be fully inside the texture. */
-       void sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, const void *data);
-
-       DEPRECATED void sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht,
-               PixelComponents fmt, DataType type, const void *data);
-
-       /** Updates the contents of the entire texture from an image.  If storage
-       has not been defined, it will be set to match the image.  Otherwise the
-       image must match the defined storage. */
-       virtual void image(const Graphics::Image &, unsigned lv = 0);
-
-       using Texture::image;
-
-private:
-       void image(const Graphics::Image &, unsigned, bool);
-
-public:
-       unsigned get_width() const { return width; }
-       unsigned get_height() const { return height; }
-
-private:
-       unsigned get_n_levels() const;
-       void get_level_size(unsigned, unsigned &, unsigned &) const;
-
-public:
-       virtual Resource::AsyncLoader *load(IO::Seekable &, const Resources * = 0);
-       virtual UInt64 get_data_size() const;
-       virtual void unload();
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/texture2darray.cpp b/source/texture2darray.cpp
deleted file mode 100644 (file)
index 1ecb4cf..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-#include <msp/datafile/collection.h>
-#include <msp/gl/extensions/ext_texture_array.h>
-#include "error.h"
-#include "pixelstore.h"
-#include "texture2darray.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Texture2DArray::Texture2DArray():
-       Texture3D(GL_TEXTURE_2D_ARRAY)
-{
-       static Require _req(EXT_texture_array);
-}
-
-void Texture2DArray::layer_image(unsigned level, unsigned z, const void *data)
-{
-       unsigned w = get_width();
-       unsigned h = get_height();
-       unsigned d = get_depth();
-       get_level_size(level, w, h, d);
-
-       sub_image(level, 0, 0, z, w, h, 1, data);
-}
-
-void Texture2DArray::layer_image(unsigned level, unsigned z, PixelComponents comp, DataType type, const void *data)
-{
-       if(comp!=get_components(format) || type!=get_component_type(format))
-               throw incompatible_data("Texture2DArray::layer_image");
-       layer_image(level, z, data);
-}
-
-void Texture2DArray::layer_image(unsigned level, unsigned z, const Graphics::Image &img)
-{
-       if(!get_width())
-               throw invalid_operation("Texture2DArray::layer_image");
-
-       unsigned w = img.get_width();
-       unsigned h = img.get_height();
-       if(w!=get_width() || h!=get_height())
-               throw incompatible_data("Texture2DArray::layer_image");
-       PixelFormat fmt = pixelformat_from_image(img);
-       if(get_components(fmt)!=get_components(format) || get_component_type(fmt)!=get_component_type(format))
-               throw incompatible_data("Texture2DArray::layer_image");
-
-       PixelStore pstore = PixelStore::from_image(img);
-       BindRestore _bind_ps(pstore);
-
-       layer_image(level, z, img.get_pixels());
-}
-
-
-Texture2DArray::Loader::Loader(Texture2DArray &t):
-       DataFile::DerivedObjectLoader<Texture2DArray, Texture3D::Loader>(t)
-{
-       init();
-}
-
-Texture2DArray::Loader::Loader(Texture2DArray &t, Collection &c):
-       DataFile::DerivedObjectLoader<Texture2DArray, Texture3D::Loader>(t, c)
-{
-       init();
-}
-
-void Texture2DArray::Loader::init()
-{
-       add("external_image", &Loader::external_image);
-}
-
-void Texture2DArray::Loader::external_image(unsigned z, const string &fn)
-{
-       Graphics::Image img;
-       load_external_image(img, fn);
-       obj.layer_image(0, z, img);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/texture2darray.h b/source/texture2darray.h
deleted file mode 100644 (file)
index bd7d03b..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-#ifndef MSP_GL_TEXTURE2DARRAY_H_
-#define MSP_GL_TEXTURE2DARRAY_H_
-
-#include "texture3d.h"
-
-namespace Msp {
-namespace GL {
-
-/**
-An array of two-dimensional textures.  It's very much like a 3D texture, with
-two important differences: there's no filtering nor mipmapping along the third
-dimension.
-*/
-class Texture2DArray: public Texture3D
-{
-public:
-       class Loader: public Msp::DataFile::DerivedObjectLoader<Texture2DArray, Texture3D::Loader>
-       {
-       public:
-               Loader(Texture2DArray &);
-               Loader(Texture2DArray &, Collection &);
-       private:
-               void init();
-
-               void external_image(unsigned, const std::string &);
-       };
-
-       Texture2DArray();
-
-       void layer_image(unsigned, unsigned, const void *);
-       DEPRECATED void layer_image(unsigned, unsigned, PixelComponents, DataType, const void *);
-       void layer_image(unsigned, unsigned, const Graphics::Image &);
-
-       unsigned get_layers() const { return get_depth(); }
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/texture3d.cpp b/source/texture3d.cpp
deleted file mode 100644 (file)
index 03c58d0..0000000
+++ /dev/null
@@ -1,229 +0,0 @@
-#include <cmath>
-#include <msp/core/raii.h>
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include <msp/gl/extensions/arb_texture_storage.h>
-#include <msp/gl/extensions/ext_texture3d.h>
-#include <msp/gl/extensions/ext_texture_array.h>
-#include <msp/graphics/image.h>
-#include "bindable.h"
-#include "error.h"
-#include "pixelstore.h"
-#include "texture3d.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Texture3D::Texture3D(GLenum t):
-       Texture(t),
-       width(0),
-       height(0),
-       depth(0),
-       allocated(0)
-{ }
-
-Texture3D::Texture3D():
-       Texture(GL_TEXTURE_3D),
-       width(0),
-       height(0),
-       depth(0),
-       allocated(0)
-{
-       static Require _req(EXT_texture3D);
-}
-
-void Texture3D::storage(PixelFormat fmt, unsigned wd, unsigned ht, unsigned dp, unsigned lv)
-{
-       if(width>0)
-               throw invalid_operation("Texture3D::storage");
-       if(wd==0 || ht==0 || dp==0)
-               throw invalid_argument("Texture3D::storage");
-
-       set_format(fmt);
-       width = wd;
-       height = ht;
-       depth = dp;
-       levels = get_n_levels();
-       if(lv>0)
-               levels = min(levels, lv);
-}
-
-void Texture3D::allocate(unsigned level)
-{
-       if(width==0 || height==0 || depth==0)
-               throw invalid_operation("Texture3D::allocate");
-       if(level>=levels)
-               throw invalid_argument("Texture3D::allocate");
-       if(allocated&(1<<level))
-               return;
-
-       if(ARB_texture_storage)
-       {
-               Conditional<BindRestore> _bind(!ARB_direct_state_access, this);
-               if(ARB_direct_state_access)
-                       glTextureStorage3D(id, levels, storage_fmt, width, height, depth);
-               else
-                       glTexStorage3D(target, levels, storage_fmt, width, height, depth);
-               apply_swizzle();
-               allocated |= (1<<levels)-1;
-       }
-       else
-               image(level, 0);
-}
-
-void Texture3D::image(unsigned level, const void *data)
-{
-       if(width==0 || height==0 || depth==0)
-               throw invalid_operation("Texture3D::image");
-
-       unsigned w = width;
-       unsigned h = height;
-       unsigned d = depth;
-       get_level_size(level, w, h, d);
-
-       if(ARB_texture_storage)
-               return sub_image(level, 0, 0, 0, w, h, d, data);
-
-       BindRestore _bind(this);
-
-       if(!allocated)
-       {
-               glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
-               apply_swizzle();
-       }
-
-       PixelComponents comp = get_components(storage_fmt);
-       DataType type = get_component_type(storage_fmt);
-       glTexImage3D(target, level, storage_fmt, width, height, depth, 0, comp, type, data);
-
-       allocated |= 1<<level;
-       if(auto_gen_mipmap && level==0)
-       {
-               generate_mipmap();
-               allocated |= (1<<levels)-1;
-       }
-}
-
-void Texture3D::image(unsigned level, PixelComponents comp, DataType type, const void *data)
-{
-       if(comp!=get_components(format) || type!=get_component_type(format))
-               throw incompatible_data("Texture3D::image");
-       image(level, data);
-}
-
-void Texture3D::sub_image(unsigned level, int x, int y, int z, unsigned wd, unsigned ht, unsigned dp, const void *data)
-{
-       if(width==0 || height==0 || depth==0)
-               throw invalid_operation("Texture3D::image");
-
-       Conditional<BindRestore> _bind(!ARB_direct_state_access, this);
-       allocate(level);
-
-       PixelComponents comp = get_components(storage_fmt);
-       DataType type = get_component_type(storage_fmt);
-       if(ARB_direct_state_access)
-               glTextureSubImage3D(id, level, x, y, z, wd, ht, dp, comp, type, data);
-       else
-               glTexSubImage3D(target, level, x, y, z, wd, ht, dp, comp, type, data);
-
-       if(auto_gen_mipmap && level==0)
-               generate_mipmap();
-}
-
-void Texture3D::sub_image(unsigned level, int x, int y, int z, unsigned wd, unsigned ht, unsigned dp, PixelComponents comp, DataType type, const void *data)
-{
-       if(comp!=get_components(format) || type!=get_component_type(format))
-               throw incompatible_data("Texture3D::sub_image");
-       sub_image(level, x, y, z, wd, ht, dp, data);
-}
-
-void Texture3D::image(const Graphics::Image &img, unsigned lv)
-{
-       unsigned w = img.get_width();
-       unsigned h = img.get_height();
-
-       if(h%w)
-               throw incompatible_data("Texture3D::load_image");
-       unsigned d = h/w;
-       h = w;
-
-       PixelFormat fmt = pixelformat_from_image(img);
-       if(width==0)
-               storage(make_pixelformat(get_components(fmt), get_component_type(fmt), use_srgb_format), w, h, d, lv);
-       else if(w!=width || h!=height || d!=depth)
-               throw incompatible_data("Texture3D::load_image");
-
-       PixelStore pstore = PixelStore::from_image(img);
-       BindRestore _bind_ps(pstore);
-
-       image(0, img.get_pixels());
-}
-
-unsigned Texture3D::get_n_levels() const
-{
-       unsigned s = max(width, height);
-       if(target!=GL_TEXTURE_2D_ARRAY)
-               s = max(s, depth);
-       unsigned n = 0;
-       for(; s; s>>=1, ++n) ;
-       return n;
-}
-
-void Texture3D::get_level_size(unsigned level, unsigned &w, unsigned &h, unsigned &d) const
-{
-       w >>= level;
-       h >>= level;
-       if(target!=GL_TEXTURE_2D_ARRAY)
-               d >>= level;
-
-       if(!w && (h || d))
-               w = 1;
-       if(!h && (w || d))
-               h = 1;
-       if(!d && (w || h))
-               d = 1;
-}
-
-UInt64 Texture3D::get_data_size() const
-{
-       return id ? width*height*depth*get_pixel_size(storage_fmt) : 0;
-}
-
-
-Texture3D::Loader::Loader(Texture3D &t):
-       DataFile::DerivedObjectLoader<Texture3D, Texture::Loader>(t)
-{
-       init();
-}
-
-Texture3D::Loader::Loader(Texture3D &t, Collection &c):
-       DataFile::DerivedObjectLoader<Texture3D, Texture::Loader>(t, c)
-{
-       init();
-}
-
-void Texture3D::Loader::init()
-{
-       add("raw_data", &Loader::raw_data);
-       add("storage", &Loader::storage);
-       add("storage", &Loader::storage_levels);
-}
-
-void Texture3D::Loader::raw_data(const string &data)
-{
-       obj.image(0, data.data());
-}
-
-void Texture3D::Loader::storage(PixelFormat fmt, unsigned w, unsigned h, unsigned d)
-{
-       obj.storage(fmt, w, h, d);
-}
-
-void Texture3D::Loader::storage_levels(PixelFormat fmt, unsigned w, unsigned h, unsigned d, unsigned l)
-{
-       obj.storage(fmt, w, h, d, l);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/texture3d.h b/source/texture3d.h
deleted file mode 100644 (file)
index 1f50e05..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-#ifndef MSP_GL_TEXTURE3D_H_
-#define MSP_GL_TEXTURE3D_H_
-
-#include <string>
-#include "texture.h"
-
-namespace Msp {
-namespace GL {
-
-/**
-Three-dimensional texture.  Consists of an array of texels in the shape of a
-right cuboid.  Texture coordinates have a principal range of [0, 1].
-*/
-class Texture3D: public Texture
-{
-public:
-       class Loader: public Msp::DataFile::DerivedObjectLoader<Texture3D, Texture::Loader>
-       {
-       public:
-               Loader(Texture3D &);
-               Loader(Texture3D &, Collection &);
-       private:
-               void init();
-
-               void raw_data(const std::string &);
-               void storage(PixelFormat, unsigned, unsigned, unsigned);
-               void storage_levels(PixelFormat, unsigned, unsigned, unsigned, unsigned);
-       };
-
-private:
-       unsigned width;
-       unsigned height;
-       unsigned depth;
-       unsigned levels;
-       unsigned allocated;
-
-protected:
-       Texture3D(GLenum);
-public:
-       Texture3D();
-
-       /** Defines storage structure for the texture.  If lv is zero, the number
-       of mipmap levels is automatically determined from storage dimensions.
-
-       Must be called before an image can be uploaded.  Once storage is defined,
-       it can't be changed. */
-       void storage(PixelFormat fmt, unsigned wd, unsigned ht, unsigned dp, unsigned lv = 0);
-
-       DEPRECATED void storage(PixelComponents c, unsigned w, unsigned h, unsigned d, unsigned l = 0)
-       { storage(make_pixelformat(c, UNSIGNED_BYTE), w, h, d, l); }
-
-       /** Allocates storage for the texture.  The contents are initially
-       undefined.  If storage has already been allocated, does nothing. */
-       void allocate(unsigned level);
-
-       /** Updates the contents of the entire texture.  Storage must be defined
-       beforehand.  The image data must have dimensions and format matching the
-       defined storage. */
-       void image(unsigned level, const void *data);
-
-       DEPRECATED void image(unsigned level, PixelComponents comp, DataType type, const void *data);
-
-       /** Updates a cuboid-shaped region of the texture.  Storage must be defined
-       beforehand.  The image data must be in a format mathing the defined storage
-       and the update region must be fully inside the texture. */
-       void sub_image(unsigned level, int x, int y, int z, unsigned wd, unsigned ht, unsigned dp, const void *data);
-
-       DEPRECATED void sub_image(unsigned level,
-               int x, int y, int z, unsigned wd, unsigned ht, unsigned dp,
-               PixelComponents comp, DataType type, const void *data);
-
-       /** Updates the contents of the entire texture from an image.  If storage
-       has not been defined, it will be set to match the image.  In this case the
-       image will be treated as a stack of square layers and its height must be
-       divisible by its width.  Otherwise the image must match the defined
-       storage. */
-       virtual void image(const Graphics::Image &, unsigned = 0);
-
-       using Texture::image;
-
-       unsigned get_width() const { return width; }
-       unsigned get_height() const { return height; }
-       unsigned get_depth() const { return depth; }
-protected:
-       unsigned get_n_levels() const;
-       void get_level_size(unsigned, unsigned &, unsigned &, unsigned &) const;
-
-public:
-       virtual AsyncLoader *load(IO::Seekable &, const Resources * = 0) { return 0; }
-       virtual UInt64 get_data_size() const;
-       virtual void unload() { }
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/texturecube.cpp b/source/texturecube.cpp
deleted file mode 100644 (file)
index 6ec3662..0000000
+++ /dev/null
@@ -1,357 +0,0 @@
-#include <msp/datafile/collection.h>
-#include <msp/gl/extensions/arb_texture_cube_map.h>
-#include <msp/gl/extensions/arb_texture_storage.h>
-#include <msp/io/memory.h>
-#include <msp/strings/format.h>
-#include "bindable.h"
-#include "error.h"
-#include "pixelstore.h"
-#include "texturecube.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-TextureCubeFace TextureCube::face_order[6] =
-{
-       POSITIVE_X,
-       NEGATIVE_X,
-       POSITIVE_Y,
-       NEGATIVE_Y,
-       POSITIVE_Z,
-       NEGATIVE_Z
-};
-
-Vector3 TextureCube::directions[6] =
-{
-       Vector3(1, 0, 0),
-       Vector3(-1, 0, 0),
-       Vector3(0, 1, 0),
-       Vector3(0, -1, 0),
-       Vector3(0, 0, 1),
-       Vector3(0, 0, -1)
-};
-
-unsigned TextureCube::orientations[12] =
-{
-       5, 3,
-       4, 3,
-       0, 4,
-       0, 5,
-       0, 3,
-       1, 3
-};
-
-TextureCube::TextureCube():
-       Texture(GL_TEXTURE_CUBE_MAP),
-       size(0),
-       allocated(0)
-{
-       static Require _req(ARB_texture_cube_map);
-}
-
-void TextureCube::storage(PixelFormat fmt, unsigned sz, unsigned lv)
-{
-       if(size>0)
-               throw invalid_operation("TextureCube::storage");
-       if(sz==0)
-               throw invalid_argument("TextureCube::storage");
-
-       set_format(fmt);
-       size = sz;
-       levels = get_n_levels();
-       if(lv>0)
-               levels = min(levels, lv);
-}
-
-void TextureCube::allocate(unsigned level)
-{
-       if(size==0)
-               throw invalid_operation("TextureCube::allocate");
-       if(level>=levels)
-               throw invalid_argument("TextureCube::allocate");
-       if(allocated&(64<<level))
-               return;
-
-       if(ARB_texture_storage)
-       {
-               BindRestore _bind(this);
-               glTexStorage2D(target, levels, storage_fmt, size, size);
-               apply_swizzle();
-               allocated |= (1<<levels)-1;
-       }
-       else
-       {
-               for(unsigned i=0; i<6; ++i)
-                       image(enumerate_faces(i), level, 0);
-       }
-}
-
-void TextureCube::image(TextureCubeFace face, unsigned level, const void *data)
-{
-       if(size==0)
-               throw invalid_operation("TextureCube::image");
-
-       unsigned s = get_level_size(level);
-       if(s==0)
-               throw out_of_range("TextureCube::image");
-
-       if(ARB_texture_storage)
-               return sub_image(face, level, 0, 0, s, s, data);
-
-       BindRestore _bind(this);
-
-       if(!allocated)
-       {
-               glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels-1);
-               apply_swizzle();
-       }
-
-       PixelComponents comp = get_components(storage_fmt);
-       DataType type = get_component_type(storage_fmt);
-       glTexImage2D(face, level, storage_fmt, s, s, 0, comp, type, data);
-
-       if(level==0)
-       {
-               allocated |= 1<<get_face_index(face);
-               if((allocated&63)==63)
-               {
-                       allocated |= 64;
-                       if(auto_gen_mipmap)
-                       {
-                               generate_mipmap();
-                               allocated |= (64<<levels)-1;
-                       }
-               }
-       }
-       else if(!(allocated&(64<<level)))
-       {
-               for(unsigned i=0; i<6; ++i)
-                       if(enumerate_faces(i)!=face)
-                               glTexImage2D(enumerate_faces(i), level, storage_fmt, s, s, 0, comp, type, 0);
-
-               allocated |= 64<<level;
-       }
-}
-
-void TextureCube::image(TextureCubeFace face, unsigned level, PixelComponents comp, DataType type, const void *data)
-{
-       if(comp!=get_components(format) || type!=get_component_type(format))
-               throw incompatible_data("TextureCube::image");
-       image(face, level, data);
-}
-
-void TextureCube::sub_image(TextureCubeFace face, unsigned level, int x, int y, unsigned wd, unsigned ht, const void *data)
-{
-       if(size==0)
-               throw invalid_operation("TextureCube::sub_image");
-
-       BindRestore _bind(this);
-       allocate(level);
-
-       PixelComponents comp = get_components(storage_fmt);
-       DataType type = get_component_type(storage_fmt);
-       glTexSubImage2D(face, level, x, y, wd, ht, comp, type, data);
-
-       if(auto_gen_mipmap && level==0)
-               generate_mipmap();
-}
-
-void TextureCube::sub_image(TextureCubeFace face, unsigned level, int x, int y, unsigned wd, unsigned ht, PixelComponents comp, DataType type, const void *data)
-{
-       if(comp!=get_components(format) || type!=get_component_type(format))
-               throw incompatible_data("TextureCube::subimage");
-       sub_image(face, level, x, y, wd, ht, data);
-}
-
-void TextureCube::image(TextureCubeFace face, const Graphics::Image &img)
-{
-       unsigned w = img.get_width();
-       unsigned h = img.get_height();
-       PixelFormat fmt = pixelformat_from_image(img);
-       if(size==0)
-       {
-               if(w!=h)
-                       throw incompatible_data("TextureCube::image");
-
-               storage(make_pixelformat(get_components(fmt), get_component_type(fmt), use_srgb_format), w);
-       }
-       else if(w!=size || h!=size)
-               throw incompatible_data("TextureCube::image");
-
-       PixelStore pstore = PixelStore::from_image(img);
-       BindRestore _bind_ps(pstore);
-
-       image(face, 0, img.get_pixels());
-}
-
-void TextureCube::image(TextureCubeFace face, const Graphics::Image &img, bool)
-{
-       image(face, img);
-}
-
-void TextureCube::image(const Graphics::Image &img, unsigned lv)
-{
-       unsigned w = img.get_width();
-       unsigned h = img.get_height();
-
-       if(h!=w*6)
-               throw incompatible_data("TextureCube::image");
-       h /= 6;
-
-       PixelFormat fmt = pixelformat_from_image(img);
-       if(size==0)
-               storage(make_pixelformat(get_components(fmt), get_component_type(fmt), use_srgb_format), w, lv);
-       else if(w!=size || h!=size)
-               throw incompatible_data("TextureCube::image");
-
-       PixelStore pstore = PixelStore::from_image(img);
-       BindRestore _bind_ps(pstore);
-
-       const char *pixels = reinterpret_cast<const char *>(img.get_pixels());
-       unsigned face_size = img.get_stride()*size;
-       for(unsigned i=0; i<6; ++i)
-               image(enumerate_faces(i), 0, pixels+i*face_size);
-}
-
-unsigned TextureCube::get_n_levels() const
-{
-       unsigned n = 0;
-       for(unsigned s=size; s; s>>=1, ++n) ;
-       return n;
-}
-
-unsigned TextureCube::get_level_size(unsigned level) const
-{
-       return size>>level;
-}
-
-TextureCubeFace TextureCube::enumerate_faces(unsigned i)
-{
-       if(i>=6)
-               throw out_of_range("TextureCube::enumerate_faces");
-       return face_order[i];
-}
-
-unsigned TextureCube::get_face_index(TextureCubeFace face)
-{
-       switch(face)
-       {
-       case POSITIVE_X: return 0;
-       case NEGATIVE_X: return 1;
-       case POSITIVE_Y: return 2;
-       case NEGATIVE_Y: return 3;
-       case POSITIVE_Z: return 4;
-       case NEGATIVE_Z: return 5;
-       default: throw invalid_argument("TextureCube::get_face_index");
-       }
-}
-
-const Vector3 &TextureCube::get_face_direction(TextureCubeFace face)
-{
-       return directions[get_face_index(face)];
-}
-
-const Vector3 &TextureCube::get_s_direction(TextureCubeFace face)
-{
-       return directions[orientations[get_face_index(face)*2]];
-}
-
-const Vector3 &TextureCube::get_t_direction(TextureCubeFace face)
-{
-       return directions[orientations[get_face_index(face)*2+1]];
-}
-
-Vector3 TextureCube::get_texel_direction(TextureCubeFace face, unsigned u, unsigned v)
-{
-       float s = (u+0.5f)*2.0f/size-1.0f;
-       float t = (v+0.5f)*2.0f/size-1.0f;
-       const Vector3 &fv = get_face_direction(face);
-       const Vector3 &sv = get_s_direction(face);
-       const Vector3 &tv = get_t_direction(face);
-       return fv+s*sv+t*tv;
-}
-
-UInt64 TextureCube::get_data_size() const
-{
-       return id ? size*size*6*get_pixel_size(storage_fmt) : 0;
-}
-
-
-TextureCube::Loader::Loader(TextureCube &t):
-       DataFile::DerivedObjectLoader<TextureCube, Texture::Loader>(t)
-{
-       init();
-}
-
-TextureCube::Loader::Loader(TextureCube &t, Collection &c):
-       DataFile::DerivedObjectLoader<TextureCube, Texture::Loader>(t, c)
-{
-       init();
-}
-
-void TextureCube::Loader::init()
-{
-       add("external_image", &Loader::external_image);
-       add("image_data", &Loader::image_data);
-       add("raw_data", &Loader::raw_data);
-       add("storage", &Loader::storage);
-       add("storage", &Loader::storage_levels);
-}
-
-void TextureCube::Loader::external_image(TextureCubeFace face, const string &fn)
-{
-       Graphics::Image img;
-       RefPtr<IO::Seekable> io = get_collection().open_raw(fn);
-       img.load_io(*io);
-
-       obj.image(face, img);
-}
-
-void TextureCube::Loader::image_data(TextureCubeFace face, const string &data)
-{
-       Graphics::Image img;
-       IO::Memory mem(data.data(), data.size());
-       img.load_io(mem);
-
-       obj.image(face, img);
-}
-
-void TextureCube::Loader::raw_data(TextureCubeFace face, const string &data)
-{
-       obj.image(face, 0, data.data());
-}
-
-void TextureCube::Loader::storage(PixelFormat fmt, unsigned s)
-{
-       obj.storage(fmt, s);
-}
-
-void TextureCube::Loader::storage_levels(PixelFormat fmt, unsigned s, unsigned l)
-{
-       obj.storage(fmt, s, l);
-}
-
-
-void operator>>(const LexicalConverter &conv, TextureCubeFace &face)
-{
-       const string &str = conv.get();
-       if(str=="POSITIVE_X")
-               face = POSITIVE_X;
-       else if(str=="NEGATIVE_X")
-               face = NEGATIVE_X;
-       else if(str=="POSITIVE_Y")
-               face = POSITIVE_Y;
-       else if(str=="NEGATIVE_Y")
-               face = NEGATIVE_Y;
-       else if(str=="POSITIVE_Z")
-               face = POSITIVE_Z;
-       else if(str=="NEGATIVE_Z")
-               face = NEGATIVE_Z;
-       else
-               throw lexical_error(format("conversion of '%s' to TextureCubeFace", str));
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/texturecube.h b/source/texturecube.h
deleted file mode 100644 (file)
index ae30515..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-#ifndef MSP_GL_TEXTURECUBE_H_
-#define MSP_GL_TEXTURECUBE_H_
-
-#include <msp/gl/extensions/arb_texture_cube_map.h>
-#include <msp/graphics/image.h>
-#include "texture.h"
-#include "vector.h"
-
-namespace Msp {
-namespace GL {
-
-enum TextureCubeFace
-{
-       POSITIVE_X = GL_TEXTURE_CUBE_MAP_POSITIVE_X,
-       NEGATIVE_X = GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
-       POSITIVE_Y = GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
-       NEGATIVE_Y = GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
-       POSITIVE_Z = GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
-       NEGATIVE_Z = GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
-};
-
-/**
-Cube map texture, consisting of six square faces.  All of the faces must be of
-the same size.  A cube map texture is addressed by three-dimensional texture
-coordinates, with a principal range of [-1, 1].  The face is first selected
-according to the largest coordinate, and the remaining two coordinates are used
-to sample the face image.  The images are oriented so that the cross product of
-the s and t axes will point into the cube.
-
-All faces of a cube map texture must be allocated for it to be usable.
-
-Requires OpenGL version 1.3.
-*/
-class TextureCube: public Texture
-{
-public:
-       class Loader: public Msp::DataFile::DerivedObjectLoader<TextureCube, Texture::Loader>
-       {
-       public:
-               Loader(TextureCube &);
-               Loader(TextureCube &, Collection &);
-       private:
-               void init();
-
-               void external_image(TextureCubeFace, const std::string &);
-               void image_data(TextureCubeFace, const std::string &);
-               void raw_data(TextureCubeFace, const std::string &);
-               void storage(PixelFormat, unsigned);
-               void storage_levels(PixelFormat, unsigned, unsigned);
-       };
-
-private:
-       unsigned size;
-       unsigned levels;
-       /* Lowest six bits track allocation status of faces on the base level.  Bit
-       seven is set if the entire base level is allocated. */
-       unsigned allocated;
-
-       static TextureCubeFace face_order[6];
-       static Vector3 directions[6];
-       static unsigned orientations[12];
-
-public:
-       TextureCube();
-
-       /** Defines storage structure for the texture.  If lv is zero, the number
-       of mipmap levels is automatically determined from storage dimensions.
-
-       Must be called before an image can be uploaded.  Once storage is defined,
-       it can't be changed. */
-       void storage(PixelFormat fmt, unsigned size, unsigned lv = 0);
-
-       DEPRECATED void storage(PixelComponents c, unsigned s, unsigned l = 0)
-       { storage(make_pixelformat(c, UNSIGNED_BYTE), s, l); }
-
-       /** Allocates storage for the cube faces.  The contents are initially
-       undefined.  If storage has already been allocated, does nothing. */
-       void allocate(unsigned level);
-
-       /** Updates the contents of a face.  Storage must be defined beforehand.
-       The image data must have dimensions and format matching the defined
-       storage. */
-       void image(TextureCubeFace face, unsigned level, const void *data);
-
-       DEPRECATED void image(TextureCubeFace face, unsigned level,
-               PixelComponents comp, DataType type, const void *data);
-
-       /** Updates a rectangular region of a face.  Storage must be defined
-       beforehand.  The image data must be in a format mathing the defined storage
-       and the update region must be fully inside the face. */
-       void sub_image(TextureCubeFace face, unsigned level, int x, int y, unsigned w, unsigned h, const void *data);
-
-       DEPRECATED void sub_image(TextureCubeFace face, unsigned level,
-               int x, int y, unsigned w, unsigned h,
-               PixelComponents comp, DataType type, const void *data);
-
-       void image(TextureCubeFace, const Graphics::Image &);
-
-       DEPRECATED void image(TextureCubeFace, const Graphics::Image &, bool);
-
-       virtual void image(const Graphics::Image &, unsigned = 0);
-       using Texture::image;
-
-       unsigned get_size() const { return size; }
-private:
-       unsigned get_n_levels() const;
-       unsigned get_level_size(unsigned) const;
-
-public:
-       /** Translates indices into face constants.  Valid indices are between 0
-       and 5, inclusive. */
-       static TextureCubeFace enumerate_faces(unsigned);
-
-       static unsigned get_face_index(TextureCubeFace);
-
-       /** Returns a vector pointing out of the face. */
-       static const Vector3 &get_face_direction(TextureCubeFace);
-
-       /** Returns a vector in the direction of the s axis of the face. */
-       static const Vector3 &get_s_direction(TextureCubeFace);
-
-       /** Returns a vector in the direction of the t axis of the face. */
-       static const Vector3 &get_t_direction(TextureCubeFace);
-
-       /** Returns a vector pointing to the center a texel. */
-       Vector3 get_texel_direction(TextureCubeFace, unsigned, unsigned);
-
-       virtual AsyncLoader *load(IO::Seekable &, const Resources * = 0) { return 0; }
-       virtual UInt64 get_data_size() const;
-       virtual void unload() { }
-};
-
-void operator>>(const LexicalConverter &, TextureCubeFace &);
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/texturing.cpp b/source/texturing.cpp
deleted file mode 100644 (file)
index ab5d39b..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-#include <msp/core/hash.h>
-#include "texture.h"
-#include "texturing.h"
-#include "texunit.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-Texturing::~Texturing()
-{
-       if(current()==this)
-               unbind();
-}
-
-int Texturing::find_free_unit(const string &name_hint) const
-{
-       unsigned max_unit = TexUnit::get_n_units();
-       // Leave some space for effect textures
-       max_unit -= min(max_unit/4, 8U);
-       unsigned initial_unit = (name_hint.empty() ? 0 : hash32(name_hint)%max_unit);
-       unsigned unit = initial_unit;
-       while(get_attached_texture(unit) || get_attached_sampler(unit))
-       {
-               unit = (unit+1)%max_unit;
-               if(unit==initial_unit)
-                       return -1;
-       }
-
-       return unit;
-}
-
-void Texturing::attach(unsigned attch, const Texture &tex, const Sampler *samp)
-{
-       set_attachment(attch, &tex, samp);
-}
-
-void Texturing::attach(unsigned attch, const Sampler &samp)
-{
-       set_attachment(attch, 0, &samp);
-}
-
-void Texturing::detach(unsigned attch)
-{
-       set_attachment(attch, 0, 0);
-}
-
-void Texturing::set_attachment(unsigned unit, const Texture *tex, const Sampler *samp)
-{
-       if(unit>=TexUnit::get_n_units())
-               throw out_of_range("Texturing::set_attachment");
-
-       if(tex || samp)
-       {
-               vector<Attachment>::iterator i;
-               for(i=attachments.begin(); (i!=attachments.end() && i->unit<=unit); ++i)
-                       if(i->unit==unit)
-                       {
-                               i->texture = tex;
-                               i->sampler = samp;
-                               if(current()==this)
-                                       bind_attachment(*i);
-                               return;
-                       }
-
-               attachments.insert(i, Attachment(unit, tex, samp));
-               if(current()==this)
-                       tex->bind_to(unit);
-       }
-       else
-       {
-               for(vector<Attachment>::iterator i=attachments.begin(); (i!=attachments.end() && i->unit<=unit); ++i)
-                       if(i->unit==unit)
-                       {
-                               attachments.erase(i);
-                               if(current()==this)
-                                       unbind_attachment(unit);
-                               return;
-                       }
-       }
-}
-
-const Texture *Texturing::get_attached_texture(unsigned unit) const
-{
-       for(vector<Attachment>::const_iterator i=attachments.begin(); (i!=attachments.end() && i->unit<=unit); ++i)
-               if(i->unit==unit)
-                       return i->texture;
-       return 0;
-}
-
-const Sampler *Texturing::get_attached_sampler(unsigned unit) const
-{
-       for(vector<Attachment>::const_iterator i=attachments.begin(); (i!=attachments.end() && i->unit<=unit); ++i)
-               if(i->unit==unit)
-                       return i->sampler;
-       return 0;
-}
-
-void Texturing::bind() const
-{
-       const Texturing *old = current();
-       if(set_current(this))
-       {
-               if(old)
-               {
-                       vector<Attachment>::const_iterator i = attachments.begin();
-                       vector<Attachment>::const_iterator j = old->attachments.begin();
-                       while(i!=attachments.end() || j!=old->attachments.end())
-                       {
-                               if(i!=attachments.end() && (j==old->attachments.end() || i->unit<=j->unit))
-                               {
-                                       bind_attachment(*i);
-                                       if(j!=old->attachments.end() && j->unit==i->unit)
-                                               ++j;
-                                       ++i;
-                               }
-                               else
-                               {
-                                       unbind_attachment(j->unit);
-                                       ++j;
-                               }
-                       }
-               }
-               else
-               {
-                       for(vector<Attachment>::const_iterator i=attachments.begin(); i!=attachments.end(); ++i)
-                               bind_attachment(*i);
-               }
-       }
-}
-
-void Texturing::bind_attachment(const Attachment &attch) const
-{
-       if(attch.sampler)
-               attch.sampler->bind_to(attch.unit);
-       else
-               Sampler::unbind_from(attch.unit);
-       attch.texture->bind_to(attch.unit);
-}
-
-void Texturing::unbind()
-{
-       const Texturing *old = current();
-       if(set_current(0))
-       {
-               for(vector<Attachment>::const_iterator i=old->attachments.begin(); i!=old->attachments.end(); ++i)
-                       unbind_attachment(i->unit);
-       }
-}
-
-void Texturing::unbind_attachment(unsigned unit)
-{
-       Texture::unbind_from(unit);
-       Sampler::unbind_from(unit);
-}
-
-
-Texturing::Attachment::Attachment(unsigned u, const Texture *t, const Sampler *s):
-       unit(u),
-       texture(t),
-       sampler(s)
-{ }
-
-} // namespace GL
-} // namespace Msp;
diff --git a/source/texturing.h b/source/texturing.h
deleted file mode 100644 (file)
index 0da3eae..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-#ifndef MSP_GL_TEXTURING_H_
-#define MSP_GL_TEXTURING_H_
-
-#include <vector>
-#include "bindable.h"
-
-namespace Msp {
-namespace GL {
-
-class Texture;
-
-class Texturing: public Bindable<Texturing>
-{
-private:
-       struct Attachment
-       {
-               unsigned unit;
-               const Texture *texture;
-               const Sampler *sampler;
-
-               Attachment(unsigned, const Texture *, const Sampler *);
-       };
-
-       std::vector<Attachment> attachments;
-
-public:
-       ~Texturing();
-
-       int find_free_unit(const std::string & = std::string()) const;
-       void attach(unsigned, const Texture &, const Sampler * = 0);
-       void attach(unsigned, const Sampler &);
-       void detach(unsigned);
-private:
-       void set_attachment(unsigned, const Texture *, const Sampler *);
-public:
-       const Texture *get_attached_texture(unsigned) const;
-       const Sampler *get_attached_sampler(unsigned) const;
-
-       void bind() const;
-
-       static void unbind();
-
-private:
-       void bind_attachment(const Attachment &) const;
-       static void unbind_attachment(unsigned);
-};
-
-} // namespace GL
-} // namespace Msp;
-
-#endif
diff --git a/source/texunit.cpp b/source/texunit.cpp
deleted file mode 100644 (file)
index 0659d61..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-#include <stdexcept>
-#include <msp/gl/extensions/arb_multitexture.h>
-#include <msp/gl/extensions/arb_vertex_shader.h>
-#include "gl.h"
-#include "misc.h"
-#include "texture.h"
-#include "texunit.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-vector<TexUnit> TexUnit::units;
-TexUnit *TexUnit::cur_unit = 0;
-
-TexUnit::TexUnit():
-       texture(0),
-       sampler(0)
-{ }
-
-bool TexUnit::set_texture(const Texture *tex)
-{
-       bool result = (tex!=texture);
-       texture = tex;
-       return result;
-}
-
-bool TexUnit::set_sampler(const Sampler *samp)
-{
-       bool result = (samp!=sampler);
-       sampler = samp;
-       return result;
-}
-
-void TexUnit::bind()
-{
-       if(cur_unit!=this && (cur_unit || index))
-               glActiveTexture(GL_TEXTURE0+index);
-       cur_unit = this;
-}
-
-unsigned TexUnit::get_n_units()
-{
-       static int count = -1;
-       if(count<0)
-       {
-               if(ARB_vertex_shader)
-                       count = get_i(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS);
-               else if(ARB_multitexture)
-                       count = get_i(GL_MAX_TEXTURE_UNITS);
-               else
-                       count = 1;
-       }
-       return count;
-}
-
-TexUnit &TexUnit::get_unit(unsigned n)
-{
-       if(n>0)
-               static Require _req(ARB_multitexture);
-       if(n>=get_n_units())
-               throw out_of_range("TexUnit::get_unit");
-
-       if(units.size()<=n)
-       {
-               unsigned i = units.size();
-               units.resize(n+1, TexUnit());
-               for(; i<units.size(); ++i)
-                       units[i].index = i;
-       }
-
-       return units[n];
-}
-
-TexUnit &TexUnit::current()
-{
-       if(!cur_unit)
-               get_unit(0).bind();
-       return *cur_unit;
-}
-
-TexUnit *TexUnit::find_unit(const Texture *tex)
-{
-       for(vector<TexUnit>::iterator i=units.begin(); i!=units.end(); ++i)
-               if(i->texture==tex)
-                       return &*i;
-       return 0;
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/texunit.h b/source/texunit.h
deleted file mode 100644 (file)
index 6253d8d..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef MSP_GL_TEXUNIT_H_
-#define MSP_GL_TEXUNIT_H_
-
-#include <vector>
-
-namespace Msp {
-namespace GL {
-
-class Sampler;
-class Texture;
-
-/**
-Keeps track of texture unit related state.  Intended for internal use.
-*/
-class TexUnit
-{
-private:
-       unsigned index;
-       const Texture *texture;
-       const Sampler *sampler;
-
-       static std::vector<TexUnit> units;
-       static TexUnit *cur_unit;
-
-       TexUnit();
-
-public:
-       unsigned get_index() const { return index; }
-       bool set_texture(const Texture *);
-       const Texture *get_texture() const { return texture; }
-       bool set_sampler(const Sampler *);
-       const Sampler *get_sampler() const { return sampler; }
-       void bind();
-
-       static unsigned get_n_units();
-       static TexUnit &get_unit(unsigned);
-       static TexUnit &current();
-       static TexUnit *find_unit(const Texture *);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/transform.cpp b/source/transform.cpp
deleted file mode 100644 (file)
index b703a5b..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-#include "transform.h"
-
-namespace Msp {
-namespace GL {
-
-Transform::Transform():
-       position(0.0f, 0.0f, 0.0f),
-       euler(Angle::zero(), Angle::zero(), Angle::zero()),
-       scale(1.0f, 1.0f, 1.0f),
-       mask(NONE)
-{ }
-
-Transform Transform::from_matrix(const Matrix &matrix)
-{
-       Transform trn;
-       trn.position = matrix.column(3).slice<3>(0);
-
-       trn.euler.z = Geometry::atan2<float>(matrix(1, 0), matrix(0, 0));
-       Matrix m = Matrix::rotation(-trn.euler.z, Vector3(0.0f, 0.0f, 1.0f))*matrix;
-       trn.euler.y = Geometry::atan2<float>(-m(2, 0), m(0, 0));
-       m = Matrix::rotation(-trn.euler.y, Vector3(0.0f, 1.0f, 0.0f))*m;
-       trn.euler.x = Geometry::atan2<float>(m(2, 1), m(1, 1));
-       m = Matrix::rotation(-trn.euler.x, Vector3(1.0f, 0.0f, 0.0f))*m;
-
-       trn.scale = Vector3(m(0, 0), m(1, 1), m(2, 2));
-
-       trn.mask = POSITION|EULER|SCALE;
-       return trn;
-}
-
-void Transform::set_position(const Vector3 &p)
-{
-       position = p;
-       mask = mask|POSITION;
-}
-
-void Transform::set_euler(const AngleVector3 &e)
-{
-       euler = e;
-       mask = mask|EULER;
-}
-
-void Transform::set_rotation(const Angle &angle, const Vector3 &axis)
-{
-       euler = from_matrix(Matrix::rotation(angle, axis)).euler;
-       mask = mask|EULER;
-}
-
-void Transform::set_scale(float s)
-{
-       set_scale(Vector3(s, s, s));
-}
-
-void Transform::set_scale(const Vector3 &s)
-{
-       scale = s;
-       mask = mask|SCALE;
-}
-
-Matrix Transform::to_matrix() const
-{
-       Matrix result;
-       result.translate(position);
-       result.rotate(euler.z, Vector3(0.0f, 0.0f, 1.0f));
-       result.rotate(euler.y, Vector3(0.0f, 1.0f, 0.0f));
-       result.rotate(euler.x, Vector3(1.0f, 0.0f, 0.0f));
-       result.scale(scale);
-       return result;
-}
-
-
-Transform::Loader::Loader(Transform &t):
-       DataFile::ObjectLoader<Transform>(t)
-{
-       add("position_x", &Loader::position_x);
-       add("position_y", &Loader::position_y);
-       add("position_z", &Loader::position_z);
-       add("position", &Loader::position);
-       add("euler_x", &Loader::euler_x);
-       add("euler_y", &Loader::euler_y);
-       add("euler_z", &Loader::euler_z);
-       add("euler", &Loader::euler);
-       add("rotation", &Loader::rotation);
-       add("scale_x", &Loader::scale_x);
-       add("scale_y", &Loader::scale_y);
-       add("scale_z", &Loader::scale_z);
-       add("scale", &Loader::scale_uniform);
-       add("scale", &Loader::scale);
-}
-
-void Transform::Loader::position_x(float x)
-{
-       obj.position.x = x;
-       obj.mask = obj.mask|POSITION_X;
-}
-
-void Transform::Loader::position_y(float y)
-{
-       obj.position.y = y;
-       obj.mask = obj.mask|POSITION_Y;
-}
-
-void Transform::Loader::position_z(float z)
-{
-       obj.position.z = z;
-       obj.mask = obj.mask|POSITION_Z;
-}
-
-void Transform::Loader::position(float x, float y, float z)
-{
-       obj.set_position(Vector3(x, y, z));
-}
-
-void Transform::Loader::euler_x(float x)
-{
-       obj.euler.x = Angle::from_degrees(x);
-       obj.mask = obj.mask|EULER_X;
-}
-
-void Transform::Loader::euler_y(float y)
-{
-       obj.euler.y = Angle::from_degrees(y);
-       obj.mask = obj.mask|EULER_Y;
-}
-
-void Transform::Loader::euler_z(float z)
-{
-       obj.euler.z = Angle::from_degrees(z);
-       obj.mask = obj.mask|EULER_Z;
-}
-
-void Transform::Loader::euler(float x, float y, float z)
-{
-       obj.set_euler(AngleVector3(Angle::from_degrees(x), Angle::from_degrees(y), Angle::from_degrees(z)));
-}
-
-void Transform::Loader::rotation(float a, float x, float y, float z)
-{
-       obj.set_rotation(Angle::from_degrees(a), Vector3(x, y, z));
-}
-
-void Transform::Loader::scale_x(float x)
-{
-       obj.scale.x = x;
-       obj.mask = obj.mask|SCALE_X;
-}
-
-void Transform::Loader::scale_y(float y)
-{
-       obj.scale.y = y;
-       obj.mask = obj.mask|SCALE_Y;
-}
-
-void Transform::Loader::scale_z(float z)
-{
-       obj.scale.z = z;
-       obj.mask = obj.mask|SCALE_Z;
-}
-
-void Transform::Loader::scale_uniform(float s)
-{
-       obj.set_scale(s);
-}
-
-void Transform::Loader::scale(float x, float y, float z)
-{
-       obj.set_scale(Vector3(x, y, z));
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/transform.h b/source/transform.h
deleted file mode 100644 (file)
index 1d0aedd..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-#ifndef MSP_GL_TRANSFORM_H_
-#define MSP_GL_TRANSFORM_H_
-
-#include <msp/datafile/objectloader.h>
-#include "matrix.h"
-
-namespace Msp {
-namespace GL {
-
-/**
-Stores a coordinate space transform as individual components.  Primarily
-intended for loading data from external sources.  At runtime transforms
-should generally be stored as matrices.
-*/
-class Transform
-{
-public:
-       class Loader: public DataFile::ObjectLoader<Transform>
-       {
-       public:
-               Loader(Transform &);
-
-       private:
-               void position_x(float);
-               void position_y(float);
-               void position_z(float);
-               void position(float, float, float);
-               void euler_x(float);
-               void euler_y(float);
-               void euler_z(float);
-               void euler(float, float, float);
-               void rotation(float, float, float, float);
-               void scale_x(float);
-               void scale_y(float);
-               void scale_z(float);
-               void scale_uniform(float);
-               void scale(float, float, float);
-       };
-
-       enum ComponentMask
-       {
-               NONE = 0,
-               POSITION_X = 1,
-               POSITION_Y = 2,
-               POSITION_Z = 4,
-               POSITION = POSITION_X|POSITION_Y|POSITION_Z,
-               EULER_X = 8,
-               EULER_Y = 16,
-               EULER_Z = 32,
-               EULER = EULER_X|EULER_Y|EULER_Z,
-               SCALE_X = 64,
-               SCALE_Y = 128,
-               SCALE_Z = 256,
-               SCALE = SCALE_X|SCALE_Y|SCALE_Z
-       };
-
-       typedef Geometry::Angle<float> Angle;
-       typedef LinAl::Vector<Angle, 3> AngleVector3;
-
-private:
-       Vector3 position;
-       AngleVector3 euler;
-       Vector3 scale;
-       ComponentMask mask;
-
-public:
-       Transform();
-
-       static Transform from_matrix(const Matrix &);
-
-       void set_position(const Vector3 &);
-       void set_euler(const AngleVector3 &);
-       void set_rotation(const Angle &, const Vector3 &);
-       void set_scale(float);
-       void set_scale(const Vector3 &);
-       const Vector3 &get_position() const { return position; }
-       const AngleVector3 &get_euler() const { return euler; }
-       const Vector3 &get_scale() const { return scale; }
-       ComponentMask get_mask() const { return mask; }
-
-       Matrix to_matrix() const;
-};
-
-inline Transform::ComponentMask operator&(Transform::ComponentMask m1, Transform::ComponentMask m2)
-{ return static_cast<Transform::ComponentMask>(static_cast<int>(m1)&static_cast<int>(m2)); }
-
-inline Transform::ComponentMask operator|(Transform::ComponentMask m1, Transform::ComponentMask m2)
-{ return static_cast<Transform::ComponentMask>(static_cast<int>(m1)|static_cast<int>(m2)); }
-
-inline Transform::ComponentMask operator^(Transform::ComponentMask m1, Transform::ComponentMask m2)
-{ return static_cast<Transform::ComponentMask>(static_cast<int>(m1)^static_cast<int>(m2)); }
-
-inline Transform::ComponentMask operator~(Transform::ComponentMask m)
-{ return static_cast<Transform::ComponentMask>(~static_cast<int>(m)); }
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/uniform.cpp b/source/uniform.cpp
deleted file mode 100644 (file)
index c18fc36..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-#include <msp/gl/extensions/arb_shader_objects.h>
-#include <msp/gl/extensions/nv_non_square_matrices.h>
-#include "uniform.h"
-
-namespace Msp {
-namespace GL {
-
-template<>
-void UniformScalar<int>::apply(int index, unsigned size, const int *value)
-{
-       glUniform1iv(index, size, value);
-}
-
-template<>
-void UniformScalar<float>::apply(int index, unsigned size, const float *value)
-{
-       glUniform1fv(index, size, value);
-}
-
-
-template<>
-void UniformVector<int, 2>::apply(int index, unsigned size, const int *value)
-{
-       glUniform2iv(index, size, value);
-}
-
-template<>
-void UniformVector<float, 2>::apply(int index, unsigned size, const float *value)
-{
-       glUniform2fv(index, size, value);
-}
-
-template<>
-void UniformVector<int, 3>::apply(int index, unsigned size, const int *value)
-{
-       glUniform3iv(index, size, value);
-}
-
-template<>
-void UniformVector<float, 3>::apply(int index, unsigned size, const float *value)
-{
-       glUniform3fv(index, size, value);
-}
-
-template<>
-void UniformVector<int, 4>::apply(int index, unsigned size, const int *value)
-{
-       glUniform4iv(index, size, value);
-}
-
-template<>
-void UniformVector<float, 4>::apply(int index, unsigned size, const float *value)
-{
-       glUniform4fv(index, size, value);
-}
-
-
-template<>
-void UniformMatrix<float, 2, 2>::apply(int index, unsigned size, const float *value)
-{
-       glUniformMatrix2fv(index, size, false, value);
-}
-
-template<>
-void UniformMatrix<float, 2, 3>::apply(int index, unsigned size, const float *value)
-{
-       glUniformMatrix3x2fv(index, size, false, value);
-}
-
-template<>
-void UniformMatrix<float, 2, 4>::apply(int index, unsigned size, const float *value)
-{
-       glUniformMatrix4x2fv(index, size, false, value);
-}
-
-template<>
-void UniformMatrix<float, 3, 2>::apply(int index, unsigned size, const float *value)
-{
-       glUniformMatrix2x3fv(index, size, false, value);
-}
-
-template<>
-void UniformMatrix<float, 3, 3>::apply(int index, unsigned size, const float *value)
-{
-       glUniformMatrix3fv(index, size, false, value);
-}
-
-template<>
-void UniformMatrix<float, 3, 4>::apply(int index, unsigned size, const float *value)
-{
-       glUniformMatrix4x3fv(index, size, false, value);
-}
-
-template<>
-void UniformMatrix<float, 4, 2>::apply(int index, unsigned size, const float *value)
-{
-       glUniformMatrix2x4fv(index, size, false, value);
-}
-
-template<>
-void UniformMatrix<float, 4, 3>::apply(int index, unsigned size, const float *value)
-{
-       glUniformMatrix3x4fv(index, size, false, value);
-}
-
-template<>
-void UniformMatrix<float, 4, 4>::apply(int index, unsigned size, const float *value)
-{
-       glUniformMatrix4fv(index, size, false, value);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/uniform.h b/source/uniform.h
deleted file mode 100644 (file)
index bc78bab..0000000
+++ /dev/null
@@ -1,193 +0,0 @@
-#ifndef MSP_GL_UNIFORM_H_
-#define MSP_GL_UNIFORM_H_
-
-#include <algorithm>
-#include "program.h"
-
-namespace Msp {
-namespace GL {
-
-class Uniform
-{
-protected:
-       Uniform() { }
-private:
-       Uniform(const Uniform &);
-       Uniform &operator=(const Uniform &);
-public:
-       virtual ~Uniform() { }
-
-       virtual void apply(int) const = 0;
-       virtual void store(const Program::UniformInfo &, void *) const = 0;
-       virtual Uniform *clone() const = 0;
-};
-
-
-template<typename T>
-class UniformScalar: public Uniform
-{
-public:
-       typedef T BaseType;
-       typedef T Type;
-
-private:
-       Type value;
-
-public:
-       UniformScalar(Type v): value(v) { }
-
-       void set(Type v) { value = v; }
-
-       Type get() const { return value; }
-
-       virtual void apply(int index) const
-       { apply(index, 1, &value); }
-
-       static void apply(int, unsigned, const T *);
-
-       virtual void store(const Program::UniformInfo &info, void *buffer) const
-       { store(info, buffer, &value); }
-
-       static void store(const Program::UniformInfo &, void *buffer, const T *value)
-       { *reinterpret_cast<T *>(buffer) = *value; }
-
-       virtual UniformScalar *clone() const
-       { return new UniformScalar(value); }
-};
-
-typedef UniformScalar<int> Uniform1i;
-typedef UniformScalar<float> Uniform1f;
-
-
-template<typename T, unsigned vecsize>
-class UniformVector: public Uniform
-{
-public:
-       typedef T BaseType;
-       typedef T Type[vecsize];
-
-private:
-       Type value;
-
-public:
-       UniformVector(const T *vp) { set(vp); }
-
-       void set(const T *vp)
-       { std::copy(vp, vp+vecsize, value); }
-
-       BaseType get(unsigned i) const { return value[i]; }
-
-       virtual void apply(int index) const
-       { apply(index, 1, value); }
-
-       static void apply(int index, unsigned size, const T *value);
-
-       virtual void store(const Program::UniformInfo &info, void *buffer) const
-       { store(info, buffer, value); }
-
-       static void store(const Program::UniformInfo &, void *buffer, const T *value)
-       { std::copy(value, value+vecsize, reinterpret_cast<T *>(buffer)); }
-
-       virtual UniformVector *clone() const
-       { return new UniformVector(value); }
-};
-
-typedef UniformVector<int, 2> Uniform2i;
-typedef UniformVector<float, 2> Uniform2f;
-typedef UniformVector<int, 3> Uniform3i;
-typedef UniformVector<float, 3> Uniform3f;
-typedef UniformVector<int, 4> Uniform4i;
-typedef UniformVector<float, 4> Uniform4f;
-
-
-template<typename T, unsigned rows, unsigned cols>
-class UniformMatrix: public Uniform
-{
-public:
-       typedef T BaseType;
-       typedef T Type[rows*cols];
-
-private:
-       Type value;
-
-public:
-       UniformMatrix(const T *vp) { set(vp); }
-
-       void set(const T *vp)
-       { std::copy(vp, vp+rows*cols, value); }
-
-       virtual void apply(int index) const
-       { apply(index, 1, value); }
-
-       static void apply(int index, unsigned size, const T *value);
-
-       virtual void store(const Program::UniformInfo &info, void *buffer) const
-       { store(info, buffer, value); }
-
-       static void store(const Program::UniformInfo &info, void *buffer, const T *value)
-       {
-               for(unsigned i=0; i<cols; ++i)
-                       UniformVector<T, rows>::store(info, reinterpret_cast<char *>(buffer)+i*info.matrix_stride, value+i*rows);
-       }
-
-       virtual UniformMatrix *clone() const
-       { return new UniformMatrix(value); }
-};
-
-// The naming of these types follows the OpenGL convention of columns x rows
-typedef UniformMatrix<float, 2, 2> UniformMatrix2x2f;
-typedef UniformMatrix<float, 2, 3> UniformMatrix3x2f;
-typedef UniformMatrix<float, 2, 4> UniformMatrix4x2f;
-typedef UniformMatrix<float, 3, 2> UniformMatrix2x3f;
-typedef UniformMatrix<float, 3, 3> UniformMatrix3x3f;
-typedef UniformMatrix<float, 3, 4> UniformMatrix4x3f;
-typedef UniformMatrix<float, 4, 2> UniformMatrix2x4f;
-typedef UniformMatrix<float, 4, 3> UniformMatrix3x4f;
-typedef UniformMatrix<float, 4, 4> UniformMatrix4x4f;
-
-
-template<typename T>
-class UniformArray: public Uniform
-{
-private:
-       typedef typename T::BaseType BaseType;
-       enum { elemsize = sizeof(typename T::Type)/sizeof(typename T::BaseType) };
-
-       unsigned size_;
-       BaseType *values;
-
-public:
-       UniformArray(unsigned n, const BaseType *vp):
-               size_(n),
-               values(new BaseType[elemsize*size_])
-       {
-               set(vp);
-       }
-
-       ~UniformArray()
-       {
-               delete[] values;
-       }
-
-       unsigned size() const { return size_; }
-
-       void set(const BaseType *vp)
-       { std::copy(vp, vp+elemsize*size_, values); }
-
-       virtual void apply(int index) const
-       { T::apply(index, size_, values); }
-
-       virtual void store(const Program::UniformInfo &info, void *buffer) const
-       {
-               for(unsigned i=0; i<size_; ++i)
-                       T::store(info, reinterpret_cast<char *>(buffer)+i*info.array_stride, values+i*elemsize);
-       }
-
-       virtual UniformArray *clone() const
-       { return new UniformArray(size_, values); }
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/uniformblock.cpp b/source/uniformblock.cpp
deleted file mode 100644 (file)
index cc8945c..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-#include <stdexcept>
-#include <msp/gl/extensions/arb_shader_objects.h>
-#include <msp/gl/extensions/arb_uniform_buffer_object.h>
-#include "buffer.h"
-#include "color.h"
-#include "error.h"
-#include "matrix.h"
-#include "uniform.h"
-#include "uniformblock.h"
-#include "vector.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-UniformBlock::UniformBlock():
-       size(0),
-       buf_range(0)
-{
-       static Require _req(ARB_shader_objects);
-}
-
-UniformBlock::UniformBlock(unsigned s):
-       size(s),
-       buf_range(0)
-{
-       static Require _req(ARB_uniform_buffer_object);
-
-       if(!size)
-               throw invalid_argument("UniformBlock::UniformBlock");
-       data.resize(size);
-}
-
-UniformBlock::~UniformBlock()
-{
-       delete buf_range;
-}
-
-unsigned UniformBlock::get_alignment() const
-{
-       return BufferRange::get_uniform_buffer_alignment();
-}
-
-void UniformBlock::location_changed(Buffer *buf, unsigned off, unsigned) const
-{
-       delete buf_range;
-       buf_range = new BufferRange(*buf, off, size);
-}
-
-void UniformBlock::attach(int index, const Uniform &uni)
-{
-       if(size)
-               throw invalid_operation("UniformBlock::attach");
-
-       uniforms[index] = &uni;
-}
-
-void UniformBlock::attach(const Program::UniformInfo &info, const Uniform &uni)
-{
-       if(size)
-       {
-               uni.store(info, &data[info.location]);
-               dirty = true;
-       }
-       else
-               uniforms[info.location] = &uni;
-}
-
-void UniformBlock::apply(int index) const
-{
-       if((index>=0) != (size>0))
-               throw invalid_operation("UniformBlock::apply");
-
-       if(size)
-       {
-               if(!get_buffer())
-                       throw invalid_operation("UniformBlock::apply");
-
-               refresh();
-               buf_range->bind_to(UNIFORM_BUFFER, index);
-       }
-       else
-       {
-               for(map<int, const Uniform *>::const_iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
-                       i->second->apply(i->first);
-       }
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/uniformblock.h b/source/uniformblock.h
deleted file mode 100644 (file)
index c8b8d36..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-#ifndef MSP_GL_UNIFORMBLOCK_H_
-#define MSP_GL_UNIFORMBLOCK_H_
-
-#include <map>
-#include <vector>
-#include "bufferable.h"
-#include "program.h"
-#include "vector.h"
-
-namespace Msp {
-namespace GL {
-
-class BufferRange;
-class Matrix;
-class Uniform;
-struct Color;
-
-/**
-Stores uniforms with a specific layout.  Both named and default uniform blocks
-are supported.
-*/
-class UniformBlock: public Bufferable
-{
-private:
-       std::map<int, const Uniform *> uniforms;
-       unsigned size;
-       std::vector<char> data;
-       mutable BufferRange *buf_range;
-
-       UniformBlock(const UniformBlock &);
-       UniformBlock &operator=(const UniformBlock &);
-public:
-       UniformBlock();
-       UniformBlock(unsigned);
-       ~UniformBlock();
-
-private:
-       virtual unsigned get_data_size() const { return size; }
-       virtual const void *get_data_pointer() const { return &data[0]; }
-       virtual unsigned get_alignment() const;
-       virtual void location_changed(Buffer *, unsigned, unsigned) const;
-
-public:
-       void attach(int, const Uniform &);
-       void attach(const Program::UniformInfo &, const Uniform &);
-
-       void apply(int) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/vector.h b/source/vector.h
deleted file mode 100644 (file)
index 5a4ffb6..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef MSP_GL_VECTOR_H_
-#define MSP_GL_VECTOR_H_
-
-#include <msp/linal/vector.h>
-
-namespace Msp {
-namespace GL {
-
-typedef LinAl::Vector<float, 3> Vector3;
-typedef LinAl::Vector<float, 4> Vector4;
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/vertexarray.cpp b/source/vertexarray.cpp
deleted file mode 100644 (file)
index e3aef7a..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-#include <msp/gl/extensions/arb_multitexture.h>
-#include <msp/gl/extensions/arb_vertex_shader.h>
-#include "buffer.h"
-#include "error.h"
-#include "gl.h"
-#include "mesh.h"
-#include "vertexarray.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-VertexArray::VertexArray(const VertexFormat &f)
-{
-       reset(f);
-}
-
-void VertexArray::reset(const VertexFormat &f)
-{
-       clear();
-       format = f;
-       stride = get_stride(format);
-}
-
-void VertexArray::clear()
-{
-       data.clear();
-}
-
-void VertexArray::reserve(unsigned n)
-{
-       data.reserve(n*stride);
-}
-
-float *VertexArray::append()
-{
-       data.insert(data.end(), stride, 0.0f);
-       update_offset();
-       dirty = true;
-       return &*(data.end()-stride);
-}
-
-float *VertexArray::modify(unsigned i)
-{
-       dirty = true;
-       return &data[0]+i*stride;
-}
-
-unsigned VertexArray::get_data_size() const
-{
-       return data.size()*sizeof(float);
-}
-
-
-VertexArray::Loader::Loader(VertexArray &a):
-       VertexArrayBuilder(a)
-{
-       add("vertex", static_cast<void (Loader::*)(float, float)>(&Loader::vertex));
-       add("vertex", static_cast<void (Loader::*)(float, float, float)>(&Loader::vertex));
-       add("vertex", static_cast<void (Loader::*)(float, float, float, float)>(&Loader::vertex));
-       add("normal", static_cast<void (Loader::*)(float, float, float)>(&Loader::normal));
-       add("texcoord", static_cast<void (Loader::*)(float)>(&Loader::texcoord));
-       add("texcoord", static_cast<void (Loader::*)(float, float)>(&Loader::texcoord));
-       add("texcoord", static_cast<void (Loader::*)(float, float, float)>(&Loader::texcoord));
-       add("texcoord", static_cast<void (Loader::*)(float, float, float, float)>(&Loader::texcoord));
-       add("multitexcoord", static_cast<void (Loader::*)(unsigned, float)>(&Loader::multitexcoord));
-       add("multitexcoord", static_cast<void (Loader::*)(unsigned, float, float)>(&Loader::multitexcoord));
-       add("multitexcoord", static_cast<void (Loader::*)(unsigned, float, float, float)>(&Loader::multitexcoord));
-       add("multitexcoord", static_cast<void (Loader::*)(unsigned, float, float, float, float)>(&Loader::multitexcoord));
-       add("color", static_cast<void (Loader::*)(float, float, float)>(&Loader::color));
-       add("color", static_cast<void (Loader::*)(float, float, float, float)>(&Loader::color));
-       add("attrib", static_cast<void (Loader::*)(unsigned, float)>(&Loader::attrib));
-       add("attrib", static_cast<void (Loader::*)(unsigned, float, float)>(&Loader::attrib));
-       add("attrib", static_cast<void (Loader::*)(unsigned, float, float, float)>(&Loader::attrib));
-       add("attrib", static_cast<void (Loader::*)(unsigned, float, float, float, float)>(&Loader::attrib));
-       add("tangent", static_cast<void (Loader::*)(float, float, float)>(&Loader::tangent));
-       add("binormal", static_cast<void (Loader::*)(float, float, float)>(&Loader::binormal));
-
-       add("vertex2",   static_cast<void (Loader::*)(float, float)>(&Loader::vertex));
-       add("vertex3",   static_cast<void (Loader::*)(float, float, float)>(&Loader::vertex));
-       add("vertex4",   static_cast<void (Loader::*)(float, float, float, float)>(&Loader::vertex));
-       add("normal3",   static_cast<void (Loader::*)(float, float, float)>(&Loader::normal));
-       add("texcoord1", static_cast<void (Loader::*)(float)>(&Loader::texcoord));
-       add("texcoord2", static_cast<void (Loader::*)(float, float)>(&Loader::texcoord));
-       add("texcoord3", static_cast<void (Loader::*)(float, float, float)>(&Loader::texcoord));
-       add("texcoord4", static_cast<void (Loader::*)(float, float, float, float)>(&Loader::texcoord));
-       add("multitexcoord1", static_cast<void (Loader::*)(unsigned, float)>(&Loader::multitexcoord));
-       add("multitexcoord2", static_cast<void (Loader::*)(unsigned, float, float)>(&Loader::multitexcoord));
-       add("multitexcoord3", static_cast<void (Loader::*)(unsigned, float, float, float)>(&Loader::multitexcoord));
-       add("multitexcoord4", static_cast<void (Loader::*)(unsigned, float, float, float, float)>(&Loader::multitexcoord));
-       add("color3",    static_cast<void (Loader::*)(float, float, float)>(&Loader::color));
-       add("color4",    static_cast<void (Loader::*)(float, float, float, float)>(&Loader::color));
-       add("attrib1",   static_cast<void (Loader::*)(unsigned, float)>(&Loader::attrib));
-       add("attrib2",   static_cast<void (Loader::*)(unsigned, float, float)>(&Loader::attrib));
-       add("attrib3",   static_cast<void (Loader::*)(unsigned, float, float, float)>(&Loader::attrib));
-       add("attrib4",   static_cast<void (Loader::*)(unsigned, float, float, float, float)>(&Loader::attrib));
-       add("tangent3",  static_cast<void (Loader::*)(float, float, float)>(&Loader::tangent));
-       add("binormal3", static_cast<void (Loader::*)(float, float, float)>(&Loader::binormal));
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/vertexarray.h b/source/vertexarray.h
deleted file mode 100644 (file)
index 95a6ec2..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-#ifndef MSP_GL_VERTEXARRAY_H_
-#define MSP_GL_VERTEXARRAY_H_
-
-#include <climits>
-#include <vector>
-#include <msp/core/refptr.h>
-#include <msp/datafile/loader.h>
-#include "bufferable.h"
-#include "datatype.h"
-#include "primitivetype.h"
-#include "vertexarraybuilder.h"
-#include "vertexformat.h"
-
-namespace Msp {
-namespace GL {
-
-class Buffer;
-
-/**
-Stores vertex data.
-
-The array's contents can be modified with the append and modify methods.  To
-obtain the location of an individual component within the vertex, use
-VertexFormat::offset.
-
-A higher-level interface for filling in vertex data is available in the
-VertexArrayBuilder class.
-*/
-class VertexArray: public Bufferable
-{
-public:
-       class Loader: public DataFile::Loader, public VertexArrayBuilder
-       {
-       public:
-               Loader(VertexArray &);
-       };
-
-private:
-       VertexFormat format;
-       std::vector<float> data;
-       unsigned stride;
-
-       VertexArray(const VertexArray &);
-       VertexArray &operator=(const VertexArray &);
-public:
-       VertexArray(const VertexFormat &);
-
-       /// Resets the VertexArray to a different format.  All data is cleared.
-       void reset(const VertexFormat &);
-
-       const VertexFormat &get_format() const { return format; }
-
-       /// Clears all vertices from the array.
-       void clear();
-
-       /// Reserve space for vertices.
-       void reserve(unsigned);
-
-       /// Append a new vertex at the end of the array and return its location.
-       float *append();
-
-       /// Returns the location of a vertex for modification.
-       float *modify(unsigned);
-private:
-       virtual unsigned get_data_size() const;
-       virtual const void *get_data_pointer() const { return &data[0]; }
-
-public:
-       unsigned size() const { return data.size()/stride; }
-       const std::vector<float> &get_data() const { return data; }
-       const float *operator[](unsigned i) const { return &data[0]+i*stride; }
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/vertexarraybuilder.cpp b/source/vertexarraybuilder.cpp
deleted file mode 100644 (file)
index 55b2a6a..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-#include "vertexarray.h"
-#include "vertexarraybuilder.h"
-
-namespace Msp {
-namespace GL {
-
-VertexArrayBuilder::VertexArrayBuilder(VertexArray &a):
-       array(a)
-{ }
-
-void VertexArrayBuilder::vertex_(const Vector4 &ver)
-{
-       float *ptr = array.append();
-       for(const unsigned char *c=array.get_format().begin(); c!=array.get_format().end(); ++c)
-       {
-               unsigned sz = get_component_size(*c);
-               unsigned t = get_component_type(*c);
-               if(*c==COLOR4_UBYTE)
-               {
-                       union { unsigned char c[4]; float f; } u;
-                       u.c[0] = static_cast<unsigned char>(col.r*255);
-                       u.c[1] = static_cast<unsigned char>(col.g*255);
-                       u.c[2] = static_cast<unsigned char>(col.b*255);
-                       u.c[3] = static_cast<unsigned char>(col.a*255);
-                       *ptr++ = u.f;
-               }
-               else if(*c==NORMAL3)
-               {
-                       *ptr++ = nor.x;
-                       *ptr++ = nor.y;
-                       *ptr++ = nor.z;
-               }
-               else if(t==get_component_type(COLOR4_FLOAT))
-               {
-                       *ptr++ = col.r;
-                       *ptr++ = col.g;
-                       *ptr++ = col.b;
-                       if(sz>=4) *ptr++ = col.a;
-               }
-               else
-               {
-                       const Vector4 *v = 0;
-                       if(t==get_component_type(VERTEX3))
-                               v = &ver;
-                       else if(*c>=TEXCOORD1 && *c<=TEXCOORD4+12)
-                               v = &texc[t-get_component_type(TEXCOORD1)];
-                       else if(*c>=ATTRIB1)
-                               v = &attr[t-get_component_type(ATTRIB1)];
-                       else
-                               v = &attr[t];
-                       *ptr++ = v->x;
-                       if(sz>=2) *ptr++ = v->y;
-                       if(sz>=3) *ptr++ = v->z;
-                       if(sz>=4) *ptr++ = v->w;
-               }
-       }
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/vertexarraybuilder.h b/source/vertexarraybuilder.h
deleted file mode 100644 (file)
index 575a475..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef MSP_GL_VERTEXARRAYBUIDER_H_
-#define MSP_GL_VERTEXARRAYBUIDER_H_
-
-#include <vector>
-#include "vertexbuilder.h"
-#include "vertexformat.h"
-
-namespace Msp {
-namespace GL {
-
-class VertexArray;
-
-class VertexArrayBuilder: public VertexBuilder
-{
-private:
-       VertexArray &array;
-
-       VertexArrayBuilder(const VertexArrayBuilder &);
-public:
-       VertexArrayBuilder(VertexArray &);
-
-private:
-       virtual void vertex_(const Vector4 &);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/vertexbuilder.h b/source/vertexbuilder.h
deleted file mode 100644 (file)
index 55f914a..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-#ifndef MSP_GL_VERTEXBUILDER_H_
-#define MSP_GL_VERTEXBUILDER_H_
-
-#include <map>
-#include "color.h"
-#include "matrix.h"
-#include "vector.h"
-#include "vertexformat.h"
-
-namespace Msp {
-namespace GL {
-
-/**
-Base class for classes that build vertices from a series of function calls.
-The operating model closely follows that of OpenGL immediate mode: vertex
-attributes can be specified at any time, and when a vertex() function is
-called, a vertex is created with the active attribute values.
-
-A derived class must overload the 4-argument vertex_() function to process the
-data.  Attributes can be read from protected member variables.
-*/
-class VertexBuilder
-{
-public:
-       class PushMatrix
-       {
-       private:
-               VertexBuilder &bld;
-               Matrix mtx;
-
-       public:
-               PushMatrix(VertexBuilder &b): bld(b), mtx(bld.mtx) { }
-               ~PushMatrix() { bld.mtx = mtx; }
-       };
-
-protected:
-       Matrix mtx;
-       Vector3 nor;
-       Color col;
-       std::map<unsigned, Vector4> texc;
-       std::map<unsigned, Vector4> attr;
-
-public:
-       VertexBuilder(): nor(0, 0, 1) { }
-
-       virtual ~VertexBuilder() { }
-
-       void set_matrix(const Matrix &m)
-       { mtx = m; }
-
-       void transform(const Matrix &m)
-       { mtx *= m; }
-
-       const Matrix &get_matrix() const
-       { return mtx; }
-
-       void vertex(float x, float y)
-       { vertex(x, y, 0, 1); }
-
-       void vertex(float x, float y, float z)
-       { vertex(x, y, z, 1); }
-
-       void vertex(float x, float y, float z, float w)
-       { vertex(Vector4(x, y, z, w)); }
-
-       void vertex(const Vector3 &v)
-       { vertex(Vector4(v.x, v.y, v.z, 1)); }
-
-       void vertex(const Vector4 &v)
-       { vertex_(mtx*v); }
-
-protected:
-       virtual void vertex_(const Vector4 &) = 0;
-
-public:
-       void normal(float x, float y, float z)
-       { normal(Vector3(x, y, z)); }
-
-       void normal(const Vector3 &n)
-       {
-               Vector4 tn = mtx*Vector4(n.x, n.y, n.z, 0);
-               nor = Vector3(tn.x, tn.y, tn.z);
-       }
-
-       void tangent(float x, float y, float z)
-       { tangent(Vector3(x, y, z)); }
-
-       void tangent(const Vector3 &t)
-       {
-               Vector4 tt = mtx*Vector4(t.x, t.y, t.z, 0);
-               attrib(get_component_type(TANGENT3), tt);
-       }
-
-       void binormal(float x, float y, float z)
-       { binormal(Vector3(x, y, z)); }
-
-       void binormal(const Vector3 &b)
-       {
-               Vector4 tb = mtx*Vector4(b.x, b.y, b.z, 0);
-               attrib(get_component_type(BINORMAL3), tb);
-       }
-
-       void texcoord(float s)
-       { texcoord(s, 0, 0, 1); }
-
-       void texcoord(float s, float t)
-       { texcoord(s, t, 0, 1); }
-
-       void texcoord(float s, float t, float r)
-       { texcoord(s, t, r, 1); }
-
-       void texcoord(float s, float t, float r, float q)
-       { texcoord(Vector4(s, t, r, q)); }
-
-       void texcoord(const Vector4 &t)
-       { multitexcoord(0, t); }
-
-       void multitexcoord(unsigned i, float s)
-       { multitexcoord(i, s, 0, 0, 1); }
-
-       void multitexcoord(unsigned i, float s, float t)
-       { multitexcoord(i, s, t, 0, 1); }
-
-       void multitexcoord(unsigned i, float s, float t, float r)
-       { multitexcoord(i, s, t, r, 1); }
-
-       void multitexcoord(unsigned i, float s, float t, float r, float q)
-       { multitexcoord(i, Vector4(s, t, r, q)); }
-
-       void multitexcoord(unsigned i, const Vector4 &t)
-       { texc[i] = t; }
-
-       void color(unsigned char r, unsigned char g, unsigned char b)
-       { color(r, g, b, 255); }
-
-       void color(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
-       { color(r/255.f, g/255.f, b/255.f, a/255.f); }
-
-       void color(float r, float g, float b)
-       { color(r, g, b, 1); }
-
-       void color(float r, float g, float b, float a)
-       { color(Color(r, g, b, a)); }
-
-       void color(const Color &c)
-       { col = c; }
-
-       void attrib(unsigned i, float x)
-       { attrib(i, x, 0, 0, 1); }
-
-       void attrib(unsigned i, float x, float y)
-       { attrib(i, x, y, 0, 1); }
-
-       void attrib(unsigned i, float x, float y, float z)
-       { attrib(i, x, y, z, 1); }
-
-       void attrib(unsigned i, float x, float y, float z, float w)
-       { attrib(i, Vector4(x, y, z, w)); }
-
-       void attrib(unsigned i, const Vector4 &a)
-       { attr[i] = a; }
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/vertexformat.cpp b/source/vertexformat.cpp
deleted file mode 100644 (file)
index 6c2e293..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-#include <algorithm>
-#include <msp/strings/format.h>
-#include <msp/strings/lexicalcast.h>
-#include <msp/strings/utils.h>
-#include "error.h"
-#include "vertexformat.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-VertexFormat::VertexFormat():
-       count(0)
-{ }
-
-VertexFormat::VertexFormat(VertexComponent c):
-       count(1)
-{
-       components[0] = c;
-}
-
-VertexFormat VertexFormat::operator,(VertexComponent c) const
-{
-       if(count>=MAX_COMPONENTS)
-               throw invalid_operation("VertexFormat::operator,");
-
-       VertexFormat r = *this;
-       r.components[r.count++] = c;
-
-       return r;
-}
-
-VertexFormat VertexFormat::operator,(unsigned i) const
-{
-       if(!count)
-               throw invalid_operation("VertexFormat::operator,");
-
-       VertexFormat r = *this;
-       unsigned char *c = &r.components[r.count-1];
-       *c = make_indexed_component(static_cast<VertexComponent>(*c), i);
-
-       return r;
-}
-
-bool VertexFormat::operator==(const VertexFormat &other) const
-{
-       if(count!=other.count)
-               return false;
-       return equal(components, components+count, other.components);
-}
-
-unsigned VertexFormat::stride() const
-{
-       unsigned s = 0;
-       for(const unsigned char *i=begin(); i!=end(); ++i)
-               s += get_component_size(*i);
-       return s;
-}
-
-int VertexFormat::offset(VertexComponent comp) const
-{
-       unsigned type = get_component_type(comp);
-       unsigned size = get_component_size(comp);
-       unsigned offs = 0;
-       for(const unsigned char *i=begin(); i!=end(); ++i)
-       {
-               if(get_component_type(*i)==type)
-               {
-                       if(get_component_size(*i)>=size)
-                               return offs;
-                       else
-                               return -1;
-               }
-               else
-                       offs += get_component_size(*i);
-       }
-
-       return -1;
-}
-
-VertexComponent make_indexed_component(VertexComponent comp, unsigned index)
-{
-       if(comp>=TEXCOORD1 && comp<=TEXCOORD4)
-       {
-               if(index>=4)
-                       throw out_of_range("make_indexed_component");
-       }
-       else if(comp>=ATTRIB1 && comp<=ATTRIB4)
-       {
-               if(index>=24)
-                       throw out_of_range("make_indexed_component");
-       }
-       else
-               throw invalid_argument("make_indexed_component");
-       return static_cast<VertexComponent>(comp+index*4);
-}
-
-void operator>>(const LexicalConverter &conv, VertexComponent &c)
-{
-       const string &str = conv.get();
-       if(str.size()==7 && !str.compare(0, 6, "VERTEX") && str[6]>='2' && str[6]<='4')
-               c = static_cast<VertexComponent>(VERTEX2+(str[6]-'2'));
-       else if(str=="NORMAL3")
-               c = NORMAL3;
-       else if(str.size()==12 && !str.compare(0, 5, "COLOR") && str[5]>='3' && str[5]<='4' && !str.compare(6, 6, "_FLOAT"))
-               c = static_cast<VertexComponent>(COLOR3_FLOAT+(str[5]-'3'));
-       else if(str=="COLOR4_UBYTE")
-               c = COLOR4_UBYTE;
-       else if(str=="TANGENT3")
-               c = TANGENT3;
-       else if(str=="BINORMAL3")
-               c = BINORMAL3;
-       else if(str.size()>=9 && !str.compare(0, 8, "TEXCOORD") && str[8]>='1' && str[8]<='4')
-       {
-               if(str.size()==9)
-                       c = static_cast<VertexComponent>(TEXCOORD1+(str[8]-'1'));
-               else if(str.size()==11 && str[9]=='_' && str[10]>='0' && str[10]<='7')
-                       c = static_cast<VertexComponent>(TEXCOORD1+(str[8]-'1')+(str[10]-'0')*4);
-               else
-                       throw lexical_error(format("conversion of '%s' to VertexComponent", str));
-       }
-       else if(str.size()>=9 && !str.compare(0, 6, "ATTRIB") && str[6]>='1' && str[6]<='4' && str[7]=='_')
-       {
-               unsigned n;
-               try
-               {
-                       n = lexical_cast<unsigned>(str.substr(8));
-               }
-               catch(const lexical_error &)
-               {
-                       throw lexical_error(format("conversion of '%s' to VertexComponent", str));
-               }
-               c = static_cast<VertexComponent>(ATTRIB1+(str[6]-'1')+n*4);
-       }
-       else
-               throw lexical_error(format("conversion of '%s' to VertexComponent", str));
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/vertexformat.h b/source/vertexformat.h
deleted file mode 100644 (file)
index 3561292..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-#ifndef MSP_GL_VERTEXFORMAT_H_
-#define MSP_GL_VERTEXFORMAT_H_
-
-#include <msp/strings/lexicalcast.h>
-
-namespace Msp {
-namespace GL {
-
-/** A single vertex component.  Symbolic names are provided for commonly used
-attributes.  These are aliased with with generic attributes, so be careful when
-picking your attribute indices. */
-enum VertexComponent
-{
-       VERTEX2 = 1,
-       VERTEX3,
-       VERTEX4,
-       NORMAL3 = 10,
-       COLOR4_UBYTE = 12,
-       COLOR3_FLOAT = 14,
-       COLOR4_FLOAT,
-       TANGENT3 = 18,
-       BINORMAL3 = 22,
-       TEXCOORD1 = 32,
-       TEXCOORD2,
-       TEXCOORD3,
-       TEXCOORD4,
-       ATTRIB1 = 64,
-       ATTRIB2,
-       ATTRIB3,
-       ATTRIB4
-};
-
-class VertexFormat
-{
-private:
-       enum { MAX_COMPONENTS = 15 };
-
-       unsigned char count;
-       unsigned char components[MAX_COMPONENTS];
-
-public:
-       VertexFormat();
-       VertexFormat(VertexComponent);
-
-       VertexFormat operator,(VertexComponent c) const;
-       VertexFormat operator,(unsigned i) const;
-       bool operator==(const VertexFormat &) const;
-       bool operator!=(const VertexFormat &other) const { return !(*this==other); }
-
-       bool empty() const { return !count; }
-       const unsigned char *begin() const { return components; }
-       const unsigned char *end() const { return components+count; }
-       unsigned stride() const;
-       int offset(VertexComponent) const;
-};
-
-inline VertexFormat operator,(VertexComponent c1, VertexComponent c2)
-{ return (VertexFormat(c1), c2); }
-
-inline VertexFormat operator,(VertexComponent c, unsigned i)
-{ return (VertexFormat(c), i); }
-
-VertexComponent make_indexed_component(VertexComponent, unsigned);
-
-inline unsigned get_component_type(unsigned char c)
-{ return c>>2; }
-
-inline unsigned get_component_size(unsigned char c)
-{ return (c&3)+1; }
-
-inline unsigned get_stride(const VertexFormat &f)
-{ return f.stride(); }
-
-void operator>>(const LexicalConverter &, VertexComponent &);
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/vertexsetup.cpp b/source/vertexsetup.cpp
deleted file mode 100644 (file)
index d50406a..0000000
+++ /dev/null
@@ -1,202 +0,0 @@
-#include <msp/core/raii.h>
-#include <msp/gl/extensions/arb_direct_state_access.h>
-#include <msp/gl/extensions/arb_instanced_arrays.h>
-#include <msp/gl/extensions/arb_vertex_array_object.h>
-#include <msp/gl/extensions/arb_vertex_attrib_binding.h>
-#include <msp/gl/extensions/arb_vertex_buffer_object.h>
-#include <msp/gl/extensions/arb_vertex_shader.h>
-#include "buffer.h"
-#include "error.h"
-#include "gl.h"
-#include "vertexarray.h"
-#include "vertexsetup.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-VertexSetup::VertexSetup():
-       dirty(0),
-       vertex_array(0),
-       inst_array(0),
-       index_buffer(0)
-{
-       static Require req(ARB_vertex_array_object);
-       if(ARB_direct_state_access)
-               glCreateVertexArrays(1, &id);
-       else
-               glGenVertexArrays(1, &id);
-}
-
-VertexSetup::~VertexSetup()
-{
-       if(current()==this)
-               unbind();
-       glDeleteVertexArrays(1, &id);
-}
-
-void VertexSetup::set_vertex_array(const VertexArray &a)
-{
-       if(!a.get_buffer())
-               throw invalid_argument("VertexSetup::set_vertex_array");
-
-       vertex_array = &a;
-       update(get_update_mask(VERTEX_ARRAY, vertex_format, *vertex_array));
-       vertex_format = vertex_array->get_format();
-}
-
-void VertexSetup::set_instance_array(const VertexArray *a)
-{
-       if(a)
-       {
-               if(!a->get_buffer())
-                       throw invalid_argument("VertexSetup::set_instance_array");
-
-               static Require req(ARB_instanced_arrays);
-       }
-
-       inst_array = a;
-       update(get_update_mask(INSTANCE_ARRAY, inst_format, *inst_array));
-       inst_format = inst_array->get_format();
-}
-
-void VertexSetup::set_index_buffer(const Buffer &ibuf)
-{
-       index_buffer = &ibuf;
-       update(INDEX_BUFFER);
-}
-
-void VertexSetup::refresh()
-{
-       if(vertex_array && vertex_array->get_format()!=vertex_format)
-               set_vertex_array(*vertex_array);
-
-       if(inst_array && inst_array->get_format()!=inst_format)
-               set_instance_array(inst_array);
-}
-
-unsigned VertexSetup::get_attribs(const VertexFormat &fmt)
-{
-       unsigned mask = 0;
-       for(const unsigned char *c=fmt.begin(); c!=fmt.end(); ++c)
-       {
-               unsigned t = get_component_type(*c);
-               if(t>=get_component_type(ATTRIB1))
-                       t -= get_component_type(ATTRIB1);
-               mask |= 1<<t;
-       }
-       return mask;
-}
-
-unsigned VertexSetup::get_update_mask(unsigned base, const VertexFormat &cur_fmt, const VertexArray &new_array)
-{
-       unsigned unused = get_attribs(cur_fmt)&~get_attribs(new_array.get_format());
-       return base | (unused ? UNUSED_ATTRIBS | (unused<<ATTRIB_SHIFT) : 0);
-}
-
-void VertexSetup::update(unsigned mask) const
-{
-       static bool direct = ARB_direct_state_access && ARB_vertex_attrib_binding;
-       if(!direct && current()!=this)
-       {
-               dirty |= mask;
-               return;
-       }
-
-       if(mask&UNUSED_ATTRIBS)
-       {
-               for(unsigned i=0, am=mask>>ATTRIB_SHIFT; am; ++i, am>>=1)
-                       if(am&1)
-                       {
-                               if(direct)
-                                       glDisableVertexArrayAttrib(id, i);
-                               else
-                                       glDisableVertexAttribArray(i);
-                       }
-       }
-
-       if(mask&VERTEX_ARRAY)
-               update_vertex_array(*vertex_array, 0, 0, direct);
-
-       if((mask&INSTANCE_ARRAY) && inst_array)
-               update_vertex_array(*inst_array, 1, 1, direct);
-
-       if(mask&INDEX_BUFFER)
-       {
-               if(direct)
-                       glVertexArrayElementBuffer(id, index_buffer->get_id());
-               else
-                       glBindBuffer(ELEMENT_ARRAY_BUFFER, index_buffer->get_id());
-       }
-}
-
-void VertexSetup::update_vertex_array(const VertexArray &array, unsigned binding, unsigned divisor, bool direct) const
-{
-       Conditional<Bind> bind_vbuf(!direct, array.get_buffer(), ARRAY_BUFFER);
-
-       const VertexFormat &fmt = array.get_format();
-       unsigned stride = get_stride(fmt)*sizeof(float);
-       if(direct)
-       {
-               glVertexArrayVertexBuffer(id, binding, array.get_buffer()->get_id(), 0, stride);
-               glVertexArrayBindingDivisor(id, binding, divisor);
-       }
-
-       unsigned offset = 0;
-       for(const unsigned char *c=fmt.begin(); c!=fmt.end(); ++c)
-       {
-               unsigned t = get_component_type(*c);
-               if(t>=get_component_type(ATTRIB1))
-                       t -= get_component_type(ATTRIB1);
-               unsigned sz = get_component_size(*c);
-               if(direct)
-               {
-                       if(*c==COLOR4_UBYTE)
-                               glVertexArrayAttribFormat(id, t, 4, GL_UNSIGNED_BYTE, true, offset);
-                       else
-                               glVertexArrayAttribFormat(id, t, sz, GL_FLOAT, false, offset);
-                       glVertexArrayAttribBinding(id, t, binding);
-                       glEnableVertexArrayAttrib(id, t);
-               }
-               else
-               {
-                       if(*c==COLOR4_UBYTE)
-                               glVertexAttribPointer(t, 4, GL_UNSIGNED_BYTE, true, stride, reinterpret_cast<unsigned char *>(offset));
-                       else
-                               glVertexAttribPointer(t, sz, GL_FLOAT, false, stride, reinterpret_cast<float *>(offset));
-                       if(ARB_instanced_arrays)
-                               glVertexAttribDivisor(t, divisor);
-                       glEnableVertexAttribArray(t);
-               }
-               offset += sz*sizeof(float);
-       }
-}
-
-void VertexSetup::bind() const
-{
-       if(!vertex_array || !index_buffer)
-               throw invalid_operation("VertexSetup::bind");
-
-       if(set_current(this))
-       {
-               vertex_array->refresh();
-               if(inst_array)
-                       inst_array->refresh();
-               glBindVertexArray(id);
-               if(dirty)
-               {
-                       update(dirty);
-                       dirty = 0;
-               }
-       }
-}
-
-void VertexSetup::unbind()
-{
-       if(set_current(0))
-               glBindVertexArray(0);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/vertexsetup.h b/source/vertexsetup.h
deleted file mode 100644 (file)
index bae4c5f..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-#ifndef MSP_GL_VERTEXSETUP_H_
-#define MSP_GL_VERTEXSETUP_H_
-
-#include "bindable.h"
-#include "vertexformat.h"
-
-namespace Msp {
-namespace GL {
-
-class VertexArray;
-
-/**
-Combines a VertexArray with an index buffer.  This wraps OpenGL's vertex array
-objects.  Intended for internal use.
-*/
-class VertexSetup: public Bindable<VertexSetup>
-{
-private:
-       enum ComponentMask
-       {
-               VERTEX_ARRAY = 1,
-               INSTANCE_ARRAY = 2,
-               INDEX_BUFFER = 4,
-               UNUSED_ATTRIBS = 8,
-               ATTRIB_SHIFT = 4
-       };
-
-       unsigned id;
-       mutable unsigned dirty;
-       const VertexArray *vertex_array;
-       VertexFormat vertex_format;
-       const VertexArray *inst_array;
-       VertexFormat inst_format;
-       const Buffer *index_buffer;
-
-public:
-       VertexSetup();
-       ~VertexSetup();
-
-       void set_vertex_array(const VertexArray &);
-       void set_instance_array(const VertexArray *);
-       void set_index_buffer(const Buffer &);
-       void refresh();
-       const VertexArray *get_vertex_array() const { return vertex_array; }
-       const VertexArray *get_instance_array() const { return inst_array; }
-       const Buffer *get_index_buffer() const { return index_buffer; }
-
-private:
-       static unsigned get_attribs(const VertexFormat &);
-       static unsigned get_update_mask(unsigned, const VertexFormat &, const VertexArray &);
-       void update(unsigned) const;
-       void update_vertex_array(const VertexArray &, unsigned, unsigned, bool) const;
-
-public:
-       void bind() const;
-       static void unbind();
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/view.cpp b/source/view.cpp
deleted file mode 100644 (file)
index d0d3542..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-#include "camera.h"
-#include "renderable.h"
-#include "renderer.h"
-#include "view.h"
-
-namespace Msp {
-namespace GL {
-
-View::View(Framebuffer &t):
-       target(t),
-       camera(0),
-       content(0),
-       internal_renderer(0)
-{ }
-
-View::~View()
-{
-       delete internal_renderer;
-}
-
-void View::set_camera(Camera *c)
-{
-       camera = c;
-       if(camera)
-               camera->set_aspect_ratio(get_aspect_ratio());
-}
-
-void View::set_content(Renderable *r)
-{
-       content = r;
-}
-
-void View::render()
-{
-       if(!internal_renderer)
-               internal_renderer = new Renderer;
-       render(*internal_renderer);
-}
-
-void View::render(Renderer &renderer)
-{
-       Bind bind_fbo(target);
-       target.clear();
-       if(content)
-       {
-               Renderer::Push push(renderer);
-               if(camera)
-                       renderer.set_camera(*camera);
-               content->setup_frame(renderer);
-               content->render(renderer);
-               content->finish_frame();
-               renderer.flush_shader_data();
-       }
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/view.h b/source/view.h
deleted file mode 100644 (file)
index df623c3..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-#ifndef MSP_GL_VIEW_H_
-#define MSP_GL_VIEW_H_
-
-#include <list>
-#include "framebuffer.h"
-
-namespace Msp {
-namespace GL {
-
-class Camera;
-class Renderable;
-class Renderer;
-
-/**
-Manages the presentation of rendering results on the screen.
-*/
-class View: public sigc::trackable
-{
-protected:
-       Framebuffer &target;
-       Camera *camera;
-       Renderable *content;
-       Renderer *internal_renderer;
-
-       View(Framebuffer &);
-public:
-       virtual ~View();
-
-       virtual unsigned get_width() const { return target.get_width(); }
-       virtual unsigned get_height() const { return target.get_height(); }
-       float get_aspect_ratio() const { return static_cast<float>(get_width())/get_height(); }
-
-       void set_camera(Camera *);
-       void set_content(Renderable *);
-
-       virtual void render();
-       virtual void render(Renderer &);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/windingtest.cpp b/source/windingtest.cpp
deleted file mode 100644 (file)
index 0b8218a..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-#include <msp/strings/format.h>
-#include "windingtest.h"
-
-namespace Msp {
-namespace GL {
-
-void operator>>(const LexicalConverter &conv, FaceWinding &winding)
-{
-       if(conv.get()=="CLOCKWISE")
-               winding = CLOCKWISE;
-       else if(conv.get()=="COUNTERCLOCKWISE")
-               winding = COUNTERCLOCKWISE;
-       else
-               throw lexical_error(format("conversion of '%s' to FaceWinding", conv.get()));
-}
-
-WindingTest::WindingTest():
-       winding(COUNTERCLOCKWISE)
-{ }
-
-WindingTest::WindingTest(FaceWinding w):
-       winding(w)
-{ }
-
-void WindingTest::bind() const
-{
-       if(set_current(this))
-       {
-               glEnable(GL_CULL_FACE);
-               glFrontFace(winding);
-       }
-}
-
-void WindingTest::unbind()
-{
-       if(set_current(0))
-               glDisable(GL_CULL_FACE);
-}
-
-const WindingTest &WindingTest::get_reverse() const
-{
-       if(winding==CLOCKWISE)
-               return counterclockwise();
-       else
-               return clockwise();
-}
-
-const WindingTest &WindingTest::clockwise()
-{
-       static WindingTest test(CLOCKWISE);
-       return test;
-}
-
-const WindingTest &WindingTest::counterclockwise()
-{
-       static WindingTest test(COUNTERCLOCKWISE);
-       return test;
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/windingtest.h b/source/windingtest.h
deleted file mode 100644 (file)
index ffc5255..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef MSP_GL_WINDINGTEST_H_
-#define MSP_GL_WINDINGTEST_H_
-
-#include <msp/strings/lexicalcast.h>
-#include "bindable.h"
-#include "gl.h"
-
-namespace Msp {
-namespace GL {
-
-enum FaceWinding
-{
-       CLOCKWISE = GL_CW,
-       COUNTERCLOCKWISE = GL_CCW
-};
-
-void operator>>(const LexicalConverter &, FaceWinding &);
-
-/**
-Tests the winding of polygons.  If the order of vertices on screen does not
-match the winding, the polygon is not rendered.
-*/
-class WindingTest: public Bindable<WindingTest>
-{
-private:
-       FaceWinding winding;
-
-public:
-       WindingTest();
-       WindingTest(FaceWinding);
-
-       void bind() const;
-
-       static void unbind();
-
-       const WindingTest &get_reverse() const;
-
-       static const WindingTest &clockwise();
-       static const WindingTest &counterclockwise();
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/windowview.cpp b/source/windowview.cpp
deleted file mode 100644 (file)
index 2cce075..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#include "camera.h"
-#include "windowview.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-WindowView::WindowView(Graphics::Window &w, Graphics::GLContext &c):
-       View(Framebuffer::system()),
-       window(w),
-       context(c)
-{
-       window.signal_resize.connect(sigc::mem_fun(this, &WindowView::window_resized));
-       window_resized(window.get_width(), window.get_height());
-}
-
-void WindowView::render(Renderer &renderer)
-{
-       View::render(renderer);
-       context.swap_buffers();
-}
-
-void WindowView::window_resized(unsigned w, unsigned h)
-{
-       target.viewport(0, 0, w, h);
-       float aspect = static_cast<float>(w)/h;
-       if(camera)
-               camera->set_aspect_ratio(aspect);
-}
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/windowview.h b/source/windowview.h
deleted file mode 100644 (file)
index 8b6f143..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef MSP_GL_WINDOWVIEW_H_
-#define MSP_GL_WINDOWVIEW_H_
-
-#include <sigc++/trackable.h>
-#include <msp/graphics/glcontext.h>
-#include <msp/graphics/window.h>
-#include "view.h"
-
-namespace Msp {
-namespace GL {
-
-class WindowView: public View
-{
-private:
-       Graphics::Window &window;
-       Graphics::GLContext &context;
-
-public:
-       WindowView(Graphics::Window &, Graphics::GLContext &);
-
-       Graphics::Window &get_window() { return window; }
-       Graphics::GLContext &get_context() { return context; }
-       virtual unsigned get_width() const { return window.get_width(); }
-       virtual unsigned get_height() const { return window.get_height(); }
-
-       using View::render;
-       virtual void render(Renderer &);
-
-private:
-       void window_resized(unsigned, unsigned);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
diff --git a/source/zsortedscene.cpp b/source/zsortedscene.cpp
deleted file mode 100644 (file)
index 46b14db..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-#include "camera.h"
-#include "renderer.h"
-#include "zsortedscene.h"
-
-using namespace std;
-
-namespace Msp {
-namespace GL {
-
-ZSortedScene::ZSortedScene():
-       order(BACK_TO_FRONT),
-       reference(FURTHEST)
-{ }
-
-void ZSortedScene::add(Renderable &r)
-{
-       if(renderables.insert(&r).second && !sorted_cache.empty())
-               sorted_cache.push_back(&r);
-}
-
-void ZSortedScene::remove(Renderable &r)
-{
-       renderables.erase(&r);
-       sorted_cache.clear();
-}
-
-void ZSortedScene::set_order(SortOrder o)
-{
-       order = o;
-}
-
-void ZSortedScene::set_reference(DepthReference r)
-{
-       reference = r;
-}
-
-void ZSortedScene::populate_cache() const
-{
-       if(sorted_cache.empty() && !renderables.empty())
-       {
-               sorted_cache.reserve(renderables.size());
-               sorted_cache.insert(sorted_cache.end(), renderables.begin(), renderables.end());
-       }
-}
-
-void ZSortedScene::setup_frame(Renderer &renderer)
-{
-       populate_cache();
-       for(SortedArray::const_iterator i=sorted_cache.begin(); i!=sorted_cache.end(); ++i)
-               i->renderable->setup_frame(renderer);
-}
-
-void ZSortedScene::finish_frame()
-{
-       for(SortedArray::const_iterator i=sorted_cache.begin(); i!=sorted_cache.end(); ++i)
-               i->renderable->finish_frame();
-}
-
-void ZSortedScene::render(Renderer &renderer, const Tag &tag) const
-{
-       if(renderables.empty())
-               return;
-
-       populate_cache();
-
-       const Camera *camera = renderer.get_camera();
-       if(!camera)
-       {
-               for(SortedArray::const_iterator i=sorted_cache.begin(); i!=sorted_cache.end(); ++i)
-                       renderer.render(*i->renderable, tag);
-               return;
-       }
-
-       const Vector3 &camera_pos = camera->get_position();
-       const Vector3 &look_dir = camera->get_look_direction();
-       float radius_factor = reference-1.0f;
-       float sign = 1.0f-order*2.0f;
-
-       bool use_frustum = setup_frustum(renderer);
-       for(SortedArray::iterator i=sorted_cache.begin(); i!=sorted_cache.end(); ++i)
-       {
-               i->in_frustum = (!use_frustum || !frustum_cull(*i->renderable));
-               if(!i->in_frustum)
-                       continue;
-
-               if(const Matrix *matrix = i->renderable->get_matrix())
-               {
-                       if(const Geometry::BoundingSphere<float, 3> *bsphere = i->renderable->get_bounding_sphere())
-                               i->depth = dot(*matrix*bsphere->get_center()-camera_pos, look_dir)+bsphere->get_radius()*radius_factor;
-                       else
-                               i->depth = dot(*matrix*Vector3()-camera_pos, look_dir);
-                       i->depth *= sign;
-               }
-               else
-                       i->depth = 0;
-       }
-
-       for(SortedArray::iterator i=sorted_cache.begin(), j=i; i!=sorted_cache.end(); ++i)
-               if(i->in_frustum)
-               {
-                       if(i!=j)
-                               swap(*i, *j);
-
-                       if(j!=sorted_cache.begin() && *j<*(j-1))
-                       {
-                               SortedRenderable sr = *j;
-                               SortedArray::iterator k = j-1;
-                               *j = *k;
-                               while(k!=sorted_cache.begin() && sr<*(k-1))
-                               {
-                                       *k = *(k-1);
-                                       --k;
-                               }
-                               *k = sr;
-                       }
-
-                       ++j;
-               }
-
-       for(SortedArray::const_iterator i=sorted_cache.begin(); (i!=sorted_cache.end() && i->in_frustum); ++i)
-               renderer.render(*i->renderable, tag);
-}
-
-
-ZSortedScene::SortedRenderable::SortedRenderable(Renderable *r):
-       renderable(r),
-       in_frustum(false),
-       depth(0.0f)
-{ }
-
-} // namespace GL
-} // namespace Msp
diff --git a/source/zsortedscene.h b/source/zsortedscene.h
deleted file mode 100644 (file)
index cb21061..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-#ifndef MSP_GL_ZSORTEDSCENE_H_
-#define MSP_GL_ZSORTEDSCENE_H_
-
-#include <set>
-#include <vector>
-#include "scene.h"
-
-namespace Msp {
-namespace GL {
-
-enum SortOrder
-{
-       FRONT_TO_BACK,
-       BACK_TO_FRONT
-};
-
-enum DepthReference
-{
-       CLOSEST,
-       CENTER,
-       FURTHEST
-};
-
-/**
-Sorts renderables by their distance from the camera before rendering.  Requires
-renderables to have a matrix.
-*/
-class ZSortedScene: public Scene
-{
-private:
-       struct SortedRenderable
-       {
-               Renderable *renderable;
-               bool in_frustum;
-               float depth;
-
-               SortedRenderable(Renderable *);
-
-               bool operator<(const SortedRenderable &o) const { return depth<o.depth; }
-       };
-
-       typedef std::set<Renderable *> RenderableSet;
-       typedef std::vector<SortedRenderable> SortedArray;
-
-       RenderableSet renderables;
-       SortOrder order;
-       DepthReference reference;
-       mutable SortedArray sorted_cache;
-
-public:
-       ZSortedScene();
-
-       virtual void add(Renderable &);
-       virtual void remove(Renderable &);
-
-       /// Sets the sort order.  Default is back to front.
-       void set_order(SortOrder);
-
-       /// Sets the reference point for sorting.  Default is furthest from camera.
-       void set_reference(DepthReference);
-
-private:
-       void populate_cache() const;
-
-public:
-       virtual void setup_frame(Renderer &);
-       virtual void finish_frame();
-
-       virtual void render(Renderer &, const Tag & = Tag()) const;
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif