1
0
Fork 0

Canvas: basic support for OpenVG (path with stroke and pattern)

- Bugfix: Don't access children by index as it's not unique
This commit is contained in:
Thomas Geymayer 2012-06-04 12:10:25 +02:00 committed by James Turner
parent a360653eb6
commit df768766d9
9 changed files with 372 additions and 29 deletions

View file

@ -5,6 +5,7 @@ set(SOURCES
canvas_mgr.cxx canvas_mgr.cxx
elements/element.cxx elements/element.cxx
elements/group.cxx elements/group.cxx
elements/path.cxx
elements/text.cxx elements/text.cxx
property_helper.cxx property_helper.cxx
) )
@ -14,6 +15,7 @@ set(HEADERS
canvas_mgr.hxx canvas_mgr.hxx
elements/element.hxx elements/element.hxx
elements/group.hxx elements/group.hxx
elements/path.hxx
elements/text.hxx elements/text.hxx
property_helper.hxx property_helper.hxx
) )

View file

@ -20,6 +20,7 @@
#include <Canvas/property_helper.hxx> #include <Canvas/property_helper.hxx>
#include <osg/Drawable> #include <osg/Drawable>
#include <osg/Geode>
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
@ -157,6 +158,10 @@ namespace canvas
_drawable = drawable; _drawable = drawable;
assert( _drawable ); assert( _drawable );
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
geode->addDrawable(_drawable);
_transform->addChild(geode);
if( _attributes_used & BOUNDING_BOX ) if( _attributes_used & BOUNDING_BOX )
{ {
SGPropertyNode* bb_node = _node->getChild("bounding-box", 0, true); SGPropertyNode* bb_node = _node->getChild("bounding-box", 0, true);

View file

@ -17,6 +17,7 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "group.hxx" #include "group.hxx"
#include "path.hxx"
#include "text.hxx" #include "text.hxx"
namespace canvas namespace canvas
@ -38,11 +39,10 @@ namespace canvas
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void Group::update(double dt) void Group::update(double dt)
{ {
for( size_t i = 0; i < _children.size(); ++i ) for( ChildMap::iterator child = _children.begin();
{ child != _children.end();
if( _children[i] ) ++child )
_children[i]->update(dt); child->second->update(dt);
}
Element::update(dt); Element::update(dt);
} }
@ -56,6 +56,8 @@ namespace canvas
element.reset( new Text(child) ); element.reset( new Text(child) );
else if( child->getNameString() == "group" ) else if( child->getNameString() == "group" )
element.reset( new Group(child) ); element.reset( new Group(child) );
else if( child->getNameString() == "path" )
element.reset( new Path(child) );
else else
SG_LOG SG_LOG
( (
@ -69,37 +71,29 @@ namespace canvas
// Add to osg scene graph... // Add to osg scene graph...
_transform->addChild( element->getMatrixTransform() ); _transform->addChild( element->getMatrixTransform() );
_children[ child ] = element;
// ...and build up canvas hierarchy
size_t index = child->getIndex();
if( index >= _children.size() )
_children.resize(index + 1);
_children[index] = element;
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void Group::childRemoved(SGPropertyNode* child) void Group::childRemoved(SGPropertyNode* node)
{ {
if( child->getNameString() == "text" if( node->getNameString() == "text"
|| child->getNameString() == "group" ) || 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_LOG
( (
SG_GL, SG_GL,
SG_WARN, SG_WARN,
"can't removed unknown child " << child->getDisplayName() "can't removed unknown child " << node->getDisplayName()
); );
else else
{ {
boost::shared_ptr<Element>& element = _children[index]; _transform->removeChild( child->second->getMatrixTransform() );
if( element ) _children.erase(child);
_transform->removeChild(element->getMatrixTransform());
element.reset();
} }
} }
} }

View file

@ -21,7 +21,7 @@
#include "element.hxx" #include "element.hxx"
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <vector> #include <map>
namespace canvas namespace canvas
{ {
@ -38,7 +38,8 @@ namespace canvas
virtual void update(double dt); virtual void update(double dt);
protected: protected:
std::vector<ElementPtr> _children; typedef std::map<const SGPropertyNode*, ElementPtr> ChildMap;
ChildMap _children;
virtual void childAdded(SGPropertyNode * child); virtual void childAdded(SGPropertyNode * child);
virtual void childRemoved(SGPropertyNode * child); virtual void childRemoved(SGPropertyNode * child);

View file

@ -0,0 +1,274 @@
// An OpenVG path on the canvas
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// 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 <Canvas/property_helper.hxx>
#include <vg/openvg.h>
#include <osg/Drawable>
#include <osg/BlendFunc>
#include <cassert>
namespace canvas
{
typedef std::vector<VGubyte> CmdList;
typedef std::vector<VGfloat> 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<float> dash = std::vector<float>() )
{
_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<VGfloat> _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<VGubyte, int>(_node, "cmd"),
getVectorFromChildren<VGfloat, float>(_node, "coord")
);
_attributes_dirty &= ~(CMDS | COORDS);
}
if( _attributes_dirty & STROKE )
{
_path->setStroke
(
_node->getFloatValue("stroke-width", 1),
getVectorFromChildren<VGfloat, float>(_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

View file

@ -0,0 +1,54 @@
// An OpenVG path on the canvas
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// 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<PathDrawable> _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_ */

View file

@ -42,10 +42,6 @@ namespace canvas
_font_size = getChildDefault<float>(_node, "character-size", 32); _font_size = getChildDefault<float>(_node, "character-size", 32);
_font_aspect = getChildDefault<float>(_node, "character-aspect-ratio", 1); _font_aspect = getChildDefault<float>(_node, "character-aspect-ratio", 1);
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
geode->addDrawable(_text);
_transform->addChild(geode);
_node->tie _node->tie
( (
"alignment", "alignment",

View file

@ -44,6 +44,22 @@ namespace canvas
return node; return node;
} }
/**
* Get vector of properties
*/
template<typename T, typename T_get /* = T */> // TODO use C++11 or traits
std::vector<T> getVectorFromChildren( const SGPropertyNode* parent,
const char* child_name )
{
const simgear::PropertyList& props = parent->getChildren(child_name);
std::vector<T> values( props.size() );
for( size_t i = 0; i < props.size(); ++i )
values[i] = getValue<T_get>(props[i]);
return values;
}
/** /**
* @param name Name of color node * @param name Name of color node
* @param parent Parent for color channel nodes * @param parent Parent for color channel nodes

View file

@ -75,6 +75,7 @@ target_link_libraries(fgfs
${SIMGEAR_CORE_LIBRARY_DEPENDENCIES} ${SIMGEAR_CORE_LIBRARY_DEPENDENCIES}
${SIMGEAR_SCENE_LIBRARY_DEPENDENCIES} ${SIMGEAR_SCENE_LIBRARY_DEPENDENCIES}
${PLATFORM_LIBS} ${PLATFORM_LIBS}
OpenVG
) )
install(TARGETS fgfs RUNTIME DESTINATION bin) install(TARGETS fgfs RUNTIME DESTINATION bin)