From: Mikko Rasa Date: Sun, 14 Feb 2021 00:52:21 +0000 (+0200) Subject: Rearrange soucre files into subdirectories X-Git-Url: http://git.tdb.fi/?a=commitdiff_plain;h=7aaec9a70b8d7733429bec043f8e33e02956f266;p=libs%2Fgl.git Rearrange soucre files into subdirectories --- diff --git a/Build b/Build index 87da59f5..f193a780 100644 --- a/Build +++ b/Build @@ -41,15 +41,39 @@ package "mspgl" library "mspgl" { - source "source"; + source "source/core"; + source "source/materials"; + source "source/render"; + source "source/effects"; + source "source/animation"; + source "source/resources"; + source "source/glsl"; + source "source/builders"; source "extensions"; source "shaderlib"; + build_info + { + incpath "source/core"; + incpath "source/materials"; + incpath "source/render"; + incpath "source/effects"; + incpath "source/animation"; + incpath "source/resources"; + incpath "source/glsl"; + incpath "source/builders"; + }; install true; install_map { - map "source" "include/msp/gl"; + map "source/core" "include/msp/gl"; + map "source/materials" "include/msp/gl"; + map "source/render" "include/msp/gl"; + map "source/effects" "include/msp/gl"; + map "source/animation" "include/msp/gl"; + map "source/resources" "include/msp/gl"; + map "source/glsl" "include/msp/gl"; + map "source/builders" "include/msp/gl"; map "extensions" "include/msp/gl/extensions"; - map "shaderlib" "include/msp/gl/resources"; }; }; diff --git a/source/ambientocclusion.cpp b/source/ambientocclusion.cpp deleted file mode 100644 index 8c1dd616..00000000 --- a/source/ambientocclusion.cpp +++ /dev/null @@ -1,142 +0,0 @@ -#include -#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 a = Geometry::Angle::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(seed)*48271)%modulus; // minstd - return static_cast(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)); -} - -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 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(t) -{ - add("darkness", &Template::darkness); - add("edge_depth_threshold", &Template::edge_depth_threshold); - add("occlusion_radius", &Template::occlusion_radius); - add("samples", &Template::n_samples); -} - -} // namespace GL -} // namespace Msp diff --git a/source/ambientocclusion.h b/source/ambientocclusion.h deleted file mode 100644 index 6c2c5d7e..00000000 --- a/source/ambientocclusion.h +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef MSP_GL_AMBIENTOCCLUSION_H_ -#define MSP_GL_AMBIENTOCCLUSION_H_ - -#include "framebuffer.h" -#include "mesh.h" -#include "postprocessor.h" -#include "program.h" -#include "programdata.h" -#include "rendertarget.h" -#include "texture2d.h" -#include "texturing.h" - -namespace Msp { -namespace GL { - -/** -Implements screen-space ambient occlusion. - -http://en.wikipedia.org/wiki/Screen_Space_Ambient_Occlusion -*/ -class AmbientOcclusion: public PostProcessor -{ -public: - struct Template: PostProcessor::Template - { - class Loader: public DataFile::DerivedObjectLoader - { - 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 quad; - RefPtr linear_sampler; - RefPtr nearest_sampler; - -public: - AmbientOcclusion(unsigned, unsigned, float = 1.0f); - -private: - static float random(unsigned &); - -public: - void set_n_samples(unsigned); - void set_occlusion_radius(float); - void set_edge_depth_threshold(float); - - void set_darkness(float); - - virtual void render(Renderer &, const Texture2D &, const Texture2D &); -}; - -} // namespace GL -} // namespace Msp - -#endif diff --git a/source/animatedobject.cpp b/source/animatedobject.cpp deleted file mode 100644 index 390b90b8..00000000 --- a/source/animatedobject.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include -#include -#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(o) -{ - // Deprecated; Use the transform statement defined in ObjectInstance instead - add("position", &Loader::position); - add("rotation", &Loader::rotation); - add("scale", &Loader::scale); - add("scale", &Loader::scale_uniform); -} - -void AnimatedObject::Loader::position(float x, float y, float z) -{ - obj.matrix.translate(x, y, z); -} - -void AnimatedObject::Loader::rotation(float a, float x, float y, float z) -{ - obj.matrix.rotate_deg(a, x, y, z); -} - -void AnimatedObject::Loader::scale(float x, float y, float z) -{ - obj.matrix.scale(x, y, z); -} - -void AnimatedObject::Loader::scale_uniform(float s) -{ - obj.matrix.scale(s); -} - -} // namespace GL -} // namespace Msp diff --git a/source/animatedobject.h b/source/animatedobject.h deleted file mode 100644 index f9b8824e..00000000 --- a/source/animatedobject.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef MSP_GL_ANIMATEDOBJECT_H_ -#define MSP_GL_ANIMATEDOBJECT_H_ - -#include -#include -#include -#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 - { - 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 pose_data; - ProgramData *shdata; - -public: - AnimatedObject(const Object &); - ~AnimatedObject(); - - void set_pose_matrix(unsigned, const Matrix &); - ProgramData &get_shader_data(); - const ProgramData &get_shader_data() const; - - DEPRECATED void set_uniform(const std::string &, const KeyFrame::AnimatedUniform &); - - virtual const Matrix *get_matrix() const { return &matrix; } - - virtual void setup_render(Renderer &, const Tag &) const; -}; - -} // namespace GL -} // namespace Msp - -#endif diff --git a/source/animation.cpp b/source/animation.cpp deleted file mode 100644 index a8d9e969..00000000 --- a/source/animation.cpp +++ /dev/null @@ -1,633 +0,0 @@ -#include -#include -#include -#include -#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::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()) - 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; csecond.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() && tget_pose() && armature && kf->get_pose()->get_armature()!=armature) - throw invalid_argument("Animation::add_keyframe"); - - const KeyFrame::UniformMap &kf_uniforms = kf->get_uniforms(); - for(vector::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::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::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::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::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(low_bit<(target, i, ExtractComponent(extract, i, bit)); - } - } -} - -template -void Animation::create_curve(CurveTarget target, int component, const T &extract) -{ - typedef typename ValueCurve::Knot Knot; - - vector knots; - unsigned n_control = 0; - for(vector::const_iterator i=keyframes.begin(); i!=keyframes.end(); ++i) - { - if(i->control && knots.empty()) - continue; - - typename Interpolate::SplineValue::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(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 -Animation::ValueCurve::ValueCurve(CurveTarget t, int c, const vector &k): - Curve(t, c), - spline(Interpolate::BezierSpline(k)) -{ } - -template -void Animation::ValueCurve::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::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::from_radians(value.z), Vector3(0, 0, 1)); - matrix.rotate(Geometry::Angle::from_radians(value.y), Vector3(0, 1, 0)); - matrix.rotate(Geometry::Angle::from_radians(value.x), Vector3(1, 0, 0)); - } - else if(target==SCALE) - matrix.scale(value); - else - throw invalid_operation("ValueCurve::apply"); -} - -template -void Animation::ValueCurve::apply(float x, KeyFrame::AnimatedUniform &uni) const -{ - uni.size = N; - typename Interpolate::Spline::Value value = spline(x); - for(unsigned i=0; i::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 -bool Animation::ExtractUniform::operator()(const KeyFrame &kf, typename Interpolate::SplineValue::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::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; iuniform_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(a, 0) -{ - init(); -} - -Animation::Loader::Loader(Animation &a, Collection &c): - DataFile::CollectionObjectLoader(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(n), c, false); -} - -void Animation::Loader::load_kf_inline(bool c) -{ - RefPtr 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(v0, v1)); -} - -void Animation::Loader::event3f(const string &n, float v0, float v1, float v2) -{ - obj.add_event(current_time, n, Vector3(v0, v1, v2)); -} - -void Animation::Loader::event4f(const string &n, float v0, float v1, float v2, float v3) -{ - obj.add_event(current_time, n, Vector4(v0, v1, v2, v3)); -} - -void Animation::Loader::interval(float t) -{ - current_time += t*Time::sec; -} - -void Animation::Loader::keyframe(const string &n) -{ - load_kf(n, false); -} - -void Animation::Loader::keyframe_inline() -{ - load_kf_inline(false); -} - -void Animation::Loader::slopes(float s, float e) -{ - check_slopes_and_control(true, (!obj.keyframes.empty() && obj.keyframes.back().control)); - - start_slope = s; - end_slope = e; - slopes_set = 1; -} - -} // namespace GL -} // namespace Msp diff --git a/source/animation.h b/source/animation.h deleted file mode 100644 index 6845d23e..00000000 --- a/source/animation.h +++ /dev/null @@ -1,213 +0,0 @@ -#ifndef MSP_GL_ANIMATION_H_ -#define MSP_GL_ANIMATION_H_ - -#include -#include -#include -#include -#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 - { - 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 - class ValueCurve: public Curve - { - public: - typedef typename Interpolate::SplineKnot Knot; - - private: - Interpolate::Spline spline; - - public: - ValueCurve(CurveTarget, int, const std::vector &); - - 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 - struct ExtractUniform - { - const std::string &name; - - ExtractUniform(const std::string &n): name(n) { } - - bool operator()(const KeyFrame &, typename Interpolate::SplineValue::Type &) const; - }; - - struct TimedKeyFrame - { - Time::TimeDelta time; - RefPtr 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::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 keyframes; - std::vector events; - bool looping; - std::vector uniforms; - std::vector 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 - void create_curve(CurveTarget target, int, const T &); - static bool extract_position(const KeyFrame &, Vector3 &); - static bool extract_euler(const KeyFrame &, Vector3 &); - static bool extract_scale(const KeyFrame &, Vector3 &); -public: - void add_event(const Time::TimeDelta &, const std::string &, const Variant & = Variant()); - - const Msp::Time::TimeDelta &get_duration() const; - - void set_looping(bool); - bool is_looping() const { return looping; } -}; - -} // namespace GL -} // namespace Msp - -#endif diff --git a/source/animation/animatedobject.cpp b/source/animation/animatedobject.cpp new file mode 100644 index 00000000..390b90b8 --- /dev/null +++ b/source/animation/animatedobject.cpp @@ -0,0 +1,110 @@ +#include +#include +#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(o) +{ + // Deprecated; Use the transform statement defined in ObjectInstance instead + add("position", &Loader::position); + add("rotation", &Loader::rotation); + add("scale", &Loader::scale); + add("scale", &Loader::scale_uniform); +} + +void AnimatedObject::Loader::position(float x, float y, float z) +{ + obj.matrix.translate(x, y, z); +} + +void AnimatedObject::Loader::rotation(float a, float x, float y, float z) +{ + obj.matrix.rotate_deg(a, x, y, z); +} + +void AnimatedObject::Loader::scale(float x, float y, float z) +{ + obj.matrix.scale(x, y, z); +} + +void AnimatedObject::Loader::scale_uniform(float s) +{ + obj.matrix.scale(s); +} + +} // namespace GL +} // namespace Msp diff --git a/source/animation/animatedobject.h b/source/animation/animatedobject.h new file mode 100644 index 00000000..f9b8824e --- /dev/null +++ b/source/animation/animatedobject.h @@ -0,0 +1,54 @@ +#ifndef MSP_GL_ANIMATEDOBJECT_H_ +#define MSP_GL_ANIMATEDOBJECT_H_ + +#include +#include +#include +#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 + { + 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 pose_data; + ProgramData *shdata; + +public: + AnimatedObject(const Object &); + ~AnimatedObject(); + + void set_pose_matrix(unsigned, const Matrix &); + ProgramData &get_shader_data(); + const ProgramData &get_shader_data() const; + + DEPRECATED void set_uniform(const std::string &, const KeyFrame::AnimatedUniform &); + + virtual const Matrix *get_matrix() const { return &matrix; } + + virtual void setup_render(Renderer &, const Tag &) const; +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/animation/animation.cpp b/source/animation/animation.cpp new file mode 100644 index 00000000..a8d9e969 --- /dev/null +++ b/source/animation/animation.cpp @@ -0,0 +1,633 @@ +#include +#include +#include +#include +#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::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()) + 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; csecond.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() && tget_pose() && armature && kf->get_pose()->get_armature()!=armature) + throw invalid_argument("Animation::add_keyframe"); + + const KeyFrame::UniformMap &kf_uniforms = kf->get_uniforms(); + for(vector::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::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::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::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::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(low_bit<(target, i, ExtractComponent(extract, i, bit)); + } + } +} + +template +void Animation::create_curve(CurveTarget target, int component, const T &extract) +{ + typedef typename ValueCurve::Knot Knot; + + vector knots; + unsigned n_control = 0; + for(vector::const_iterator i=keyframes.begin(); i!=keyframes.end(); ++i) + { + if(i->control && knots.empty()) + continue; + + typename Interpolate::SplineValue::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(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 +Animation::ValueCurve::ValueCurve(CurveTarget t, int c, const vector &k): + Curve(t, c), + spline(Interpolate::BezierSpline(k)) +{ } + +template +void Animation::ValueCurve::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::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::from_radians(value.z), Vector3(0, 0, 1)); + matrix.rotate(Geometry::Angle::from_radians(value.y), Vector3(0, 1, 0)); + matrix.rotate(Geometry::Angle::from_radians(value.x), Vector3(1, 0, 0)); + } + else if(target==SCALE) + matrix.scale(value); + else + throw invalid_operation("ValueCurve::apply"); +} + +template +void Animation::ValueCurve::apply(float x, KeyFrame::AnimatedUniform &uni) const +{ + uni.size = N; + typename Interpolate::Spline::Value value = spline(x); + for(unsigned i=0; i::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 +bool Animation::ExtractUniform::operator()(const KeyFrame &kf, typename Interpolate::SplineValue::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::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; iuniform_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(a, 0) +{ + init(); +} + +Animation::Loader::Loader(Animation &a, Collection &c): + DataFile::CollectionObjectLoader(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(n), c, false); +} + +void Animation::Loader::load_kf_inline(bool c) +{ + RefPtr 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(v0, v1)); +} + +void Animation::Loader::event3f(const string &n, float v0, float v1, float v2) +{ + obj.add_event(current_time, n, Vector3(v0, v1, v2)); +} + +void Animation::Loader::event4f(const string &n, float v0, float v1, float v2, float v3) +{ + obj.add_event(current_time, n, Vector4(v0, v1, v2, v3)); +} + +void Animation::Loader::interval(float t) +{ + current_time += t*Time::sec; +} + +void Animation::Loader::keyframe(const string &n) +{ + load_kf(n, false); +} + +void Animation::Loader::keyframe_inline() +{ + load_kf_inline(false); +} + +void Animation::Loader::slopes(float s, float e) +{ + check_slopes_and_control(true, (!obj.keyframes.empty() && obj.keyframes.back().control)); + + start_slope = s; + end_slope = e; + slopes_set = 1; +} + +} // namespace GL +} // namespace Msp diff --git a/source/animation/animation.h b/source/animation/animation.h new file mode 100644 index 00000000..6845d23e --- /dev/null +++ b/source/animation/animation.h @@ -0,0 +1,213 @@ +#ifndef MSP_GL_ANIMATION_H_ +#define MSP_GL_ANIMATION_H_ + +#include +#include +#include +#include +#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 + { + 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 + class ValueCurve: public Curve + { + public: + typedef typename Interpolate::SplineKnot Knot; + + private: + Interpolate::Spline spline; + + public: + ValueCurve(CurveTarget, int, const std::vector &); + + 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 + struct ExtractUniform + { + const std::string &name; + + ExtractUniform(const std::string &n): name(n) { } + + bool operator()(const KeyFrame &, typename Interpolate::SplineValue::Type &) const; + }; + + struct TimedKeyFrame + { + Time::TimeDelta time; + RefPtr 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::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 keyframes; + std::vector events; + bool looping; + std::vector uniforms; + std::vector 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 + void create_curve(CurveTarget target, int, const T &); + static bool extract_position(const KeyFrame &, Vector3 &); + static bool extract_euler(const KeyFrame &, Vector3 &); + static bool extract_scale(const KeyFrame &, Vector3 &); +public: + void add_event(const Time::TimeDelta &, const std::string &, const Variant & = Variant()); + + const Msp::Time::TimeDelta &get_duration() const; + + void set_looping(bool); + bool is_looping() const { return looping; } +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/animation/animationeventobserver.h b/source/animation/animationeventobserver.h new file mode 100644 index 00000000..1eafc4d2 --- /dev/null +++ b/source/animation/animationeventobserver.h @@ -0,0 +1,25 @@ +#ifndef MSP_GL_ANIMATIONEVENTOBSERVER_H_ +#define MSP_GL_ANIMATIONEVENTOBSERVER_H_ + +#include +#include + +namespace Msp { +namespace GL { + +class Placeable; + +class AnimationEventObserver +{ +protected: + AnimationEventObserver() { } +public: + virtual ~AnimationEventObserver() { } + + virtual void animation_event(Placeable *, const std::string &, const Variant &) { } +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/animation/animationplayer.cpp b/source/animation/animationplayer.cpp new file mode 100644 index 00000000..ac163218 --- /dev/null +++ b/source/animation/animationplayer.cpp @@ -0,0 +1,240 @@ +#include +#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::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::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::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; iget_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::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; janimation->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::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::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::const_iterator i=event_observers.begin(); i!=event_observers.end(); ++i) + (*i)->animation_event(&placeable, name, value); +} + +} // namespace GL +} // namespace Msp diff --git a/source/animation/animationplayer.h b/source/animation/animationplayer.h new file mode 100644 index 00000000..c45027d9 --- /dev/null +++ b/source/animation/animationplayer.h @@ -0,0 +1,98 @@ +#ifndef MSP_GL_ANIMATIONPLAYER_H_ +#define MSP_GL_ANIMATIONPLAYER_H_ + +#include +#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 animations; + bool stacked; + std::vector event_observers; + + Target(Placeable &); + + virtual void animation_event(Placeable *, const std::string &, const Variant &); + }; + + typedef std::map ObjectMap; + + ObjectMap objects; + +private: + Target &get_slot(Placeable &); + + Target &play_(Placeable &, const Animation &, bool, float); +public: + /// Plays an animation on an object. Any previous animations are replaced. + void play(AnimatedObject &, const Animation &, float = 1.0f); + + void play(Placeable &, const Animation &, float = 1.0f); + + /** Plays an animation, stacked with other animations. If no animations are + playing yet, the object's current matrix is used as the base. */ + void play_stacked(AnimatedObject &, const Animation &, float = 1.0f); + + void play_stacked(Placeable &, const Animation &, float = 1.0f); + + /// Returns the number of animations currently affecting an object. + unsigned get_n_active_animations(const AnimatedObject &) const; + + /** Request delivery of animation events for the given object. Events will + be delivered from all current and future animations until the observer is + removed. */ + void observe_events(AnimatedObject &, AnimationEventObserver &); + + /// Remove an event observer from one object. + void unobserve_events(AnimatedObject &, AnimationEventObserver &); + + /// Remove an event observer from all objects. + void unobserve_events(AnimationEventObserver &); + + /// Stops all animations affecting an object. + void stop(Placeable &); + + /// Stops a single animation affecting an object. + void stop(Placeable &, const Animation &); + + /** Advances all playing animations. Should be called in a regular manner, + preferably just before rendering. */ + void tick(const Time::TimeDelta &); + +private: + void tick_single(Target &, const Time::TimeDelta &); + void tick_stacked(Target &, const Time::TimeDelta &); + static void set_object_uniform(AnimatedObject &, const std::string &, const KeyFrame::AnimatedUniform &); +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/animation/armature.cpp b/source/animation/armature.cpp new file mode 100644 index 00000000..6e921e3f --- /dev/null +++ b/source/animation/armature.cpp @@ -0,0 +1,91 @@ +#include +#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::const_iterator i=links.begin(); i!=links.end(); ++i) + if(i->get_index()==index) + return *i; + throw key_error(typeid(list)); +} + +const Armature::Link &Armature::get_link(const string &name) const +{ + for(vector::const_iterator i=links.begin(); i!=links.end(); ++i) + if(i->get_name()==name) + return *i; + throw key_error(typeid(list)); +} + +unsigned Armature::get_max_link_index() const +{ + unsigned max_index = 0; + for(vector::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(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(l), + armature(a) +{ + add("base", &Loader::base); + add("index", &Link::index); + add("parent", &Loader::parent); +} + +void Armature::Link::Loader::base(float x, float y, float z) +{ + obj.base = Vector3(x, y, z); +} + +void Armature::Link::Loader::parent(const string &n) +{ + obj.parent = &armature.get_link(n); +} + +} // namespace GL +} // namespace Msp diff --git a/source/animation/armature.h b/source/animation/armature.h new file mode 100644 index 00000000..9dc2f505 --- /dev/null +++ b/source/animation/armature.h @@ -0,0 +1,71 @@ +#ifndef MSP_GL_ARMATURE_H_ +#define MSP_GL_ARMATURE_H_ + +#include +#include +#include +#include "pose.h" +#include "vector.h" + +namespace Msp { +namespace GL { + +class Armature +{ +public: + class Loader: public DataFile::ObjectLoader + { + public: + Loader(Armature &); + private: + void link(const std::string &); + }; + + class Link + { + public: + class Loader: public DataFile::ObjectLoader + { + 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 links; + +public: + Link &add_link(); + + const Link &get_link(unsigned) const; + const Link &get_link(const std::string &) const; + unsigned get_max_link_index() const; +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/animation/keyframe.cpp b/source/animation/keyframe.cpp new file mode 100644 index 00000000..f610de2d --- /dev/null +++ b/source/animation/keyframe.cpp @@ -0,0 +1,157 @@ +#include +#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(k, 0) +{ + init(); +} + +KeyFrame::Loader::Loader(KeyFrame &k, Collection &c): + DataFile::CollectionObjectLoader(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(n); + obj.pose.keep(); +} + +void KeyFrame::Loader::pose_inline() +{ + RefPtr 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(k) +{ + add("uniform", &UniformsLoader::uniform1f); + add("uniform", &UniformsLoader::uniform2f); + add("uniform", &UniformsLoader::uniform3f); + add("uniform", &UniformsLoader::uniform4f); + + // Deprecated + add("uniform1f", &UniformsLoader::uniform1f); + add("uniform2f", &UniformsLoader::uniform2f); + add("uniform3f", &UniformsLoader::uniform3f); + add("uniform4f", &UniformsLoader::uniform4f); +} + +void KeyFrame::UniformsLoader::uniform1f(const string &n, float v) +{ + obj.uniforms.insert(UniformMap::value_type(n, AnimatedUniform(1, v))); +} + +void KeyFrame::UniformsLoader::uniform2f(const string &n, float v0, float v1) +{ + obj.uniforms.insert(UniformMap::value_type(n, AnimatedUniform(2, v0, v1))); +} + +void KeyFrame::UniformsLoader::uniform3f(const string &n, float v0, float v1, float v2) +{ + obj.uniforms.insert(UniformMap::value_type(n, AnimatedUniform(3, v0, v1, v2))); +} + +void KeyFrame::UniformsLoader::uniform4f(const string &n, float v0, float v1, float v2, float v3) +{ + obj.uniforms.insert(UniformMap::value_type(n, AnimatedUniform(4, v0, v1, v2, v3))); +} + +} // namespace GL +} // namespace Msp diff --git a/source/animation/keyframe.h b/source/animation/keyframe.h new file mode 100644 index 00000000..5b748add --- /dev/null +++ b/source/animation/keyframe.h @@ -0,0 +1,82 @@ +#ifndef MSP_GL_KEYFRAME_H_ +#define MSP_GL_KEYFRAME_H_ + +#include +#include +#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 + { + 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 + { + 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 UniformMap; + +private: + Transform transform; + UniformMap uniforms; + RefPtr pose; + +public: + KeyFrame(); + ~KeyFrame(); + + void set_transform(const Transform &); + void set_matrix(const Matrix &); + void set_uniform(const std::string &, const AnimatedUniform &); + void set_pose(const Pose &); + const Transform &get_transform() const { return transform; } + Matrix get_matrix() const { return transform.to_matrix(); } + const UniformMap &get_uniforms() const { return uniforms; } + const Pose *get_pose() const { return pose.get(); } +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/animation/pose.cpp b/source/animation/pose.cpp new file mode 100644 index 00000000..99fa6bb3 --- /dev/null +++ b/source/animation/pose.cpp @@ -0,0 +1,90 @@ +#include +#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(p, &c) +{ + add("armature", &Loader::armature); + add("link", &Loader::link); +} + +void Pose::Loader::armature(const string &n) +{ + obj.set_armature(get_collection().get(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(p), + link_index(l) +{ + add("rotation", &LinkLoader::rotation); +} + +void Pose::LinkLoader::rotation(float a, float x, float y, float z) +{ + obj.rotate_link(link_index, a, Vector3(x, y, z)); +} + +} // namespace GL +} // namespace Msp diff --git a/source/animation/pose.h b/source/animation/pose.h new file mode 100644 index 00000000..cbd8137b --- /dev/null +++ b/source/animation/pose.h @@ -0,0 +1,59 @@ +#ifndef MSP_GL_POSE_H_ +#define MSP_GL_POSE_H_ + +#include +#include +#include "matrix.h" + +namespace Msp { +namespace GL { + +class Armature; + +class Pose +{ +public: + class Loader: public DataFile::CollectionObjectLoader + { + 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 + { + private: + unsigned link_index; + + public: + LinkLoader(Pose &, unsigned); + private: + void rotation(float, float, float, float); + }; + + const Armature *armature; + std::vector links; + +public: + Pose(); + Pose(const Armature &); + + void set_armature(const Armature &); + const Armature *get_armature() const { return armature; } + void rotate_link(unsigned, float, const Vector3 &); + const Matrix &get_link_matrix(unsigned) const; +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/animation/transform.cpp b/source/animation/transform.cpp new file mode 100644 index 00000000..b703a5b1 --- /dev/null +++ b/source/animation/transform.cpp @@ -0,0 +1,171 @@ +#include "transform.h" + +namespace Msp { +namespace GL { + +Transform::Transform(): + position(0.0f, 0.0f, 0.0f), + euler(Angle::zero(), Angle::zero(), Angle::zero()), + scale(1.0f, 1.0f, 1.0f), + mask(NONE) +{ } + +Transform Transform::from_matrix(const Matrix &matrix) +{ + Transform trn; + trn.position = matrix.column(3).slice<3>(0); + + trn.euler.z = Geometry::atan2(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(-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(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(t) +{ + add("position_x", &Loader::position_x); + add("position_y", &Loader::position_y); + add("position_z", &Loader::position_z); + add("position", &Loader::position); + add("euler_x", &Loader::euler_x); + add("euler_y", &Loader::euler_y); + add("euler_z", &Loader::euler_z); + add("euler", &Loader::euler); + add("rotation", &Loader::rotation); + add("scale_x", &Loader::scale_x); + add("scale_y", &Loader::scale_y); + add("scale_z", &Loader::scale_z); + add("scale", &Loader::scale_uniform); + add("scale", &Loader::scale); +} + +void Transform::Loader::position_x(float x) +{ + obj.position.x = x; + obj.mask = obj.mask|POSITION_X; +} + +void Transform::Loader::position_y(float y) +{ + obj.position.y = y; + obj.mask = obj.mask|POSITION_Y; +} + +void Transform::Loader::position_z(float z) +{ + obj.position.z = z; + obj.mask = obj.mask|POSITION_Z; +} + +void Transform::Loader::position(float x, float y, float z) +{ + obj.set_position(Vector3(x, y, z)); +} + +void Transform::Loader::euler_x(float x) +{ + obj.euler.x = Angle::from_degrees(x); + obj.mask = obj.mask|EULER_X; +} + +void Transform::Loader::euler_y(float y) +{ + obj.euler.y = Angle::from_degrees(y); + obj.mask = obj.mask|EULER_Y; +} + +void Transform::Loader::euler_z(float z) +{ + obj.euler.z = Angle::from_degrees(z); + obj.mask = obj.mask|EULER_Z; +} + +void Transform::Loader::euler(float x, float y, float z) +{ + obj.set_euler(AngleVector3(Angle::from_degrees(x), Angle::from_degrees(y), Angle::from_degrees(z))); +} + +void Transform::Loader::rotation(float a, float x, float y, float z) +{ + obj.set_rotation(Angle::from_degrees(a), Vector3(x, y, z)); +} + +void Transform::Loader::scale_x(float x) +{ + obj.scale.x = x; + obj.mask = obj.mask|SCALE_X; +} + +void Transform::Loader::scale_y(float y) +{ + obj.scale.y = y; + obj.mask = obj.mask|SCALE_Y; +} + +void Transform::Loader::scale_z(float z) +{ + obj.scale.z = z; + obj.mask = obj.mask|SCALE_Z; +} + +void Transform::Loader::scale_uniform(float s) +{ + obj.set_scale(s); +} + +void Transform::Loader::scale(float x, float y, float z) +{ + obj.set_scale(Vector3(x, y, z)); +} + +} // namespace GL +} // namespace Msp diff --git a/source/animation/transform.h b/source/animation/transform.h new file mode 100644 index 00000000..1d0aeddd --- /dev/null +++ b/source/animation/transform.h @@ -0,0 +1,99 @@ +#ifndef MSP_GL_TRANSFORM_H_ +#define MSP_GL_TRANSFORM_H_ + +#include +#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 + { + 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 Angle; + typedef LinAl::Vector 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(static_cast(m1)&static_cast(m2)); } + +inline Transform::ComponentMask operator|(Transform::ComponentMask m1, Transform::ComponentMask m2) +{ return static_cast(static_cast(m1)|static_cast(m2)); } + +inline Transform::ComponentMask operator^(Transform::ComponentMask m1, Transform::ComponentMask m2) +{ return static_cast(static_cast(m1)^static_cast(m2)); } + +inline Transform::ComponentMask operator~(Transform::ComponentMask m) +{ return static_cast(~static_cast(m)); } + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/animationeventobserver.h b/source/animationeventobserver.h deleted file mode 100644 index 1eafc4d2..00000000 --- a/source/animationeventobserver.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef MSP_GL_ANIMATIONEVENTOBSERVER_H_ -#define MSP_GL_ANIMATIONEVENTOBSERVER_H_ - -#include -#include - -namespace Msp { -namespace GL { - -class Placeable; - -class AnimationEventObserver -{ -protected: - AnimationEventObserver() { } -public: - virtual ~AnimationEventObserver() { } - - virtual void animation_event(Placeable *, const std::string &, const Variant &) { } -}; - -} // namespace GL -} // namespace Msp - -#endif diff --git a/source/animationplayer.cpp b/source/animationplayer.cpp deleted file mode 100644 index ac163218..00000000 --- a/source/animationplayer.cpp +++ /dev/null @@ -1,240 +0,0 @@ -#include -#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::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::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::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; iget_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::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; janimation->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::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::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::const_iterator i=event_observers.begin(); i!=event_observers.end(); ++i) - (*i)->animation_event(&placeable, name, value); -} - -} // namespace GL -} // namespace Msp diff --git a/source/animationplayer.h b/source/animationplayer.h deleted file mode 100644 index c45027d9..00000000 --- a/source/animationplayer.h +++ /dev/null @@ -1,98 +0,0 @@ -#ifndef MSP_GL_ANIMATIONPLAYER_H_ -#define MSP_GL_ANIMATIONPLAYER_H_ - -#include -#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 animations; - bool stacked; - std::vector event_observers; - - Target(Placeable &); - - virtual void animation_event(Placeable *, const std::string &, const Variant &); - }; - - typedef std::map ObjectMap; - - ObjectMap objects; - -private: - Target &get_slot(Placeable &); - - Target &play_(Placeable &, const Animation &, bool, float); -public: - /// Plays an animation on an object. Any previous animations are replaced. - void play(AnimatedObject &, const Animation &, float = 1.0f); - - void play(Placeable &, const Animation &, float = 1.0f); - - /** Plays an animation, stacked with other animations. If no animations are - playing yet, the object's current matrix is used as the base. */ - void play_stacked(AnimatedObject &, const Animation &, float = 1.0f); - - void play_stacked(Placeable &, const Animation &, float = 1.0f); - - /// Returns the number of animations currently affecting an object. - unsigned get_n_active_animations(const AnimatedObject &) const; - - /** Request delivery of animation events for the given object. Events will - be delivered from all current and future animations until the observer is - removed. */ - void observe_events(AnimatedObject &, AnimationEventObserver &); - - /// Remove an event observer from one object. - void unobserve_events(AnimatedObject &, AnimationEventObserver &); - - /// Remove an event observer from all objects. - void unobserve_events(AnimationEventObserver &); - - /// Stops all animations affecting an object. - void stop(Placeable &); - - /// Stops a single animation affecting an object. - void stop(Placeable &, const Animation &); - - /** Advances all playing animations. Should be called in a regular manner, - preferably just before rendering. */ - void tick(const Time::TimeDelta &); - -private: - void tick_single(Target &, const Time::TimeDelta &); - void tick_stacked(Target &, const Time::TimeDelta &); - static void set_object_uniform(AnimatedObject &, const std::string &, const KeyFrame::AnimatedUniform &); -}; - -} // namespace GL -} // namespace Msp - -#endif diff --git a/source/armature.cpp b/source/armature.cpp deleted file mode 100644 index 6e921e3f..00000000 --- a/source/armature.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include -#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::const_iterator i=links.begin(); i!=links.end(); ++i) - if(i->get_index()==index) - return *i; - throw key_error(typeid(list)); -} - -const Armature::Link &Armature::get_link(const string &name) const -{ - for(vector::const_iterator i=links.begin(); i!=links.end(); ++i) - if(i->get_name()==name) - return *i; - throw key_error(typeid(list)); -} - -unsigned Armature::get_max_link_index() const -{ - unsigned max_index = 0; - for(vector::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(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(l), - armature(a) -{ - add("base", &Loader::base); - add("index", &Link::index); - add("parent", &Loader::parent); -} - -void Armature::Link::Loader::base(float x, float y, float z) -{ - obj.base = Vector3(x, y, z); -} - -void Armature::Link::Loader::parent(const string &n) -{ - obj.parent = &armature.get_link(n); -} - -} // namespace GL -} // namespace Msp diff --git a/source/armature.h b/source/armature.h deleted file mode 100644 index 9dc2f505..00000000 --- a/source/armature.h +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef MSP_GL_ARMATURE_H_ -#define MSP_GL_ARMATURE_H_ - -#include -#include -#include -#include "pose.h" -#include "vector.h" - -namespace Msp { -namespace GL { - -class Armature -{ -public: - class Loader: public DataFile::ObjectLoader - { - public: - Loader(Armature &); - private: - void link(const std::string &); - }; - - class Link - { - public: - class Loader: public DataFile::ObjectLoader - { - 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 links; - -public: - Link &add_link(); - - const Link &get_link(unsigned) const; - const Link &get_link(const std::string &) const; - unsigned get_max_link_index() const; -}; - -} // namespace GL -} // namespace Msp - -#endif diff --git a/source/basicmaterial.cpp b/source/basicmaterial.cpp deleted file mode 100644 index 362fc9db..00000000 --- a/source/basicmaterial.cpp +++ /dev/null @@ -1,153 +0,0 @@ -#include "basicmaterial.h" - -using namespace std; - -namespace Msp { -namespace GL { - -BasicMaterial::BasicMaterial(): - receive_shadows(false) -{ - set_diffuse(Color(1.0f)); - set_specular(Color(0.0f)); - set_emission(Color(0.0f)); - set_shininess(50.0f); - set_reflectivity(0.0f); -} - -string BasicMaterial::create_program_source() const -{ - string source = "import phong;\n"; - if(diffuse.texture) - source += "const bool use_diffuse_map = true;\n"; - if(specular.texture || specular.value.r || specular.value.g || specular.value.b) - { - source += "const bool use_specular = true;\n"; - if(specular.texture) - source += "const bool use_specular_map = true;\n"; - if(shininess.texture) - source += "const bool use_shininess_map = true;\n"; - } - if(normal.texture) - source += "const bool use_normal_map = true;\n"; - if(emission.texture || emission.value.r || emission.value.g || emission.value.b) - { - source += "const bool use_emission = true;\n"; - if(emission.texture) - source += "const bool use_emission_map = true;\n"; - } - if(reflectivity.value || reflectivity.texture) - { - source += "const bool use_reflectivity = true;\n"; - if (reflectivity.texture) - source += "const bool use_reflectivity_map = true;\n"; - } - if(receive_shadows) - source += "const bool use_shadow_map = true;\n"; - return source; -} - -void BasicMaterial::attach_textures_to(Texturing &texturing, ProgramData &tex_shdata) const -{ - attach_texture_to(diffuse.texture, texturing, tex_shdata, "diffuse_map"); - attach_texture_to(specular.texture, texturing, tex_shdata, "specular_map"); - attach_texture_to(normal.texture, texturing, tex_shdata, "normal_map"); - attach_texture_to(emission.texture, texturing, tex_shdata, "emission_map"); - attach_texture_to(shininess.texture, texturing, tex_shdata, "shininess_map"); - attach_texture_to(reflectivity.texture, texturing, tex_shdata, "reflectivity_map"); -} - -void BasicMaterial::set_diffuse(const Color &color) -{ - diffuse.value = color; - shdata.uniform("basic_material.diffuse", color); -} - -void BasicMaterial::set_diffuse_map(const Texture *tex) -{ - diffuse.texture = tex; -} - -void BasicMaterial::set_specular(const Color &color) -{ - specular.value = color; - shdata.uniform("basic_material.specular", color); -} - -void BasicMaterial::set_specular_map(const Texture *tex) -{ - specular.texture = tex; -} - -void BasicMaterial::set_normal_map(const Texture *tex) -{ - normal.texture = tex; -} - -void BasicMaterial::set_emission(const Color &color) -{ - emission.value = color; - shdata.uniform("basic_material.emission", color); -} - -void BasicMaterial::set_emission_map(const Texture *tex) -{ - emission.texture = tex; -} - -void BasicMaterial::set_shininess(float value) -{ - shininess.value = value; - shdata.uniform("basic_material.shininess", value); -} - -void BasicMaterial::set_shininess_map(const Texture *tex) -{ - shininess.texture = tex; -} - -void BasicMaterial::set_reflectivity(float value) -{ - reflectivity.value = value; - shdata.uniform("basic_material.reflectivity", value); -} - -void BasicMaterial::set_reflectivity_map(const Texture *tex) -{ - reflectivity.texture = tex; -} - -void BasicMaterial::set_receive_shadows(bool s) -{ - receive_shadows = s; -} - - -DataFile::Loader::ActionMap BasicMaterial::Loader::shared_actions; - -BasicMaterial::Loader::Loader(BasicMaterial &m): - DerivedObjectLoader >(m) -{ - set_actions(shared_actions); -} - -BasicMaterial::Loader::Loader(BasicMaterial &m, Collection &c): - DerivedObjectLoader >(m, c) -{ - set_actions(shared_actions); -} - -void BasicMaterial::Loader::init_actions() -{ - Material::PropertyLoader::init_actions(); - add_property("diffuse", &BasicMaterial::set_diffuse, &BasicMaterial::set_diffuse_map, true); - add_property("specular", &BasicMaterial::set_specular, &BasicMaterial::set_specular_map, false); - add_property("normal", &BasicMaterial::set_normal_map); - add_property("emission", &BasicMaterial::set_emission, &BasicMaterial::set_emission_map, false); - add_property("shininess", &BasicMaterial::set_shininess, &BasicMaterial::set_shininess_map); - add_property("reflectivity", &BasicMaterial::set_reflectivity, &BasicMaterial::set_reflectivity_map); - add("receive_shadows", &BasicMaterial::receive_shadows); -} - -} // namespace GL -} // namespace Msp diff --git a/source/basicmaterial.h b/source/basicmaterial.h deleted file mode 100644 index 95259f47..00000000 --- a/source/basicmaterial.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef MSP_GL_BASICMATERIAL_H_ -#define MSP_GL_BASICMATERIAL_H_ - -#include "material.h" - -namespace Msp { -namespace GL { - -class BasicMaterial: public Material -{ -public: - class Loader: public DataFile::DerivedObjectLoader > - { - private: - static ActionMap shared_actions; - - public: - Loader(BasicMaterial &); - Loader(BasicMaterial &, Collection &); - - private: - virtual void init_actions(); - }; - -private: - Property diffuse; - Property specular; - Property shininess; - Property normal; - Property emission; - Property reflectivity; - bool receive_shadows; - -public: - BasicMaterial(); - -protected: - virtual std::string create_program_source() const; - -public: - virtual void attach_textures_to(Texturing &, ProgramData &) const; - - void set_diffuse(const Color &); - void set_diffuse_map(const Texture *); - void set_specular(const Color &); - void set_specular_map(const Texture *); - void set_normal_map(const Texture *); - void set_emission(const Color &); - void set_emission_map(const Texture *); - void set_shininess(float); - void set_shininess_map(const Texture *); - void set_reflectivity(float); - void set_reflectivity_map(const Texture *); - void set_receive_shadows(bool); -}; - -} // namespace GL -} // namespace Msp - -#endif diff --git a/source/batch.cpp b/source/batch.cpp deleted file mode 100644 index f14cc307..00000000 --- a/source/batch.cpp +++ /dev/null @@ -1,252 +0,0 @@ -#include -#include -#include "batch.h" -#include "bindable.h" -#include "buffer.h" -#include "error.h" -#include "mesh.h" -#include "vertexarray.h" - -using namespace std; - -namespace { - -template -void append(vector &data, T i) -{ - data.insert(data.end(), sizeof(T), 0); - *(T *)(&data[data.size()-sizeof(T)]) = i; -} - -template -U convert(T n) -{ - if(!static_cast(~n)) - return ~0; - else - return n; -} - -template -void expand(vector &data) -{ - unsigned count = data.size()/sizeof(T); - data.resize(count*sizeof(U)); - for(unsigned i=count; i--;) - *(U *)(&data[i*sizeof(U)]) = convert(*(T *)(&data[i*sizeof(T)])); -} - -template -void shrink(vector &data) -{ - unsigned count = data.size()/sizeof(T); - for(unsigned i=0; i(*(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(data); - else if(index_type==UNSIGNED_INT && t==UNSIGNED_SHORT) - shrink(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 &ind) -{ - if(ind.empty()) - return *this; - - data.reserve(data.size()+ind.size()*get_index_size()); - for(vector::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(data, 0xFFFFFFFF); - else - ::append(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; i0xFFFE) - set_index_type(UNSIGNED_INT); - - if(index_type==UNSIGNED_INT) - ::append(data, i); - else - ::append(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(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(b) -{ - add("indices", &Loader::indices); -} - -void Batch::Loader::indices(const vector &ind) -{ - obj.append(ind); -} - -} // namespace GL -} // namespace Msp diff --git a/source/batch.h b/source/batch.h deleted file mode 100644 index 2e4ad9ef..00000000 --- a/source/batch.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef MSP_GL_BATCH_H_ -#define MSP_GL_BATCH_H_ - -#include -#include -#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 - { - public: - Loader(Batch &); - private: - void indices(const std::vector &); - }; - -private: - PrimitiveType prim_type; - DataType index_type; - std::vector 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 &); - bool can_append(PrimitiveType); - Batch &append(const Batch &); -private: - void append_index(unsigned); - virtual unsigned get_data_size() const { return data.size(); } - virtual const void *get_data_pointer() const { return &data[0]; } - virtual unsigned get_alignment() const { return get_index_size(); } - unsigned get_index_size() const; -public: - unsigned size() const { return data.size()/get_index_size(); } - - unsigned get_index(unsigned) const; - - void draw() const; - void draw_instanced(unsigned) const; -private: - const void *setup_draw() const; - static void set_restart_index(unsigned); -}; - -} // namespace GL -} // namespace Msp - -#endif diff --git a/source/bindable.h b/source/bindable.h deleted file mode 100644 index b2634ea9..00000000 --- a/source/bindable.h +++ /dev/null @@ -1,225 +0,0 @@ -#ifndef MSP_GL_BINDABLE_H_ -#define MSP_GL_BINDABLE_H_ - -namespace Msp { -namespace GL { - -/** -A helper class for single-point binding. Provides tracking of the currently -bound object. -*/ -template -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 -const T *Bindable::cur_obj; - - -/** -A helper class for Bindables that revert to a default object on unbind. -*/ -template -class BindableWithDefault: protected Bindable -{ - friend class Bindable; - -protected: - BindableWithDefault() { } - ~BindableWithDefault() { if(this==&default_object()) Bindable::set_current(0); } - -public: - static const T *current() - { - if(!Bindable::cur_obj) - Bindable::cur_obj = &default_object(); - return Bindable::cur_obj; - } - - static void unbind() - { - if(Bindable::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 - Bind(T *o) { init(o); } - - template - Bind(const T *o) { init(o); } - - template - Bind(const T &o) { init(&o); } - - template - Bind(T *o, S s) { init(o, s); } - - template - Bind(const T *o, S s) { init(o, s); } - - template - Bind(const T &o, S s) { init(&o, s); } - -private: - template - void init(const T *o) - { - cleanup = (o ? static_cast(&unbind) : 0); - slot = 0; - if(o) - o->bind(); - else - T::unbind(); - } - - template - void init(const T *o, S s) - { - cleanup = (o ? static_cast(&unbind_from) : 0); - slot = s; - if(o) - o->bind_to(s); - else - T::unbind_from(s); - } - -public: - ~Bind() - { if(cleanup) cleanup(slot); } - -private: - template - static void unbind(int) - { T::unbind(); } - - template - static void unbind_from(int s) - { T::unbind_from(static_cast(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 - BindRestore(T *o) { init(o); } - - template - BindRestore(const T *o) { init(o); } - - template - BindRestore(const T &o) { init(&o); } - - template - BindRestore(T *o, S s) { init(o, s); } - - template - BindRestore(const T *o, S s) { init(o, s); } - - template - BindRestore(const T &o, S s) { init(&o, s); } - -private: - template - void init(T *o) - { - old = T::current(); - slot = 0; - cleanup = (o!=old ? static_cast(&restore) : 0); - if(o) - o->bind(); - else if(old) - T::unbind(); - } - - template - void init(T *o, S s) - { - old = T::current(s); - slot = s; - cleanup = (o!=old ? static_cast(&restore_to) : 0); - if(o) - o->bind_to(s); - else if(old) - T::unbind_from(s); - } - -public: - ~BindRestore() - { if(cleanup) cleanup(old, slot); } - -private: - template - static void restore(const void *o, int) - { - if(o) - reinterpret_cast(o)->bind(); - else - T::unbind(); - } - - template - static void restore_to(const void *o, int si) - { - S s = static_cast(si); - if(o) - reinterpret_cast(o)->bind_to(s); - else - T::unbind_from(s); - } -}; - -} // namespace GL -} // namespace Msp - -#endif diff --git a/source/blend.cpp b/source/blend.cpp deleted file mode 100644 index 3327885c..00000000 --- a/source/blend.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include -#include -#include -#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(factor))); - } -} - -} // namespace GL -} // namespace Msp diff --git a/source/blend.h b/source/blend.h deleted file mode 100644 index e7b291e6..00000000 --- a/source/blend.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef MSP_GL_BLEND_H_ -#define MSP_GL_BLEND_H_ - -#include -#include "bindable.h" -#include "gl.h" -#include - -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 -{ -private: - BlendEquation eq; - BlendFactor src_factor; - BlendFactor dst_factor; - -public: - Blend(); - Blend(BlendFactor, BlendFactor); - Blend(BlendEquation, BlendFactor, BlendFactor); - - void bind() const; - - static void unbind(); - - static const Blend &alpha(); - static const Blend &additive(); - static const Blend &additive_alpha(); -}; - -void operator>>(const LexicalConverter &, BlendFactor &); -void operator<<(LexicalConverter &, BlendFactor); - -} // namespace GL -} // namespace Msp - -#endif diff --git a/source/bloom.cpp b/source/bloom.cpp deleted file mode 100644 index ee7c76c3..00000000 --- a/source/bloom.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include -#include -#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(r*3.0f), 9); - common_shdata.uniform("size", size); - - vector 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 = 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(t) -{ - add("strength", &Template::strength); - add("radius", &Template::radius); -} - -} // namespace GL -} // namespace Msp diff --git a/source/bloom.h b/source/bloom.h deleted file mode 100644 index 6b10195b..00000000 --- a/source/bloom.h +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef MSP_GL_BLOOM_H_ -#define MSP_GL_BLOOM_H_ - -#include "framebuffer.h" -#include "mesh.h" -#include "postprocessor.h" -#include "texture2d.h" -#include "texturing.h" -#include "program.h" -#include "programdata.h" -#include "rendertarget.h" - -namespace Msp { -namespace GL { - -/** -The Bloom post-processing effect causes very bright areas of the image to bleed -into surrounding pixels. Commonly used together with HDR rendering. - -The technique used is to gaussian blur the image and then blend the result with -the original image. With suitable parameters, this effect may also be used as -a blur filter. -*/ -class Bloom: public PostProcessor -{ -public: - struct Template: public PostProcessor::Template - { - class Loader: public DataFile::DerivedObjectLoader - { - 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 quad; - RefPtr nearest_sampler; - RefPtr linear_sampler; - -public: - Bloom(unsigned, unsigned); - ~Bloom(); - - /** Sets the σ value of the gaussian blur. Values much larger than 4.0 are - likely to cause artifacts. */ - void set_radius(float); - - /** Sets the blend factor between original and blurred images. Larger - values mean more blurriness. */ - void set_strength(float); - - virtual void render(Renderer &, const Texture2D &, const Texture2D &); -}; - -} // namespace GL -} // namespace Msp - -#endif diff --git a/source/box.cpp b/source/box.cpp deleted file mode 100644 index 46f1b48e..00000000 --- a/source/box.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include -#include "box.h" -#include "primitivebuilder.h" - -using namespace std; - -namespace Msp { -namespace GL { - -BoxBuilder::BoxBuilder(float w, float h, float d): - origin(-w/2, -h/2, -d/2), - span(w, h, d) -{ } - -BoxBuilder::BoxBuilder(const Vector3 &o, const Vector3 &s): - origin(o), - span(s) -{ } - -void BoxBuilder::build(PrimitiveBuilder &builder) const -{ - builder.normal(1, 0, 0); - build_face(builder, Vector3(origin.x+span.x, origin.y, origin.z), Vector3(0, span.y, 0), Vector3(0, 0, span.z)); - builder.normal(0, 1, 0); - build_face(builder, Vector3(origin.x+span.x, origin.y+span.y, origin.z), Vector3(-span.x, 0, 0), Vector3(0, 0, span.z)); - builder.normal(-1, 0, 0); - build_face(builder, Vector3(origin.x, origin.y+span.y, origin.z), Vector3(0, -span.y, 0), Vector3(0, 0, span.z)); - builder.normal(0, -1, 0); - build_face(builder, origin, Vector3(span.x, 0, 0), Vector3(0, 0, span.z)); - builder.normal(0, 0, 1); - build_face(builder, Vector3(origin.x, origin.y, origin.z+span.z), Vector3(span.x, 0, 0), Vector3(0, span.y, 0)); - builder.normal(0, 0, -1); - build_face(builder, Vector3(origin.x+span.x, origin.y, origin.z), Vector3(-span.x, 0, 0), Vector3(0, span.y, 0)); -} - -void BoxBuilder::build_face(PrimitiveBuilder &builder, const Vector3 &o, const Vector3 &s1, const Vector3 &s2) const -{ - float l1 = 1, l2 = 1; - if(generate_tbn || tex_fit!=STRETCH) - { - l1 = s1.norm(); - l2 = s2.norm(); - } - - if(generate_tbn) - { - builder.tangent(s1/l1); - builder.binormal(s2/l2); - } - - float u_size = 1; - float v_size = 1; - adjust_texture_scale(u_size, v_size, l1, l2); - - builder.begin(TRIANGLE_STRIP); - builder.texcoord(0, v_size); - builder.vertex(o+s2); - builder.texcoord(0, 0); - builder.vertex(o); - builder.texcoord(u_size, v_size); - builder.vertex(o+s1+s2); - builder.texcoord(u_size, 0); - builder.vertex(o+s1); - builder.end(); -} - -} // namespace GL -} // namespace Msp diff --git a/source/box.h b/source/box.h deleted file mode 100644 index 1059d219..00000000 --- a/source/box.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef MSP_GL_BOX_H_ -#define MSP_GL_BOX_H_ - -#include "geometrybuilder.h" -#include "vector.h" - -namespace Msp { -namespace GL { - -class BoxBuilder: public GeometryBuilder -{ -private: - Vector3 origin; - Vector3 span; - -public: - BoxBuilder(float, float, float); - BoxBuilder(const Vector3 &, const Vector3 &); - - using GeometryBuilder::build; - virtual void build(PrimitiveBuilder &) const; -private: - void build_face(PrimitiveBuilder &, const Vector3 &, const Vector3 &, const Vector3 &) const; -}; - -} // namespace GL -} // namespace Msp - -#endif diff --git a/source/buffer.cpp b/source/buffer.cpp deleted file mode 100644 index 56705b87..00000000 --- a/source/buffer.cpp +++ /dev/null @@ -1,296 +0,0 @@ -#include -#include -#include -#include -#include -#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(sizeget_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 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=get_n_uniform_buffer_bindings()) - throw out_of_range("BufferRange::binding"); - if(bound_uniform.size()<=index) - bound_uniform.resize(index+1); - return bound_uniform[index]; - } - else - throw invalid_argument("BufferRange::binding"); -} - -bool BufferRange::set_current(BufferType type, unsigned index, const BufferRange *buf) -{ - const BufferRange *&ptr = binding(type, index); - if(ptr==buf) - return false; - - ptr = buf; - return true; -} - -unsigned BufferRange::get_n_uniform_buffer_bindings() -{ - static unsigned count = get_i(GL_MAX_UNIFORM_BUFFER_BINDINGS); - return count; -} - -unsigned BufferRange::get_uniform_buffer_alignment() -{ - static unsigned align = get_i(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT); - return align; -} - -} // namespace GL -} // namespace Msp diff --git a/source/buffer.h b/source/buffer.h deleted file mode 100644 index cc5e5874..00000000 --- a/source/buffer.h +++ /dev/null @@ -1,165 +0,0 @@ -#ifndef MSP_GL_BUFFER_H_ -#define MSP_GL_BUFFER_H_ - -#include -#include -#include -#include -#include "gl.h" -#include -#include -#include -#include - -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 bound_uniform; - -public: - BufferRange(Buffer &, unsigned, unsigned); - ~BufferRange(); - - void data(const void *); - - void bind_to(BufferType, unsigned); - - static const BufferRange *current(BufferType t, unsigned i) { return binding(t, i); } - static void unbind_from(BufferType, unsigned); -private: - static const BufferRange *&binding(BufferType, unsigned); - static bool set_current(BufferType, unsigned, const BufferRange *); - -public: - static unsigned get_n_uniform_buffer_bindings(); - static unsigned get_uniform_buffer_alignment(); -}; - -} // namespace GL -} // namespace Msp - -#endif diff --git a/source/bufferable.cpp b/source/bufferable.cpp deleted file mode 100644 index d07f57cc..00000000 --- a/source/bufferable.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include -#include -#include -#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(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(bufferable.buffer->map()); -} - -Bufferable::AsyncUpdater::~AsyncUpdater() -{ - bufferable.buffer->unmap(); -} - -void Bufferable::AsyncUpdater::upload_data() -{ - bufferable.upload_data(mapped_address+bufferable.offset); - // Update all bufferables in the same buffer at once - for(const Bufferable *b=bufferable.prev_in_buffer; b; b=b->prev_in_buffer) - if(b->dirty) - b->upload_data(mapped_address+b->offset); - for(const Bufferable *b=bufferable.next_in_buffer; b; b=b->next_in_buffer) - if(b->dirty) - b->upload_data(mapped_address+b->offset); -} - -} // namespace GL -} // namespace Msp diff --git a/source/bufferable.h b/source/bufferable.h deleted file mode 100644 index cbbde4e1..00000000 --- a/source/bufferable.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef MSP_GL_BUFFERABLE_H_ -#define MSP_GL_BUFFERABLE_H_ - -namespace Msp { -namespace GL { - -class Buffer; - -/** -Base class for things that can store data in buffers. Supports buffer sharing. -A dirty flag is provided for derived classes. It should be set when the data -in the buffer is considered out of date, and is cleared by Bufferable after -uploading fresh data to the buffer. -*/ -class Bufferable -{ -public: - class AsyncUpdater - { - private: - const Bufferable &bufferable; - char *mapped_address; - - public: - AsyncUpdater(const Bufferable &); - ~AsyncUpdater(); - - void upload_data(); - }; - -private: - Buffer *buffer; - unsigned offset; - Bufferable *next_in_buffer; - Bufferable *prev_in_buffer; - mutable bool location_dirty; -protected: - mutable bool dirty; - - Bufferable(); -public: - virtual ~Bufferable(); - - /** Sets the buffer to use. If prev is not null, it must use the same - buffer, and this object is inserted after it. */ - void use_buffer(Buffer *buf, Bufferable *prev = 0); - - /** Sets the buffer for the entire chain of objects. */ - void change_buffer(Buffer *); - - /** Returns the total amount of storage required by this object and others - in the same chain, including any alignment between objects. */ - unsigned get_required_buffer_size() const; - - /** Uploads new data into the buffer if necessary. */ - void refresh() const { if(buffer && dirty) upload_data(0); } - - /** Returns an object which can be used to upload data to the buffer using - mapped memory. */ - AsyncUpdater *refresh_async() const; - -private: - void unlink_from_buffer(); - -public: - /** Returns the buffer in which the data is stored. */ - const Buffer *get_buffer() const { return buffer; } - -protected: - /** Returns the amount of data to be stored in the buffer, in bytes. */ - virtual unsigned get_data_size() const = 0; - - /** Returns a pointer to the start of data in client memory. */ - virtual const void *get_data_pointer() const = 0; - - /** Returns the alignment required for the data, in bytes. The offset is - guaranteed to be a multiple of this. */ - virtual unsigned get_alignment() const { return 1; } - - /** Updates the offsets for the chain so that data from different objects - does not overlap. Should be called if either data size or alignment - changes. */ - void update_offset(); - - /** Returns the offset where the data should be uploaded. */ - unsigned get_offset() const { return offset; } - - /** Called when the target buffer or offset within it has changed. */ - virtual void location_changed(Buffer *, unsigned, unsigned) const { } - -private: - /** Uploads data to the buffer. Receives pointer to mapped buffer memory as - parameter, or null to use the buffer upload interface. */ - void upload_data(char *) const; -}; - -} // namespace GL -} // namespace Msp - -#endif diff --git a/source/builders/box.cpp b/source/builders/box.cpp new file mode 100644 index 00000000..46f1b48e --- /dev/null +++ b/source/builders/box.cpp @@ -0,0 +1,68 @@ +#include +#include "box.h" +#include "primitivebuilder.h" + +using namespace std; + +namespace Msp { +namespace GL { + +BoxBuilder::BoxBuilder(float w, float h, float d): + origin(-w/2, -h/2, -d/2), + span(w, h, d) +{ } + +BoxBuilder::BoxBuilder(const Vector3 &o, const Vector3 &s): + origin(o), + span(s) +{ } + +void BoxBuilder::build(PrimitiveBuilder &builder) const +{ + builder.normal(1, 0, 0); + build_face(builder, Vector3(origin.x+span.x, origin.y, origin.z), Vector3(0, span.y, 0), Vector3(0, 0, span.z)); + builder.normal(0, 1, 0); + build_face(builder, Vector3(origin.x+span.x, origin.y+span.y, origin.z), Vector3(-span.x, 0, 0), Vector3(0, 0, span.z)); + builder.normal(-1, 0, 0); + build_face(builder, Vector3(origin.x, origin.y+span.y, origin.z), Vector3(0, -span.y, 0), Vector3(0, 0, span.z)); + builder.normal(0, -1, 0); + build_face(builder, origin, Vector3(span.x, 0, 0), Vector3(0, 0, span.z)); + builder.normal(0, 0, 1); + build_face(builder, Vector3(origin.x, origin.y, origin.z+span.z), Vector3(span.x, 0, 0), Vector3(0, span.y, 0)); + builder.normal(0, 0, -1); + build_face(builder, Vector3(origin.x+span.x, origin.y, origin.z), Vector3(-span.x, 0, 0), Vector3(0, span.y, 0)); +} + +void BoxBuilder::build_face(PrimitiveBuilder &builder, const Vector3 &o, const Vector3 &s1, const Vector3 &s2) const +{ + float l1 = 1, l2 = 1; + if(generate_tbn || tex_fit!=STRETCH) + { + l1 = s1.norm(); + l2 = s2.norm(); + } + + if(generate_tbn) + { + builder.tangent(s1/l1); + builder.binormal(s2/l2); + } + + float u_size = 1; + float v_size = 1; + adjust_texture_scale(u_size, v_size, l1, l2); + + builder.begin(TRIANGLE_STRIP); + builder.texcoord(0, v_size); + builder.vertex(o+s2); + builder.texcoord(0, 0); + builder.vertex(o); + builder.texcoord(u_size, v_size); + builder.vertex(o+s1+s2); + builder.texcoord(u_size, 0); + builder.vertex(o+s1); + builder.end(); +} + +} // namespace GL +} // namespace Msp diff --git a/source/builders/box.h b/source/builders/box.h new file mode 100644 index 00000000..1059d219 --- /dev/null +++ b/source/builders/box.h @@ -0,0 +1,29 @@ +#ifndef MSP_GL_BOX_H_ +#define MSP_GL_BOX_H_ + +#include "geometrybuilder.h" +#include "vector.h" + +namespace Msp { +namespace GL { + +class BoxBuilder: public GeometryBuilder +{ +private: + Vector3 origin; + Vector3 span; + +public: + BoxBuilder(float, float, float); + BoxBuilder(const Vector3 &, const Vector3 &); + + using GeometryBuilder::build; + virtual void build(PrimitiveBuilder &) const; +private: + void build_face(PrimitiveBuilder &, const Vector3 &, const Vector3 &, const Vector3 &) const; +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/builders/capsule.cpp b/source/builders/capsule.cpp new file mode 100644 index 00000000..65a18eb3 --- /dev/null +++ b/source/builders/capsule.cpp @@ -0,0 +1,74 @@ +#define _USE_MATH_DEFINES +#include +#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; irings/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 +#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 +#include +#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(f, 0) +{ + init(); +} + +Font::Loader::Loader(Font &f, Collection &c): + DataFile::CollectionObjectLoader(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 tex = new Texture2D; + load_sub(*tex); + obj.texture = tex; +} + +void Font::Loader::texture_ref(const string &name) +{ + obj.texture = &get_collection().get(name); + obj.texture.keep(); +} + + +Font::Glyph::Loader::Loader(Glyph &g): + DataFile::ObjectLoader(g) +{ + add("texcoords", &Loader::texcoords); + add("size", &Glyph::w, &Glyph::h); + add("offset", &Glyph::off_x, &Glyph::off_y); + add("advance", &Glyph::advance); +} + +void Font::Glyph::Loader::texcoords(float x1_, float y1_, float x2_, float y2_) +{ + obj.x1 = x1_; + obj.y1 = y1_; + obj.x2 = x2_; + obj.y2 = y2_; +} + +} // namespace GL +} // namespace Msp diff --git a/source/builders/font.h b/source/builders/font.h new file mode 100644 index 00000000..9f040dc9 --- /dev/null +++ b/source/builders/font.h @@ -0,0 +1,133 @@ +#ifndef MSP_GL_FONT_H_ +#define MSP_GL_FONT_H_ + +#include +#include +#include +#include +#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 + { + 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 + { + 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 GlyphMap; + typedef std::pair CodePair; + typedef std::map KerningMap; + typedef std::map LigatureMap; + + RefPtr 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 + 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(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 + 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(str, pb); } + +private: + void create_glyph_quad(const Glyph &, PrimitiveBuilder &) const; + float get_glyph_advance(unsigned, unsigned = 0) const; + unsigned get_ligature(unsigned, unsigned) const; +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/builders/geometrybuilder.cpp b/source/builders/geometrybuilder.cpp new file mode 100644 index 00000000..729b40fa --- /dev/null +++ b/source/builders/geometrybuilder.cpp @@ -0,0 +1,43 @@ +#include "geometrybuilder.h" +#include "meshbuilder.h" + +namespace Msp { +namespace GL { + +GeometryBuilder::GeometryBuilder(): + generate_tbn(false), + tex_fit(STRETCH) +{ } + +GeometryBuilder &GeometryBuilder::tbn(bool t) +{ + generate_tbn = t; + return *this; +} + +GeometryBuilder &GeometryBuilder::texture_fit(TextureFit tf) +{ + tex_fit = tf; + return *this; +} + +void GeometryBuilder::adjust_texture_scale(float &u_scale, float &v_scale, float width, float height) const +{ + if(tex_fit!=STRETCH) + { + if((width +#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)append(i); +} + +} // namespace GL +} // namespace Msp diff --git a/source/builders/meshbuilder.h b/source/builders/meshbuilder.h new file mode 100644 index 00000000..5f4a9dc8 --- /dev/null +++ b/source/builders/meshbuilder.h @@ -0,0 +1,32 @@ +#ifndef MSP_GL_MESHBUILDER_H_ +#define MSP_GL_MESHBUILDER_H_ + +#include "primitivebuilder.h" + +namespace Msp { +namespace GL { + +class Batch; +class Mesh; + +class MeshBuilder: public PrimitiveBuilder +{ +private: + Mesh &mesh; + Batch *batch; + +public: + MeshBuilder(Mesh &); + ~MeshBuilder(); + + void auto_offset(); +private: + virtual void begin_(); + virtual void end_(); + virtual void element_(unsigned); +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/builders/pipelinebuilder.cpp b/source/builders/pipelinebuilder.cpp new file mode 100644 index 00000000..51e9cc85 --- /dev/null +++ b/source/builders/pipelinebuilder.cpp @@ -0,0 +1,98 @@ +#include +#include +#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 &passes = tmpl.get_passes(); + for(vector::const_iterator i=passes.begin(); i!=passes.end(); ++i) + renderables[i->renderable_name] = 0; + const vector &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(samplesrenderable_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 = new Pipeline(w, h); + build(*pipeline); + return pipeline.release(); +} + +Pipeline *PipelineBuilder::build(const View &view) const +{ + RefPtr pipeline = new Pipeline(view); + build(*pipeline); + return pipeline.release(); +} + +Pipeline *PipelineBuilder::build(const Framebuffer &fbo) const +{ + RefPtr pipeline = new Pipeline(fbo); + build(*pipeline); + return pipeline.release(); +} + +} // namespace GL +} // namespace Msp diff --git a/source/builders/pipelinebuilder.h b/source/builders/pipelinebuilder.h new file mode 100644 index 00000000..ab0dc4b9 --- /dev/null +++ b/source/builders/pipelinebuilder.h @@ -0,0 +1,39 @@ +#ifndef PIPELINEBUILDER_H_ +#define PIPELINEBUILDER_H_ + +#include +#include + +namespace Msp { +namespace GL { + +class Framebuffer; +class Pipeline; +class PipelineTemplate; +class PostProcessor; +class Renderable; +class View; + +class PipelineBuilder +{ +private: + const PipelineTemplate &tmpl; + std::map renderables; + std::map postprocessors; + +public: + PipelineBuilder(const PipelineTemplate &); + + void set_renderable(const std::string &, Renderable &); + void set_postprocessor(const std::string &, PostProcessor &); + + void build(Pipeline &) const; + Pipeline *build(unsigned, unsigned) const; + Pipeline *build(const View &) const; + Pipeline *build(const Framebuffer &) const; +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/builders/pipelinetemplate.cpp b/source/builders/pipelinetemplate.cpp new file mode 100644 index 00000000..b78b4c40 --- /dev/null +++ b/source/builders/pipelinetemplate.cpp @@ -0,0 +1,202 @@ +#include +#include +#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("ambient_occlusion"); + registry.register_type("bloom"); + registry.register_type("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(t, 0) +{ + init(); +} + +PipelineTemplate::Loader::Loader(PipelineTemplate &t, Collection &c): + DataFile::CollectionObjectLoader(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(p, 0) +{ + init(); +} + +PipelineTemplate::Pass::Loader::Loader(Pass &p, Collection &c): + DataFile::CollectionObjectLoader(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 lightn = new Lighting; + load_sub(*lightn); + obj.lighting = lightn; +} + +void PipelineTemplate::Pass::Loader::lighting(const string &name) +{ + obj.lighting = &get_collection().get(name); + obj.lighting.keep(); +} + +/*void PipelineTemplate::Pass::Loader::scene(const string &name) +{ + obj.default_renderable = get_collection().get(name); +}*/ + +} // namespace GL +} // namespace Msp diff --git a/source/builders/pipelinetemplate.h b/source/builders/pipelinetemplate.h new file mode 100644 index 00000000..fb24ea29 --- /dev/null +++ b/source/builders/pipelinetemplate.h @@ -0,0 +1,153 @@ +#ifndef PIPELINETEMPLATE_H_ +#define PIPELINETEMPLATE_H_ + +#include +#include +#include +#include +#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 + struct AddPostProc + { + static void add(PostProcLoader &ldr, const std::string &kw) { ldr.add(kw, &PostProcLoader::postprocessor); } + }; + + protected: + RefPtr postproc; + + public: + PostProcLoader(); + + PostProcessor::Template *get_postprocessor_template() { return postproc.release(); } + + protected: + virtual void postprocessor_loaded() { } + + private: + template + void postprocessor(); + + friend class PipelineTemplate; + }; + +public: + class Loader: public DataFile::CollectionObjectLoader, 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 + { + 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; + RefPtr depth_test; + RefPtr 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 PassArray; + typedef std::vector PostProcessorArray; + +private: + typedef DataFile::LoadableTypeRegistry 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 + static void register_postprocessor(const std::string &); +private: + static PostProcessorRegistry &get_postprocessor_registry(); +}; + +template +void PipelineTemplate::register_postprocessor(const std::string &kw) +{ + get_postprocessor_registry().register_type(kw); +} + +template +void PipelineTemplate::PostProcLoader::postprocessor() +{ + if(postproc) + throw std::logic_error("Only one postprocessor allowed per slot"); + RefPtr pp = new typename T::Template; + load_sub(*pp); + postproc = pp; + pp = 0; + postprocessor_loaded(); +} + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/builders/primitivebuilder.cpp b/source/builders/primitivebuilder.cpp new file mode 100644 index 00000000..dd869908 --- /dev/null +++ b/source/builders/primitivebuilder.cpp @@ -0,0 +1,75 @@ +#include "error.h" +#include "primitivebuilder.h" + +using namespace std; + +namespace Msp { +namespace GL { + +PrimitiveBuilder::PrimitiveBuilder(VertexArray &a): + array(a), + vab(array), + in_batch(false), + offs(0) +{ } + +void PrimitiveBuilder::begin(PrimitiveType t) +{ + if(in_batch) + throw invalid_operation("PrimitiveBuilder::begin"); + + type = t; + in_batch = true; + + begin_(); +} + +void PrimitiveBuilder::end() +{ + if(!in_batch) + throw invalid_operation("PrimitiveBuilder::end"); + + in_batch = false; + + end_(); +} + +void PrimitiveBuilder::offset(unsigned o) +{ + if(o>array.size()) + throw out_of_range("PrimitiveBuilder::offset"); + offs = o; +} + +void PrimitiveBuilder::element(unsigned i) +{ + if(!in_batch) + throw invalid_operation("PrimitiveBuilder::element"); + if(offs+i>=array.size()) + throw out_of_range("PrimitiveBuilder::element"); + element_(offs+i); +} + +PrimitiveType PrimitiveBuilder::get_type() const +{ + if(!in_batch) + throw invalid_operation("PrimitiveBuilder::get_type"); + return type; +} + +void PrimitiveBuilder::vertex_(const Vector4 &v) +{ + vab.color(col); + vab.normal(nor); + for(std::map::iterator i=texc.begin(); i!=texc.end(); ++i) + vab.multitexcoord(i->first, i->second); + for(std::map::iterator i=attr.begin(); i!=attr.end(); ++i) + vab.attrib(i->first, i->second); + vab.vertex(v); + + if(in_batch) + element_(array.size()-1); +} + +} // namespace GL +} // namespace Msp diff --git a/source/builders/primitivebuilder.h b/source/builders/primitivebuilder.h new file mode 100644 index 00000000..0ffd170c --- /dev/null +++ b/source/builders/primitivebuilder.h @@ -0,0 +1,45 @@ +#ifndef MSP_GL_PRIMITIVEBUILDER_H_ +#define MSP_GL_PRIMITIVEBUILDER_H_ + +#include "primitivetype.h" +#include "vertexarray.h" +#include "vertexbuilder.h" + +namespace Msp { +namespace GL { + +class VertexArray; +class VertexArrayBuilder; + +/** +Base class for primitive builders. This is derived from VertexBuilder and adds +begin() and end() functions for specifying batches of primitives instead of +just vertices. +*/ +class PrimitiveBuilder: public VertexBuilder +{ +protected: + VertexArray &array; + VertexArrayBuilder vab; + PrimitiveType type; + bool in_batch; + unsigned offs; + + PrimitiveBuilder(VertexArray &); +public: + void begin(PrimitiveType); + void end(); + void offset(unsigned); + void element(unsigned); + PrimitiveType get_type() const; +protected: + virtual void vertex_(const Vector4 &); + virtual void begin_() = 0; + virtual void end_() = 0; + virtual void element_(unsigned) = 0; +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/builders/sphere.cpp b/source/builders/sphere.cpp new file mode 100644 index 00000000..dd27fad4 --- /dev/null +++ b/source/builders/sphere.cpp @@ -0,0 +1,261 @@ +#define _USE_MATH_DEFINES +#include +#include +#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 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 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(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(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(col.r*255); + u.c[1] = static_cast(col.g*255); + u.c[2] = static_cast(col.b*255); + u.c[3] = static_cast(col.a*255); + *ptr++ = u.f; + } + else if(*c==NORMAL3) + { + *ptr++ = nor.x; + *ptr++ = nor.y; + *ptr++ = nor.z; + } + else if(t==get_component_type(COLOR4_FLOAT)) + { + *ptr++ = col.r; + *ptr++ = col.g; + *ptr++ = col.b; + if(sz>=4) *ptr++ = col.a; + } + else + { + const Vector4 *v = 0; + if(t==get_component_type(VERTEX3)) + v = &ver; + else if(*c>=TEXCOORD1 && *c<=TEXCOORD4+12) + v = &texc[t-get_component_type(TEXCOORD1)]; + else if(*c>=ATTRIB1) + v = &attr[t-get_component_type(ATTRIB1)]; + else + v = &attr[t]; + *ptr++ = v->x; + if(sz>=2) *ptr++ = v->y; + if(sz>=3) *ptr++ = v->z; + if(sz>=4) *ptr++ = v->w; + } + } +} + +} // namespace GL +} // namespace Msp diff --git a/source/builders/vertexarraybuilder.h b/source/builders/vertexarraybuilder.h new file mode 100644 index 00000000..575a4753 --- /dev/null +++ b/source/builders/vertexarraybuilder.h @@ -0,0 +1,29 @@ +#ifndef MSP_GL_VERTEXARRAYBUIDER_H_ +#define MSP_GL_VERTEXARRAYBUIDER_H_ + +#include +#include "vertexbuilder.h" +#include "vertexformat.h" + +namespace Msp { +namespace GL { + +class VertexArray; + +class VertexArrayBuilder: public VertexBuilder +{ +private: + VertexArray &array; + + VertexArrayBuilder(const VertexArrayBuilder &); +public: + VertexArrayBuilder(VertexArray &); + +private: + virtual void vertex_(const Vector4 &); +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/builders/vertexbuilder.h b/source/builders/vertexbuilder.h new file mode 100644 index 00000000..55f914a3 --- /dev/null +++ b/source/builders/vertexbuilder.h @@ -0,0 +1,167 @@ +#ifndef MSP_GL_VERTEXBUILDER_H_ +#define MSP_GL_VERTEXBUILDER_H_ + +#include +#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 texc; + std::map attr; + +public: + VertexBuilder(): nor(0, 0, 1) { } + + virtual ~VertexBuilder() { } + + void set_matrix(const Matrix &m) + { mtx = m; } + + void transform(const Matrix &m) + { mtx *= m; } + + const Matrix &get_matrix() const + { return mtx; } + + void vertex(float x, float y) + { vertex(x, y, 0, 1); } + + void vertex(float x, float y, float z) + { vertex(x, y, z, 1); } + + void vertex(float x, float y, float z, float w) + { vertex(Vector4(x, y, z, w)); } + + void vertex(const Vector3 &v) + { vertex(Vector4(v.x, v.y, v.z, 1)); } + + void vertex(const Vector4 &v) + { vertex_(mtx*v); } + +protected: + virtual void vertex_(const Vector4 &) = 0; + +public: + void normal(float x, float y, float z) + { normal(Vector3(x, y, z)); } + + void normal(const Vector3 &n) + { + Vector4 tn = mtx*Vector4(n.x, n.y, n.z, 0); + nor = Vector3(tn.x, tn.y, tn.z); + } + + void tangent(float x, float y, float z) + { tangent(Vector3(x, y, z)); } + + void tangent(const Vector3 &t) + { + Vector4 tt = mtx*Vector4(t.x, t.y, t.z, 0); + attrib(get_component_type(TANGENT3), tt); + } + + void binormal(float x, float y, float z) + { binormal(Vector3(x, y, z)); } + + void binormal(const Vector3 &b) + { + Vector4 tb = mtx*Vector4(b.x, b.y, b.z, 0); + attrib(get_component_type(BINORMAL3), tb); + } + + void texcoord(float s) + { texcoord(s, 0, 0, 1); } + + void texcoord(float s, float t) + { texcoord(s, t, 0, 1); } + + void texcoord(float s, float t, float r) + { texcoord(s, t, r, 1); } + + void texcoord(float s, float t, float r, float q) + { texcoord(Vector4(s, t, r, q)); } + + void texcoord(const Vector4 &t) + { multitexcoord(0, t); } + + void multitexcoord(unsigned i, float s) + { multitexcoord(i, s, 0, 0, 1); } + + void multitexcoord(unsigned i, float s, float t) + { multitexcoord(i, s, t, 0, 1); } + + void multitexcoord(unsigned i, float s, float t, float r) + { multitexcoord(i, s, t, r, 1); } + + void multitexcoord(unsigned i, float s, float t, float r, float q) + { multitexcoord(i, Vector4(s, t, r, q)); } + + void multitexcoord(unsigned i, const Vector4 &t) + { texc[i] = t; } + + void color(unsigned char r, unsigned char g, unsigned char b) + { color(r, g, b, 255); } + + void color(unsigned char r, unsigned char g, unsigned char b, unsigned char a) + { color(r/255.f, g/255.f, b/255.f, a/255.f); } + + void color(float r, float g, float b) + { color(r, g, b, 1); } + + void color(float r, float g, float b, float a) + { color(Color(r, g, b, a)); } + + void color(const Color &c) + { col = c; } + + void attrib(unsigned i, float x) + { attrib(i, x, 0, 0, 1); } + + void attrib(unsigned i, float x, float y) + { attrib(i, x, y, 0, 1); } + + void attrib(unsigned i, float x, float y, float z) + { attrib(i, x, y, z, 1); } + + void attrib(unsigned i, float x, float y, float z, float w) + { attrib(i, Vector4(x, y, z, w)); } + + void attrib(unsigned i, const Vector4 &a) + { attr[i] = a; } +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/camera.cpp b/source/camera.cpp deleted file mode 100644 index 3bba0eaf..00000000 --- a/source/camera.cpp +++ /dev/null @@ -1,201 +0,0 @@ -#include -#include "camera.h" -#include "matrix.h" - -namespace Msp { -namespace GL { - -Camera::Camera(): - fov(Geometry::Angle::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 &f) -{ - fov = f; - update_projection_matrix(); -} - -void Camera::set_orthographic(float w, float h) -{ - fov = Geometry::Angle::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 &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::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::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(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::from_degrees(a)); -} - -void Camera::Loader::look_at(float x, float y, float z) -{ - obj.look_at(Vector3(x, y, z)); -} - -void Camera::Loader::look_direction(float x, float y, float z) -{ - obj.set_look_direction(Vector3(x, y, z)); -} - -void Camera::Loader::orthographic(float w, float h) -{ - obj.set_orthographic(w, h); -} - -void Camera::Loader::position(float x, float y, float z) -{ - obj.set_position(Vector3(x, y, z)); -} - -void Camera::Loader::up_direction(float x, float y, float z) -{ - obj.set_up_direction(Vector3(x, y, z)); -} - -} // namespace GL -} // namespace Msp diff --git a/source/camera.h b/source/camera.h deleted file mode 100644 index b10ccb93..00000000 --- a/source/camera.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef MSP_GL_CAMERA_H_ -#define MSP_GL_CAMERA_H_ - -#include -#include "placeable.h" - -namespace Msp { -namespace GL { - -class Camera: public Placeable -{ -public: - class Loader: public DataFile::ObjectLoader - { - 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 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 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 &); - 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 &); - const Geometry::Angle &get_field_of_view() const { return fov; } - bool is_orthographic() const { return fov==Geometry::Angle::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 &get_frustum_rotation() const { return rotate; } - - void set_position(const Vector3 &); - void set_look_direction(const Vector3 &); - void look_at(const Vector3 &); - void set_up_direction(const Vector3 &); - const Vector3 &get_position() const { return position; } - const Vector3 &get_look_direction() const { return look_dir; } - const Vector3 &get_up_direction() const { return up_dir; } - - virtual void set_matrix(const Matrix &m) { set_object_matrix(m); } - - /** Sets the position and orientation of the camera from an object matrix. */ - void set_object_matrix(const Matrix &); - - /** Returns the view matrix, used to transform coordinates from world space - to eye space. */ - const Matrix &get_view_matrix() const { return view_matrix; } - - /** Returns the object matrix, used to transform coordinates from eye space - to world space. */ - const Matrix &get_object_matrix() const { return matrix; } - - /** Returns the projection matrix. */ - const Matrix &get_projection_matrix() const { return proj_matrix; } - - Vector3 project(const Vector4 &) const; - Vector3 project(const Vector3 &) const; - Vector4 unproject(const Vector4 &) const; - Vector3 unproject(const Vector3 &) const; - -private: - void update_projection_matrix(); - void update_object_matrix(); -}; - -} // namespace GL -} // namespcae Msp - -#endif diff --git a/source/capsule.cpp b/source/capsule.cpp deleted file mode 100644 index 65a18eb3..00000000 --- a/source/capsule.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#define _USE_MATH_DEFINES -#include -#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; irings/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 -#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; iupdate_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; iplanes.size(); ++i) - disable(GL_CLIP_PLANE0+i); - } -} - -void Clipping::unbind() -{ - const Clipping *old = current(); - if(!set_current(0)) - return; - - for(unsigned i=0; iplanes.size(); ++i) - if(old->planes[i]) - disable(GL_CLIP_PLANE0+i); -} - -} // namespace GL -} // namespace Msp diff --git a/source/clipping.h b/source/clipping.h deleted file mode 100644 index e42c28e6..00000000 --- a/source/clipping.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef MSP_GL_CLIPPING_H_ -#define MSP_GL_CLIPPING_H_ - -#include -#include "bindable.h" - -namespace Msp { -namespace GL { - -class ClipPlane; -class Matrix; -class ProgramData; - -class Clipping: public Bindable -{ -private: - std::vector planes; - -public: - static unsigned get_n_attach_points(); - - void attach(unsigned, const ClipPlane &); - void detach(unsigned); - - void update_shader_data(ProgramData &, const Matrix &) const; - - void bind() const; - - static void unbind(); -}; - -} // namespace GL -} // namespace Msp - -#endif diff --git a/source/clipplane.cpp b/source/clipplane.cpp deleted file mode 100644 index 4adf2bf5..00000000 --- a/source/clipplane.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include "clipplane.h" -#include "gl.h" -#include "matrix.h" -#include "misc.h" -#include "programdata.h" - -namespace Msp { -namespace GL { - -ClipPlane::ClipPlane(): - eq(0, 0, 0, 1) -{ } - -ClipPlane::ClipPlane(const Vector4 &e): - eq(e) -{ } - -ClipPlane::ClipPlane(const Vector3 &p, const Vector3 &d): - eq(compose(d, -dot(p, d))) -{ } - -void ClipPlane::set_equation(const Vector4 &e) -{ - eq = e; -} - -void ClipPlane::set_plane(const Vector3 &p, const Vector3 &d) -{ - Vector3 nd = normalize(d); - set_equation(compose(nd, -dot(p, nd))); -} - -void ClipPlane::update_shader_data(ProgramData &shdata, const Matrix &view_inverse, unsigned i) const -{ - shdata.uniform(format("clip_planes[%d].equation", i), eq*view_inverse); -} - -} // namespace GL -} // namespace Msp diff --git a/source/clipplane.h b/source/clipplane.h deleted file mode 100644 index 98c633b7..00000000 --- a/source/clipplane.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef MSP_GL_CLIP_H_ -#define MSP_GL_CLIP_H_ - -#include "vector.h" - -namespace Msp { -namespace GL { - -class Matrix; -class ProgramData; - -class ClipPlane -{ -private: - Vector4 eq; - -public: - ClipPlane(); - ClipPlane(const Vector4 &); - ClipPlane(const Vector3 &, const Vector3 &); - - void set_equation(const Vector4 &); - void set_plane(const Vector3 &, const Vector3 &); - void update_shader_data(ProgramData &, const Matrix &, unsigned) const; -}; - -} // namespace GL -} // namespace Msp - -#endif diff --git a/source/color.h b/source/color.h deleted file mode 100644 index e57689bf..00000000 --- a/source/color.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef MSP_GL_COLOR_H_ -#define MSP_GL_COLOR_H_ - -#include - -namespace Msp { -namespace GL { - -inline float to_srgb(float c) -{ return (c<=0.0031308f ? 12.92f*c : 1.055f*std::pow(c, 1/2.4f)-0.055f); } - -inline float to_linear(float c) -{ return (c<=0.04045 ? c/12.92f : std::pow((c+0.055f)/1.055f, 2.4f)); } - -struct Color -{ - float r, g, b, a; - - Color(): r(1), g(1), b(1), a(1) { } - Color(float v): r(v), g(v), b(v), a(1) { } - Color(float r_, float g_, float b_): r(r_), g(g_), b(b_), a(1) { } - Color(float r_, float g_, float b_, float a_): r(r_), g(g_), b(b_), a(a_) { } - Color operator*(float f) const { return Color(r*f, g*f, b*f, a); } - Color operator+(const Color &c) const { return Color(r+c.r, g+c.g, b+c.b, 1-(1-a)*(1-c.a)); } - bool operator==(const Color &c) const { return (r==c.r && g==c.g && b==c.b && a==c.a); } - bool operator!=(const Color &c) const { return !operator==(c); } - - Color to_srgb() const { return Color(GL::to_srgb(r), GL::to_srgb(g), GL::to_srgb(b), a); } - Color to_linear() const { return Color(GL::to_linear(r), GL::to_linear(g), GL::to_linear(b), a); } -}; - -} // namespace GL -} // namespace Msp - -#endif diff --git a/source/colorcurve.cpp b/source/colorcurve.cpp deleted file mode 100644 index 8811b065..00000000 --- a/source/colorcurve.cpp +++ /dev/null @@ -1,124 +0,0 @@ -#include -#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 = 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(t) -{ - add("brightness_response", &Template::brightness_response); - add("exposure_adjust", &Template::exposure_adjust); - add("gamma", &Loader::gamma); - add("srgb", &Loader::srgb); -} - -void ColorCurve::Template::Loader::gamma(float g) -{ - obj.gamma = g; - obj.srgb = false; -} - -void ColorCurve::Template::Loader::srgb() -{ - obj.srgb = true; -} - -} // namespace GL -} // namespace Msp diff --git a/source/colorcurve.h b/source/colorcurve.h deleted file mode 100644 index 3f65b049..00000000 --- a/source/colorcurve.h +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef MSP_GL_COLORCURVE_H_ -#define MSP_GL_COLORCURVE_H_ - -#include "postprocessor.h" -#include "program.h" -#include "programdata.h" -#include "texture1d.h" -#include "texturing.h" - -namespace Msp { -namespace GL { - -/** -Processes oversaturated colors to preserve hues. When one color component -exceeds 1.0, the overflow is distributed to the other components, scaling the -color towards white. - -Gamma or sRGB correction can also be applied to the output. It can be used to -improve color reproduction by performing lighting calculations in linear color -space and converting to sRGB for display. -*/ -class ColorCurve: public PostProcessor -{ -public: - struct Template: public PostProcessor::Template - { - class Loader: public DataFile::DerivedObjectLoader - { - 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 quad; - RefPtr linear_sampler; - RefPtr nearest_sampler; - -public: - ColorCurve(); - - /** Set exposure adjustment in EV units. Positive values brighten the - image, negative values darken it. Zero is neutral. */ - void set_exposure_adjust(float); - - /** Sets the exponent of the */ - void set_brightness_response(float); - - /** Sets the gamma value used for mapping output colors. Allowed range is - from 0.1 to 10. */ - void set_gamma(float); - - /** Sets output mapping to sRGB. This is almost, but not exactly equivalent - to set_gamma(2.2). */ - void set_srgb(); - - /// Sets output mapping to linear. This is equivalent to set_gamma(1). - void set_linear(); - - virtual void render(Renderer &, const Texture2D &, const Texture2D &); -}; - -} // namespace GL -} // namespace Msp - -#endif diff --git a/source/core/batch.cpp b/source/core/batch.cpp new file mode 100644 index 00000000..f14cc307 --- /dev/null +++ b/source/core/batch.cpp @@ -0,0 +1,252 @@ +#include +#include +#include "batch.h" +#include "bindable.h" +#include "buffer.h" +#include "error.h" +#include "mesh.h" +#include "vertexarray.h" + +using namespace std; + +namespace { + +template +void append(vector &data, T i) +{ + data.insert(data.end(), sizeof(T), 0); + *(T *)(&data[data.size()-sizeof(T)]) = i; +} + +template +U convert(T n) +{ + if(!static_cast(~n)) + return ~0; + else + return n; +} + +template +void expand(vector &data) +{ + unsigned count = data.size()/sizeof(T); + data.resize(count*sizeof(U)); + for(unsigned i=count; i--;) + *(U *)(&data[i*sizeof(U)]) = convert(*(T *)(&data[i*sizeof(T)])); +} + +template +void shrink(vector &data) +{ + unsigned count = data.size()/sizeof(T); + for(unsigned i=0; i(*(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(data); + else if(index_type==UNSIGNED_INT && t==UNSIGNED_SHORT) + shrink(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 &ind) +{ + if(ind.empty()) + return *this; + + data.reserve(data.size()+ind.size()*get_index_size()); + for(vector::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(data, 0xFFFFFFFF); + else + ::append(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; i0xFFFE) + set_index_type(UNSIGNED_INT); + + if(index_type==UNSIGNED_INT) + ::append(data, i); + else + ::append(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(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(b) +{ + add("indices", &Loader::indices); +} + +void Batch::Loader::indices(const vector &ind) +{ + obj.append(ind); +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/batch.h b/source/core/batch.h new file mode 100644 index 00000000..2e4ad9ef --- /dev/null +++ b/source/core/batch.h @@ -0,0 +1,79 @@ +#ifndef MSP_GL_BATCH_H_ +#define MSP_GL_BATCH_H_ + +#include +#include +#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 + { + public: + Loader(Batch &); + private: + void indices(const std::vector &); + }; + +private: + PrimitiveType prim_type; + DataType index_type; + std::vector 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 &); + bool can_append(PrimitiveType); + Batch &append(const Batch &); +private: + void append_index(unsigned); + virtual unsigned get_data_size() const { return data.size(); } + virtual const void *get_data_pointer() const { return &data[0]; } + virtual unsigned get_alignment() const { return get_index_size(); } + unsigned get_index_size() const; +public: + unsigned size() const { return data.size()/get_index_size(); } + + unsigned get_index(unsigned) const; + + void draw() const; + void draw_instanced(unsigned) const; +private: + const void *setup_draw() const; + static void set_restart_index(unsigned); +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/bindable.h b/source/core/bindable.h new file mode 100644 index 00000000..b2634ea9 --- /dev/null +++ b/source/core/bindable.h @@ -0,0 +1,225 @@ +#ifndef MSP_GL_BINDABLE_H_ +#define MSP_GL_BINDABLE_H_ + +namespace Msp { +namespace GL { + +/** +A helper class for single-point binding. Provides tracking of the currently +bound object. +*/ +template +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 +const T *Bindable::cur_obj; + + +/** +A helper class for Bindables that revert to a default object on unbind. +*/ +template +class BindableWithDefault: protected Bindable +{ + friend class Bindable; + +protected: + BindableWithDefault() { } + ~BindableWithDefault() { if(this==&default_object()) Bindable::set_current(0); } + +public: + static const T *current() + { + if(!Bindable::cur_obj) + Bindable::cur_obj = &default_object(); + return Bindable::cur_obj; + } + + static void unbind() + { + if(Bindable::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 + Bind(T *o) { init(o); } + + template + Bind(const T *o) { init(o); } + + template + Bind(const T &o) { init(&o); } + + template + Bind(T *o, S s) { init(o, s); } + + template + Bind(const T *o, S s) { init(o, s); } + + template + Bind(const T &o, S s) { init(&o, s); } + +private: + template + void init(const T *o) + { + cleanup = (o ? static_cast(&unbind) : 0); + slot = 0; + if(o) + o->bind(); + else + T::unbind(); + } + + template + void init(const T *o, S s) + { + cleanup = (o ? static_cast(&unbind_from) : 0); + slot = s; + if(o) + o->bind_to(s); + else + T::unbind_from(s); + } + +public: + ~Bind() + { if(cleanup) cleanup(slot); } + +private: + template + static void unbind(int) + { T::unbind(); } + + template + static void unbind_from(int s) + { T::unbind_from(static_cast(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 + BindRestore(T *o) { init(o); } + + template + BindRestore(const T *o) { init(o); } + + template + BindRestore(const T &o) { init(&o); } + + template + BindRestore(T *o, S s) { init(o, s); } + + template + BindRestore(const T *o, S s) { init(o, s); } + + template + BindRestore(const T &o, S s) { init(&o, s); } + +private: + template + void init(T *o) + { + old = T::current(); + slot = 0; + cleanup = (o!=old ? static_cast(&restore) : 0); + if(o) + o->bind(); + else if(old) + T::unbind(); + } + + template + void init(T *o, S s) + { + old = T::current(s); + slot = s; + cleanup = (o!=old ? static_cast(&restore_to) : 0); + if(o) + o->bind_to(s); + else if(old) + T::unbind_from(s); + } + +public: + ~BindRestore() + { if(cleanup) cleanup(old, slot); } + +private: + template + static void restore(const void *o, int) + { + if(o) + reinterpret_cast(o)->bind(); + else + T::unbind(); + } + + template + static void restore_to(const void *o, int si) + { + S s = static_cast(si); + if(o) + reinterpret_cast(o)->bind_to(s); + else + T::unbind_from(s); + } +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/blend.cpp b/source/core/blend.cpp new file mode 100644 index 00000000..3327885c --- /dev/null +++ b/source/core/blend.cpp @@ -0,0 +1,127 @@ +#include +#include +#include +#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(factor))); + } +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/blend.h b/source/core/blend.h new file mode 100644 index 00000000..e7b291e6 --- /dev/null +++ b/source/core/blend.h @@ -0,0 +1,69 @@ +#ifndef MSP_GL_BLEND_H_ +#define MSP_GL_BLEND_H_ + +#include +#include "bindable.h" +#include "gl.h" +#include + +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 +{ +private: + BlendEquation eq; + BlendFactor src_factor; + BlendFactor dst_factor; + +public: + Blend(); + Blend(BlendFactor, BlendFactor); + Blend(BlendEquation, BlendFactor, BlendFactor); + + void bind() const; + + static void unbind(); + + static const Blend &alpha(); + static const Blend &additive(); + static const Blend &additive_alpha(); +}; + +void operator>>(const LexicalConverter &, BlendFactor &); +void operator<<(LexicalConverter &, BlendFactor); + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/buffer.cpp b/source/core/buffer.cpp new file mode 100644 index 00000000..56705b87 --- /dev/null +++ b/source/core/buffer.cpp @@ -0,0 +1,296 @@ +#include +#include +#include +#include +#include +#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(sizeget_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 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=get_n_uniform_buffer_bindings()) + throw out_of_range("BufferRange::binding"); + if(bound_uniform.size()<=index) + bound_uniform.resize(index+1); + return bound_uniform[index]; + } + else + throw invalid_argument("BufferRange::binding"); +} + +bool BufferRange::set_current(BufferType type, unsigned index, const BufferRange *buf) +{ + const BufferRange *&ptr = binding(type, index); + if(ptr==buf) + return false; + + ptr = buf; + return true; +} + +unsigned BufferRange::get_n_uniform_buffer_bindings() +{ + static unsigned count = get_i(GL_MAX_UNIFORM_BUFFER_BINDINGS); + return count; +} + +unsigned BufferRange::get_uniform_buffer_alignment() +{ + static unsigned align = get_i(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT); + return align; +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/buffer.h b/source/core/buffer.h new file mode 100644 index 00000000..cc5e5874 --- /dev/null +++ b/source/core/buffer.h @@ -0,0 +1,165 @@ +#ifndef MSP_GL_BUFFER_H_ +#define MSP_GL_BUFFER_H_ + +#include +#include +#include +#include +#include "gl.h" +#include +#include +#include +#include + +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 bound_uniform; + +public: + BufferRange(Buffer &, unsigned, unsigned); + ~BufferRange(); + + void data(const void *); + + void bind_to(BufferType, unsigned); + + static const BufferRange *current(BufferType t, unsigned i) { return binding(t, i); } + static void unbind_from(BufferType, unsigned); +private: + static const BufferRange *&binding(BufferType, unsigned); + static bool set_current(BufferType, unsigned, const BufferRange *); + +public: + static unsigned get_n_uniform_buffer_bindings(); + static unsigned get_uniform_buffer_alignment(); +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/bufferable.cpp b/source/core/bufferable.cpp new file mode 100644 index 00000000..d07f57cc --- /dev/null +++ b/source/core/bufferable.cpp @@ -0,0 +1,165 @@ +#include +#include +#include +#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(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(bufferable.buffer->map()); +} + +Bufferable::AsyncUpdater::~AsyncUpdater() +{ + bufferable.buffer->unmap(); +} + +void Bufferable::AsyncUpdater::upload_data() +{ + bufferable.upload_data(mapped_address+bufferable.offset); + // Update all bufferables in the same buffer at once + for(const Bufferable *b=bufferable.prev_in_buffer; b; b=b->prev_in_buffer) + if(b->dirty) + b->upload_data(mapped_address+b->offset); + for(const Bufferable *b=bufferable.next_in_buffer; b; b=b->next_in_buffer) + if(b->dirty) + b->upload_data(mapped_address+b->offset); +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/bufferable.h b/source/core/bufferable.h new file mode 100644 index 00000000..cbbde4e1 --- /dev/null +++ b/source/core/bufferable.h @@ -0,0 +1,100 @@ +#ifndef MSP_GL_BUFFERABLE_H_ +#define MSP_GL_BUFFERABLE_H_ + +namespace Msp { +namespace GL { + +class Buffer; + +/** +Base class for things that can store data in buffers. Supports buffer sharing. +A dirty flag is provided for derived classes. It should be set when the data +in the buffer is considered out of date, and is cleared by Bufferable after +uploading fresh data to the buffer. +*/ +class Bufferable +{ +public: + class AsyncUpdater + { + private: + const Bufferable &bufferable; + char *mapped_address; + + public: + AsyncUpdater(const Bufferable &); + ~AsyncUpdater(); + + void upload_data(); + }; + +private: + Buffer *buffer; + unsigned offset; + Bufferable *next_in_buffer; + Bufferable *prev_in_buffer; + mutable bool location_dirty; +protected: + mutable bool dirty; + + Bufferable(); +public: + virtual ~Bufferable(); + + /** Sets the buffer to use. If prev is not null, it must use the same + buffer, and this object is inserted after it. */ + void use_buffer(Buffer *buf, Bufferable *prev = 0); + + /** Sets the buffer for the entire chain of objects. */ + void change_buffer(Buffer *); + + /** Returns the total amount of storage required by this object and others + in the same chain, including any alignment between objects. */ + unsigned get_required_buffer_size() const; + + /** Uploads new data into the buffer if necessary. */ + void refresh() const { if(buffer && dirty) upload_data(0); } + + /** Returns an object which can be used to upload data to the buffer using + mapped memory. */ + AsyncUpdater *refresh_async() const; + +private: + void unlink_from_buffer(); + +public: + /** Returns the buffer in which the data is stored. */ + const Buffer *get_buffer() const { return buffer; } + +protected: + /** Returns the amount of data to be stored in the buffer, in bytes. */ + virtual unsigned get_data_size() const = 0; + + /** Returns a pointer to the start of data in client memory. */ + virtual const void *get_data_pointer() const = 0; + + /** Returns the alignment required for the data, in bytes. The offset is + guaranteed to be a multiple of this. */ + virtual unsigned get_alignment() const { return 1; } + + /** Updates the offsets for the chain so that data from different objects + does not overlap. Should be called if either data size or alignment + changes. */ + void update_offset(); + + /** Returns the offset where the data should be uploaded. */ + unsigned get_offset() const { return offset; } + + /** Called when the target buffer or offset within it has changed. */ + virtual void location_changed(Buffer *, unsigned, unsigned) const { } + +private: + /** Uploads data to the buffer. Receives pointer to mapped buffer memory as + parameter, or null to use the buffer upload interface. */ + void upload_data(char *) const; +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/clipping.cpp b/source/core/clipping.cpp new file mode 100644 index 00000000..3bdeee3c --- /dev/null +++ b/source/core/clipping.cpp @@ -0,0 +1,85 @@ +#include +#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; iupdate_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; iplanes.size(); ++i) + disable(GL_CLIP_PLANE0+i); + } +} + +void Clipping::unbind() +{ + const Clipping *old = current(); + if(!set_current(0)) + return; + + for(unsigned i=0; iplanes.size(); ++i) + if(old->planes[i]) + disable(GL_CLIP_PLANE0+i); +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/clipping.h b/source/core/clipping.h new file mode 100644 index 00000000..e42c28e6 --- /dev/null +++ b/source/core/clipping.h @@ -0,0 +1,35 @@ +#ifndef MSP_GL_CLIPPING_H_ +#define MSP_GL_CLIPPING_H_ + +#include +#include "bindable.h" + +namespace Msp { +namespace GL { + +class ClipPlane; +class Matrix; +class ProgramData; + +class Clipping: public Bindable +{ +private: + std::vector planes; + +public: + static unsigned get_n_attach_points(); + + void attach(unsigned, const ClipPlane &); + void detach(unsigned); + + void update_shader_data(ProgramData &, const Matrix &) const; + + void bind() const; + + static void unbind(); +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/clipplane.cpp b/source/core/clipplane.cpp new file mode 100644 index 00000000..4adf2bf5 --- /dev/null +++ b/source/core/clipplane.cpp @@ -0,0 +1,40 @@ +#include +#include "clipplane.h" +#include "gl.h" +#include "matrix.h" +#include "misc.h" +#include "programdata.h" + +namespace Msp { +namespace GL { + +ClipPlane::ClipPlane(): + eq(0, 0, 0, 1) +{ } + +ClipPlane::ClipPlane(const Vector4 &e): + eq(e) +{ } + +ClipPlane::ClipPlane(const Vector3 &p, const Vector3 &d): + eq(compose(d, -dot(p, d))) +{ } + +void ClipPlane::set_equation(const Vector4 &e) +{ + eq = e; +} + +void ClipPlane::set_plane(const Vector3 &p, const Vector3 &d) +{ + Vector3 nd = normalize(d); + set_equation(compose(nd, -dot(p, nd))); +} + +void ClipPlane::update_shader_data(ProgramData &shdata, const Matrix &view_inverse, unsigned i) const +{ + shdata.uniform(format("clip_planes[%d].equation", i), eq*view_inverse); +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/clipplane.h b/source/core/clipplane.h new file mode 100644 index 00000000..98c633b7 --- /dev/null +++ b/source/core/clipplane.h @@ -0,0 +1,30 @@ +#ifndef MSP_GL_CLIP_H_ +#define MSP_GL_CLIP_H_ + +#include "vector.h" + +namespace Msp { +namespace GL { + +class Matrix; +class ProgramData; + +class ClipPlane +{ +private: + Vector4 eq; + +public: + ClipPlane(); + ClipPlane(const Vector4 &); + ClipPlane(const Vector3 &, const Vector3 &); + + void set_equation(const Vector4 &); + void set_plane(const Vector3 &, const Vector3 &); + void update_shader_data(ProgramData &, const Matrix &, unsigned) const; +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/color.h b/source/core/color.h new file mode 100644 index 00000000..e57689bf --- /dev/null +++ b/source/core/color.h @@ -0,0 +1,35 @@ +#ifndef MSP_GL_COLOR_H_ +#define MSP_GL_COLOR_H_ + +#include + +namespace Msp { +namespace GL { + +inline float to_srgb(float c) +{ return (c<=0.0031308f ? 12.92f*c : 1.055f*std::pow(c, 1/2.4f)-0.055f); } + +inline float to_linear(float c) +{ return (c<=0.04045 ? c/12.92f : std::pow((c+0.055f)/1.055f, 2.4f)); } + +struct Color +{ + float r, g, b, a; + + Color(): r(1), g(1), b(1), a(1) { } + Color(float v): r(v), g(v), b(v), a(1) { } + Color(float r_, float g_, float b_): r(r_), g(g_), b(b_), a(1) { } + Color(float r_, float g_, float b_, float a_): r(r_), g(g_), b(b_), a(a_) { } + Color operator*(float f) const { return Color(r*f, g*f, b*f, a); } + Color operator+(const Color &c) const { return Color(r+c.r, g+c.g, b+c.b, 1-(1-a)*(1-c.a)); } + bool operator==(const Color &c) const { return (r==c.r && g==c.g && b==c.b && a==c.a); } + bool operator!=(const Color &c) const { return !operator==(c); } + + Color to_srgb() const { return Color(GL::to_srgb(r), GL::to_srgb(g), GL::to_srgb(b), a); } + Color to_linear() const { return Color(GL::to_linear(r), GL::to_linear(g), GL::to_linear(b), a); } +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/datatype.cpp b/source/core/datatype.cpp new file mode 100644 index 00000000..1def3ca3 --- /dev/null +++ b/source/core/datatype.cpp @@ -0,0 +1,26 @@ +#include +#include "datatype.h" + +using namespace std; + +namespace Msp { +namespace GL { + +unsigned get_type_size(DataType type) +{ + switch(type) + { + case BYTE: + case UNSIGNED_BYTE: return 1; + case SHORT: + case UNSIGNED_SHORT: + case HALF_FLOAT: return 2; + case INT: + case UNSIGNED_INT: + case FLOAT: return 4; + default: throw invalid_argument("get_type_size"); + } +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/datatype.h b/source/core/datatype.h new file mode 100644 index 00000000..2d783f61 --- /dev/null +++ b/source/core/datatype.h @@ -0,0 +1,27 @@ +#ifndef MSP_GL_DATATYPE_H_ +#define MSP_GL_DATATYPE_H_ + +#include "gl.h" +#include + +namespace Msp { +namespace GL { + +enum DataType +{ + BYTE = GL_BYTE, + UNSIGNED_BYTE = GL_UNSIGNED_BYTE, + SHORT = GL_SHORT, + UNSIGNED_SHORT = GL_UNSIGNED_SHORT, + INT = GL_INT, + UNSIGNED_INT = GL_UNSIGNED_INT, + FLOAT = GL_FLOAT, + HALF_FLOAT = GL_HALF_FLOAT +}; + +unsigned get_type_size(DataType); + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/error.h b/source/core/error.h new file mode 100644 index 00000000..8af3ec6b --- /dev/null +++ b/source/core/error.h @@ -0,0 +1,54 @@ +#ifndef MSP_GL_ERROR_H_ +#define MSP_GL_ERROR_H_ + +#include + +namespace Msp { +namespace GL { + +class unsupported_extension: public std::runtime_error +{ +public: + unsupported_extension(const std::string &w): std::runtime_error(w) { } + virtual ~unsupported_extension() throw() { } +}; + +class invalid_operation: public std::logic_error +{ +public: + invalid_operation(const std::string &w): std::logic_error(w) { } + virtual ~invalid_operation() throw() { } +}; + +class stack_underflow: public std::logic_error +{ +public: + stack_underflow(const std::string &w): std::logic_error(w) { } + virtual ~stack_underflow() throw() { } +}; + +class incompatible_data: public std::logic_error +{ +public: + incompatible_data(const std::string &w): std::logic_error(w) { } + virtual ~incompatible_data() throw() { } +}; + +class compile_error: public std::runtime_error +{ +public: + compile_error(const std::string &w): std::runtime_error(w) { } + virtual ~compile_error() throw() { } +}; + +class incomplete_uniform_block: public std::runtime_error +{ +public: + incomplete_uniform_block(const std::string &w): std::runtime_error(w) { } + virtual ~incomplete_uniform_block() throw() { } +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/extension.cpp b/source/core/extension.cpp new file mode 100644 index 00000000..8618cc3e --- /dev/null +++ b/source/core/extension.cpp @@ -0,0 +1,257 @@ +#include +#include +#if defined(__ANDROID__) +#include +#elif defined(_WIN32) +#include +#elif !defined(__APPLE__) +#define GLX_GLXEXT_PROTOTYPES +#include +#endif +#include +#include +#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 parts = split(s, '.'); + major = lexical_cast(parts[0]); + minor = lexical_cast(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 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(get_proc_address("glGetStringi")); + int n_extensions; + glGetIntegerv(GL_NUM_EXTENSIONS, &n_extensions); + for(int i=0; i(glGetStringi(GL_EXTENSIONS, i))); + } + else + { + if(const char *gl_ext = reinterpret_cast(glGetString(GL_EXTENSIONS))) + { + vector 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 disabled_exts; + static bool init_done = false; + + if(!init_done) + { + if(const char *disable_ptr = getenv("MSPGL_DISABLE_EXTENSIONS")) + { + vector disable = split(disable_ptr); + disabled_exts.insert(disable.begin(), disable.end()); + } + + if(const char *renderer_ptr = reinterpret_cast(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(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(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(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(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(name.c_str())); +#endif +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/extension.h b/source/core/extension.h new file mode 100644 index 00000000..cf824e24 --- /dev/null +++ b/source/core/extension.h @@ -0,0 +1,105 @@ +#ifndef MSP_GL_EXTENSION_H_ +#define MSP_GL_EXTENSION_H_ + +#include + +namespace Msp { +namespace GL { + +enum GLApi +{ + OPENGL, + OPENGL_ES2 +}; + +enum GLProfile +{ + CORE_PROFILE, + COMPATIBILITY_PROFILE +}; + + +struct Version +{ + unsigned short major; + unsigned short minor; + + Version(); + Version(unsigned short, unsigned short); + Version(const std::string &); + + bool operator>=(const Version &) const; + bool operator<(const Version &o) const { return !(*this>=o); } + operator bool() const { return major || minor; } +}; + + +/** +Holds metadata about an extension. Evaluates to true if the extension is +supported. +*/ +class Extension +{ +public: + enum SupportLevel + { + UNSUPPORTED, + EXTENSION, + CORE + }; + + typedef SupportLevel (*InitFunc)(); + +private: + const char *name; + InitFunc init_func; + mutable bool init_done; + mutable SupportLevel support; + +public: + Extension(const char *, InitFunc); + + const char *get_name() const { return name; } + operator bool() const; +}; + + +struct Require +{ + Require(const Extension &); +}; + + +typedef void ExtFunc(); + +/** Checks for extension support. Only intended for internal use. */ +bool is_supported(const std::string &); + +/** Checks for OpenGL version support. Only intended for internal use. */ +bool is_supported(const Version &, const Version & = Version()); + +/** Indicates whether an extension has been disabled, either explicitly through +the MSPGL_DISABLE_EXTENSIONS environment variable or implicitly as a workaround +for a driver bug. Only intended for internal use. */ +bool is_disabled(const std::string &); + +/** Returns the API for which the library was compiled. */ +GLApi get_gl_api(); + +/** Returns the OpenGL profile for the active context. */ +GLProfile get_gl_profile(); + +/** Returns the OpenGL version number, as reported by the implementation. */ +const Version &get_gl_version(); + +/** Returns the GLSL version number, as reported by the implementation. */ +const Version &get_glsl_version(); + +/** Returns the address of an extension function. Only indended for internal +use. */ +ExtFunc *get_proc_address(const std::string &); + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/framebuffer.cpp b/source/core/framebuffer.cpp new file mode 100644 index 00000000..c90bb224 --- /dev/null +++ b/source/core/framebuffer.cpp @@ -0,0 +1,436 @@ +#include +#include +#include +#include +#include +#include +#include +#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(status, "%#x")); + break; + } +} + +framebuffer_incomplete::framebuffer_incomplete(FramebufferStatus status): + runtime_error(lexical_cast(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 color_bufs; + color_bufs.reserve(attachments.size()); + for(unsigned i=0; iget_id()); + else + glFramebufferRenderbuffer(GL_FRAMEBUFFER, attch.attachment, GL_RENDERBUFFER, attch.rbuf->get_id()); + } + else if(attch.type==GL_TEXTURE_2D) + { + static_cast(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(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(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::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(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(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(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(glCheckNamedFramebufferStatus(id, GL_FRAMEBUFFER)); + else + { + BindRestore _bind(this); + return static_cast(glCheckFramebufferStatus(GL_FRAMEBUFFER)); + } +} + +void Framebuffer::require_complete() const +{ + FramebufferStatus status = check_status(); + if(status!=FRAMEBUFFER_COMPLETE) + throw framebuffer_incomplete(status); +} + +void Framebuffer::viewport(int l, int b, unsigned w, unsigned h) +{ + view.left = l; + view.bottom = b; + view.width = w; + view.height = h; + + if(current()==this) + glViewport(view.left, view.bottom, view.width, view.height); +} + +void Framebuffer::reset_viewport() +{ + viewport(0, 0, width, height); +} + +void Framebuffer::clear() +{ + clear(COLOR_BUFFER_BIT|DEPTH_BUFFER_BIT|STENCIL_BUFFER_BIT); +} + +void Framebuffer::clear(BufferBits bits) +{ + BindRestore _bind(this); + glClear(bits); +} + +void Framebuffer::blit_from(const Framebuffer &other, int sx0, int sy0, int sx1, int sy1, int dx0, int dy0, int dx1, int dy1, BufferBits bits, bool filter) +{ + static Require _req(EXT_framebuffer_blit); + + if(ARB_direct_state_access) + { + glBlitNamedFramebuffer(other.id, id, sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1, bits, (filter ? GL_LINEAR : GL_NEAREST)); + return; + } + + const Framebuffer *old = current(); + if(set_current(this)) + { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, id); + if(dirty) + { + update_attachment(dirty); + dirty = 0; + } + } + if(old!=&other) + glBindFramebuffer(GL_READ_FRAMEBUFFER, other.id); + + glBlitFramebuffer(sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1, bits, (filter ? GL_LINEAR : GL_NEAREST)); + + set_current(old); + glBindFramebuffer(GL_FRAMEBUFFER, (old ? old->id : 0)); +} + +void Framebuffer::blit_from(const Framebuffer &other, int sx, int sy, unsigned wd, unsigned ht, int dx, int dy, BufferBits bits) +{ + blit_from(other, sx, sy, sx+wd, sy+ht, dx, dy, dx+wd, dy+ht, bits, false); +} + +void Framebuffer::blit_from(const Framebuffer &other, BufferBits bits, bool filter) +{ + blit_from(other, 0, 0, other.width, other.height, 0, 0, width, height, bits, filter); +} + +void Framebuffer::bind() const +{ + if(id && attachments.empty()) + throw invalid_operation("Framebuffer::bind"); + + if(set_current(this)) + { + glBindFramebuffer(GL_FRAMEBUFFER, id); + if(dirty) + { + update_attachment(dirty); + dirty = 0; + } + + if(width && height) + glViewport(view.left, view.bottom, view.width, view.height); + } +} + +const Framebuffer *Framebuffer::current() +{ + if(!cur_obj) + cur_obj = &system(); + return cur_obj; +} + +void Framebuffer::unbind() +{ + system().bind(); +} + +Framebuffer &Framebuffer::system() +{ + static Framebuffer sys_framebuf(0); + return sys_framebuf; +} + + +Framebuffer::Attachment::Attachment(FramebufferAttachment a): + attachment(a), + type(0), + level(0), + layer(0) +{ } + +void Framebuffer::Attachment::set(Renderbuffer &r) +{ + type = GL_RENDERBUFFER; + rbuf = &r; + level = 0; + layer = 0; +} + +void Framebuffer::Attachment::set(Texture &t, unsigned l, unsigned z) +{ + type = t.get_target(); + tex = &t; + level = l; + layer = z; +} + +void Framebuffer::Attachment::clear() +{ + type = 0; +} + + +Framebuffer::Viewport::Viewport(): + left(0), + bottom(0), + width(0), + height(0) +{ } + +} // namespace GL +} // namespace Msp diff --git a/source/core/framebuffer.h b/source/core/framebuffer.h new file mode 100644 index 00000000..3054bd64 --- /dev/null +++ b/source/core/framebuffer.h @@ -0,0 +1,173 @@ +#ifndef MSP_GL_FRAMEBUFFER_H_ +#define MSP_GL_FRAMEBUFFER_H_ + +#include +#include "bindable.h" +#include "gl.h" +#include "texturecube.h" +#include +#include +#include +#include + +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 +{ +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 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(static_cast(a)|static_cast(b)); } + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/gl.h b/source/core/gl.h new file mode 100644 index 00000000..f27b63fe --- /dev/null +++ b/source/core/gl.h @@ -0,0 +1,44 @@ +#ifndef MSP_GL_GL_H_ +#define MSP_GL_GL_H_ + +#ifdef __APPLE__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wkeyword-macro" +#define extern extern __attribute__((weak_import)) +#include +#include +#undef extern +#pragma clang diagnostic pop +#elif defined(__ANDROID__) +#include +#include +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 +#include +#endif + +#ifndef APIENTRY +#define APIENTRY +#endif + +#endif diff --git a/source/core/matrix.cpp b/source/core/matrix.cpp new file mode 100644 index 00000000..311958ab --- /dev/null +++ b/source/core/matrix.cpp @@ -0,0 +1,120 @@ +#include +#include +#include +#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 &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::translation(t).get_matrix(); +} + +Matrix Matrix::rotation(const Angle &a, const Vector3 &x) +{ + return Geometry::AffineTransformation::rotation(a, x).get_matrix(); +} + +Matrix Matrix::scaling(const Vector3 &s) +{ + return Geometry::AffineTransformation::scaling(s).get_matrix(); +} + +Matrix Matrix::ortho(float l, float r, float b, float t, float n, float f) +{ + if(l==r || b==t || n==f) + throw invalid_argument("Matrix::ortho"); + + Matrix result; + result(0, 0) = 2/(r-l); + result(1, 1) = 2/(t-b); + result(2, 2) = -2/(f-n); + result(0, 3) = -(r+l)/(r-l); + result(1, 3) = -(t+b)/(t-b); + result(2, 3) = -(f+n)/(f-n); + return result; +} + +Matrix Matrix::ortho_centered(float w, float h) +{ + return ortho(-w/2, w/2, -h/2, h/2, -1, 1); +} + +Matrix Matrix::ortho_bottomleft(float w, float h) +{ + return ortho(0, w, 0, h, -1, 1); +} + +Matrix Matrix::ortho_topleft(float w, float h) +{ + return ortho(0, w, h, 0, -1, 1); +} + +Matrix Matrix::frustum(float l, float r, float b, float t, float n, float f) +{ + if(l==r || b==t || n<=0 || f<=n) + throw invalid_argument("Matrix::frustum"); + + Matrix result; + result(0, 0) = 2*n/(r-l); + result(1, 1) = 2*n/(t-b); + result(0, 2) = (r+l)/(r-l); + result(1, 2) = (t+b)/(t-b); + result(2, 2) = -(f+n)/(f-n); + result(3, 2) = -1; + result(2, 3) = -2*f*n/(f-n); + result(3, 3) = 0; + return result; +} + +Matrix Matrix::frustum_centered(float w, float h, float n, float f) +{ + return frustum(-w/2, w/2, -h/2, h/2, n, f); +} + +Matrix Matrix::perspective(const Angle &h, float a, float n, float f) +{ + float hh = tan(h/2.0f)*n; + return frustum(-hh*a, hh*a, -hh, hh, n, f); +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/matrix.h b/source/core/matrix.h new file mode 100644 index 00000000..cb61e292 --- /dev/null +++ b/source/core/matrix.h @@ -0,0 +1,71 @@ +#ifndef MSP_GL_MATRIX_H_ +#define MSP_GL_MATRIX_H_ + +#include +#include +#include +#include "gl.h" +#include "vector.h" + +namespace Msp { +namespace GL { + +class Matrix: public LinAl::SquareMatrix +{ +private: + typedef LinAl::SquareMatrix Base; + typedef Geometry::Angle Angle; + +public: + Matrix(); + Matrix(const float *); + Matrix(const LinAl::Matrix &); + + 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(*this)*static_cast(m); } + Matrix &operator*=(const Matrix &m) { Base::operator*=(m); return *this; } + Matrix operator*(float s) const { return static_cast(*this)*s; } + Matrix &operator*=(float s) { Base::operator*=(s); return *this; } + Vector4 operator*(const Vector4 &v) const { return static_cast(*this)*v; } + Vector3 operator*(const Vector3 &v) const { return ((*this)*compose(v, 1.0f)).slice<3>(0); } + float operator[](unsigned) const; + + static Matrix translation(float x, float y, float z) { return translation(Vector3(x, y, z)); } + static Matrix translation(const Vector3 &); + static Matrix rotation(const Angle &a, float x, float y, float z) { return rotation(a, Vector3(x, y, z)); } + static Matrix rotation(const Angle &, const Vector3 &); + static Matrix rotation(float a, float x, float y, float z) { return rotation(Angle::from_radians(a), Vector3(x, y, z)); } + static Matrix rotation(float a, const Vector3 &x) { return rotation(Angle::from_radians(a), x); } + static Matrix rotation_deg(float a, float x, float y, float z) { return rotation(Angle::from_degrees(a), Vector3(x, y, z)); } + static Matrix rotation_deg(float a, const Vector3 &x) { return rotation(Angle::from_degrees(a), x); } + static Matrix scaling(float s) { return scaling(Vector3(s, s, s)); } + static Matrix scaling(float x, float y, float z) { return scaling(Vector3(x, y, z)); } + static Matrix scaling(const Vector3 &); + + static Matrix ortho(float, float, float, float, float, float); + static Matrix ortho_centered(float, float); + static Matrix ortho_bottomleft(float, float); + static Matrix ortho_topleft(float, float); + static Matrix frustum(float, float, float, float, float, float); + static Matrix frustum_centered(float, float, float, float); + static Matrix perspective(const Angle &, float, float, float); +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/mesh.cpp b/source/core/mesh.cpp new file mode 100644 index 00000000..dba8a991 --- /dev/null +++ b/source/core/mesh.cpp @@ -0,0 +1,316 @@ +#include +#include +#include +#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()get_size()>0 && ibuf->get_size()::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::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::const_iterator i=batches.begin(); i!=batches.end(); ++i) + renderer.draw(*i); + } + else + { + for(vector::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(m), + allow_gl_calls(g) +{ + add("batch", &Loader::batch); + add("vertices", &Loader::vertices); + add("winding", &Loader::winding); +} + +void Mesh::Loader::vertices(const vector &c) +{ + if(c.empty()) + throw invalid_argument("No vertex components"); + + VertexFormat fmt; + for(vector::const_iterator i=c.begin(); i!=c.end(); ++i) + fmt = (fmt, *i); + obj.vertices.reset(fmt); + load_sub(obj.vertices); + if(allow_gl_calls) + { + obj.check_buffers(VERTEX_BUFFER); + obj.vtx_setup.refresh(); + } +} + +void Mesh::Loader::batch(PrimitiveType p) +{ + Batch btc(p); + load_sub(btc); + obj.add_batch(btc); +} + +void Mesh::Loader::winding(FaceWinding w) +{ + if(w==CLOCKWISE) + obj.winding = &WindingTest::clockwise(); + else if(w==COUNTERCLOCKWISE) + obj.winding = &WindingTest::counterclockwise(); +} + + +Mesh::AsyncLoader::AsyncLoader(Mesh &m, IO::Seekable &i): + mesh(m), + io(i), + vertex_updater(0), + index_updater(0), + phase(0) +{ + mesh.disallow_rendering = true; + mesh.check_buffers(VERTEX_BUFFER|INDEX_BUFFER); +} + +Mesh::AsyncLoader::~AsyncLoader() +{ + mesh.disallow_rendering = false; + delete vertex_updater; + delete index_updater; +} + +bool Mesh::AsyncLoader::needs_sync() const +{ + return phase%2; +} + +bool Mesh::AsyncLoader::process() +{ + if(phase==0) + { + // TODO use correct filename + DataFile::Parser parser(io, "async"); + Loader loader(mesh, false); + loader.load(parser); + } + else if(phase==1) + { + mesh.resize_buffers(); + mesh.vtx_setup.refresh(); + vertex_updater = mesh.vertices.refresh_async(); + if(!mesh.batches.empty()) + index_updater = mesh.batches.front().refresh_async(); + } + else if(phase==2) + { + if(vertex_updater) + vertex_updater->upload_data(); + if(index_updater) + index_updater->upload_data(); + } + else if(phase==3) + { + delete vertex_updater; + vertex_updater = 0; + delete index_updater; + index_updater = 0; + } + + ++phase; + if(phase==1 && !mesh.vbuf && !mesh.ibuf) + phase += 3; + return phase>3; +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/mesh.h b/source/core/mesh.h new file mode 100644 index 00000000..57c87b7e --- /dev/null +++ b/source/core/mesh.h @@ -0,0 +1,113 @@ +#ifndef MSP_GL_MESH_H_ +#define MSP_GL_MESH_H_ + +#include +#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 + { + private: + bool allow_gl_calls; + + public: + Loader(Mesh &, bool = true); + private: + void vertices(const std::vector &); + 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 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 &get_batches() const { return batches; } + + void set_winding(const WindingTest *); + + void draw(Renderer &) const; + void draw_instanced(Renderer &, const VertexSetup &, unsigned) const; +private: + void draw(Renderer &, const VertexSetup *, unsigned) const; + void resize_buffers() const; + +public: + virtual int get_load_priority() const { return 1; } + virtual Resource::AsyncLoader *load(IO::Seekable &, const Resources * = 0); + virtual UInt64 get_data_size() const; + virtual void unload(); +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/misc.cpp b/source/core/misc.cpp new file mode 100644 index 00000000..39810be3 --- /dev/null +++ b/source/core/misc.cpp @@ -0,0 +1,47 @@ +#include +#include "misc.h" + +namespace Msp { +namespace GL { + +void enable(GLenum state) +{ + glEnable(state); +} + +void disable(GLenum state) +{ + glDisable(state); +} + +void set(GLenum state, bool value) +{ + if(value) + enable(state); + else + disable(state); +} + +int get_i(GLenum state) +{ + int data; + glGetIntegerv(state, &data); + return data; +} + +int get_shader_i(unsigned id, GLenum state) +{ + int data; + glGetShaderiv(id, state, &data); + return data; +} + +int get_program_i(unsigned id, GLenum state) +{ + int data; + glGetProgramiv(id, state, &data); + return data; +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/misc.h b/source/core/misc.h new file mode 100644 index 00000000..32adc9bf --- /dev/null +++ b/source/core/misc.h @@ -0,0 +1,20 @@ +#ifndef MSP_GL_MISC_H_ +#define MSP_GL_MISC_H_ + +#include "gl.h" + +namespace Msp { +namespace GL { + +void enable(GLenum); +void disable(GLenum); +void set(GLenum, bool); + +int get_i(GLenum); +int get_shader_i(unsigned, GLenum); +int get_program_i(unsigned, GLenum); + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/pixelformat.cpp b/source/core/pixelformat.cpp new file mode 100644 index 00000000..87191c34 --- /dev/null +++ b/source/core/pixelformat.cpp @@ -0,0 +1,389 @@ +#include +#include +#include +#include "pixelformat.h" + +using namespace std; + +namespace Msp { +namespace GL { + +void operator>>(const LexicalConverter &conv, PixelComponents &comp) +{ + if(conv.get()=="STENCIL_INDEX") + comp = STENCIL_INDEX; + else if(conv.get()=="DEPTH_COMPONENT") + comp = DEPTH_COMPONENT; + else if(conv.get()=="RED") + comp = RED; + else if(conv.get()=="RG") + comp = RG; + else if(conv.get()=="RGB") + comp = RGB; + else if(conv.get()=="RGBA") + comp = RGBA; + else if(conv.get()=="BGR") + comp = BGR; + else if(conv.get()=="BGRA") + comp = BGRA; + else if(conv.get()=="LUMINANCE") + comp = LUMINANCE; + else if(conv.get()=="LUMINANCE_ALPHA") + comp = LUMINANCE_ALPHA; + else + throw lexical_error(format("conversion of '%s' to PixelFormat", conv.get())); +} + +void operator>>(const LexicalConverter &conv, PixelFormat &fmt) +{ + if(conv.get()=="R8") + fmt = R8; + else if(conv.get()=="R16F") + fmt = R16F; + else if(conv.get()=="R32F") + fmt = R32F; + else if(conv.get()=="RG8") + fmt = RG8; + else if(conv.get()=="RG16F") + fmt = RG16F; + else if(conv.get()=="RG32F") + fmt = RG32F; + else if(conv.get()=="RGB8") + fmt = RGB8; + else if(conv.get()=="RGB16F") + fmt = RGB16F; + else if(conv.get()=="RGB32F") + fmt = RGB32F; + else if(conv.get()=="RGBA8") + fmt = RGBA8; + else if(conv.get()=="RGBA16F") + fmt = RGBA16F; + else if(conv.get()=="RGBA32F") + fmt = RGBA32F; + else if(conv.get()=="SRGB8") + fmt = SRGB8; + else if(conv.get()=="SRGB8_ALPHA8") + fmt = SRGB8_ALPHA8; + else if(conv.get()=="BGR8") + fmt = BGR8; + else if(conv.get()=="BGRA8") + fmt = BGRA8; + else if(conv.get()=="LUMINANCE8") + fmt = LUMINANCE8; + else if(conv.get()=="LUMINANCE8_ALPHA8") + fmt = LUMINANCE8_ALPHA8; + else if(conv.get()=="DEPTH_COMPONENT16") + fmt = DEPTH_COMPONENT16; + else if(conv.get()=="DEPTH_COMPONENT24") + fmt = DEPTH_COMPONENT24; + else if(conv.get()=="DEPTH_COMPONENT32") + fmt = DEPTH_COMPONENT32; + else if(conv.get()=="DEPTH_COMPONENT32F") + fmt = DEPTH_COMPONENT32F; + else + { + PixelComponents comp; + conv >> comp; + fmt = make_pixelformat(comp, (comp==DEPTH_COMPONENT ? FLOAT : UNSIGNED_BYTE)); + IO::print(IO::cerr, "Warning: deprecated conversion of '%s' to PixelFormat\n", conv.get()); + } +} + +PixelComponents pixelformat_from_graphics(Graphics::PixelFormat pf) +{ + switch(pf) + { + case Graphics::LUMINANCE: return LUMINANCE; + case Graphics::LUMINANCE_ALPHA: return LUMINANCE_ALPHA; + case Graphics::RGB: return RGB; + case Graphics::RGBX: + case Graphics::RGBA: return RGBA; + case Graphics::BGR: return BGR; + case Graphics::BGRX: + case Graphics::BGRA: return BGRA; + default: throw invalid_argument("pixelformat_from_graphics"); + } +} + +PixelComponents storage_pixelformat_from_graphics(Graphics::PixelFormat pf) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + switch(pf) + { + case Graphics::RGBX: + case Graphics::BGR: + case Graphics::BGRX: return RGB; + case Graphics::BGRA: return RGBA; + default: return pixelformat_from_graphics(pf); + } +#pragma GCC diagnostic pop +} + +PixelFormat pixelformat_from_image(const Graphics::Image &image) +{ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + PixelComponents comp = pixelformat_from_graphics(image.get_format()); +#pragma GCC diagnostic pop + return make_pixelformat(comp, UNSIGNED_BYTE); +} + +PixelFormat make_pixelformat(PixelComponents comp, DataType type, bool srgb) +{ + if(srgb && type!=UNSIGNED_BYTE && comp!=RGB && comp!=RGBA && comp!=BGR && comp!=BGRA) + throw invalid_argument("make_pixelformat"); + + switch(comp) + { + case RED: + switch(type) + { + case UNSIGNED_BYTE: return R8; + case HALF_FLOAT: return R16F; + case FLOAT: return R32F; + default: throw invalid_argument("make_pixelformat"); + } + case RG: + switch(type) + { + case UNSIGNED_BYTE: return RG8; + case HALF_FLOAT: return RG16F; + case FLOAT: return RG32F; + default: throw invalid_argument("make_pixelformat"); + } + case RGB: + switch(type) + { + case UNSIGNED_BYTE: return (srgb ? SRGB8 : RGB8); + case HALF_FLOAT: return RGB16F; + case FLOAT: return RGB32F; + default: throw invalid_argument("make_pixelformat"); + } + case RGBA: + switch(type) + { + case UNSIGNED_BYTE: return (srgb ? SRGB8_ALPHA8 : RGBA8); + case HALF_FLOAT: return RGBA16F; + case FLOAT: return RGBA32F; + default: throw invalid_argument("make_pixelformat"); + } + case BGR: + if(type!=UNSIGNED_BYTE) + throw invalid_argument("make_pixelformat"); + return (srgb ? BGR8 : SBGR8); + case BGRA: + if(type!=UNSIGNED_BYTE) + throw invalid_argument("make_pixelformat"); + return (srgb ? BGRA8 : SBGR8_ALPHA8); + case LUMINANCE: + if(type!=UNSIGNED_BYTE) + throw invalid_argument("make_pixelformat"); + return LUMINANCE8; + case LUMINANCE_ALPHA: + if(type!=UNSIGNED_BYTE) + throw invalid_argument("make_pixelformat"); + return LUMINANCE8; + case STENCIL_INDEX: + if(type!=UNSIGNED_BYTE) + throw invalid_argument("make_pixelformat"); + return STENCIL_INDEX8; + case DEPTH_COMPONENT: + switch(type) + { + case UNSIGNED_SHORT: return DEPTH_COMPONENT16; + case UNSIGNED_INT: return DEPTH_COMPONENT32; + case FLOAT: return DEPTH_COMPONENT32F; + default: throw invalid_argument("make_pixelformat"); + } + default: + throw invalid_argument("make_pixelformat"); + } +} + +PixelFormat get_base_pixelformat(PixelFormat pf) +{ + switch(pf) + { + case SRGB8: return RGB8; + case SRGB8_ALPHA8: return RGBA8; + default: return pf; + } +} + +PixelComponents get_components(PixelFormat pf) +{ + switch(pf) + { + case R8: + case R16F: + case R32F: return RED; + case RG8: + case RG16F: + case RG32F: return RG; + case RGB8: + case RGB16F: + case RGB32F: + case SRGB8: return RGB; + case RGBA8: + case RGBA16F: + case RGBA32F: + case SRGB8_ALPHA8: return RGBA; + case BGR8: + case SBGR8: return BGR; + case BGRA8: + case SBGR8_ALPHA8: return BGRA; + case LUMINANCE8: return LUMINANCE; + case LUMINANCE8_ALPHA8: return LUMINANCE_ALPHA; + case STENCIL_INDEX8: return STENCIL_INDEX; + case DEPTH_COMPONENT16: + case DEPTH_COMPONENT24: + case DEPTH_COMPONENT32: + case DEPTH_COMPONENT32F: return DEPTH_COMPONENT; + default: throw invalid_argument("get_components"); + } +} + +PixelFormat get_default_sized_pixelformat(PixelComponents comp) +{ + DataType type = UNSIGNED_BYTE; + if(comp==DEPTH_COMPONENT) + { + if(get_gl_api()==OPENGL_ES2 && !ARB_depth_buffer_float) + type = UNSIGNED_SHORT; + else + type = FLOAT; + } + return make_pixelformat(comp, type); +} + +PixelFormat get_srgb_pixelformat(PixelFormat pf) +{ + switch(pf) + { + case RGB8: return SRGB8; + case RGBA8: return SRGB8_ALPHA8; + default: return pf; + } +} + +unsigned get_component_count(PixelComponents comp) +{ + switch(comp) + { + case RED: + case LUMINANCE: + case DEPTH_COMPONENT: + case STENCIL_INDEX: + return 1; + case RG: + case LUMINANCE_ALPHA: + return 2; + case RGB: + case BGR: + return 3; + case RGBA: + case BGRA: + return 4; + default: + throw invalid_argument("get_component_count"); + } +} + +DataType get_component_type(PixelFormat pf) +{ + switch(pf) + { + case R8: + case RG8: + case RGB8: + case RGBA8: + case SRGB8: + case SRGB8_ALPHA8: + case BGR8: + case BGRA8: + case SBGR8: + case SBGR8_ALPHA8: + case LUMINANCE8: + case LUMINANCE8_ALPHA8: + return UNSIGNED_BYTE; + case R16F: + case RG16F: + case RGB16F: + case RGBA16F: + return HALF_FLOAT; + case DEPTH_COMPONENT16: + return UNSIGNED_SHORT; + case R32F: + case RG32F: + case RGB32F: + case RGBA32F: + case DEPTH_COMPONENT32: + return UNSIGNED_INT; + case DEPTH_COMPONENT32F: + return FLOAT; + case DEPTH_COMPONENT24: + // There's no DataType value with 24-bit size + default: + throw invalid_argument("get_component_type"); + } +} + +unsigned get_pixel_size(PixelFormat pf) +{ + return get_component_count(pf)*get_type_size(get_component_type(pf)); +} + +void require_pixelformat(PixelFormat pf) +{ + /* TODO These checks are only accurate for textures. On OpenGL ES some + formats are allowed for render buffers earlier than textures. In particular + it's possible to create a 16-bit depth renderbuffer on OpenGL ES 2.0 but + depth textures are only available with 3.0 or the OES_depth_texture + extension.*/ + switch(pf) + { + case RGB8: + case RGBA8: + { static Require _req(OES_required_internalformat); } + break; + case R8: + case RG8: + { static Require _req(ARB_texture_rg); } + break; + case R16F: + case R32F: + case RG16F: + case RG32F: + { static Require _req(ARB_texture_rg); } + { static Require _req(ARB_texture_float); } + break; + case RGB16F: + case RGB32F: + case RGBA16F: + case RGBA32F: + { static Require _req(ARB_texture_float); } + break; + case SRGB8: + case SRGB8_ALPHA8: + { static Require _req(EXT_texture_sRGB); } + break; + case DEPTH_COMPONENT16: + case DEPTH_COMPONENT24: + case DEPTH_COMPONENT32: + { static Require _req(ARB_depth_texture); } + { static Require _req(OES_required_internalformat); } + break; + case DEPTH_COMPONENT32F: + { static Require _req(ARB_depth_buffer_float); } + break; + case STENCIL_INDEX8: + { static Require _req(OES_texture_stencil8); } + break; + default: + throw invalid_argument("require_pixelformat"); + } +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/pixelformat.h b/source/core/pixelformat.h new file mode 100644 index 00000000..213328a5 --- /dev/null +++ b/source/core/pixelformat.h @@ -0,0 +1,99 @@ +#ifndef MSP_GL_PIXELFORMAT_H_ +#define MSP_GL_PIXELFORMAT_H_ + +#include +#include +#include +#include "gl.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "datatype.h" + +namespace Msp { +namespace GL { + +enum PixelComponents +{ + RED = GL_RED, + RG = GL_RG, + RGB = GL_RGB, + RGBA = GL_RGBA, + BGR = GL_BGR, + BGRA = GL_BGRA, + LUMINANCE = GL_LUMINANCE, + LUMINANCE_ALPHA = GL_LUMINANCE_ALPHA, + DEPTH_COMPONENT = GL_DEPTH_COMPONENT, + STENCIL_INDEX = GL_STENCIL_INDEX +}; + +enum PixelFormat +{ + R8 = GL_R8, + R16F = GL_R16F, + R32F = GL_R32F, + RG8 = GL_RG8, + RG16F = GL_RG16F, + RG32F = GL_RG32F, + RGB8 = GL_RGB8, + RGB16F = GL_RGB16F, + RGB32F = GL_RGB32F, + RGBA8 = GL_RGBA8, + RGBA16F = GL_RGBA16F, + RGBA32F = GL_RGBA32F, + SRGB8 = GL_SRGB8, + SRGB8_ALPHA8 = GL_SRGB8_ALPHA8, + BGR8 = 200000, + BGRA8 = 200001, + SBGR8 = 200002, + SBGR8_ALPHA8 = 200003, + LUMINANCE8 = GL_LUMINANCE8, + LUMINANCE8_ALPHA8 = GL_LUMINANCE8_ALPHA8, + DEPTH_COMPONENT16 = GL_DEPTH_COMPONENT16, + DEPTH_COMPONENT24 = GL_DEPTH_COMPONENT24, + DEPTH_COMPONENT32 = GL_DEPTH_COMPONENT32, + DEPTH_COMPONENT32F = GL_DEPTH_COMPONENT32F, + STENCIL_INDEX8 = GL_STENCIL_INDEX8 +}; + +void operator>>(const LexicalConverter &, PixelComponents &); +void operator>>(const LexicalConverter &, PixelFormat &); + +DEPRECATED PixelComponents pixelformat_from_graphics(Graphics::PixelFormat); +DEPRECATED PixelComponents storage_pixelformat_from_graphics(Graphics::PixelFormat, bool); +PixelFormat pixelformat_from_image(const Graphics::Image &); + +PixelFormat make_pixelformat(PixelComponents, DataType, bool = false); +DEPRECATED PixelFormat get_base_pixelformat(PixelFormat); +PixelComponents get_components(PixelFormat); +DEPRECATED PixelFormat get_default_sized_pixelformat(PixelComponents); +DEPRECATED PixelFormat get_srgb_pixelformat(PixelFormat); + +unsigned get_component_count(PixelComponents); +inline unsigned get_component_count(PixelFormat f) +{ return get_component_count(get_components(f)); } + +DataType get_component_type(PixelFormat); +inline unsigned get_component_size(PixelFormat f) +{ return get_type_size(get_component_type(f)); } + +unsigned get_pixel_size(PixelFormat); + +void require_pixelformat(PixelFormat); + +DEPRECATED inline PixelFormat get_sized_pixelformat(PixelComponents c, unsigned s = 1) +{ return make_pixelformat(c, (s==2 ? HALF_FLOAT : s==4 ? FLOAT : UNSIGNED_BYTE)); } + +DEPRECATED inline PixelComponents get_unsized_pixelformat(PixelFormat f) +{ return get_components(f); } + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/pixelstore.cpp b/source/core/pixelstore.cpp new file mode 100644 index 00000000..409f0f9a --- /dev/null +++ b/source/core/pixelstore.cpp @@ -0,0 +1,86 @@ +#include +#include +#include +#include "gl.h" +#include "pixelformat.h" +#include "pixelstore.h" + +using namespace std; + +namespace Msp { +namespace GL { + +PixelStore::PixelStore(): + row_length(0), + image_height(0), + skip_pixels(0), + skip_rows(0), + skip_images(0), + alignment(4) +{ } + +PixelStore PixelStore::from_image(const Graphics::Image &img) +{ + PixelStore pstore; + unsigned stride = img.get_stride(); + unsigned ncomp = get_component_count(pixelformat_from_image(img)); + pstore.set_canvas_size(img.get_stride()/ncomp, 0); + pstore.set_alignment(min(stride&~(stride-1), 8U)); + return pstore; +} + +void PixelStore::update_parameter(int mask) const +{ + if(cur_obj!=this) + return; + + if(mask&SIZE) + { + glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length); + if(EXT_texture3D) + glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, image_height); + } + if(mask&ORIGIN) + { + glPixelStorei(GL_UNPACK_SKIP_PIXELS, skip_pixels); + glPixelStorei(GL_UNPACK_SKIP_ROWS, skip_rows); + if(EXT_texture3D) + glPixelStorei(GL_UNPACK_SKIP_IMAGES, skip_images); + } + if(mask&ALIGNMENT) + glPixelStorei(GL_UNPACK_ALIGNMENT, alignment); +} + +void PixelStore::set_canvas_size(unsigned w, unsigned h) +{ + static Require _req(EXT_unpack_subimage); + row_length = w; + image_height = h; + update_parameter(SIZE); +} + +void PixelStore::set_origin(unsigned x, unsigned y, unsigned z) +{ + static Require _req(EXT_unpack_subimage); + if(z>0) + static Require _req3d(EXT_texture3D); + skip_pixels = x; + skip_rows = y; + skip_images = z; + update_parameter(ORIGIN); +} + +void PixelStore::set_alignment(unsigned a) +{ + alignment = a; + update_parameter(ALIGNMENT); +} + +void PixelStore::bind() const +{ + if(set_current(this)) + update_parameter(-1); +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/pixelstore.h b/source/core/pixelstore.h new file mode 100644 index 00000000..80032c3c --- /dev/null +++ b/source/core/pixelstore.h @@ -0,0 +1,46 @@ +#ifndef MSP_GL_PIXELSTORE_H_ +#define MSP_GL_PIXELSTORE_H_ + +#include +#include "bindable.h" + +namespace Msp { +namespace GL { + +class PixelStore: public BindableWithDefault +{ +private: + enum ParameterMask + { + SIZE = 1, + ORIGIN = 2, + ALIGNMENT = 4 + }; + + unsigned row_length; + unsigned image_height; + unsigned skip_pixels; + unsigned skip_rows; + unsigned skip_images; + unsigned alignment; + +public: + PixelStore(); + + static PixelStore from_image(const Graphics::Image &); + +private: + void update_parameter(int) const; + +public: + void set_canvas_size(unsigned, unsigned); + void set_origin(unsigned, unsigned, unsigned); + void set_alignment(unsigned); + + void bind() const; +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/predicate.cpp b/source/core/predicate.cpp new file mode 100644 index 00000000..a1953733 --- /dev/null +++ b/source/core/predicate.cpp @@ -0,0 +1,49 @@ +#include +#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(pred))); break; + } +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/predicate.h b/source/core/predicate.h new file mode 100644 index 00000000..13c22e84 --- /dev/null +++ b/source/core/predicate.h @@ -0,0 +1,28 @@ +#ifndef MSP_GL_PREDICATE_H_ +#define MSP_GL_PREDICATE_H_ + +#include +#include "gl.h" + +namespace Msp { +namespace GL { + +enum Predicate +{ + NEVER = GL_NEVER, + ALWAYS = GL_ALWAYS, + LESS = GL_LESS, + LEQUAL = GL_LEQUAL, + EQUAL = GL_EQUAL, + GREATER = GL_GREATER, + GEQUAL = GL_GEQUAL, + NOTEQUAL = GL_NOTEQUAL +}; + +void operator>>(const LexicalConverter &, Predicate &); +void operator<<(LexicalConverter &, Predicate); + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/primitivetype.cpp b/source/core/primitivetype.cpp new file mode 100644 index 00000000..81375843 --- /dev/null +++ b/source/core/primitivetype.cpp @@ -0,0 +1,28 @@ +#include +#include "primitivetype.h" + +namespace Msp { +namespace GL { + +void operator>>(const LexicalConverter &conv, PrimitiveType &pt) +{ + if(conv.get()=="POINTS") + pt = POINTS; + else if(conv.get()=="LINES") + pt = LINES; + else if(conv.get()=="LINE_LOOP") + pt = LINE_LOOP; + else if(conv.get()=="LINE_STRIP") + pt = LINE_STRIP; + else if(conv.get()=="TRIANGLES") + pt = TRIANGLES; + else if(conv.get()=="TRIANGLE_STRIP") + pt = TRIANGLE_STRIP; + else if(conv.get()=="TRIANGLE_FAN") + pt = TRIANGLE_FAN; + else + throw lexical_error(format("conversion of '%s' to PrimitiveType", conv.get())); +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/primitivetype.h b/source/core/primitivetype.h new file mode 100644 index 00000000..8e33b1da --- /dev/null +++ b/source/core/primitivetype.h @@ -0,0 +1,26 @@ +#ifndef MSP_GL_PRIMITIVETYPE_H_ +#define MSP_GL_PRIMITIVETYPE_H_ + +#include +#include "gl.h" + +namespace Msp { +namespace GL { + +enum PrimitiveType +{ + POINTS = GL_POINTS, + LINES = GL_LINES, + LINE_STRIP = GL_LINE_STRIP, + LINE_LOOP = GL_LINE_LOOP, + TRIANGLES = GL_TRIANGLES, + TRIANGLE_STRIP = GL_TRIANGLE_STRIP, + TRIANGLE_FAN = GL_TRIANGLE_FAN +}; + +void operator>>(const LexicalConverter &, PrimitiveType &); + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/program.cpp b/source/core/program.cpp new file mode 100644 index 00000000..00bca834 --- /dev/null +++ b/source/core/program.cpp @@ -0,0 +1,421 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 = 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 uniforms_by_index(count); + for(unsigned i=0; i3 && !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 &uniforms_by_index) +{ + uniform_blocks.clear(); + + std::set used_bind_points; + unsigned count = get_program_i(id, GL_ACTIVE_UNIFORM_BLOCKS); + for(unsigned i=0; i indices(value); + glGetActiveUniformBlockiv(id, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, &indices[0]); + for(vector::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 indices2(indices.begin(), indices.end()); + vector values(indices.size()); + glGetActiveUniformsiv(id, indices.size(), &indices2[0], GL_UNIFORM_OFFSET, &values[0]); + for(unsigned j=0; jlocation = values[j]; + + indices2.clear(); + for(vector::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; jarray_stride = values[j]; + } + + indices2.clear(); + for(vector::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; jmatrix_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; i3 && !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 &uniforms) +{ + string layout_descriptor; + for(vector::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->locationlocation; +} + +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(p) +{ + add("attribute", &Loader::attribute); + add("fragment_shader", &Loader::fragment_shader); + add("geometry_shader", &Loader::geometry_shader); + add("vertex_shader", &Loader::vertex_shader); +} + +void Program::Loader::finish() +{ + obj.link(); +} + +void Program::Loader::attribute(unsigned i, const string &n) +{ + obj.bind_attribute(i, n); +} + +void Program::Loader::fragment_shader(const string &src) +{ + obj.attach_shader_owned(new FragmentShader(src)); +} + +void Program::Loader::geometry_shader(const string &src) +{ + obj.attach_shader_owned(new GeometryShader(src)); +} + +void Program::Loader::vertex_shader(const string &src) +{ + obj.attach_shader_owned(new VertexShader(src)); +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/program.h b/source/core/program.h new file mode 100644 index 00000000..fc60db46 --- /dev/null +++ b/source/core/program.h @@ -0,0 +1,136 @@ +#ifndef MSP_GL_PROGRAM_H_ +#define MSP_GL_PROGRAM_H_ + +#include +#include +#include +#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 +{ +public: + class Loader: public DataFile::ObjectLoader + { + 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 uniforms; + LayoutHash layout_hash; + }; + + struct AttributeInfo + { + std::string name; + unsigned location; + unsigned size; + GLenum type; + }; + + typedef std::vector ShaderList; + typedef std::map UniformMap; + typedef std::map UniformBlockMap; + typedef std::map 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 &); + void query_attributes(); + static LayoutHash compute_layout_hash(const std::vector &); + static bool uniform_location_compare(const UniformInfo *, const UniformInfo *); +public: + bool is_linked() const { return linked; } + std::string get_info_log() const; + + LayoutHash get_uniform_layout_hash() const { return uniform_layout_hash; } + const UniformBlockMap &get_uniform_blocks() const { return uniform_blocks; } + const UniformBlockInfo &get_uniform_block_info(const std::string &) const; + const UniformMap &get_uniforms() const { return uniforms; } + const UniformInfo &get_uniform_info(const std::string &) const; + int get_uniform_location(const std::string &) const; + const AttributeMap &get_attributes() const { return attributes; } + const AttributeInfo &get_attribute_info(const std::string &) const; + int get_attribute_location(const std::string &) const; + + void bind() const; + static void unbind(); +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/renderbuffer.cpp b/source/core/renderbuffer.cpp new file mode 100644 index 00000000..e86a97a5 --- /dev/null +++ b/source/core/renderbuffer.cpp @@ -0,0 +1,82 @@ +#include +#include +#include +#include "misc.h" +#include "renderbuffer.h" + +using namespace std; + +namespace Msp { +namespace GL { + +Renderbuffer::Renderbuffer() +{ + static Require _req(EXT_framebuffer_object); + + if(ARB_direct_state_access) + glCreateRenderbuffers(1, &id); + else + glGenRenderbuffers(1, &id); +} + +Renderbuffer::~Renderbuffer() +{ + glDeleteRenderbuffers(1, &id); +} + +void Renderbuffer::storage(PixelFormat fmt, unsigned wd, unsigned ht) +{ + require_pixelformat(fmt); + width = wd; + height = ht; + if(ARB_direct_state_access) + glNamedRenderbufferStorage(id, fmt, width, height); + else + { + BindRestore _bind(this); + glRenderbufferStorage(GL_RENDERBUFFER, fmt, width, height); + } +} + +unsigned Renderbuffer::get_max_samples() +{ + static unsigned max_samples = (EXT_framebuffer_multisample ? get_i(GL_MAX_SAMPLES) : 0); + return max_samples; +} + +void Renderbuffer::storage_multisample(unsigned samples, PixelFormat fmt, unsigned wd, unsigned ht) +{ + if(!samples) + return storage(fmt, wd, ht); + + static Require _req(EXT_framebuffer_multisample); + if(samples>get_max_samples()) + throw out_of_range("Renderbuffer::storage_multisample"); + + require_pixelformat(fmt); + + width = wd; + height = ht; + if(ARB_direct_state_access) + glNamedRenderbufferStorageMultisample(id, samples, fmt, width, height); + else + { + BindRestore _bind(this); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, fmt, width, height); + } +} + +void Renderbuffer::bind() const +{ + if(set_current(this)) + glBindRenderbuffer(GL_RENDERBUFFER, id); +} + +void Renderbuffer::unbind() +{ + if(set_current(0)) + glBindRenderbuffer(GL_RENDERBUFFER, 0); +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/renderbuffer.h b/source/core/renderbuffer.h new file mode 100644 index 00000000..afc6ed20 --- /dev/null +++ b/source/core/renderbuffer.h @@ -0,0 +1,55 @@ +#ifndef MSP_GL_RENDERBUFFER_H_ +#define MSP_GL_RENDERBUFFER_H_ + +#include "bindable.h" +#include "pixelformat.h" + +namespace Msp { +namespace GL { + +/** +A Renderbuffer contains a single renderable image. It can be attached to a +Framebuffer to provide a logical buffer that is required to render the scene +correctly but that is not needed as a texture later. Renderbuffers also +provide a capability for multisampling, which is not available in textures. + +Requires the GL_EXT_framebuffer_object extension. Multisample renderbuffers +additionally require the GL_EXT_framebuffer_multisample extension. +*/ +class Renderbuffer: public Bindable +{ +private: + unsigned id; + unsigned width; + unsigned height; + +public: + Renderbuffer(); + ~Renderbuffer(); + + unsigned get_id() const { return id; } + unsigned get_width() const { return width; } + unsigned get_height() const { return height; } + + /** Allocates storage for the renderbuffer. */ + void storage(PixelFormat fmt, unsigned wd, unsigned ht); + + /** Returns the maximum supported sample count for multisampling. If + multisampling is not supported, returns 0. */ + static unsigned get_max_samples(); + + /** Allocates multisample storage for the renderbuffer. All attachments in + a framebuffer must have the same number of samples. To transfer the + contents to a texture for furter processing, use the framebuffer blit + functions.*/ + void storage_multisample(unsigned samples, PixelFormat fmt, unsigned wd, unsigned ht); + + void bind() const; + + static void unbind(); +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/sampler.cpp b/source/core/sampler.cpp new file mode 100644 index 00000000..5375d17d --- /dev/null +++ b/source/core/sampler.cpp @@ -0,0 +1,332 @@ +#include +#include +#include +#include +#include +#include +#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(s) +{ + add("filter", &Loader::filter); + add("mag_filter", &Loader::mag_filter); + add("max_anisotropy", &Loader::max_anisotropy); + add("min_filter", &Loader::min_filter); + add("wrap", &Loader::wrap); + add("wrap_r", &Loader::wrap_r); + add("wrap_s", &Loader::wrap_s); + add("wrap_t", &Loader::wrap_t); +} + +void Sampler::Loader::filter(TextureFilter f) +{ + obj.set_filter(f); +} + +void Sampler::Loader::mag_filter(TextureFilter f) +{ + obj.set_mag_filter(f); +} + +void Sampler::Loader::max_anisotropy(float a) +{ + obj.set_max_anisotropy(a); +} + +void Sampler::Loader::min_filter(TextureFilter f) +{ + obj.set_min_filter(f); +} + +void Sampler::Loader::wrap(TextureWrap w) +{ + obj.set_wrap(w); +} + +void Sampler::Loader::wrap_r(TextureWrap w) +{ + obj.set_wrap_r(w); +} + +void Sampler::Loader::wrap_s(TextureWrap w) +{ + obj.set_wrap_s(w); +} + +void Sampler::Loader::wrap_t(TextureWrap w) +{ + obj.set_wrap_t(w); +} + + +bool is_mipmapped(TextureFilter filter) +{ + return (filter==NEAREST_MIPMAP_NEAREST || filter==NEAREST_MIPMAP_LINEAR || + filter==LINEAR_MIPMAP_NEAREST || filter==LINEAR_MIPMAP_LINEAR); +} + +void operator>>(const LexicalConverter &c, TextureFilter &tf) +{ + if(c.get()=="NEAREST") + tf = NEAREST; + else if(c.get()=="LINEAR") + tf = LINEAR; + else if(c.get()=="NEAREST_MIPMAP_NEAREST") + tf = NEAREST_MIPMAP_NEAREST; + else if(c.get()=="NEAREST_MIPMAP_LINEAR") + tf = NEAREST_MIPMAP_LINEAR; + else if(c.get()=="LINEAR_MIPMAP_NEAREST") + tf = LINEAR_MIPMAP_NEAREST; + else if(c.get()=="LINEAR_MIPMAP_LINEAR") + tf = LINEAR_MIPMAP_LINEAR; + else + throw lexical_error(format("conversion of '%s' to TextureFilter", c.get())); +} + +void operator>>(const LexicalConverter &c, TextureWrap &tw) +{ + if(c.get()=="REPEAT") + tw = REPEAT; + else if(c.get()=="CLAMP_TO_EDGE") + tw = CLAMP_TO_EDGE; + else if(c.get()=="MIRRORED_REPEAT") + tw = MIRRORED_REPEAT; + else + throw lexical_error(format("conversion of '%s' to TextureWrap", c.get())); +} + + +} // namespace GL +} // namespace Msp diff --git a/source/core/sampler.h b/source/core/sampler.h new file mode 100644 index 00000000..e1e9bec5 --- /dev/null +++ b/source/core/sampler.h @@ -0,0 +1,168 @@ +#ifndef MSP_GL_SAMPLER_H_ +#define MSP_GL_SAMPLER_H_ + +#include +#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 + { + public: + Loader(Sampler &); + private: + void init(); + + void filter(TextureFilter); + void mag_filter(TextureFilter); + void max_anisotropy(float); + void min_filter(TextureFilter); + void wrap(TextureWrap); + void wrap_r(TextureWrap); + void wrap_s(TextureWrap); + void wrap_t(TextureWrap); + }; + +private: + enum ParameterMask + { + MIN_FILTER = 1, + MAG_FILTER = 2, + MAX_ANISOTROPY = 4, + WRAP_S = 8, + WRAP_T = 16, + WRAP_R = 32, + COMPARE = 64 + }; + + unsigned id; + const Texture *owner; + TextureFilter min_filter; + TextureFilter mag_filter; + float max_anisotropy; + TextureWrap wrap_s; + TextureWrap wrap_t; + TextureWrap wrap_r; + bool compare; + Predicate cmp_func; + mutable int dirty_params; + +public: + Sampler(); + Sampler(const Texture &); +private: + void init(); + + void update_parameter(int) const; + void set_parameter_i(unsigned, int) const; + void set_parameter_f(unsigned, float) const; + +public: + void set_min_filter(TextureFilter); + void set_mag_filter(TextureFilter); + + /** Sets filter for both minification and magnification. If a mipmapping + filter is specified, LINEAR is used for magnification. */ + void set_filter(TextureFilter); + + TextureFilter get_min_filter() const { return min_filter; } + TextureFilter get_mag_filter() const { return mag_filter; } + + void set_max_anisotropy(float); + float get_max_anisotropy() const { return max_anisotropy; } + + /** Sets the wrapping mode for all coordinates. */ + void set_wrap(TextureWrap); + + void set_wrap_s(TextureWrap); + void set_wrap_t(TextureWrap); + void set_wrap_r(TextureWrap); + + /** Disables depth comparison. */ + void disable_compare(); + + /** Enables depth comparison and sets the compare function. Only has an + effect when used with a depth texture. When depth comparison is enabled, + the third component of the texture coordinate is compared against the texel + value, and the result is returned as the texture sample.*/ + void set_compare(Predicate); + + bool is_compare_enabled() const { return compare; } + Predicate get_compare_function() const { return cmp_func; } + + void bind() const { bind_to(0); } + void bind_to(unsigned) const; + + static const Sampler *current(unsigned = 0); + static void unbind() { unbind_from(0); } + static void unbind_from(unsigned); + + void unload(); +}; + + +bool is_mipmapped(TextureFilter); + +void operator>>(const LexicalConverter &, TextureFilter &); +void operator>>(const LexicalConverter &, TextureWrap &); + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/shader.cpp b/source/core/shader.cpp new file mode 100644 index 00000000..02dcc0dc --- /dev/null +++ b/source/core/shader.cpp @@ -0,0 +1,114 @@ +#include +#include +#include +#include +#include +#include "error.h" +#include "misc.h" +#include "shader.h" + +using namespace std; + +namespace Msp { +namespace GL { + +Shader::Shader(GLenum t) +{ + init(t); +} + +Shader::Shader(GLenum t, const string &src) +{ + init(t); + + source(src); + compile(); +} + +void Shader::init(GLenum t) +{ + static Require _req_base(ARB_shader_objects); + compiled = false; + + if(t==GL_FRAGMENT_SHADER) + static Require _req(ARB_fragment_shader); + else if(t==GL_VERTEX_SHADER) + static Require _req(ARB_vertex_shader); + else if(t==GL_GEOMETRY_SHADER) + static Require _req(ARB_geometry_shader4); + + id = glCreateShader(t); +} + +Shader::~Shader() +{ + glDeleteShader(id); +} + +void Shader::source(unsigned count, const char **str, const int *len) +{ + glShaderSource(id, count, str, len); +} + +void Shader::source(const string &str) +{ + source(str.data(), str.size()); +} + +void Shader::source(const char *str, int len) +{ + source(1, &str, &len); +} + +void Shader::compile() +{ + glCompileShader(id); + compiled = get_shader_i(id, GL_COMPILE_STATUS); + if(!compiled) + throw compile_error(get_info_log()); + +#ifdef DEBUG + string info_log = get_info_log(); + if(!info_log.empty()) + IO::print("Shader compile info log:\n%s", info_log); +#endif +} + +string Shader::get_info_log() const +{ + GLsizei len = get_shader_i(id, GL_INFO_LOG_LENGTH); + string log(len+1, 0); + glGetShaderInfoLog(id, len+1, &len, &log[0]); + log.erase(len); + return log; +} + + +VertexShader::VertexShader(): + Shader(GL_VERTEX_SHADER) +{ } + +VertexShader::VertexShader(const string &src): + Shader(GL_VERTEX_SHADER, src) +{ } + + +FragmentShader::FragmentShader(): + Shader(GL_FRAGMENT_SHADER) +{ } + +FragmentShader::FragmentShader(const string &src): + Shader(GL_FRAGMENT_SHADER, src) +{ } + + +GeometryShader::GeometryShader(): + Shader(GL_GEOMETRY_SHADER) +{ } + +GeometryShader::GeometryShader(const string &src): + Shader(GL_GEOMETRY_SHADER, src) +{ } + +} // namespace GL +} // namespace Msp diff --git a/source/core/shader.h b/source/core/shader.h new file mode 100644 index 00000000..bab11a7e --- /dev/null +++ b/source/core/shader.h @@ -0,0 +1,66 @@ +#ifndef MSP_GL_SHADER_H_ +#define MSP_GL_SHADER_H_ + +#include +#include "gl.h" + +namespace Msp { +namespace GL { + +/** +A single shader stage. Shaders must be attached to a Program to be used. + +This class can't be instantiated directly. Use one of the VertexShader and +FragmentShader classes to create Shaders. +*/ +class Shader +{ +private: + unsigned id; + bool compiled; + +protected: + Shader(GLenum t); + Shader(GLenum t, const std::string &); +private: + void init(GLenum); +public: + virtual ~Shader(); + + void source(unsigned count, const char **str, const int *len); + void source(const std::string &str); + void source(const char *str, int len); + void compile(); + unsigned get_id() const { return id; } + bool is_compiled() const { return compiled; } + std::string get_info_log() const; +}; + + +class VertexShader: public Shader +{ +public: + VertexShader(); + VertexShader(const std::string &); +}; + + +class FragmentShader: public Shader +{ +public: + FragmentShader(); + FragmentShader(const std::string &); +}; + + +class GeometryShader: public Shader +{ +public: + GeometryShader(); + GeometryShader(const std::string &); +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/stencil.cpp b/source/core/stencil.cpp new file mode 100644 index 00000000..9de8b1f0 --- /dev/null +++ b/source/core/stencil.cpp @@ -0,0 +1,17 @@ +#include "stencil.h" + +namespace Msp { +namespace GL { + +void stencil_func(Predicate func, int ref, unsigned mask) +{ + glStencilFunc(func, ref, mask); +} + +void stencil_op(StencilOp sfail, StencilOp dfail, StencilOp dpass) +{ + glStencilOp(sfail, dfail, dpass); +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/stencil.h b/source/core/stencil.h new file mode 100644 index 00000000..02f4ed3f --- /dev/null +++ b/source/core/stencil.h @@ -0,0 +1,33 @@ +#ifndef MSP_GL_STENCIL_H_ +#define MSP_GL_STENCIL_H_ + +#include "gl.h" +#include "predicate.h" + +namespace Msp { +namespace GL { + +enum +{ + STENCIL_TEST = GL_STENCIL_TEST +}; + +enum StencilOp +{ + KEEP = GL_KEEP, + SET_ZERO = GL_ZERO, + REPLACE = GL_REPLACE, + INCR = GL_INCR, + DECR = GL_DECR, + INVERT = GL_INVERT, + INCR_WRAP = GL_INCR_WRAP, + DECR_WRAP = GL_DECR_WRAP +}; + +void stencil_func(Predicate func, int ref, unsigned mask); +void stencil_op(StencilOp sfail, StencilOp dfail, StencilOp dpass); + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/tests.cpp b/source/core/tests.cpp new file mode 100644 index 00000000..8e24bb52 --- /dev/null +++ b/source/core/tests.cpp @@ -0,0 +1,73 @@ +#include "tests.h" + +namespace Msp { +namespace GL { + +DepthTest::DepthTest(): + write(true), + pred(LESS) +{ } + +DepthTest::DepthTest(Predicate p, bool w): + write(w), + pred(p) +{ } + +void DepthTest::bind() const +{ + if(set_current(this)) + { + glEnable(GL_DEPTH_TEST); + glDepthFunc(pred); + glDepthMask(write); + } +} + +const DepthTest &DepthTest::lequal() +{ + static DepthTest test(LEQUAL); + return test; +} + +void DepthTest::unbind() +{ + if(set_current(0)) + { + glDisable(GL_DEPTH_TEST); + // Allow glClear(GL_DEPTH_BUFFER_BIT) to work + glDepthMask(true); + } +} + + +ScissorTest::ScissorTest(): + left(0), + bottom(0), + width(1), + height(1) +{ } + +ScissorTest::ScissorTest(int l, int b, unsigned w, unsigned h): + left(l), + bottom(b), + width(w), + height(h) +{ } + +void ScissorTest::bind() const +{ + if(set_current(this)) + { + glEnable(GL_SCISSOR_TEST); + glScissor(left, bottom, width, height); + } +} + +void ScissorTest::unbind() +{ + if(set_current(0)) + glDisable(GL_SCISSOR_TEST); +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/tests.h b/source/core/tests.h new file mode 100644 index 00000000..f7d72950 --- /dev/null +++ b/source/core/tests.h @@ -0,0 +1,56 @@ +#ifndef MSP_GL_TESTS_H_ +#define MSP_GL_TESTS_H_ + +#include "bindable.h" +#include "gl.h" +#include "predicate.h" + +namespace Msp { +namespace GL { + +/** +Tests incoming fragment depth values against the depth buffer. If the test +fails, the fragment is discarded. +*/ +class DepthTest: public Bindable +{ +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 +{ +private: + int left; + int bottom; + unsigned width; + unsigned height; + +public: + ScissorTest(); + ScissorTest(int, int, unsigned, unsigned); + + void bind() const; + + static void unbind(); +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/texture.cpp b/source/core/texture.cpp new file mode 100644 index 00000000..f6594ab1 --- /dev/null +++ b/source/core/texture.cpp @@ -0,0 +1,410 @@ +#include +#include +#include +#include +#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(t, 0) +{ + init(); +} + +Texture::Loader::Loader(Texture &t, Collection &c): + DataFile::CollectionObjectLoader(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 = get_collection().open_raw(fn); + if(!io) + throw IO::file_not_found(fn); + img.load_io(*io); +} + +void Texture::Loader::external_image(const string &fn) +{ + obj.use_srgb_format = false; + external_image_common(fn); +} + +void Texture::Loader::external_image_srgb(const string &fn) +{ + obj.use_srgb_format = true; + external_image_common(fn); +} + +void Texture::Loader::external_image_common(const string &fn) +{ + if(obj.manager) + obj.manager->set_resource_location(obj, get_collection(), fn); + else + { + Graphics::Image img; + load_external_image(img, fn); + obj.image(img, get_levels()); + } +} + +void Texture::Loader::filter(TextureFilter f) +{ + obj.set_filter(f); +} + +void Texture::Loader::generate_mipmap(bool gm) +{ + obj.set_auto_generate_mipmap(gm); +} + +void Texture::Loader::image_data(const string &data) +{ + Graphics::Image img; + IO::Memory mem(data.data(), data.size()); + img.load_io(mem); + + obj.image(img, get_levels()); +} + +void Texture::Loader::mag_filter(TextureFilter f) +{ + obj.set_mag_filter(f); +} + +void Texture::Loader::max_anisotropy(float a) +{ + obj.set_max_anisotropy(a); +} + +void Texture::Loader::min_filter(TextureFilter f) +{ + obj.set_min_filter(f); +} + +void Texture::Loader::mipmap_levels(unsigned l) +{ + levels = l; +} + +void Texture::Loader::sampler() +{ + load_sub(obj.default_sampler); +} + +void Texture::Loader::wrap(TextureWrap w) +{ + obj.set_wrap(w); +} + +void Texture::Loader::wrap_r(TextureWrap w) +{ + obj.set_wrap_r(w); +} + +void Texture::Loader::wrap_s(TextureWrap w) +{ + obj.set_wrap_s(w); +} + +void Texture::Loader::wrap_t(TextureWrap w) +{ + obj.set_wrap_t(w); +} +#pragma GCC diagnostic pop + +} // namespace GL +} // namespace Msp diff --git a/source/core/texture.h b/source/core/texture.h new file mode 100644 index 00000000..c4e72031 --- /dev/null +++ b/source/core/texture.h @@ -0,0 +1,167 @@ +#ifndef MSP_GL_TEXTURE_H_ +#define MSP_GL_TEXTURE_H_ + +#include +#include +#include +#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 + { + protected: + unsigned levels; + + public: + Loader(Texture &); + Loader(Texture &, Collection &); + private: + void init(); + + unsigned get_levels() const; + protected: + void load_external_image(Graphics::Image &, const std::string &); + + private: + void external_image(const std::string &); + void external_image_srgb(const std::string &); + void external_image_common(const std::string &); + void filter(TextureFilter); + void generate_mipmap(bool); + void image_data(const std::string &); + void mag_filter(TextureFilter); + void max_anisotropy(float); + void min_filter(TextureFilter); + void mipmap_levels(unsigned); + void sampler(); + void wrap(TextureWrap); + void wrap_r(TextureWrap); + void wrap_s(TextureWrap); + void wrap_t(TextureWrap); + }; + + enum ParameterMask + { + FORMAT_SWIZZLE = 512 + }; + + enum FormatSwizzle + { + NO_SWIZZLE, + R_TO_LUMINANCE, + RG_TO_LUMINANCE_ALPHA, + RGB_TO_BGR + }; + + unsigned id; + GLenum target; + PixelFormat format; + PixelFormat storage_fmt; + FormatSwizzle swizzle; + bool use_srgb_format; + bool auto_gen_mipmap; + Sampler default_sampler; + + static int swizzle_orders[]; + + Texture(GLenum, ResourceManager * = 0); + Texture(const Texture &); + Texture &operator=(const Texture &); +public: + ~Texture(); + +protected: + void set_format(PixelFormat); + void apply_swizzle(); + void set_parameter_i(GLenum, int) const; + +public: + Sampler &get_default_sampler() { return default_sampler; } + const Sampler &get_default_sampler() const { return default_sampler; } + + DEPRECATED void set_min_filter(TextureFilter); + DEPRECATED void set_mag_filter(TextureFilter); + + /** Sets filter for both minification and magnification. Since mipmapping + is not applicable to magnification, LINEAR is used instead. */ + DEPRECATED void set_filter(TextureFilter); + + DEPRECATED void set_mipmap_levels(unsigned) { } + + DEPRECATED void set_max_anisotropy(float); + + /** Sets the wrapping mode for all coordinates. */ + DEPRECATED void set_wrap(TextureWrap); + + DEPRECATED void set_wrap_s(TextureWrap); + DEPRECATED void set_wrap_t(TextureWrap); + DEPRECATED void set_wrap_r(TextureWrap); + + static bool can_generate_mipmap(); + + void generate_mipmap(); + + /** Sets automatic mipmap generation. If enabled, mipmaps are generated + when a texture image is uploaded. */ + void set_auto_generate_mipmap(bool); + + /// Deprecated. Use set_auto_generate_mipmap instead. + DEPRECATED void set_generate_mipmap(bool g) { set_auto_generate_mipmap(g); } + + /** Sets depth texture comparison. Has no effect on other formats. When + comparison is enabled, the third component of the texture coordinate is + compared against the texel value, and the result is returned as the texture + sample. */ + DEPRECATED void set_compare_enabled(bool); + + /** Sets the function to use for depth comparison. */ + DEPRECATED void set_compare_func(Predicate); + + /// Loads a Graphics::Image from a file and uploads it to the texture. + virtual void load_image(const std::string &, unsigned = 0); + + DEPRECATED void load_image(const std::string &, bool srgb); + + /** Uploads an image to the texture. If storage has not been defined, it + will be set to match the image. Otherwise the image must be compatible + with the defined storage. Semantics depend on the type of texture. */ + virtual void image(const Graphics::Image &, unsigned = 0) = 0; + + DEPRECATED void image(const Graphics::Image &, bool srgb); + + GLenum get_target() const { return target; } + unsigned get_id() const { return id; } + + void bind() const { bind_to(0); } + void bind_to(unsigned) const; + + static const Texture *current(unsigned = 0); + static void unbind() { unbind_from(0); } + static void unbind_from(unsigned); + + virtual UInt64 get_data_size() const { return 0; } +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/texture1d.cpp b/source/core/texture1d.cpp new file mode 100644 index 00000000..742857be --- /dev/null +++ b/source/core/texture1d.cpp @@ -0,0 +1,190 @@ +#include +#include +#include +#include +#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< _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< _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(t) +{ + init(); +} + +Texture1D::Loader::Loader(Texture1D &t, Collection &c): + DataFile::DerivedObjectLoader(t, c) +{ + init(); +} + +void Texture1D::Loader::init() +{ + add("raw_data", &Loader::raw_data); + add("storage", &Loader::storage); + add("storage", &Loader::storage_levels); +} + +void Texture1D::Loader::raw_data(const string &data) +{ + obj.image(0, data.data()); +} + +void Texture1D::Loader::storage(PixelFormat fmt, unsigned w) +{ + obj.storage(fmt, w); +} + +void Texture1D::Loader::storage_levels(PixelFormat fmt, unsigned w, unsigned l) +{ + obj.storage(fmt, w, l); +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/texture1d.h b/source/core/texture1d.h new file mode 100644 index 00000000..b14ff917 --- /dev/null +++ b/source/core/texture1d.h @@ -0,0 +1,60 @@ +#ifndef MSP_GL_TEXTURE1D_H_ +#define MSP_GL_TEXTURE1D_H_ + +#include "texture.h" + +namespace Msp { +namespace GL { + +class Texture1D: public Texture +{ +public: + class Loader: public DataFile::DerivedObjectLoader + { + public: + Loader(Texture1D &); + Loader(Texture1D &, Collection &); + private: + void init(); + + void raw_data(const std::string &); + void storage(PixelFormat, unsigned); + void storage_levels(PixelFormat, unsigned, unsigned); + }; + +private: + unsigned width; + unsigned levels; + unsigned allocated; + +public: + Texture1D(); + + void storage(PixelFormat, unsigned, unsigned = 0); + + DEPRECATED void storage(PixelComponents c, unsigned w, unsigned l = 0) + { storage(make_pixelformat(c, UNSIGNED_BYTE), w, l); } + + void allocate(unsigned); + void image(unsigned, const void *); + DEPRECATED void image(unsigned, PixelComponents, DataType, const void *); + void sub_image(unsigned, int, unsigned, const void *); + DEPRECATED void sub_image(unsigned, int, unsigned, PixelComponents, DataType, const void *); + virtual void image(const Graphics::Image &, unsigned = 0); + using Texture::image; + unsigned get_width() const { return width; } + +private: + unsigned get_n_levels() const; + unsigned get_level_size(unsigned) const; + +public: + virtual AsyncLoader *load(IO::Seekable &, const Resources * = 0) { return 0; } + virtual UInt64 get_data_size() const; + virtual void unload() { } +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/texture2d.cpp b/source/core/texture2d.cpp new file mode 100644 index 00000000..0e76128c --- /dev/null +++ b/source/core/texture2d.cpp @@ -0,0 +1,307 @@ +#include +#include +#include +#include +#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< _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< _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(t) +{ + init(); +} + +Texture2D::Loader::Loader(Texture2D &t, Collection &c): + DataFile::DerivedObjectLoader(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(pixel_buffer.map()); + } + else if(phase==2) + image.load_into(*img_loader, mapped_address); + else if(phase==3) + { + Bind _bind_buf(pixel_buffer, PIXEL_UNPACK_BUFFER); + mapped_address = 0; + if(!pixel_buffer.unmap()) + { + phase = 1; + return false; + } + + if(!texture.id) + { + if(ARB_direct_state_access) + glCreateTextures(texture.target, 1, &texture.id); + else + glGenTextures(1, &texture.id); + } + texture.image(image, 0, true); + } + + ++phase; + return phase>3; +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/texture2d.h b/source/core/texture2d.h new file mode 100644 index 00000000..7553b6b7 --- /dev/null +++ b/source/core/texture2d.h @@ -0,0 +1,100 @@ +#ifndef MSP_GL_TEXTURE2D_H_ +#define MSP_GL_TEXTURE2D_H_ + +#include +#include +#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 + { + public: + Loader(Texture2D &); + Loader(Texture2D &, Collection &); + private: + void init(); + + void raw_data(const std::string &); + void storage(PixelFormat, unsigned, unsigned); + void storage_levels(PixelFormat, unsigned, unsigned, unsigned); + }; + +private: + class AsyncLoader; + + unsigned width; + unsigned height; + unsigned levels; + unsigned allocated; + +public: + Texture2D(ResourceManager * = 0); + virtual ~Texture2D(); + + /** Defines storage structure for the texture. If lv is zero, the number + of mipmap levels is automatically determined from storage dimensions. + + Must be called before an image can be uploaded. Once storage is defined, + it can't be changed. */ + void storage(PixelFormat fmt, unsigned wd, unsigned ht, unsigned lv = 0); + + DEPRECATED void storage(PixelComponents cm, unsigned wd, unsigned ht, unsigned lv = 0) + { storage(make_pixelformat(cm, UNSIGNED_BYTE), wd, ht, lv); } + + /** Allocates storage for the texture. The contents are initially + undefined. If storage has already been allocated, does nothing. */ + void allocate(unsigned level); + + /** Updates the contents of the entire texture. Storage must be defined + beforehand. The image data must have dimensions and format matching the + defined storage. */ + virtual void image(unsigned level, const void *data); + + DEPRECATED void image(unsigned level, PixelComponents fmt, DataType type, const void *data); + + /** Updates a rectangular region of the texture. Storage must be defined + beforehand. The image data must be in a format mathing the defined storage + and the update region must be fully inside the texture. */ + void sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, const void *data); + + DEPRECATED void sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, + PixelComponents fmt, DataType type, const void *data); + + /** Updates the contents of the entire texture from an image. If storage + has not been defined, it will be set to match the image. Otherwise the + image must match the defined storage. */ + virtual void image(const Graphics::Image &, unsigned lv = 0); + + using Texture::image; + +private: + void image(const Graphics::Image &, unsigned, bool); + +public: + unsigned get_width() const { return width; } + unsigned get_height() const { return height; } + +private: + unsigned get_n_levels() const; + void get_level_size(unsigned, unsigned &, unsigned &) const; + +public: + virtual Resource::AsyncLoader *load(IO::Seekable &, const Resources * = 0); + virtual UInt64 get_data_size() const; + virtual void unload(); +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/texture2darray.cpp b/source/core/texture2darray.cpp new file mode 100644 index 00000000..1ecb4cf2 --- /dev/null +++ b/source/core/texture2darray.cpp @@ -0,0 +1,80 @@ +#include +#include +#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(t) +{ + init(); +} + +Texture2DArray::Loader::Loader(Texture2DArray &t, Collection &c): + DataFile::DerivedObjectLoader(t, c) +{ + init(); +} + +void Texture2DArray::Loader::init() +{ + add("external_image", &Loader::external_image); +} + +void Texture2DArray::Loader::external_image(unsigned z, const string &fn) +{ + Graphics::Image img; + load_external_image(img, fn); + obj.layer_image(0, z, img); +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/texture2darray.h b/source/core/texture2darray.h new file mode 100644 index 00000000..bd7d03be --- /dev/null +++ b/source/core/texture2darray.h @@ -0,0 +1,40 @@ +#ifndef MSP_GL_TEXTURE2DARRAY_H_ +#define MSP_GL_TEXTURE2DARRAY_H_ + +#include "texture3d.h" + +namespace Msp { +namespace GL { + +/** +An array of two-dimensional textures. It's very much like a 3D texture, with +two important differences: there's no filtering nor mipmapping along the third +dimension. +*/ +class Texture2DArray: public Texture3D +{ +public: + class Loader: public Msp::DataFile::DerivedObjectLoader + { + public: + Loader(Texture2DArray &); + Loader(Texture2DArray &, Collection &); + private: + void init(); + + void external_image(unsigned, const std::string &); + }; + + Texture2DArray(); + + void layer_image(unsigned, unsigned, const void *); + DEPRECATED void layer_image(unsigned, unsigned, PixelComponents, DataType, const void *); + void layer_image(unsigned, unsigned, const Graphics::Image &); + + unsigned get_layers() const { return get_depth(); } +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/texture3d.cpp b/source/core/texture3d.cpp new file mode 100644 index 00000000..03c58d0a --- /dev/null +++ b/source/core/texture3d.cpp @@ -0,0 +1,229 @@ +#include +#include +#include +#include +#include +#include +#include +#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< _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< _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(t) +{ + init(); +} + +Texture3D::Loader::Loader(Texture3D &t, Collection &c): + DataFile::DerivedObjectLoader(t, c) +{ + init(); +} + +void Texture3D::Loader::init() +{ + add("raw_data", &Loader::raw_data); + add("storage", &Loader::storage); + add("storage", &Loader::storage_levels); +} + +void Texture3D::Loader::raw_data(const string &data) +{ + obj.image(0, data.data()); +} + +void Texture3D::Loader::storage(PixelFormat fmt, unsigned w, unsigned h, unsigned d) +{ + obj.storage(fmt, w, h, d); +} + +void Texture3D::Loader::storage_levels(PixelFormat fmt, unsigned w, unsigned h, unsigned d, unsigned l) +{ + obj.storage(fmt, w, h, d, l); +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/texture3d.h b/source/core/texture3d.h new file mode 100644 index 00000000..1f50e056 --- /dev/null +++ b/source/core/texture3d.h @@ -0,0 +1,97 @@ +#ifndef MSP_GL_TEXTURE3D_H_ +#define MSP_GL_TEXTURE3D_H_ + +#include +#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 + { + public: + Loader(Texture3D &); + Loader(Texture3D &, Collection &); + private: + void init(); + + void raw_data(const std::string &); + void storage(PixelFormat, unsigned, unsigned, unsigned); + void storage_levels(PixelFormat, unsigned, unsigned, unsigned, unsigned); + }; + +private: + unsigned width; + unsigned height; + unsigned depth; + unsigned levels; + unsigned allocated; + +protected: + Texture3D(GLenum); +public: + Texture3D(); + + /** Defines storage structure for the texture. If lv is zero, the number + of mipmap levels is automatically determined from storage dimensions. + + Must be called before an image can be uploaded. Once storage is defined, + it can't be changed. */ + void storage(PixelFormat fmt, unsigned wd, unsigned ht, unsigned dp, unsigned lv = 0); + + DEPRECATED void storage(PixelComponents c, unsigned w, unsigned h, unsigned d, unsigned l = 0) + { storage(make_pixelformat(c, UNSIGNED_BYTE), w, h, d, l); } + + /** Allocates storage for the texture. The contents are initially + undefined. If storage has already been allocated, does nothing. */ + void allocate(unsigned level); + + /** Updates the contents of the entire texture. Storage must be defined + beforehand. The image data must have dimensions and format matching the + defined storage. */ + void image(unsigned level, const void *data); + + DEPRECATED void image(unsigned level, PixelComponents comp, DataType type, const void *data); + + /** Updates a cuboid-shaped region of the texture. Storage must be defined + beforehand. The image data must be in a format mathing the defined storage + and the update region must be fully inside the texture. */ + void sub_image(unsigned level, int x, int y, int z, unsigned wd, unsigned ht, unsigned dp, const void *data); + + DEPRECATED void sub_image(unsigned level, + int x, int y, int z, unsigned wd, unsigned ht, unsigned dp, + PixelComponents comp, DataType type, const void *data); + + /** Updates the contents of the entire texture from an image. If storage + has not been defined, it will be set to match the image. In this case the + image will be treated as a stack of square layers and its height must be + divisible by its width. Otherwise the image must match the defined + storage. */ + virtual void image(const Graphics::Image &, unsigned = 0); + + using Texture::image; + + unsigned get_width() const { return width; } + unsigned get_height() const { return height; } + unsigned get_depth() const { return depth; } +protected: + unsigned get_n_levels() const; + void get_level_size(unsigned, unsigned &, unsigned &, unsigned &) const; + +public: + virtual AsyncLoader *load(IO::Seekable &, const Resources * = 0) { return 0; } + virtual UInt64 get_data_size() const; + virtual void unload() { } +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/texturecube.cpp b/source/core/texturecube.cpp new file mode 100644 index 00000000..6ec36625 --- /dev/null +++ b/source/core/texturecube.cpp @@ -0,0 +1,357 @@ +#include +#include +#include +#include +#include +#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<(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(t) +{ + init(); +} + +TextureCube::Loader::Loader(TextureCube &t, Collection &c): + DataFile::DerivedObjectLoader(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 = get_collection().open_raw(fn); + img.load_io(*io); + + obj.image(face, img); +} + +void TextureCube::Loader::image_data(TextureCubeFace face, const string &data) +{ + Graphics::Image img; + IO::Memory mem(data.data(), data.size()); + img.load_io(mem); + + obj.image(face, img); +} + +void TextureCube::Loader::raw_data(TextureCubeFace face, const string &data) +{ + obj.image(face, 0, data.data()); +} + +void TextureCube::Loader::storage(PixelFormat fmt, unsigned s) +{ + obj.storage(fmt, s); +} + +void TextureCube::Loader::storage_levels(PixelFormat fmt, unsigned s, unsigned l) +{ + obj.storage(fmt, s, l); +} + + +void operator>>(const LexicalConverter &conv, TextureCubeFace &face) +{ + const string &str = conv.get(); + if(str=="POSITIVE_X") + face = POSITIVE_X; + else if(str=="NEGATIVE_X") + face = NEGATIVE_X; + else if(str=="POSITIVE_Y") + face = POSITIVE_Y; + else if(str=="NEGATIVE_Y") + face = NEGATIVE_Y; + else if(str=="POSITIVE_Z") + face = POSITIVE_Z; + else if(str=="NEGATIVE_Z") + face = NEGATIVE_Z; + else + throw lexical_error(format("conversion of '%s' to TextureCubeFace", str)); +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/texturecube.h b/source/core/texturecube.h new file mode 100644 index 00000000..ae305153 --- /dev/null +++ b/source/core/texturecube.h @@ -0,0 +1,138 @@ +#ifndef MSP_GL_TEXTURECUBE_H_ +#define MSP_GL_TEXTURECUBE_H_ + +#include +#include +#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 + { + public: + Loader(TextureCube &); + Loader(TextureCube &, Collection &); + private: + void init(); + + void external_image(TextureCubeFace, const std::string &); + void image_data(TextureCubeFace, const std::string &); + void raw_data(TextureCubeFace, const std::string &); + void storage(PixelFormat, unsigned); + void storage_levels(PixelFormat, unsigned, unsigned); + }; + +private: + unsigned size; + unsigned levels; + /* Lowest six bits track allocation status of faces on the base level. Bit + seven is set if the entire base level is allocated. */ + unsigned allocated; + + static TextureCubeFace face_order[6]; + static Vector3 directions[6]; + static unsigned orientations[12]; + +public: + TextureCube(); + + /** Defines storage structure for the texture. If lv is zero, the number + of mipmap levels is automatically determined from storage dimensions. + + Must be called before an image can be uploaded. Once storage is defined, + it can't be changed. */ + void storage(PixelFormat fmt, unsigned size, unsigned lv = 0); + + DEPRECATED void storage(PixelComponents c, unsigned s, unsigned l = 0) + { storage(make_pixelformat(c, UNSIGNED_BYTE), s, l); } + + /** Allocates storage for the cube faces. The contents are initially + undefined. If storage has already been allocated, does nothing. */ + void allocate(unsigned level); + + /** Updates the contents of a face. Storage must be defined beforehand. + The image data must have dimensions and format matching the defined + storage. */ + void image(TextureCubeFace face, unsigned level, const void *data); + + DEPRECATED void image(TextureCubeFace face, unsigned level, + PixelComponents comp, DataType type, const void *data); + + /** Updates a rectangular region of a face. Storage must be defined + beforehand. The image data must be in a format mathing the defined storage + and the update region must be fully inside the face. */ + void sub_image(TextureCubeFace face, unsigned level, int x, int y, unsigned w, unsigned h, const void *data); + + DEPRECATED void sub_image(TextureCubeFace face, unsigned level, + int x, int y, unsigned w, unsigned h, + PixelComponents comp, DataType type, const void *data); + + void image(TextureCubeFace, const Graphics::Image &); + + DEPRECATED void image(TextureCubeFace, const Graphics::Image &, bool); + + virtual void image(const Graphics::Image &, unsigned = 0); + using Texture::image; + + unsigned get_size() const { return size; } +private: + unsigned get_n_levels() const; + unsigned get_level_size(unsigned) const; + +public: + /** Translates indices into face constants. Valid indices are between 0 + and 5, inclusive. */ + static TextureCubeFace enumerate_faces(unsigned); + + static unsigned get_face_index(TextureCubeFace); + + /** Returns a vector pointing out of the face. */ + static const Vector3 &get_face_direction(TextureCubeFace); + + /** Returns a vector in the direction of the s axis of the face. */ + static const Vector3 &get_s_direction(TextureCubeFace); + + /** Returns a vector in the direction of the t axis of the face. */ + static const Vector3 &get_t_direction(TextureCubeFace); + + /** Returns a vector pointing to the center a texel. */ + Vector3 get_texel_direction(TextureCubeFace, unsigned, unsigned); + + virtual AsyncLoader *load(IO::Seekable &, const Resources * = 0) { return 0; } + virtual UInt64 get_data_size() const; + virtual void unload() { } +}; + +void operator>>(const LexicalConverter &, TextureCubeFace &); + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/texunit.cpp b/source/core/texunit.cpp new file mode 100644 index 00000000..0659d615 --- /dev/null +++ b/source/core/texunit.cpp @@ -0,0 +1,92 @@ +#include +#include +#include +#include "gl.h" +#include "misc.h" +#include "texture.h" +#include "texunit.h" + +using namespace std; + +namespace Msp { +namespace GL { + +vector 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::iterator i=units.begin(); i!=units.end(); ++i) + if(i->texture==tex) + return &*i; + return 0; +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/texunit.h b/source/core/texunit.h new file mode 100644 index 00000000..6253d8d9 --- /dev/null +++ b/source/core/texunit.h @@ -0,0 +1,44 @@ +#ifndef MSP_GL_TEXUNIT_H_ +#define MSP_GL_TEXUNIT_H_ + +#include + +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 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 diff --git a/source/core/uniform.cpp b/source/core/uniform.cpp new file mode 100644 index 00000000..c18fc360 --- /dev/null +++ b/source/core/uniform.cpp @@ -0,0 +1,113 @@ +#include +#include +#include "uniform.h" + +namespace Msp { +namespace GL { + +template<> +void UniformScalar::apply(int index, unsigned size, const int *value) +{ + glUniform1iv(index, size, value); +} + +template<> +void UniformScalar::apply(int index, unsigned size, const float *value) +{ + glUniform1fv(index, size, value); +} + + +template<> +void UniformVector::apply(int index, unsigned size, const int *value) +{ + glUniform2iv(index, size, value); +} + +template<> +void UniformVector::apply(int index, unsigned size, const float *value) +{ + glUniform2fv(index, size, value); +} + +template<> +void UniformVector::apply(int index, unsigned size, const int *value) +{ + glUniform3iv(index, size, value); +} + +template<> +void UniformVector::apply(int index, unsigned size, const float *value) +{ + glUniform3fv(index, size, value); +} + +template<> +void UniformVector::apply(int index, unsigned size, const int *value) +{ + glUniform4iv(index, size, value); +} + +template<> +void UniformVector::apply(int index, unsigned size, const float *value) +{ + glUniform4fv(index, size, value); +} + + +template<> +void UniformMatrix::apply(int index, unsigned size, const float *value) +{ + glUniformMatrix2fv(index, size, false, value); +} + +template<> +void UniformMatrix::apply(int index, unsigned size, const float *value) +{ + glUniformMatrix3x2fv(index, size, false, value); +} + +template<> +void UniformMatrix::apply(int index, unsigned size, const float *value) +{ + glUniformMatrix4x2fv(index, size, false, value); +} + +template<> +void UniformMatrix::apply(int index, unsigned size, const float *value) +{ + glUniformMatrix2x3fv(index, size, false, value); +} + +template<> +void UniformMatrix::apply(int index, unsigned size, const float *value) +{ + glUniformMatrix3fv(index, size, false, value); +} + +template<> +void UniformMatrix::apply(int index, unsigned size, const float *value) +{ + glUniformMatrix4x3fv(index, size, false, value); +} + +template<> +void UniformMatrix::apply(int index, unsigned size, const float *value) +{ + glUniformMatrix2x4fv(index, size, false, value); +} + +template<> +void UniformMatrix::apply(int index, unsigned size, const float *value) +{ + glUniformMatrix3x4fv(index, size, false, value); +} + +template<> +void UniformMatrix::apply(int index, unsigned size, const float *value) +{ + glUniformMatrix4fv(index, size, false, value); +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/uniform.h b/source/core/uniform.h new file mode 100644 index 00000000..bc78bab8 --- /dev/null +++ b/source/core/uniform.h @@ -0,0 +1,193 @@ +#ifndef MSP_GL_UNIFORM_H_ +#define MSP_GL_UNIFORM_H_ + +#include +#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 +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(buffer) = *value; } + + virtual UniformScalar *clone() const + { return new UniformScalar(value); } +}; + +typedef UniformScalar Uniform1i; +typedef UniformScalar Uniform1f; + + +template +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(buffer)); } + + virtual UniformVector *clone() const + { return new UniformVector(value); } +}; + +typedef UniformVector Uniform2i; +typedef UniformVector Uniform2f; +typedef UniformVector Uniform3i; +typedef UniformVector Uniform3f; +typedef UniformVector Uniform4i; +typedef UniformVector Uniform4f; + + +template +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::store(info, reinterpret_cast(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 UniformMatrix2x2f; +typedef UniformMatrix UniformMatrix3x2f; +typedef UniformMatrix UniformMatrix4x2f; +typedef UniformMatrix UniformMatrix2x3f; +typedef UniformMatrix UniformMatrix3x3f; +typedef UniformMatrix UniformMatrix4x3f; +typedef UniformMatrix UniformMatrix2x4f; +typedef UniformMatrix UniformMatrix3x4f; +typedef UniformMatrix UniformMatrix4x4f; + + +template +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(buffer)+i*info.array_stride, values+i*elemsize); + } + + virtual UniformArray *clone() const + { return new UniformArray(size_, values); } +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/uniformblock.cpp b/source/core/uniformblock.cpp new file mode 100644 index 00000000..cc8945cf --- /dev/null +++ b/source/core/uniformblock.cpp @@ -0,0 +1,91 @@ +#include +#include +#include +#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::const_iterator i=uniforms.begin(); i!=uniforms.end(); ++i) + i->second->apply(i->first); + } +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/uniformblock.h b/source/core/uniformblock.h new file mode 100644 index 00000000..c8b8d36a --- /dev/null +++ b/source/core/uniformblock.h @@ -0,0 +1,53 @@ +#ifndef MSP_GL_UNIFORMBLOCK_H_ +#define MSP_GL_UNIFORMBLOCK_H_ + +#include +#include +#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 uniforms; + unsigned size; + std::vector data; + mutable BufferRange *buf_range; + + UniformBlock(const UniformBlock &); + UniformBlock &operator=(const UniformBlock &); +public: + UniformBlock(); + UniformBlock(unsigned); + ~UniformBlock(); + +private: + virtual unsigned get_data_size() const { return size; } + virtual const void *get_data_pointer() const { return &data[0]; } + virtual unsigned get_alignment() const; + virtual void location_changed(Buffer *, unsigned, unsigned) const; + +public: + void attach(int, const Uniform &); + void attach(const Program::UniformInfo &, const Uniform &); + + void apply(int) const; +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/vector.h b/source/core/vector.h new file mode 100644 index 00000000..5a4ffb66 --- /dev/null +++ b/source/core/vector.h @@ -0,0 +1,15 @@ +#ifndef MSP_GL_VECTOR_H_ +#define MSP_GL_VECTOR_H_ + +#include + +namespace Msp { +namespace GL { + +typedef LinAl::Vector Vector3; +typedef LinAl::Vector Vector4; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/vertexarray.cpp b/source/core/vertexarray.cpp new file mode 100644 index 00000000..e3aef7a0 --- /dev/null +++ b/source/core/vertexarray.cpp @@ -0,0 +1,103 @@ +#include +#include +#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(&Loader::vertex)); + add("vertex", static_cast(&Loader::vertex)); + add("vertex", static_cast(&Loader::vertex)); + add("normal", static_cast(&Loader::normal)); + add("texcoord", static_cast(&Loader::texcoord)); + add("texcoord", static_cast(&Loader::texcoord)); + add("texcoord", static_cast(&Loader::texcoord)); + add("texcoord", static_cast(&Loader::texcoord)); + add("multitexcoord", static_cast(&Loader::multitexcoord)); + add("multitexcoord", static_cast(&Loader::multitexcoord)); + add("multitexcoord", static_cast(&Loader::multitexcoord)); + add("multitexcoord", static_cast(&Loader::multitexcoord)); + add("color", static_cast(&Loader::color)); + add("color", static_cast(&Loader::color)); + add("attrib", static_cast(&Loader::attrib)); + add("attrib", static_cast(&Loader::attrib)); + add("attrib", static_cast(&Loader::attrib)); + add("attrib", static_cast(&Loader::attrib)); + add("tangent", static_cast(&Loader::tangent)); + add("binormal", static_cast(&Loader::binormal)); + + add("vertex2", static_cast(&Loader::vertex)); + add("vertex3", static_cast(&Loader::vertex)); + add("vertex4", static_cast(&Loader::vertex)); + add("normal3", static_cast(&Loader::normal)); + add("texcoord1", static_cast(&Loader::texcoord)); + add("texcoord2", static_cast(&Loader::texcoord)); + add("texcoord3", static_cast(&Loader::texcoord)); + add("texcoord4", static_cast(&Loader::texcoord)); + add("multitexcoord1", static_cast(&Loader::multitexcoord)); + add("multitexcoord2", static_cast(&Loader::multitexcoord)); + add("multitexcoord3", static_cast(&Loader::multitexcoord)); + add("multitexcoord4", static_cast(&Loader::multitexcoord)); + add("color3", static_cast(&Loader::color)); + add("color4", static_cast(&Loader::color)); + add("attrib1", static_cast(&Loader::attrib)); + add("attrib2", static_cast(&Loader::attrib)); + add("attrib3", static_cast(&Loader::attrib)); + add("attrib4", static_cast(&Loader::attrib)); + add("tangent3", static_cast(&Loader::tangent)); + add("binormal3", static_cast(&Loader::binormal)); +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/vertexarray.h b/source/core/vertexarray.h new file mode 100644 index 00000000..95a6ec2f --- /dev/null +++ b/source/core/vertexarray.h @@ -0,0 +1,77 @@ +#ifndef MSP_GL_VERTEXARRAY_H_ +#define MSP_GL_VERTEXARRAY_H_ + +#include +#include +#include +#include +#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 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 &get_data() const { return data; } + const float *operator[](unsigned i) const { return &data[0]+i*stride; } +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/vertexformat.cpp b/source/core/vertexformat.cpp new file mode 100644 index 00000000..6c2e2933 --- /dev/null +++ b/source/core/vertexformat.cpp @@ -0,0 +1,141 @@ +#include +#include +#include +#include +#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(*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(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(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(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(TEXCOORD1+(str[8]-'1')); + else if(str.size()==11 && str[9]=='_' && str[10]>='0' && str[10]<='7') + c = static_cast(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(str.substr(8)); + } + catch(const lexical_error &) + { + throw lexical_error(format("conversion of '%s' to VertexComponent", str)); + } + c = static_cast(ATTRIB1+(str[6]-'1')+n*4); + } + else + throw lexical_error(format("conversion of '%s' to VertexComponent", str)); +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/vertexformat.h b/source/core/vertexformat.h new file mode 100644 index 00000000..35612922 --- /dev/null +++ b/source/core/vertexformat.h @@ -0,0 +1,79 @@ +#ifndef MSP_GL_VERTEXFORMAT_H_ +#define MSP_GL_VERTEXFORMAT_H_ + +#include + +namespace Msp { +namespace GL { + +/** A single vertex component. Symbolic names are provided for commonly used +attributes. These are aliased with with generic attributes, so be careful when +picking your attribute indices. */ +enum VertexComponent +{ + VERTEX2 = 1, + VERTEX3, + VERTEX4, + NORMAL3 = 10, + COLOR4_UBYTE = 12, + COLOR3_FLOAT = 14, + COLOR4_FLOAT, + TANGENT3 = 18, + BINORMAL3 = 22, + TEXCOORD1 = 32, + TEXCOORD2, + TEXCOORD3, + TEXCOORD4, + ATTRIB1 = 64, + ATTRIB2, + ATTRIB3, + ATTRIB4 +}; + +class VertexFormat +{ +private: + enum { MAX_COMPONENTS = 15 }; + + unsigned char count; + unsigned char components[MAX_COMPONENTS]; + +public: + VertexFormat(); + VertexFormat(VertexComponent); + + VertexFormat operator,(VertexComponent c) const; + VertexFormat operator,(unsigned i) const; + bool operator==(const VertexFormat &) const; + bool operator!=(const VertexFormat &other) const { return !(*this==other); } + + bool empty() const { return !count; } + const unsigned char *begin() const { return components; } + const unsigned char *end() const { return components+count; } + unsigned stride() const; + int offset(VertexComponent) const; +}; + +inline VertexFormat operator,(VertexComponent c1, VertexComponent c2) +{ return (VertexFormat(c1), c2); } + +inline VertexFormat operator,(VertexComponent c, unsigned i) +{ return (VertexFormat(c), i); } + +VertexComponent make_indexed_component(VertexComponent, unsigned); + +inline unsigned get_component_type(unsigned char c) +{ return c>>2; } + +inline unsigned get_component_size(unsigned char c) +{ return (c&3)+1; } + +inline unsigned get_stride(const VertexFormat &f) +{ return f.stride(); } + +void operator>>(const LexicalConverter &, VertexComponent &); + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/vertexsetup.cpp b/source/core/vertexsetup.cpp new file mode 100644 index 00000000..d50406a1 --- /dev/null +++ b/source/core/vertexsetup.cpp @@ -0,0 +1,202 @@ +#include +#include +#include +#include +#include +#include +#include +#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<>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_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(offset)); + else + glVertexAttribPointer(t, sz, GL_FLOAT, false, stride, reinterpret_cast(offset)); + if(ARB_instanced_arrays) + glVertexAttribDivisor(t, divisor); + glEnableVertexAttribArray(t); + } + offset += sz*sizeof(float); + } +} + +void VertexSetup::bind() const +{ + if(!vertex_array || !index_buffer) + throw invalid_operation("VertexSetup::bind"); + + if(set_current(this)) + { + vertex_array->refresh(); + if(inst_array) + inst_array->refresh(); + glBindVertexArray(id); + if(dirty) + { + update(dirty); + dirty = 0; + } + } +} + +void VertexSetup::unbind() +{ + if(set_current(0)) + glBindVertexArray(0); +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/vertexsetup.h b/source/core/vertexsetup.h new file mode 100644 index 00000000..bae4c5f3 --- /dev/null +++ b/source/core/vertexsetup.h @@ -0,0 +1,62 @@ +#ifndef MSP_GL_VERTEXSETUP_H_ +#define MSP_GL_VERTEXSETUP_H_ + +#include "bindable.h" +#include "vertexformat.h" + +namespace Msp { +namespace GL { + +class VertexArray; + +/** +Combines a VertexArray with an index buffer. This wraps OpenGL's vertex array +objects. Intended for internal use. +*/ +class VertexSetup: public Bindable +{ +private: + enum ComponentMask + { + VERTEX_ARRAY = 1, + INSTANCE_ARRAY = 2, + INDEX_BUFFER = 4, + UNUSED_ATTRIBS = 8, + ATTRIB_SHIFT = 4 + }; + + unsigned id; + mutable unsigned dirty; + const VertexArray *vertex_array; + VertexFormat vertex_format; + const VertexArray *inst_array; + VertexFormat inst_format; + const Buffer *index_buffer; + +public: + VertexSetup(); + ~VertexSetup(); + + void set_vertex_array(const VertexArray &); + void set_instance_array(const VertexArray *); + void set_index_buffer(const Buffer &); + void refresh(); + const VertexArray *get_vertex_array() const { return vertex_array; } + const VertexArray *get_instance_array() const { return inst_array; } + const Buffer *get_index_buffer() const { return index_buffer; } + +private: + static unsigned get_attribs(const VertexFormat &); + static unsigned get_update_mask(unsigned, const VertexFormat &, const VertexArray &); + void update(unsigned) const; + void update_vertex_array(const VertexArray &, unsigned, unsigned, bool) const; + +public: + void bind() const; + static void unbind(); +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/core/windingtest.cpp b/source/core/windingtest.cpp new file mode 100644 index 00000000..0b8218ad --- /dev/null +++ b/source/core/windingtest.cpp @@ -0,0 +1,61 @@ +#include +#include "windingtest.h" + +namespace Msp { +namespace GL { + +void operator>>(const LexicalConverter &conv, FaceWinding &winding) +{ + if(conv.get()=="CLOCKWISE") + winding = CLOCKWISE; + else if(conv.get()=="COUNTERCLOCKWISE") + winding = COUNTERCLOCKWISE; + else + throw lexical_error(format("conversion of '%s' to FaceWinding", conv.get())); +} + +WindingTest::WindingTest(): + winding(COUNTERCLOCKWISE) +{ } + +WindingTest::WindingTest(FaceWinding w): + winding(w) +{ } + +void WindingTest::bind() const +{ + if(set_current(this)) + { + glEnable(GL_CULL_FACE); + glFrontFace(winding); + } +} + +void WindingTest::unbind() +{ + if(set_current(0)) + glDisable(GL_CULL_FACE); +} + +const WindingTest &WindingTest::get_reverse() const +{ + if(winding==CLOCKWISE) + return counterclockwise(); + else + return clockwise(); +} + +const WindingTest &WindingTest::clockwise() +{ + static WindingTest test(CLOCKWISE); + return test; +} + +const WindingTest &WindingTest::counterclockwise() +{ + static WindingTest test(COUNTERCLOCKWISE); + return test; +} + +} // namespace GL +} // namespace Msp diff --git a/source/core/windingtest.h b/source/core/windingtest.h new file mode 100644 index 00000000..ffc52556 --- /dev/null +++ b/source/core/windingtest.h @@ -0,0 +1,45 @@ +#ifndef MSP_GL_WINDINGTEST_H_ +#define MSP_GL_WINDINGTEST_H_ + +#include +#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 +{ +private: + FaceWinding winding; + +public: + WindingTest(); + WindingTest(FaceWinding); + + void bind() const; + + static void unbind(); + + const WindingTest &get_reverse() const; + + static const WindingTest &clockwise(); + static const WindingTest &counterclockwise(); +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/cylinder.cpp b/source/cylinder.cpp deleted file mode 100644 index 901a0f4b..00000000 --- a/source/cylinder.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#define _USE_MATH_DEFINES -#include -#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 -#include "datatype.h" - -using namespace std; - -namespace Msp { -namespace GL { - -unsigned get_type_size(DataType type) -{ - switch(type) - { - case BYTE: - case UNSIGNED_BYTE: return 1; - case SHORT: - case UNSIGNED_SHORT: - case HALF_FLOAT: return 2; - case INT: - case UNSIGNED_INT: - case FLOAT: return 4; - default: throw invalid_argument("get_type_size"); - } -} - -} // namespace GL -} // namespace Msp diff --git a/source/datatype.h b/source/datatype.h deleted file mode 100644 index 2d783f61..00000000 --- a/source/datatype.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef MSP_GL_DATATYPE_H_ -#define MSP_GL_DATATYPE_H_ - -#include "gl.h" -#include - -namespace Msp { -namespace GL { - -enum DataType -{ - BYTE = GL_BYTE, - UNSIGNED_BYTE = GL_UNSIGNED_BYTE, - SHORT = GL_SHORT, - UNSIGNED_SHORT = GL_UNSIGNED_SHORT, - INT = GL_INT, - UNSIGNED_INT = GL_UNSIGNED_INT, - FLOAT = GL_FLOAT, - HALF_FLOAT = GL_HALF_FLOAT -}; - -unsigned get_type_size(DataType); - -} // namespace GL -} // namespace Msp - -#endif diff --git a/source/effect.cpp b/source/effect.cpp deleted file mode 100644 index ad789af0..00000000 --- a/source/effect.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "effect.h" -#include "sampler.h" - -namespace Msp { -namespace GL { - -WeakPtr 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 Effect::get_linear_sampler() -{ - RefPtr sampler = linear_sampler; - if(!sampler) - { - sampler = new Sampler; - sampler->set_filter(LINEAR); - sampler->set_wrap(CLAMP_TO_EDGE); - linear_sampler = sampler; - } - return sampler; -} - -} // namespace GL -} // namespace Msp diff --git a/source/effect.h b/source/effect.h deleted file mode 100644 index bc9e19ec..00000000 --- a/source/effect.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef MSP_GL_EFFECT_H_ -#define MSP_GL_EFFECT_H_ - -#include -#include -#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 enabled_passes; - -private: - static WeakPtr 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 *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 get_linear_sampler(); -}; - -} // namespace GL -} // namespace Msp - -#endif diff --git a/source/effects/ambientocclusion.cpp b/source/effects/ambientocclusion.cpp new file mode 100644 index 00000000..8c1dd616 --- /dev/null +++ b/source/effects/ambientocclusion.cpp @@ -0,0 +1,142 @@ +#include +#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 a = Geometry::Angle::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(seed)*48271)%modulus; // minstd + return static_cast(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)); +} + +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 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(t) +{ + add("darkness", &Template::darkness); + add("edge_depth_threshold", &Template::edge_depth_threshold); + add("occlusion_radius", &Template::occlusion_radius); + add("samples", &Template::n_samples); +} + +} // namespace GL +} // namespace Msp diff --git a/source/effects/ambientocclusion.h b/source/effects/ambientocclusion.h new file mode 100644 index 00000000..6c2c5d7e --- /dev/null +++ b/source/effects/ambientocclusion.h @@ -0,0 +1,72 @@ +#ifndef MSP_GL_AMBIENTOCCLUSION_H_ +#define MSP_GL_AMBIENTOCCLUSION_H_ + +#include "framebuffer.h" +#include "mesh.h" +#include "postprocessor.h" +#include "program.h" +#include "programdata.h" +#include "rendertarget.h" +#include "texture2d.h" +#include "texturing.h" + +namespace Msp { +namespace GL { + +/** +Implements screen-space ambient occlusion. + +http://en.wikipedia.org/wiki/Screen_Space_Ambient_Occlusion +*/ +class AmbientOcclusion: public PostProcessor +{ +public: + struct Template: PostProcessor::Template + { + class Loader: public DataFile::DerivedObjectLoader + { + 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 quad; + RefPtr linear_sampler; + RefPtr nearest_sampler; + +public: + AmbientOcclusion(unsigned, unsigned, float = 1.0f); + +private: + static float random(unsigned &); + +public: + void set_n_samples(unsigned); + void set_occlusion_radius(float); + void set_edge_depth_threshold(float); + + void set_darkness(float); + + virtual void render(Renderer &, const Texture2D &, const Texture2D &); +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/effects/bloom.cpp b/source/effects/bloom.cpp new file mode 100644 index 00000000..ee7c76c3 --- /dev/null +++ b/source/effects/bloom.cpp @@ -0,0 +1,112 @@ +#include +#include +#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(r*3.0f), 9); + common_shdata.uniform("size", size); + + vector 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 = 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(t) +{ + add("strength", &Template::strength); + add("radius", &Template::radius); +} + +} // namespace GL +} // namespace Msp diff --git a/source/effects/bloom.h b/source/effects/bloom.h new file mode 100644 index 00000000..6b10195b --- /dev/null +++ b/source/effects/bloom.h @@ -0,0 +1,72 @@ +#ifndef MSP_GL_BLOOM_H_ +#define MSP_GL_BLOOM_H_ + +#include "framebuffer.h" +#include "mesh.h" +#include "postprocessor.h" +#include "texture2d.h" +#include "texturing.h" +#include "program.h" +#include "programdata.h" +#include "rendertarget.h" + +namespace Msp { +namespace GL { + +/** +The Bloom post-processing effect causes very bright areas of the image to bleed +into surrounding pixels. Commonly used together with HDR rendering. + +The technique used is to gaussian blur the image and then blend the result with +the original image. With suitable parameters, this effect may also be used as +a blur filter. +*/ +class Bloom: public PostProcessor +{ +public: + struct Template: public PostProcessor::Template + { + class Loader: public DataFile::DerivedObjectLoader + { + 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 quad; + RefPtr nearest_sampler; + RefPtr linear_sampler; + +public: + Bloom(unsigned, unsigned); + ~Bloom(); + + /** Sets the σ value of the gaussian blur. Values much larger than 4.0 are + likely to cause artifacts. */ + void set_radius(float); + + /** Sets the blend factor between original and blurred images. Larger + values mean more blurriness. */ + void set_strength(float); + + virtual void render(Renderer &, const Texture2D &, const Texture2D &); +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/effects/colorcurve.cpp b/source/effects/colorcurve.cpp new file mode 100644 index 00000000..8811b065 --- /dev/null +++ b/source/effects/colorcurve.cpp @@ -0,0 +1,124 @@ +#include +#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 = 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(t) +{ + add("brightness_response", &Template::brightness_response); + add("exposure_adjust", &Template::exposure_adjust); + add("gamma", &Loader::gamma); + add("srgb", &Loader::srgb); +} + +void ColorCurve::Template::Loader::gamma(float g) +{ + obj.gamma = g; + obj.srgb = false; +} + +void ColorCurve::Template::Loader::srgb() +{ + obj.srgb = true; +} + +} // namespace GL +} // namespace Msp diff --git a/source/effects/colorcurve.h b/source/effects/colorcurve.h new file mode 100644 index 00000000..3f65b049 --- /dev/null +++ b/source/effects/colorcurve.h @@ -0,0 +1,83 @@ +#ifndef MSP_GL_COLORCURVE_H_ +#define MSP_GL_COLORCURVE_H_ + +#include "postprocessor.h" +#include "program.h" +#include "programdata.h" +#include "texture1d.h" +#include "texturing.h" + +namespace Msp { +namespace GL { + +/** +Processes oversaturated colors to preserve hues. When one color component +exceeds 1.0, the overflow is distributed to the other components, scaling the +color towards white. + +Gamma or sRGB correction can also be applied to the output. It can be used to +improve color reproduction by performing lighting calculations in linear color +space and converting to sRGB for display. +*/ +class ColorCurve: public PostProcessor +{ +public: + struct Template: public PostProcessor::Template + { + class Loader: public DataFile::DerivedObjectLoader + { + 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 quad; + RefPtr linear_sampler; + RefPtr nearest_sampler; + +public: + ColorCurve(); + + /** Set exposure adjustment in EV units. Positive values brighten the + image, negative values darken it. Zero is neutral. */ + void set_exposure_adjust(float); + + /** Sets the exponent of the */ + void set_brightness_response(float); + + /** Sets the gamma value used for mapping output colors. Allowed range is + from 0.1 to 10. */ + void set_gamma(float); + + /** Sets output mapping to sRGB. This is almost, but not exactly equivalent + to set_gamma(2.2). */ + void set_srgb(); + + /// Sets output mapping to linear. This is equivalent to set_gamma(1). + void set_linear(); + + virtual void render(Renderer &, const Texture2D &, const Texture2D &); +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/effects/effect.cpp b/source/effects/effect.cpp new file mode 100644 index 00000000..ad789af0 --- /dev/null +++ b/source/effects/effect.cpp @@ -0,0 +1,39 @@ +#include "effect.h" +#include "sampler.h" + +namespace Msp { +namespace GL { + +WeakPtr 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 Effect::get_linear_sampler() +{ + RefPtr sampler = linear_sampler; + if(!sampler) + { + sampler = new Sampler; + sampler->set_filter(LINEAR); + sampler->set_wrap(CLAMP_TO_EDGE); + linear_sampler = sampler; + } + return sampler; +} + +} // namespace GL +} // namespace Msp diff --git a/source/effects/effect.h b/source/effects/effect.h new file mode 100644 index 00000000..bc9e19ec --- /dev/null +++ b/source/effects/effect.h @@ -0,0 +1,48 @@ +#ifndef MSP_GL_EFFECT_H_ +#define MSP_GL_EFFECT_H_ + +#include +#include +#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 enabled_passes; + +private: + static WeakPtr 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 *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 get_linear_sampler(); +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/effects/environmentmap.cpp b/source/effects/environmentmap.cpp new file mode 100644 index 00000000..3902eb8c --- /dev/null +++ b/source/effects/environmentmap.cpp @@ -0,0 +1,121 @@ +#include +#include +#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::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(unit)); + Bind _bind_sampler(*sampler, unit); + Bind _bind_env(env_tex, unit); + + const Matrix &camera_matrix = renderer.get_camera()->get_object_matrix(); + shdata.uniform("env_eye_matrix", camera_matrix.block<3, 3>(0, 0)); + + renderer.add_shader_data(shdata); + renderer.render(renderable, tag); +} + +} // namespace GL +} // namespace Msp diff --git a/source/effects/environmentmap.h b/source/effects/environmentmap.h new file mode 100644 index 00000000..c54968de --- /dev/null +++ b/source/effects/environmentmap.h @@ -0,0 +1,58 @@ +#ifndef MSP_GL_ENVIRONMENTMAP_H_ +#define MSP_GL_ENVIRONMENTMAP_H_ + +#include "camera.h" +#include "effect.h" +#include "framebuffer.h" +#include "matrix.h" +#include "programdata.h" +#include "renderbuffer.h" +#include "texturecube.h" +#include "vector.h" + +namespace Msp { +namespace GL { + +class Renderable; + +/** +Creates a cube map texture of the surroundings of the renderable. This texture +can then be used to implement effects such as reflections or refractions. + +If the EnvironmentMap is used in a Pipeline, it's worth noting that the cube +map will be prepared outside of any rendering pass. It's recommended to use +another Pipeline to define which passes should be used to render the +environment. +*/ +class EnvironmentMap: public Effect +{ +private: + unsigned size; + Renderable &environment; + TextureCube env_tex; + Renderbuffer depth_buf; + Framebuffer fbo[6]; + RefPtr sampler; + Camera camera; + mutable ProgramData shdata; + bool rendered; + unsigned update_interval; + unsigned update_delay; + +public: + EnvironmentMap(unsigned size, Renderable &rend, Renderable &env); + + void set_depth_clip(float, float); + void set_update_interval(unsigned); + void queue_update(); + + virtual void setup_frame(Renderer &); + virtual void finish_frame(); + + virtual void render(Renderer &, const Tag & = Tag()) const; +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/effects/postprocessor.cpp b/source/effects/postprocessor.cpp new file mode 100644 index 00000000..6af069e9 --- /dev/null +++ b/source/effects/postprocessor.cpp @@ -0,0 +1,76 @@ +#include "mesh.h" +#include "meshbuilder.h" +#include "postprocessor.h" +#include "sampler.h" +#include "shader.h" + +namespace Msp { +namespace GL { + +WeakPtr PostProcessor::fullscreen_quad; +WeakPtr PostProcessor::nearest_sampler; +WeakPtr PostProcessor::linear_sampler; + +void PostProcessor::render(Renderer &, const Texture2D &color, const Texture2D &depth) +{ + render(color, depth); +} + +RefPtr PostProcessor::get_fullscreen_quad() +{ + RefPtr 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 PostProcessor::get_nearest_sampler() +{ + RefPtr sampler = nearest_sampler; + if(!sampler) + { + sampler = new Sampler; + sampler->set_filter(NEAREST); + sampler->set_wrap(CLAMP_TO_EDGE); + nearest_sampler = sampler; + } + return sampler; +} + +RefPtr PostProcessor::get_linear_sampler() +{ + RefPtr 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