/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined(INCLUDED_CURVE_H) #define INCLUDED_CURVE_H #include "ientity.h" #include "selectable.h" #include "renderable.h" #include #include "math/curve.h" #include "stream/stringstream.h" #include "signal/signal.h" #include "selectionlib.h" #include "render.h" #include "stringio.h" class RenderableCurve : public OpenGLRenderable { public: std::vector m_vertices; void render(RenderStateFlags state) const { pointvertex_gl_array(&m_vertices.front()); glDrawArrays(GL_LINE_STRIP, 0, GLsizei(m_vertices.size())); } }; inline void plotBasisFunction(std::size_t numSegments, int point, int degree) { Knots knots; KnotVector_openUniform(knots, 4, degree); globalOutputStream() << "plotBasisFunction point " << point << " of 4, knot vector:"; for(Knots::iterator i = knots.begin(); i != knots.end(); ++i) { globalOutputStream() << " " << *i; } globalOutputStream() << "\n"; globalOutputStream() << "t=0 basis=" << BSpline_basis(knots, point, degree, 0.0) << "\n"; for(std::size_t i = 1; i < numSegments; ++i) { double t = (1.0 / double(numSegments)) * double(i); globalOutputStream() << "t=" << t << " basis=" << BSpline_basis(knots, point, degree, t) << "\n"; } globalOutputStream() << "t=1 basis=" << BSpline_basis(knots, point, degree, 1.0) << "\n"; } inline bool ControlPoints_parse(ControlPoints& controlPoints, const char* value) { StringTokeniser tokeniser(value, " "); std::size_t size; if(!string_parse_size(tokeniser.getToken(), size)) { return false; } if(size < 3) { return false; } controlPoints.resize(size); if(!string_equal(tokeniser.getToken(), "(")) { return false; } for(ControlPoints::iterator i = controlPoints.begin(); i != controlPoints.end(); ++i) { if(!string_parse_float(tokeniser.getToken(), (*i).x()) || !string_parse_float(tokeniser.getToken(), (*i).y()) || !string_parse_float(tokeniser.getToken(), (*i).z())) { return false; } } if(!string_equal(tokeniser.getToken(), ")")) { return false; } return true; } inline void ControlPoints_write(const ControlPoints& controlPoints, StringOutputStream& value) { value << Unsigned(controlPoints.size()) << " ("; for(ControlPoints::const_iterator i = controlPoints.begin(); i != controlPoints.end(); ++i) { value << " " << (*i).x() << " " << (*i).y() << " " << (*i).z() << " "; } value << ")"; } inline void ControlPoint_testSelect(const Vector3& point, ObservedSelectable& selectable, Selector& selector, SelectionTest& test) { SelectionIntersection best; test.TestPoint(point, best); if(best.valid()) { Selector_add(selector, selectable, best); } } class ControlPointTransform { const Matrix4& m_matrix; public: ControlPointTransform(const Matrix4& matrix) : m_matrix(matrix) { } void operator()(Vector3& point) const { matrix4_transform_point(m_matrix, point); } }; class ControlPointSnap { float m_snap; public: ControlPointSnap(float snap) : m_snap(snap) { } void operator()(Vector3& point) const { vector3_snap(point, m_snap); } }; class ControlPointAdd { RenderablePointVector& m_points; public: ControlPointAdd(RenderablePointVector& points) : m_points(points) { } void operator()(const Vector3& point) const { m_points.push_back(PointVertex(vertex3f_for_vector3(point), colour_vertex)); } }; class ControlPointAddSelected { RenderablePointVector& m_points; public: ControlPointAddSelected(RenderablePointVector& points) : m_points(points) { } void operator()(const Vector3& point) const { m_points.push_back(PointVertex(vertex3f_for_vector3(point), colour_selected)); } }; class CurveEditType { public: Shader* m_controlsShader; Shader* m_selectedShader; }; inline void ControlPoints_write(ControlPoints& controlPoints, const char* key, Entity& entity) { StringOutputStream value(256); if(!controlPoints.empty()) { ControlPoints_write(controlPoints, value); } entity.setKeyValue(key, value.c_str()); } class CurveEdit { SelectionChangeCallback m_selectionChanged; ControlPoints& m_controlPoints; typedef Array Selectables; Selectables m_selectables; RenderablePointVector m_controlsRender; mutable RenderablePointVector m_selectedRender; public: typedef Static Type; CurveEdit(ControlPoints& controlPoints, const SelectionChangeCallback& selectionChanged) : m_selectionChanged(selectionChanged), m_controlPoints(controlPoints), m_controlsRender(GL_POINTS), m_selectedRender(GL_POINTS) { } template const Functor& forEachSelected(const Functor& functor) { ASSERT_MESSAGE(m_controlPoints.size() == m_selectables.size(), "curve instance mismatch"); ControlPoints::iterator p = m_controlPoints.begin(); for(Selectables::iterator i = m_selectables.begin(); i != m_selectables.end(); ++i, ++p) { if((*i).isSelected()) { functor(*p); } } return functor; } template const Functor& forEachSelected(const Functor& functor) const { ASSERT_MESSAGE(m_controlPoints.size() == m_selectables.size(), "curve instance mismatch"); ControlPoints::const_iterator p = m_controlPoints.begin(); for(Selectables::const_iterator i = m_selectables.begin(); i != m_selectables.end(); ++i, ++p) { if((*i).isSelected()) { functor(*p); } } return functor; } template const Functor& forEach(const Functor& functor) const { for(ControlPoints::const_iterator i = m_controlPoints.begin(); i != m_controlPoints.end(); ++i) { functor(*i); } return functor; } void testSelect(Selector& selector, SelectionTest& test) { ASSERT_MESSAGE(m_controlPoints.size() == m_selectables.size(), "curve instance mismatch"); ControlPoints::const_iterator p = m_controlPoints.begin(); for(Selectables::iterator i = m_selectables.begin(); i != m_selectables.end(); ++i, ++p) { ControlPoint_testSelect(*p, *i, selector, test); } } bool isSelected() const { for(Selectables::const_iterator i = m_selectables.begin(); i != m_selectables.end(); ++i) { if((*i).isSelected()) { return true; } } return false; } void setSelected(bool selected) { for(Selectables::iterator i = m_selectables.begin(); i != m_selectables.end(); ++i) { (*i).setSelected(selected); } } void write(const char* key, Entity& entity) { ControlPoints_write(m_controlPoints, key, entity); } void transform(const Matrix4& matrix) { forEachSelected(ControlPointTransform(matrix)); } void snapto(float snap) { forEachSelected(ControlPointSnap(snap)); } void updateSelected() const { m_selectedRender.clear(); forEachSelected(ControlPointAddSelected(m_selectedRender)); } void renderComponents(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld) const { renderer.SetState(Type::instance().m_controlsShader, Renderer::eWireframeOnly); renderer.SetState(Type::instance().m_controlsShader, Renderer::eFullMaterials); renderer.addRenderable(m_controlsRender, localToWorld); } void renderComponentsSelected(Renderer& renderer, const VolumeTest& volume, const Matrix4& localToWorld) const { updateSelected(); if(!m_selectedRender.empty()) { renderer.Highlight(Renderer::ePrimitive, false); renderer.SetState(Type::instance().m_selectedShader, Renderer::eWireframeOnly); renderer.SetState(Type::instance().m_selectedShader, Renderer::eFullMaterials); renderer.addRenderable(m_selectedRender, localToWorld); } } void curveChanged() { m_selectables.resize(m_controlPoints.size(), m_selectionChanged); m_controlsRender.clear(); m_controlsRender.reserve(m_controlPoints.size()); forEach(ControlPointAdd(m_controlsRender)); m_selectedRender.reserve(m_controlPoints.size()); } typedef MemberCaller CurveChangedCaller; }; const int NURBS_degree = 3; class NURBSCurve { Signal0 m_curveChanged; Callback m_boundsChanged; public: ControlPoints m_controlPoints; ControlPoints m_controlPointsTransformed; NURBSWeights m_weights; Knots m_knots; RenderableCurve m_renderCurve; AABB m_bounds; NURBSCurve(const Callback& boundsChanged) : m_boundsChanged(boundsChanged) { } SignalHandlerId connect(const SignalHandler& curveChanged) { curveChanged(); return m_curveChanged.connectLast(curveChanged); } void disconnect(SignalHandlerId id) { m_curveChanged.disconnect(id); } void notify() { m_curveChanged(); } void tesselate() { if(!m_controlPointsTransformed.empty()) { const std::size_t numSegments = (m_controlPointsTransformed.size() - 1) * 16; m_renderCurve.m_vertices.resize(numSegments + 1); m_renderCurve.m_vertices[0].vertex = vertex3f_for_vector3(m_controlPointsTransformed[0]); for(std::size_t i = 1; i < numSegments; ++i) { m_renderCurve.m_vertices[i].vertex = vertex3f_for_vector3(NURBS_evaluate(m_controlPointsTransformed, m_weights, m_knots, NURBS_degree, (1.0 / double(numSegments)) * double(i))); } m_renderCurve.m_vertices[numSegments].vertex = vertex3f_for_vector3(m_controlPointsTransformed[m_controlPointsTransformed.size() - 1]); } else { m_renderCurve.m_vertices.clear(); } } void curveChanged() { tesselate(); m_bounds = AABB(); for(ControlPoints::iterator i = m_controlPointsTransformed.begin(); i != m_controlPointsTransformed.end(); ++i) { aabb_extend_by_point_safe(m_bounds, (*i)); } m_boundsChanged(); notify(); } bool parseCurve(const char* value) { if(!ControlPoints_parse(m_controlPoints, value)) { return false; } m_weights.resize(m_controlPoints.size()); for(NURBSWeights::iterator i = m_weights.begin(); i != m_weights.end(); ++i) { (*i) = 1; } KnotVector_openUniform(m_knots, m_controlPoints.size(), NURBS_degree); //plotBasisFunction(8, 0, NURBS_degree); return true; } void curveChanged(const char* value) { if(string_empty(value) || !parseCurve(value)) { m_controlPoints.resize(0); m_knots.resize(0); m_weights.resize(0); } m_controlPointsTransformed = m_controlPoints; curveChanged(); } typedef MemberCaller1 CurveChangedCaller; }; class CatmullRomSpline { Signal0 m_curveChanged; Callback m_boundsChanged; public: ControlPoints m_controlPoints; ControlPoints m_controlPointsTransformed; RenderableCurve m_renderCurve; AABB m_bounds; CatmullRomSpline(const Callback& boundsChanged) : m_boundsChanged(boundsChanged) { } SignalHandlerId connect(const SignalHandler& curveChanged) { curveChanged(); return m_curveChanged.connectLast(curveChanged); } void disconnect(SignalHandlerId id) { m_curveChanged.disconnect(id); } void notify() { m_curveChanged(); } void tesselate() { if(!m_controlPointsTransformed.empty()) { const std::size_t numSegments = (m_controlPointsTransformed.size() - 1) * 16; m_renderCurve.m_vertices.resize(numSegments + 1); m_renderCurve.m_vertices[0].vertex = vertex3f_for_vector3(m_controlPointsTransformed[0]); for(std::size_t i = 1; i < numSegments; ++i) { m_renderCurve.m_vertices[i].vertex = vertex3f_for_vector3(CatmullRom_evaluate(m_controlPointsTransformed, (1.0 / double(numSegments)) * double(i))); } m_renderCurve.m_vertices[numSegments].vertex = vertex3f_for_vector3(m_controlPointsTransformed[m_controlPointsTransformed.size() - 1]); } else { m_renderCurve.m_vertices.clear(); } } bool parseCurve(const char* value) { return ControlPoints_parse(m_controlPoints, value); } void curveChanged() { tesselate(); m_bounds = AABB(); for(ControlPoints::iterator i = m_controlPointsTransformed.begin(); i != m_controlPointsTransformed.end(); ++i) { aabb_extend_by_point_safe(m_bounds, (*i)); } m_boundsChanged(); notify(); } void curveChanged(const char* value) { if(string_empty(value) || !parseCurve(value)) { m_controlPoints.resize(0); } m_controlPointsTransformed = m_controlPoints; curveChanged(); } typedef MemberCaller1 CurveChangedCaller; }; const char* const curve_Nurbs = "curve_Nurbs"; const char* const curve_CatmullRomSpline = "curve_CatmullRomSpline"; #endif