/* 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 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([&](Vector3 &point) { matrix4_transform_point(matrix, point); }); } void snapto(float snap) { forEachSelected([&](Vector3 &point) { vector3_snap(point, snap); }); } void updateSelected() const { m_selectedRender.clear(); forEachSelected([&](const Vector3 &point) { m_selectedRender.push_back(PointVertex(vertex3f_for_vector3(point), colour_selected)); }); } 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([&](const Vector3 &point) { m_controlsRender.push_back(PointVertex(vertex3f_for_vector3(point), colour_vertex)); }); 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 MemberCaller 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 MemberCaller CurveChangedCaller; }; const char *const curve_Nurbs = "curve_Nurbs"; const char *const curve_CatmullRomSpline = "curve_CatmullRomSpline"; #endif