--- /dev/null
+#include <msp/gl/extensions/msp_legacy_features.h>
+#include "clipping.h"
+#include "clipplane.h"
+#include "clipunit.h"
+#include "matrix.h"
+#include "misc.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+bool Clipping::bound_with_legacy = false;
+
+void Clipping::attach(unsigned i, const ClipPlane &p)
+{
+ if(i>=ClipUnit::get_n_units())
+ throw out_of_range("Clipping::attach");
+
+ if(i>=planes.size())
+ planes.resize(i+1);
+
+ planes[i] = &p;
+ if(current()==this)
+ {
+ if(bound_with_legacy)
+ p.bind_to(i);
+ else
+ glEnable(GL_CLIP_PLANE0+i);
+ }
+}
+
+void Clipping::detach(unsigned i)
+{
+ if(i>=planes.size())
+ return;
+
+ planes[i] = 0;
+ if(current()==this)
+ {
+ if(bound_with_legacy)
+ ClipPlane::unbind_from(i);
+ else
+ disable(GL_CLIP_PLANE0+i);
+ }
+}
+
+void Clipping::update_shader_data(ProgramData &shdata, const Matrix &view_matrix) const
+{
+ Matrix view_inverse = invert(view_matrix);
+ for(unsigned i=0; i<planes.size(); ++i)
+ if(planes[i])
+ planes[i]->update_shader_data(shdata, view_inverse, i);
+}
+
+void Clipping::bind(bool legacy) const
+{
+ if(legacy)
+ static Require _req(MSP_legacy_features);
+
+ const Clipping *old = current();
+ if(!set_current(this))
+ return;
+
+ bound_with_legacy = legacy;
+ if(legacy)
+ {
+ for(unsigned i=0; i<planes.size(); ++i)
+ {
+ if(planes[i])
+ planes[i]->bind_to(i);
+ else
+ ClipPlane::unbind_from(i);
+ }
+
+ if(old)
+ {
+ for(unsigned i=planes.size(); i<old->planes.size(); ++i)
+ ClipPlane::unbind_from(i);
+ }
+ }
+ else
+ {
+ for(unsigned i=0; i<planes.size(); ++i)
+ {
+ if(planes[i])
+ enable(GL_CLIP_PLANE0+i);
+ else
+ disable(GL_CLIP_PLANE0+i);
+ }
+
+ if(old)
+ {
+ for(unsigned i=planes.size(); i<old->planes.size(); ++i)
+ disable(GL_CLIP_PLANE0+i);
+ }
+ }
+}
+
+void Clipping::unbind()
+{
+}
+
+} // namespace GL
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GL_CLIPPING_H_
+#define MSP_GL_CLIPPING_H_
+
+#include <vector>
+#include "bindable.h"
+
+namespace Msp {
+namespace GL {
+
+class ClipPlane;
+class Matrix;
+class ProgramData;
+
+class Clipping: public Bindable<Clipping>
+{
+private:
+ std::vector<const ClipPlane *> planes;
+
+ static bool bound_with_legacy;
+
+public:
+ void attach(unsigned, const ClipPlane &);
+ void detach(unsigned);
+
+ void update_shader_data(ProgramData &, const Matrix &) const;
+
+ void bind(bool legacy = true) const;
+
+ static void unbind();
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
--- /dev/null
+#include <msp/strings/format.h>
+#include "clipplane.h"
+#include "clipunit.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::update(unsigned index) const
+{
+ double deq[4];
+ for(unsigned i=0; i<4; ++i)
+ deq[i] = eq[i];
+ glClipPlane(GL_CLIP_PLANE0+index, deq);
+}
+
+void ClipPlane::set_equation(const Vector4 &e)
+{
+ eq = e;
+ if(ClipUnit *unit = ClipUnit::find_unit(this))
+ update(unit->get_index());
+}
+
+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);
+}
+
+void ClipPlane::bind_to(unsigned i) const
+{
+ ClipUnit &unit = ClipUnit::get_unit(i);
+ if(unit.set_plane(this))
+ {
+ enable(GL_CLIP_PLANE0+unit.get_index());
+ update(unit.get_index());
+ }
+}
+
+void ClipPlane::unbind_from(unsigned i)
+{
+ ClipUnit &unit = ClipUnit::get_unit(i);
+ if(unit.set_plane(0))
+ disable(GL_CLIP_PLANE0+unit.get_index());
+}
+
+} // namespace GL
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GL_CLIP_H_
+#define MSP_GL_CLIP_H_
+
+#include "vector.h"
+
+namespace Msp {
+namespace GL {
+
+class Matrix;
+class ProgramData;
+
+class ClipPlane
+{
+private:
+ Vector4 eq;
+
+public:
+ ClipPlane();
+ ClipPlane(const Vector4 &);
+ ClipPlane(const Vector3 &, const Vector3 &);
+
+private:
+ void update(unsigned) const;
+public:
+ void set_equation(const Vector4 &);
+ void set_plane(const Vector3 &, const Vector3 &);
+ void update_shader_data(ProgramData &, const Matrix &, unsigned) const;
+
+ void bind_to(unsigned) const;
+
+ static void unbind_from(unsigned);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif
--- /dev/null
+#include <stdexcept>
+#include "clipunit.h"
+#include "gl.h"
+#include "misc.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+vector<ClipUnit> ClipUnit::units;
+
+ClipUnit::ClipUnit():
+ index(0),
+ plane(0)
+{ }
+
+bool ClipUnit::set_plane(const ClipPlane *p)
+{
+ bool result = (p!=plane);
+ plane = p;
+ return result;
+}
+
+unsigned ClipUnit::get_n_units()
+{
+ static int count = get_i(GL_MAX_CLIP_PLANES);
+ return count;
+}
+
+ClipUnit &ClipUnit::get_unit(unsigned i)
+{
+ if(i>=get_n_units())
+ throw out_of_range("ClipUnit::get_unit");
+
+ if(units.size()<=i)
+ {
+ unsigned j = units.size();
+ units.resize(i+1, ClipUnit());
+ for(; j<units.size(); ++j)
+ units[j].index = j;
+ }
+
+ return units[i];
+}
+
+ClipUnit *ClipUnit::find_unit(const ClipPlane *p)
+{
+ for(vector<ClipUnit>::iterator i=units.begin(); i!=units.end(); ++i)
+ if(i->plane==p)
+ return &*i;
+ return 0;
+}
+
+} // namespace GL
+} // namespace Msp
--- /dev/null
+#ifndef MSP_GL_CLIPUNIT_H_
+#define MSP_GL_CLIPUNIT_H_
+
+#include <vector>
+
+namespace Msp {
+namespace GL {
+
+class ClipPlane;
+
+class ClipUnit
+{
+private:
+ unsigned index;
+ const ClipPlane *plane;
+
+ static std::vector<ClipUnit> units;
+
+ ClipUnit();
+
+public:
+ unsigned get_index() const { return index; }
+ bool set_plane(const ClipPlane *);
+ const ClipPlane *get_plane() const { return plane; }
+
+ static unsigned get_n_units();
+ static ClipUnit &get_unit(unsigned);
+ static ClipUnit *find_unit(const ClipPlane *);
+};
+
+} // namespace GL
+} // namespace Msp
+
+#endif