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";
};
};
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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 ¤t();
+ static TexUnit *find_unit(const Texture *);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#include "placeable.h"
-
-namespace Msp {
-namespace GL {
-
-void Placeable::set_matrix(const Matrix &m)
-{
- matrix = m;
-}
-
-} // namespace GL
-} // namespace Msp
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#include "placeable.h"
+
+namespace Msp {
+namespace GL {
+
+void Placeable::set_matrix(const Matrix &m)
+{
+ matrix = m;
+}
+
+} // namespace GL
+} // namespace Msp
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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;
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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 ⌖
+ 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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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;
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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 ¤t();
- static TexUnit *find_unit(const Texture *);
-};
-
-} // namespace GL
-} // namespace Msp
-
-#endif
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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 ⌖
- 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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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