]> git.mxchange.org Git - flightgear.git/commitdiff
Canvas: basic support for OpenVG (path with stroke and pattern)
authorThomas Geymayer <tomgey@gmail.com>
Mon, 4 Jun 2012 10:10:25 +0000 (12:10 +0200)
committerJames Turner <zakalawe@mac.com>
Sun, 17 Jun 2012 20:13:47 +0000 (21:13 +0100)
 - Bugfix: Don't access children by index as it's not unique

src/Canvas/CMakeLists.txt
src/Canvas/elements/element.cxx
src/Canvas/elements/group.cxx
src/Canvas/elements/group.hxx
src/Canvas/elements/path.cxx [new file with mode: 0644]
src/Canvas/elements/path.hxx [new file with mode: 0644]
src/Canvas/elements/text.cxx
src/Canvas/property_helper.hxx
src/Main/CMakeLists.txt

index 5de9945c0e10ae187b29b2aadc88546efb1ba925..eac43973545155e32132dbfda5f7918fb94e07d9 100644 (file)
@@ -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
 )
index 87ce8e6097dd575146265f745c539c38258590b8..a27ad619718494468bba9b428264d39a271cf578 100644 (file)
@@ -20,6 +20,7 @@
 #include <Canvas/property_helper.hxx>
 
 #include <osg/Drawable>
+#include <osg/Geode>
 
 #include <cassert>
 #include <cstring>
@@ -157,6 +158,10 @@ namespace canvas
     _drawable = drawable;
     assert( _drawable );
 
+    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
+    geode->addDrawable(_drawable);
+    _transform->addChild(geode);
+
     if( _attributes_used & BOUNDING_BOX )
     {
       SGPropertyNode* bb_node = _node->getChild("bounding-box", 0, true);
index 94e0836a6e8ee0378b9b641768e1060e9038d09b..67f5d58f2c716dfe484beee7c5c8f4385772956c 100644 (file)
@@ -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>& element = _children[index];
-        if( element )
-          _transform->removeChild(element->getMatrixTransform());
-        element.reset();
+        _transform->removeChild( child->second->getMatrixTransform() );
+        _children.erase(child);
       }
     }
   }
index d13bb255cea1e9de1a0c92e7f9c084210305c805..1c07847f919c993655fd93b30ba47a2f8e724909 100644 (file)
@@ -21,7 +21,7 @@
 
 #include "element.hxx"
 #include <boost/shared_ptr.hpp>
-#include <vector>
+#include <map>
 
 namespace canvas
 {
@@ -38,7 +38,8 @@ namespace canvas
       virtual void update(double dt);
 
     protected:
-      std::vector<ElementPtr> _children;
+      typedef std::map<const SGPropertyNode*, ElementPtr> 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 (file)
index 0000000..e369492
--- /dev/null
@@ -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
diff --git a/src/Canvas/elements/path.hxx b/src/Canvas/elements/path.hxx
new file mode 100644 (file)
index 0000000..a2bd8e5
--- /dev/null
@@ -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_ */
index 6f7a961d5a49be5e2eccc38e9216d3a245fb064b..dd23e921fc7ce9c7962be660430c44e6662e568b 100644 (file)
@@ -42,10 +42,6 @@ namespace canvas
     _font_size = getChildDefault<float>(_node, "character-size", 32);
     _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
     (
       "alignment",
index c62134ca1ca9d156543f2ca5615ea6c14aa878fe..8e14e554809995d368c3a60feea0d6e3c5fc029a 100644 (file)
@@ -44,6 +44,22 @@ namespace canvas
     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 parent  Parent for color channel nodes
index 92dbb7ceb3347addfea6459d620dd2df5e137309..80a7c7c24f79c44e9e6ae58da71ecdd8e341e894 100644 (file)
@@ -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)