From df768766d9abaa46bd73c2f39e6d205408192ed7 Mon Sep 17 00:00:00 2001 From: Thomas Geymayer Date: Mon, 4 Jun 2012 12:10:25 +0200 Subject: [PATCH] Canvas: basic support for OpenVG (path with stroke and pattern) - Bugfix: Don't access children by index as it's not unique --- src/Canvas/CMakeLists.txt | 2 + src/Canvas/elements/element.cxx | 5 + src/Canvas/elements/group.cxx | 40 ++--- src/Canvas/elements/group.hxx | 5 +- src/Canvas/elements/path.cxx | 274 ++++++++++++++++++++++++++++++++ src/Canvas/elements/path.hxx | 54 +++++++ src/Canvas/elements/text.cxx | 4 - src/Canvas/property_helper.hxx | 16 ++ src/Main/CMakeLists.txt | 1 + 9 files changed, 372 insertions(+), 29 deletions(-) create mode 100644 src/Canvas/elements/path.cxx create mode 100644 src/Canvas/elements/path.hxx diff --git a/src/Canvas/CMakeLists.txt b/src/Canvas/CMakeLists.txt index 5de9945c0..eac439735 100644 --- a/src/Canvas/CMakeLists.txt +++ b/src/Canvas/CMakeLists.txt @@ -5,6 +5,7 @@ set(SOURCES canvas_mgr.cxx elements/element.cxx elements/group.cxx + elements/path.cxx elements/text.cxx property_helper.cxx ) @@ -14,6 +15,7 @@ set(HEADERS canvas_mgr.hxx elements/element.hxx elements/group.hxx + elements/path.hxx elements/text.hxx property_helper.hxx ) diff --git a/src/Canvas/elements/element.cxx b/src/Canvas/elements/element.cxx index 87ce8e609..a27ad6197 100644 --- a/src/Canvas/elements/element.cxx +++ b/src/Canvas/elements/element.cxx @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -157,6 +158,10 @@ namespace canvas _drawable = drawable; assert( _drawable ); + osg::ref_ptr geode = new osg::Geode; + geode->addDrawable(_drawable); + _transform->addChild(geode); + if( _attributes_used & BOUNDING_BOX ) { SGPropertyNode* bb_node = _node->getChild("bounding-box", 0, true); diff --git a/src/Canvas/elements/group.cxx b/src/Canvas/elements/group.cxx index 94e0836a6..67f5d58f2 100644 --- a/src/Canvas/elements/group.cxx +++ b/src/Canvas/elements/group.cxx @@ -17,6 +17,7 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "group.hxx" +#include "path.hxx" #include "text.hxx" namespace canvas @@ -38,11 +39,10 @@ namespace canvas //---------------------------------------------------------------------------- void Group::update(double dt) { - for( size_t i = 0; i < _children.size(); ++i ) - { - if( _children[i] ) - _children[i]->update(dt); - } + for( ChildMap::iterator child = _children.begin(); + child != _children.end(); + ++child ) + child->second->update(dt); Element::update(dt); } @@ -56,6 +56,8 @@ namespace canvas element.reset( new Text(child) ); else if( child->getNameString() == "group" ) element.reset( new Group(child) ); + else if( child->getNameString() == "path" ) + element.reset( new Path(child) ); else SG_LOG ( @@ -69,37 +71,29 @@ namespace canvas // Add to osg scene graph... _transform->addChild( element->getMatrixTransform() ); - - // ...and build up canvas hierarchy - size_t index = child->getIndex(); - - if( index >= _children.size() ) - _children.resize(index + 1); - - _children[index] = element; + _children[ child ] = element; } //---------------------------------------------------------------------------- - void Group::childRemoved(SGPropertyNode* child) + void Group::childRemoved(SGPropertyNode* node) { - if( child->getNameString() == "text" - || child->getNameString() == "group" ) + if( node->getNameString() == "text" + || node->getNameString() == "group" + || node->getNameString() == "path") { - size_t index = child->getIndex(); + ChildMap::iterator child = _children.find(node); - if( index >= _children.size() ) + if( child == _children.end() ) SG_LOG ( SG_GL, SG_WARN, - "can't removed unknown child " << child->getDisplayName() + "can't removed unknown child " << node->getDisplayName() ); else { - boost::shared_ptr& element = _children[index]; - if( element ) - _transform->removeChild(element->getMatrixTransform()); - element.reset(); + _transform->removeChild( child->second->getMatrixTransform() ); + _children.erase(child); } } } diff --git a/src/Canvas/elements/group.hxx b/src/Canvas/elements/group.hxx index d13bb255c..1c07847f9 100644 --- a/src/Canvas/elements/group.hxx +++ b/src/Canvas/elements/group.hxx @@ -21,7 +21,7 @@ #include "element.hxx" #include -#include +#include namespace canvas { @@ -38,7 +38,8 @@ namespace canvas virtual void update(double dt); protected: - std::vector _children; + typedef std::map ChildMap; + ChildMap _children; virtual void childAdded(SGPropertyNode * child); virtual void childRemoved(SGPropertyNode * child); diff --git a/src/Canvas/elements/path.cxx b/src/Canvas/elements/path.cxx new file mode 100644 index 000000000..e3694928b --- /dev/null +++ b/src/Canvas/elements/path.cxx @@ -0,0 +1,274 @@ +// An OpenVG path on the canvas +// +// Copyright (C) 2012 Thomas Geymayer +// +// This program 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. +// +// This program 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 this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "path.hxx" +#include + +#include + +#include +#include + +#include + +namespace canvas +{ + typedef std::vector CmdList; + typedef std::vector CoordList; + + class PathDrawable: + public osg::Drawable + { + public: + PathDrawable(): + _path(VG_INVALID_HANDLE), + _paint(VG_INVALID_HANDLE), + _attributes_dirty(~0), + _stroke_width(1) + { + setSupportsDisplayList(false); + setDataVariance(Object::DYNAMIC); + + _paint_color[0] = 0; + _paint_color[1] = 1; + _paint_color[2] = 1; + _paint_color[3] = 1; + } + + virtual ~PathDrawable() + { + vgDestroyPath(_path); + vgDestroyPaint(_paint); + } + + virtual const char* className() const { return "PathDrawable"; } + virtual osg::Object* cloneType() const { return new PathDrawable; } + virtual osg::Object* clone(const osg::CopyOp&) const { return new PathDrawable; } + + /** + * Replace the current path segments with the new ones + * + * @param cmds List of OpenVG path commands + * @param coords List of coordinates/parameters used by #cmds + */ + void setSegments(const CmdList& cmds, const CoordList& coords) + { + _cmds = cmds; + _coords = coords; + + _attributes_dirty |= PATH; + } + + /** + * Set stroke width and dash (line stipple) + */ + void setStroke( float width, + const std::vector dash = std::vector() ) + { + _stroke_width = width; + _stroke_dash = dash; + + _attributes_dirty |= STROKE; + } + + /** + * Set the line color + */ + void setColor(const osg::Vec4& color) + { + for( size_t i = 0; i < 4; ++i ) + _paint_color[i] = color[i]; + _attributes_dirty |= PAINT_COLOR; + } + + /** + * Draw callback + */ + virtual void drawImplementation(osg::RenderInfo& renderInfo) const + { + osg::State* state = renderInfo.getState(); + assert(state); + + state->setActiveTextureUnit(0); + state->setClientActiveTextureUnit(0); + state->disableAllVertexArrays(); + + glPushAttrib(~0u); // Don't use GL_ALL_ATTRIB_BITS as on my machine it + // eg. doesn't include GL_MULTISAMPLE_BIT + glPushClientAttrib(~0u); + + // Initialize OpenVG itself + if( !_vg_initialized ) + { + GLint vp[4]; + glGetIntegerv(GL_VIEWPORT, vp); + + vgCreateContextSH(vp[2], vp[3]); + _vg_initialized = true; + } + + // Initialize/Update the path + if( _attributes_dirty & PATH ) + { + const VGbitfield caps = VG_PATH_CAPABILITY_APPEND_TO + | VG_PATH_CAPABILITY_MODIFY; + + if( _path == VG_INVALID_HANDLE ) + _path = vgCreatePath( + VG_PATH_FORMAT_STANDARD, + VG_PATH_DATATYPE_F, + 1.f, 0.f, // scale,bias + _cmds.size(), _coords.size(), + caps + ); + else + vgClearPath(_path, caps); + + if( !_cmds.empty() && !_coords.empty() ) + vgAppendPathData(_path, _cmds.size(), &_cmds[0], &_coords[0]); + + _attributes_dirty &= ~PATH; + } + + // Initialize/Update the paint + if( _attributes_dirty & (PAINT_COLOR | STROKE) ) + { + if( _paint == VG_INVALID_HANDLE ) + { + _paint = vgCreatePaint(); + vgSetPaint(_paint, VG_STROKE_PATH); + } + + if( _attributes_dirty & PAINT_COLOR ) + { + vgSetParameterfv(_paint, VG_PAINT_COLOR, 4, _paint_color); + } + if( _attributes_dirty & STROKE ) + { + vgSetf(VG_STROKE_LINE_WIDTH, _stroke_width); + + vgSetfv( VG_STROKE_DASH_PATTERN, + _stroke_dash.size(), + _stroke_dash.empty() ? 0 : &_stroke_dash[0] ); + } + + _attributes_dirty &= ~(PAINT_COLOR | STROKE); + } + + // And finally draw the path + vgDrawPath(_path, VG_STROKE_PATH); + + VGErrorCode err = vgGetError(); + if( err != VG_NO_ERROR ) + std::cout << "vgError: " << err << std::endl; + + glPopAttrib(); + glPopClientAttrib(); + } + + private: + + static bool _vg_initialized; + + enum Attributes + { + PATH = 0x0001, + PAINT_COLOR = 0x0002, + STROKE = 0x0004 + }; + + mutable VGPath _path; + mutable VGPaint _paint; + mutable uint32_t _attributes_dirty; + + CmdList _cmds; + CoordList _coords; + + VGfloat _paint_color[4]; + VGfloat _stroke_width; + std::vector _stroke_dash; + }; + + bool PathDrawable::_vg_initialized = false; + + //---------------------------------------------------------------------------- + Path::Path(SGPropertyNode_ptr node): + Element(node, COLOR /*| COLOR_FILL*/), // TODO fill color + _path( new PathDrawable() ) + { + setDrawable(_path); + } + + //---------------------------------------------------------------------------- + Path::~Path() + { + + } + + //---------------------------------------------------------------------------- + void Path::update(double dt) + { + if( _attributes_dirty & (CMDS | COORDS) ) + { + _path->setSegments + ( + getVectorFromChildren(_node, "cmd"), + getVectorFromChildren(_node, "coord") + ); + + _attributes_dirty &= ~(CMDS | COORDS); + } + if( _attributes_dirty & STROKE ) + { + _path->setStroke + ( + _node->getFloatValue("stroke-width", 1), + getVectorFromChildren(_node, "stroke-dasharray") + ); + + _attributes_dirty &= ~STROKE; + } + + Element::update(dt); + } + + //---------------------------------------------------------------------------- + void Path::childChanged(SGPropertyNode* child) + { + if( child->getNameString() == "cmd" ) + _attributes_dirty |= CMDS; + else if( child->getNameString() == "coord" ) + _attributes_dirty |= COORDS; + else if( child->getNameString() == "stroke-width" + || child->getNameString() == "stroke-dasharray" ) + _attributes_dirty |= STROKE; + } + + //---------------------------------------------------------------------------- + void Path::colorChanged(const osg::Vec4& color) + { + _path->setColor(color); + } + + //---------------------------------------------------------------------------- + void Path::colorFillChanged(const osg::Vec4& color) + { + + } + +} // namespace canvas diff --git a/src/Canvas/elements/path.hxx b/src/Canvas/elements/path.hxx new file mode 100644 index 000000000..a2bd8e5ac --- /dev/null +++ b/src/Canvas/elements/path.hxx @@ -0,0 +1,54 @@ +// An OpenVG path on the canvas +// +// Copyright (C) 2012 Thomas Geymayer +// +// This program 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. +// +// This program 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 this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef CANVAS_PATH_HXX_ +#define CANVAS_PATH_HXX_ + +#include "element.hxx" + +namespace canvas +{ + class PathDrawable; + class Path: + public Element + { + public: + Path(SGPropertyNode_ptr node); + virtual ~Path(); + + virtual void update(double dt); + + protected: + + enum PathAttributes + { + CMDS = LAST_ATTRIBUTE << 1, + COORDS = CMDS << 1, + STROKE = COORDS << 1 + }; + + osg::ref_ptr _path; + + virtual void childChanged(SGPropertyNode * child); + virtual void colorChanged(const osg::Vec4& color); + virtual void colorFillChanged(const osg::Vec4& color); + }; + +} // namespace canvas + +#endif /* CANVAS_PATH_HXX_ */ diff --git a/src/Canvas/elements/text.cxx b/src/Canvas/elements/text.cxx index 6f7a961d5..dd23e921f 100644 --- a/src/Canvas/elements/text.cxx +++ b/src/Canvas/elements/text.cxx @@ -42,10 +42,6 @@ namespace canvas _font_size = getChildDefault(_node, "character-size", 32); _font_aspect = getChildDefault(_node, "character-aspect-ratio", 1); - osg::ref_ptr geode = new osg::Geode; - geode->addDrawable(_text); - _transform->addChild(geode); - _node->tie ( "alignment", diff --git a/src/Canvas/property_helper.hxx b/src/Canvas/property_helper.hxx index c62134ca1..8e14e5548 100644 --- a/src/Canvas/property_helper.hxx +++ b/src/Canvas/property_helper.hxx @@ -44,6 +44,22 @@ namespace canvas return node; } + /** + * Get vector of properties + */ + template // TODO use C++11 or traits + std::vector getVectorFromChildren( const SGPropertyNode* parent, + const char* child_name ) + { + const simgear::PropertyList& props = parent->getChildren(child_name); + std::vector values( props.size() ); + + for( size_t i = 0; i < props.size(); ++i ) + values[i] = getValue(props[i]); + + return values; + } + /** * @param name Name of color node * @param parent Parent for color channel nodes diff --git a/src/Main/CMakeLists.txt b/src/Main/CMakeLists.txt index 92dbb7ceb..80a7c7c24 100644 --- a/src/Main/CMakeLists.txt +++ b/src/Main/CMakeLists.txt @@ -75,6 +75,7 @@ target_link_libraries(fgfs ${SIMGEAR_CORE_LIBRARY_DEPENDENCIES} ${SIMGEAR_SCENE_LIBRARY_DEPENDENCIES} ${PLATFORM_LIBS} + OpenVG ) install(TARGETS fgfs RUNTIME DESTINATION bin)