]> git.mxchange.org Git - flightgear.git/commitdiff
Basic 2D canvas implementation.
authorThomas Geymayer <tomgey@gmail.com>
Sat, 21 Apr 2012 13:31:20 +0000 (15:31 +0200)
committerJames Turner <zakalawe@mac.com>
Fri, 18 May 2012 06:05:37 +0000 (07:05 +0100)
Support text and transformations for the moment, more
features to follow in due course.

19 files changed:
docs-mini/README.canvas [new file with mode: 0644]
src/CMakeLists.txt
src/Canvas/CMakeLists.txt [new file with mode: 0644]
src/Canvas/canvas.cxx [new file with mode: 0644]
src/Canvas/canvas.hxx [new file with mode: 0644]
src/Canvas/canvas_mgr.cxx [new file with mode: 0644]
src/Canvas/canvas_mgr.hxx [new file with mode: 0644]
src/Canvas/elements/element.cxx [new file with mode: 0644]
src/Canvas/elements/element.hxx [new file with mode: 0644]
src/Canvas/elements/group.cxx [new file with mode: 0644]
src/Canvas/elements/group.hxx [new file with mode: 0644]
src/Canvas/elements/text-alignment.hxx [new file with mode: 0644]
src/Canvas/elements/text.cxx [new file with mode: 0644]
src/Canvas/elements/text.hxx [new file with mode: 0644]
src/Instrumentation/od_gauge.cxx
src/Instrumentation/od_gauge.hxx
src/Main/fg_init.cxx
src/Main/globals.cxx
src/Main/globals.hxx

diff --git a/docs-mini/README.canvas b/docs-mini/README.canvas
new file mode 100644 (file)
index 0000000..670f1d1
--- /dev/null
@@ -0,0 +1,157 @@
+Canvas - A 2D Drawing API
+=========================
+
+Author: Thomas Geymayer <admin@tomprogs.at>
+Revision: 2012/05/04
+
+Introduction
+------------
+
+With the increasing complexity of (glass) cockpits the need for a simple API to
+draw on a 2D surface without modifying the C++ core increased heavily in the
+last time. The 2D canvas is an effort to satisfy this needs. It is now possible
+to create offscreen rendertargets only by using the property tree and placing
+them on any 3D object on the aircraft by using certain filter criteria.
+
+Currently it is only possible to place text on the canvas but 2d shapes (using
+OpenVG) are going to follow.
+
+Creating a canvas
+-----------------
+
+A new canvas can be instantiated by creating a node /canvas/texture[<INDEX>]
+with at least the following children:
+
+ <size-x type="int">       The width of the underlying texture
+ <size-y type="int">       The height of the underlying texture
+
+ <view-width type="int">   The width of the canvas
+ <view-height type="int">  The height of the canvas
+
+The dimensions of the canvas are needed to be able to use textures with
+different resolutions but use the same units for rendering to the canvas.
+Therefore you can choose any texture size with the same canvas size and always
+get the same results (apart from resolution dependent artifacts).
+
+* Filtering:
+
+ Optionally you can enable mipmapping and/or multisampling (Coverage Sampling
+ Antialiasing):
+
+  <mipmapping type="bool">       Use mipmapping (default: false)
+  <coverage-samples type="int">  Coverage Samples (default: 0)
+  <color-samples type="int">     Color Samples (default: 0, always
+                                 have to be <= coverage-samples)
+Drawing
+-------
+
+Drawing to the canvas is accomplished by creating nodes as childs of the
+canvas root node. Every shape has to be a child of a <group> node. Currently
+only drawing Text is possible:
+
+* General:
+ The following parameters are used by multiple elements:
+
+ Color:
+  A color can be specified by the following subtree (NAME is replaced by
+  another name depending on the usage of the color)
+
+  <NAME>
+    <red type="float">
+    <green type="float">
+    <blue type="float">
+  </NAME>
+
+* Text:
+ Create a <text> node and configure with the following properties:
+
+ <text type="string">        The text to be displayed
+ <font type="string">        The font to be used (Searched in
+                              1. aircraft-dir/Fonts
+                              2. aircraft-dir
+                              3. $FG_DATA/Fonts
+                              4. Default osg font paths
+ <size type="float">         The font size (default: 32)
+ <tf>               A 3x3 transformation matrix specified by 6 values
+                    (child elements <a>, ..., <f>) See
+                    http://www.w3.org/TR/SVG/coords.html#TransformMatrixDefined
+                    for details.
+                    You can also use shortcuts and use an alternative to
+                    specifying six values:
+                     - Translation: <tx>, <ty> (ty, ty default to 0)
+                     - Totation: <rot>
+                     - Scale: <sx>, <sy> (sx is required, sy defaults to sx)
+ <alginment type="string">   Text alignment (default: "left-baseline") One of:
+
+                              "left-top"
+                              "left-center"
+                              "left-bottom"
+                               
+                              "center-top"
+                              "center-center"
+                              "center-bottom"
+                               
+                              "right-top"
+                              "right-center"
+                              "right-bottom"
+                               
+                              "left-baseline"
+                              "center-baseline"
+                              "right-baseline"
+                               
+                              "left-bottom-baseline"
+                              "center-bottom-baseline"
+                              "right-bottom-baseline"
+ <draw-mode type="int">      A bitwise combination of the following values
+                             1 (Text - default)
+                             2 (Boundingbox)
+                             4 (Filled boundingbox)
+                             8 (Alignment -> Draw a cross at the position
+                               of the text)
+ <padding type="float">      Padding between for the boundingbox (default: 0)
+ <color>                     Text color
+ <color-fill>                Fill color (for the bounding box)
+
+Placement
+---------
+
+To place the canvas into the scene one ore more <placement> elements can be
+added to the texture node. By setting at least on of the following nodes
+the objects where the canvas texture should be placed on are selected:
+
+ <texture type="string">  Match objects with the given texture assigned
+ <node type="string">     Match objects with the given name
+ <parent type="string">   Match objects with a parent matching the given name
+                          (Not necessarily the direct parent)
+
+Example
+-------
+
+<canvas>
+  <texture>
+    <size-x type="int">384</size-x>
+    <size-y type="int">512</size-y>
+    <view-width type="int">768</view-width>
+    <view-height type="int">1024</view-height>
+    <mipmapping type="bool">false</mipmapping>
+    <coverage-samples type="int">0</coverage-samples>
+    <color-samples type="int">0</color-samples>
+    <group>
+      <text>
+        <text type="string">TEST MESSAGE</text>
+        <font type="string">helvetica_bold.txf</font>
+        <tf>
+          <!-- Translate (18|50) -->
+          <tx>18</tx>
+          <ty>50</ty>
+        </tf>
+      </text>
+    </group>
+    <placement>
+      <!-- Place on objects with the texture EICAS.png
+           and a parent called HDD 1 -->
+      <texture type="string">EICAS.png</texture>
+      <parent type="string">HDD 1</parent>
+    </placement>
+  </texture>
+</canvas>
index ab32ec186e170b4626bda96f2792d63e5c7d1ceb..b09b6a348cd5bfb728588a4eb7c69a461f09291b 100644 (file)
@@ -8,6 +8,7 @@ foreach( mylibfolder
                Aircraft
                ATC
                ATCDCL
+               Canvas
                Radio
                Autopilot
                Cockpit
diff --git a/src/Canvas/CMakeLists.txt b/src/Canvas/CMakeLists.txt
new file mode 100644 (file)
index 0000000..1dde0d2
--- /dev/null
@@ -0,0 +1,19 @@
+include(FlightGearComponent)
+
+set(SOURCES
+  canvas.cxx
+  canvas_mgr.cxx
+  elements/element.cxx
+  elements/group.cxx
+  elements/text.cxx
+)
+
+set(HEADERS
+  canvas.hxx
+  canvas_mgr.hxx
+  elements/element.hxx
+  elements/group.hxx
+  elements/text.hxx
+)
+    
+flightgear_component(Canvas "${SOURCES}" "${HEADERS}")
diff --git a/src/Canvas/canvas.cxx b/src/Canvas/canvas.cxx
new file mode 100644 (file)
index 0000000..31e8265
--- /dev/null
@@ -0,0 +1,430 @@
+// The canvas for rendering with the 2d api
+//
+// 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 "canvas.hxx"
+#include "elements/group.hxx"
+
+#include <osg/Camera>
+#include <osg/Geode>
+#include <osgText/Text>
+
+#include <iostream>
+
+//#include <Main/globals.hxx>
+//#include <Viewer/renderer.hxx>
+
+//------------------------------------------------------------------------------
+/**
+ * Callback used to disable/enable rendering to the texture if it is not
+ * visible
+ */
+class CameraCullCallback:
+  public osg::NodeCallback
+{
+  public:
+
+    CameraCullCallback():
+      _render( true )
+    {}
+
+    /**
+     * Enable rendering for the next frame
+     */
+    void enableRendering()
+    {
+      _render = true;
+    }
+
+  private:
+
+    bool _render;
+
+    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
+    {
+      if( _render )
+      {
+        traverse(node, nv);
+        _render = false;
+      }
+    }
+};
+
+/**
+ * This callback is installed on every placement of the canvas in the scene to
+ * only render the canvas if at least one placement is visible
+ */
+class PlacementCullCallback:
+  public osg::NodeCallback
+{
+  public:
+
+    PlacementCullCallback(Canvas* canvas, CameraCullCallback* camera_cull):
+      _canvas( canvas ),
+      _camera_cull( camera_cull )
+    {}
+
+  private:
+
+    Canvas *_canvas;
+    CameraCullCallback *_camera_cull;
+
+    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
+    {
+      _camera_cull->enableRendering();
+      traverse(node, nv);
+    }
+};
+
+//------------------------------------------------------------------------------
+Canvas::Canvas():
+  _size_x(-1),
+  _size_y(-1),
+  _view_width(-1),
+  _view_height(-1),
+  _status(0),
+  _node(0),
+  _sampling_dirty(false)
+{
+  setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
+
+  CameraCullCallback *camera_callback = new CameraCullCallback;
+  _camera_callback = camera_callback;
+  _cull_callback = new PlacementCullCallback(this, camera_callback);
+}
+
+//------------------------------------------------------------------------------
+Canvas::~Canvas()
+{
+
+}
+
+//------------------------------------------------------------------------------
+int Canvas::getStatus() const
+{
+  return _status;
+}
+
+//------------------------------------------------------------------------------
+void Canvas::reset(SGPropertyNode* node)
+{
+  if( _node )
+  {
+    _node->untie("size[0]");
+    _node->untie("size[1]");
+    _node->untie("view[0]");
+    _node->untie("view[1]");
+    _node->untie("status");
+    _node->untie("status-msg");
+    _node->removeChangeListener(this);
+    _node = 0;
+  }
+
+  setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
+
+  if( node )
+  {
+    _node = node;
+    _node->tie
+    (
+      "size[0]",
+      SGRawValueMethods<Canvas, int>( *this, &Canvas::getSizeX,
+                                             &Canvas::setSizeX )
+    );
+    _node->tie
+    (
+      "size[1]",
+      SGRawValueMethods<Canvas, int>( *this, &Canvas::getSizeY,
+                                             &Canvas::setSizeY )
+    );
+    _node->tie
+    (
+      "view[0]",
+      SGRawValueMethods<Canvas, int>( *this, &Canvas::getViewWidth,
+                                             &Canvas::setViewWidth )
+    );
+    _node->tie
+    (
+      "view[1]",
+      SGRawValueMethods<Canvas, int>( *this, &Canvas::getViewHeight,
+                                             &Canvas::setViewHeight )
+    );
+    _node->tie
+    (
+      "status",
+      SGRawValueMethods<Canvas, int>(*this, &Canvas::getStatus)
+    );
+    _node->tie
+    (
+      "status-msg",
+      SGRawValueMethods<Canvas, const char*>(*this, &Canvas::getStatusMsg)
+    );
+    _node->addChangeListener(this);
+  }
+}
+
+//------------------------------------------------------------------------------
+void Canvas::update(double delta_time_sec)
+{
+  if( !_texture.serviceable() )
+  {
+    if( _status != STATUS_OK )
+      return;
+
+    _texture.setSize(_size_x, _size_y);
+    _texture.useImageCoords(true);
+    _texture.useStencil(true);
+    _texture.allocRT(_camera_callback);
+    _texture.getCamera()->setClearColor(osg::Vec4(0.0f, 0.0f, 0.0f , 1.0f));
+
+    for( size_t i = 0; i < _groups.size(); ++i )
+      _texture.getCamera()->addChild( _groups[i]->getMatrixTransform() );
+
+    if( _texture.serviceable() )
+    {
+      setStatusFlags(STATUS_OK);
+    }
+    else
+    {
+      setStatusFlags(CREATE_FAILED);
+      return;
+    }
+  }
+
+  for( size_t i = 0; i < _groups.size(); ++i )
+    _groups[i]->update(delta_time_sec);
+
+  if( _sampling_dirty )
+  {
+    _texture.setSampling(
+      _node->getBoolValue("mipmapping"),
+      _node->getIntValue("coverage-samples"),
+      _node->getIntValue("color-samples")
+    );
+    _sampling_dirty = false;
+  }
+
+  while( !_dirty_placements.empty() )
+  {
+    SGPropertyNode *node = _dirty_placements.back();
+    _dirty_placements.pop_back();
+
+    if( node->getIndex() >= static_cast<int>(_placements.size()) )
+      // New placement
+      _placements.resize(node->getIndex() + 1);
+    else
+      // Remove maybe already existing placements
+      clearPlacements(node->getIndex());
+
+    // add new placements
+    _placements[node->getIndex()] = _texture.set_texture(
+      node,
+      _texture.getTexture(),
+      _cull_callback
+    );
+  }
+}
+
+//------------------------------------------------------------------------------
+void Canvas::setSizeX(int sx)
+{
+  if( _size_x == sx )
+    return;
+  _size_x = sx;
+
+  // TODO resize if texture already allocated
+
+  if( _size_x <= 0 )
+    setStatusFlags(MISSING_SIZE_X);
+  else
+    setStatusFlags(MISSING_SIZE_X, false);
+
+  // reset flag to allow creation with new size
+  setStatusFlags(CREATE_FAILED, false);
+}
+
+//------------------------------------------------------------------------------
+int Canvas::getSizeX() const
+{
+  return _size_x;
+}
+
+//------------------------------------------------------------------------------
+void Canvas::setSizeY(int sy)
+{
+  if( _size_y == sy )
+    return;
+  _size_y = sy;
+
+  // TODO resize if texture already allocated
+
+  if( _size_y <= 0 )
+    setStatusFlags(MISSING_SIZE_Y);
+  else
+    setStatusFlags(MISSING_SIZE_Y, false);
+
+  // reset flag to allow creation with new size
+  setStatusFlags(CREATE_FAILED, false);
+}
+
+//------------------------------------------------------------------------------
+int Canvas::getSizeY() const
+{
+  return _size_y;
+}
+
+//------------------------------------------------------------------------------
+void Canvas::setViewWidth(int w)
+{
+  if( _view_width == w )
+    return;
+  _view_width = w;
+
+  _texture.setViewSize(_view_width, _view_height);
+}
+
+//------------------------------------------------------------------------------
+int Canvas::getViewWidth() const
+{
+  return _view_width;
+}
+
+//------------------------------------------------------------------------------
+void Canvas::setViewHeight(int h)
+{
+  if( _view_height == h )
+    return;
+  _view_height = h;
+
+  _texture.setViewSize(_view_width, _view_height);
+}
+
+//------------------------------------------------------------------------------
+int Canvas::getViewHeight() const
+{
+  return _view_height;
+}
+
+//------------------------------------------------------------------------------
+const char* Canvas::getStatusMsg() const
+{
+  return _status_msg.c_str();
+}
+
+//------------------------------------------------------------------------------
+void Canvas::childAdded( SGPropertyNode * parent,
+                         SGPropertyNode * child )
+{
+  if( parent != _node )
+    return;
+
+  if( child->getNameString() == "group" )
+  {
+    _groups.push_back
+    (
+      boost::shared_ptr<canvas::Group>(new canvas::Group(child))
+    );
+    if( _texture.serviceable() )
+      _texture.getCamera()->addChild( _groups.back()->getMatrixTransform() );
+  }
+  else if( child->getNameString() == "placement" )
+  {
+    _dirty_placements.push_back(child);
+  }
+//  else
+//    std::cout << "Canvas::childAdded: " << child->getPath() << std::endl;
+}
+
+//------------------------------------------------------------------------------
+void Canvas::childRemoved( SGPropertyNode * parent,
+                           SGPropertyNode * child )
+{
+  if( parent != _node )
+    return;
+
+  if( child->getNameString() == "placement" )
+    clearPlacements(child->getIndex());
+  else
+    std::cout << "Canvas::childRemoved: " << child->getPath() << std::endl;
+}
+
+//----------------------------------------------------------------------------
+void Canvas::valueChanged(SGPropertyNode * node)
+{
+  if( node->getParent()->getParent() == _node
+      && node->getParent()->getNameString() == "placement" )
+  {
+    // prevent double updates...
+    for( size_t i = 0; i < _dirty_placements.size(); ++i )
+    {
+      if( node->getParent() == _dirty_placements[i] )
+        return;
+    }
+
+    _dirty_placements.push_back(node->getParent());
+  }
+  else if( node->getParent() == _node )
+  {
+    if(    node->getNameString() == "mipmapping"
+        || node->getNameString() == "coverage-samples"
+        || node->getNameString() == "color-samples" )
+      _sampling_dirty = true;
+  }
+}
+
+//------------------------------------------------------------------------------
+void Canvas::setStatusFlags(unsigned int flags, bool set)
+{
+  if( set )
+    _status |= flags;
+  else
+    _status &= ~flags;
+
+  if( (_status & MISSING_SIZE_X) && (_status & MISSING_SIZE_Y) )
+    _status_msg = "Missing size";
+  else if( _status & MISSING_SIZE_X )
+    _status_msg = "Missing size-x";
+  else if( _status & MISSING_SIZE_Y )
+    _status_msg = "Missing size-y";
+  else if( _status & CREATE_FAILED )
+    _status_msg = "Creating render target failed";
+  else if( _status == STATUS_OK && !_texture.serviceable() )
+    _status_msg = "Creation pending...";
+  else
+    _status_msg = "Ok";
+}
+
+//------------------------------------------------------------------------------
+void Canvas::clearPlacements(int index)
+{
+  Placements& placements = _placements.at(index);
+  while( !placements.empty() )
+  {
+    osg::ref_ptr<osg::Group> group = placements.back();
+    placements.pop_back();
+
+    assert( group->getNumParents() == 1 );
+    assert( group->getNumChildren() == 1 );
+
+    osg::Group *parent = group->getParent(0);
+    osg::Node *child = group->getChild(0);
+
+    parent->addChild(child);
+    group->removeChild(child);
+    parent->removeChild(group);
+  }
+}
diff --git a/src/Canvas/canvas.hxx b/src/Canvas/canvas.hxx
new file mode 100644 (file)
index 0000000..1769b8e
--- /dev/null
@@ -0,0 +1,101 @@
+// The canvas for rendering with the 2d api
+//
+// 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_HXX_
+#define CANVAS_HXX_
+
+#include <Instrumentation/od_gauge.hxx>
+#include <simgear/props/props.hxx>
+#include <osg/NodeCallback>
+
+#include <boost/shared_ptr.hpp>
+#include <string>
+
+namespace canvas
+{
+  class Group;
+}
+
+class Canvas:
+  public SGPropertyChangeListener
+{
+  public:
+
+    enum StatusFlags
+    {
+      STATUS_OK,
+      MISSING_SIZE_X = 0x0001,
+      MISSING_SIZE_Y = 0x0002,
+      CREATE_FAILED  = 0x0004
+    };
+
+    Canvas();
+    virtual ~Canvas();
+
+    void reset(SGPropertyNode* node);
+    void update(double delta_time_sec);
+
+    void setSizeX(int sx);
+    int getSizeX() const;
+
+    void setSizeY(int sy);
+    int getSizeY() const;
+
+    void setViewWidth(int w);
+    int getViewWidth() const;
+
+    void setViewHeight(int h);
+    int getViewHeight() const;
+
+    int getStatus() const;
+    const char* getStatusMsg() const;
+
+    virtual void childAdded( SGPropertyNode * parent,
+                             SGPropertyNode * child );
+    virtual void childRemoved( SGPropertyNode * parent,
+                               SGPropertyNode * child );
+    virtual void valueChanged (SGPropertyNode * node);
+
+  private:
+
+    int _size_x,
+        _size_y,
+        _view_width,
+        _view_height;
+
+    int         _status;
+    std::string _status_msg;
+
+    FGODGauge       _texture;
+    SGPropertyNode *_node;
+
+    bool _sampling_dirty;
+
+    osg::ref_ptr<osg::NodeCallback> _camera_callback;
+    osg::ref_ptr<osg::NodeCallback> _cull_callback;
+    std::vector<SGPropertyNode*> _dirty_placements;
+    std::vector<Placements> _placements;
+
+    // TODO replace auto_ptr with unique_ptr as soon as C++11 is used!
+    std::vector<boost::shared_ptr<canvas::Group> > _groups;
+
+    void setStatusFlags(unsigned int flags, bool set = true);
+    void clearPlacements(int index);
+};
+
+#endif /* CANVAS_HXX_ */
diff --git a/src/Canvas/canvas_mgr.cxx b/src/Canvas/canvas_mgr.cxx
new file mode 100644 (file)
index 0000000..eab30b5
--- /dev/null
@@ -0,0 +1,144 @@
+// Canvas with 2D rendering api
+//
+// 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 "canvas_mgr.hxx"
+#include "canvas.hxx"
+
+#include <Main/fg_props.hxx>
+
+#include <osg/Camera>
+#include <osg/Texture2D>
+
+#include <stdexcept>
+#include <string>
+
+//------------------------------------------------------------------------------
+CanvasMgr::CanvasMgr():
+  _props( fgGetNode("/canvas", true) )
+{
+
+}
+
+//------------------------------------------------------------------------------
+CanvasMgr::~CanvasMgr()
+{
+
+}
+
+//------------------------------------------------------------------------------
+void CanvasMgr::init()
+{
+  _props->addChangeListener(this);
+  triggerChangeRecursive(_props);
+}
+
+//------------------------------------------------------------------------------
+void CanvasMgr::reinit()
+{
+
+}
+
+//------------------------------------------------------------------------------
+void CanvasMgr::shutdown()
+{
+  _props->removeChangeListener(this);
+}
+
+//------------------------------------------------------------------------------
+void CanvasMgr::bind()
+{
+}
+
+//------------------------------------------------------------------------------
+void CanvasMgr::unbind()
+{
+}
+
+//------------------------------------------------------------------------------
+void CanvasMgr::update(double delta_time_sec)
+{
+ for( size_t i = 0; i < _canvases.size(); ++i )
+   _canvases[i].update(delta_time_sec);
+}
+
+//------------------------------------------------------------------------------
+void CanvasMgr::childAdded( SGPropertyNode * parent,
+                            SGPropertyNode * child )
+{
+  if( parent != _props )
+    return;
+
+  if( child->getNameString() == "texture" )
+    textureAdded(child);
+  else
+    std::cout << "CanvasMgr::childAdded: " << child->getPath() << std::endl;
+}
+
+//------------------------------------------------------------------------------
+void CanvasMgr::childRemoved( SGPropertyNode * parent,
+                              SGPropertyNode * child )
+{
+  if( parent != _props )
+    return;
+
+  std::cout << "CanvasMgr::childRemoved: " << child->getPath() << std::endl;
+}
+
+//------------------------------------------------------------------------------
+void CanvasMgr::textureAdded(SGPropertyNode* node)
+{
+  size_t index = node->getIndex();
+
+  if( index >= _canvases.size() )
+  {
+    if( index > _canvases.size() )
+      SG_LOG(SG_GL, SG_WARN, "Skipping unused texture slot(s)!");
+    SG_LOG(SG_GL, SG_INFO, "Add new texture[" << index << "]");
+
+    _canvases.resize(index + 1);
+    _canvases[index];
+  }
+  else
+  {
+    SG_LOG(SG_GL, SG_WARN, "texture[" << index << "] already exists!");
+  }
+
+  _canvases[index].reset(node);
+}
+
+//------------------------------------------------------------------------------
+void CanvasMgr::triggerChangeRecursive(SGPropertyNode* node)
+{
+  node->getParent()->fireChildAdded(node);
+
+  if( node->nChildren() == 0 && node->getType() != simgear::props::NONE )
+    return node->fireValueChanged();
+
+  for( int i = 0; i < node->nChildren(); ++i )
+    triggerChangeRecursive( node->getChild(i) );
+}
+
+//------------------------------------------------------------------------------
+template<class T>
+T CanvasMgr::getParam(const SGPropertyNode* node, const char* prop)
+{
+  const SGPropertyNode* child = node->getChild(prop);
+  if( !child )
+    throw std::runtime_error(std::string("Missing property ") + prop);
+  return getValue<T>(child);
+}
diff --git a/src/Canvas/canvas_mgr.hxx b/src/Canvas/canvas_mgr.hxx
new file mode 100644 (file)
index 0000000..b3b5bac
--- /dev/null
@@ -0,0 +1,76 @@
+// Canvas with 2D rendering api
+//
+// 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_MGR_H_
+#define CANVAS_MGR_H_
+
+#include <simgear/props/props.hxx>
+#include <simgear/structure/subsystem_mgr.hxx>
+
+#include <boost/shared_ptr.hpp>
+#include <vector>
+
+class Canvas;
+
+class CanvasMgr:
+  public SGSubsystem,
+  public SGPropertyChangeListener
+{
+  public:
+    CanvasMgr();
+    virtual ~CanvasMgr();
+
+    virtual void init();
+    virtual void reinit();
+    virtual void shutdown();
+
+    virtual void bind();
+    virtual void unbind();
+
+    virtual void update(double delta_time_sec);
+
+    virtual void childAdded( SGPropertyNode * parent,
+                             SGPropertyNode * child );
+    virtual void childRemoved( SGPropertyNode * parent,
+                               SGPropertyNode * child );
+
+  private:
+
+    /** Root node for everything concerning the canvas system */
+    SGPropertyNode_ptr _props;
+
+    /** The actual canvases */
+    std::vector<Canvas> _canvases;
+
+    void textureAdded(SGPropertyNode* node);
+
+    /**
+     * Trigger a childAdded and valueChanged event for every child of node
+     * (Unlimited depth) and node itself.
+     */
+    void triggerChangeRecursive(SGPropertyNode* node);
+
+    /**
+     * Get the value of a property or throw an exception if it doesn't exist.
+     */
+    template<class T>
+    T getParam(const SGPropertyNode* node, const char* prop);
+
+};
+
+#endif /* CANVAS_MGR_H_ */
diff --git a/src/Canvas/elements/element.cxx b/src/Canvas/elements/element.cxx
new file mode 100644 (file)
index 0000000..119f109
--- /dev/null
@@ -0,0 +1,266 @@
+// Interface for 2D canvas element
+//
+// 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 "element.hxx"
+#include <cassert>
+
+#include <osg/Drawable>
+
+namespace canvas
+{
+  const std::string NAME_TRANSFORM = "tf";
+  const std::string NAME_COLOR = "color";
+  const std::string NAME_COLOR_FILL = "color-fill";
+
+  //----------------------------------------------------------------------------
+  Element::~Element()
+  {
+
+  }
+
+  //----------------------------------------------------------------------------
+  SGPropertyNode* Element::getPropertyNode()
+  {
+    return _node;
+  }
+
+  //----------------------------------------------------------------------------
+  void Element::update(double dt)
+  {
+    if( _transform_dirty )
+    {
+      osg::Matrix m;
+      for( size_t i = 0; i < _transform_types.size(); ++i )
+      {
+        // Skip unused indizes...
+        if( _transform_types[i] == TT_NONE )
+          continue;
+
+        SGPropertyNode* tf_node = _node->getChild("tf", i, true);
+
+        // Build up the matrix representation of the current transform node
+        osg::Matrix tf;
+        switch( _transform_types[i] )
+        {
+          case TT_MATRIX:
+            tf = osg::Matrix( tf_node->getDoubleValue("a", 1),
+                              tf_node->getDoubleValue("b", 0), 0, 0,
+
+                              tf_node->getDoubleValue("c", 0),
+                              tf_node->getDoubleValue("d", 1), 0, 0,
+
+                              0,    0,    1, 0,
+                              tf_node->getDoubleValue("e", 0),
+                              tf_node->getDoubleValue("f", 0), 0, 1 );
+            break;
+          case TT_TRANSLATE:
+            tf.makeTranslate( osg::Vec3f( tf_node->getDoubleValue("tx", 0),
+                                          tf_node->getDoubleValue("ty", 0),
+                                          0 ) );
+            break;
+          case TT_ROTATE:
+            tf.makeRotate( tf_node->getDoubleValue("rot", 0), 0, 0, 1 );
+            break;
+          case TT_SCALE:
+          {
+            float sx = tf_node->getDoubleValue("sx", 1);
+            // sy defaults to sx...
+            tf.makeScale( sx, tf_node->getDoubleValue("sy", sx), 1 );
+            break;
+          }
+          default:
+            break;
+        }
+        m.postMult( tf );
+      }
+      _transform->setMatrix(m);
+      _transform_dirty = false;
+    }
+
+    if( _attributes_dirty & COLOR )
+    {
+      colorChanged( osg::Vec4( _color[0]->getFloatValue(),
+                               _color[1]->getFloatValue(),
+                               _color[2]->getFloatValue(),
+                               1 ) );
+      _attributes_dirty &= ~COLOR;
+    }
+
+    if( _attributes_dirty & COLOR_FILL )
+    {
+      colorFillChanged( osg::Vec4( _color_fill[0]->getFloatValue(),
+                                   _color_fill[1]->getFloatValue(),
+                                   _color_fill[2]->getFloatValue(),
+                                   1 ) );
+      _attributes_dirty &= ~COLOR_FILL;
+    }
+
+    if( _drawable && (_attributes_dirty & BOUNDING_BOX) )
+    {
+      _bounding_box[0]->setFloatValue(_drawable->getBound()._min.x());
+      _bounding_box[1]->setFloatValue(_drawable->getBound()._min.y());
+      _bounding_box[2]->setFloatValue(_drawable->getBound()._max.x());
+      _bounding_box[3]->setFloatValue(_drawable->getBound()._max.y());
+
+      _attributes_dirty &= ~BOUNDING_BOX;
+    }
+  }
+
+  //----------------------------------------------------------------------------
+  osg::ref_ptr<osg::MatrixTransform> Element::getMatrixTransform()
+  {
+    return _transform;
+  }
+
+  //----------------------------------------------------------------------------
+  Element::Element(SGPropertyNode* node, uint32_t attributes_used):
+    _node( node ),
+    _drawable( 0 ),
+    _attributes_used( attributes_used ),
+    _attributes_dirty( 0 ),
+    _transform_dirty( false ),
+    _transform( new osg::MatrixTransform )
+  {
+    assert( _node );
+    _node->addChangeListener(this);
+
+    if( _attributes_used & COLOR )
+      linkColorNodes("color", _color, osg::Vec4f(0,1,0,1));
+    if( _attributes_used & COLOR_FILL )
+      linkColorNodes("color-fill", _color_fill);
+    if( _attributes_used & BOUNDING_BOX )
+    {
+      SGPropertyNode* bb = _node->getChild("bounding-box", 0, true);
+      _bounding_box[0] = bb->getChild("x-min", 0, true);
+      _bounding_box[1] = bb->getChild("y-min", 0, true);
+      _bounding_box[2] = bb->getChild("x-max", 0, true);
+      _bounding_box[3] = bb->getChild("y-max", 0, true);
+    }
+
+    SG_LOG
+    (
+      SG_GL,
+      SG_DEBUG,
+      "New canvas element " << node->getPath()
+    );
+  }
+
+  //----------------------------------------------------------------------------
+  void Element::linkColorNodes( const char* name,
+                                SGPropertyNode** nodes,
+                                const osg::Vec4& def )
+  {
+    // Don't tie to allow the usage of aliases
+    SGPropertyNode* color = _node->getChild(name, 0, true);
+
+    static const char* color_names[] = {"red", "green", "blue"};
+    for( size_t i = 0; i < sizeof(color_names)/sizeof(color_names[0]); ++i )
+    {
+      color->setFloatValue
+      (
+        color_names[i],
+        color->getFloatValue(color_names[i], def[i])
+      );
+      nodes[i] = color->getChild(color_names[i]);
+    }
+  }
+
+  //----------------------------------------------------------------------------
+  void Element::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
+  {
+    if( parent == _node )
+    {
+      if( child->getNameString() == NAME_TRANSFORM )
+      {
+        if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
+          _transform_types.resize( child->getIndex() + 1 );
+
+        _transform_types[ child->getIndex() ] = TT_NONE;
+        _transform_dirty = true;
+      }
+      else
+        childAdded(child);
+    }
+    else if(    parent->getParent() == _node
+             && parent->getNameString() == NAME_TRANSFORM )
+    {
+      assert(parent->getIndex() < static_cast<int>(_transform_types.size()));
+
+      const std::string& name = child->getNameString();
+
+      TransformType& type = _transform_types[parent->getIndex()];
+
+      if(    name == "a" || name == "b" || name == "c"
+          || name == "d" || name == "e" || name == "f" )
+        type = TT_MATRIX;
+      else if( name == "tx" || name == "ty" )
+        type = TT_TRANSLATE;
+      else if( name == "rot" )
+        type = TT_ROTATE;
+      else if( name == "sx" || name == "sy" )
+        type = TT_SCALE;
+      else
+        SG_LOG
+        (
+          SG_GL,
+          SG_WARN,
+          "Unknown transform element " << child->getPath()
+        );
+
+      _transform_dirty = true;
+    }
+  }
+
+  //----------------------------------------------------------------------------
+  void Element::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
+  {
+    if( parent != _node )
+      return;
+
+    if( child->getNameString() == NAME_TRANSFORM )
+    {
+      assert(child->getIndex() < static_cast<int>(_transform_types.size()));
+      _transform_types[ child->getIndex() ] = TT_NONE;
+
+      while( !_transform_types.empty() && _transform_types.back() == TT_NONE )
+        _transform_types.pop_back();
+
+      _transform_dirty = true;
+    }
+    else
+      childRemoved(child);
+  }
+
+  //----------------------------------------------------------------------------
+  void Element::valueChanged(SGPropertyNode* child)
+  {
+    SGPropertyNode *parent = child->getParent();
+    if( parent->getParent() == _node )
+    {
+      if( parent->getNameString() == NAME_TRANSFORM )
+        _transform_dirty = true;
+      else if( parent->getNameString() == NAME_COLOR )
+        _attributes_dirty |= COLOR;
+      else if( parent->getNameString() == NAME_COLOR_FILL )
+        _attributes_dirty |= COLOR_FILL;
+    }
+    else
+      childChanged(child);
+  }
+
+} // namespace canvas
diff --git a/src/Canvas/elements/element.hxx b/src/Canvas/elements/element.hxx
new file mode 100644 (file)
index 0000000..2588f9c
--- /dev/null
@@ -0,0 +1,106 @@
+// Interface for 2D canvas element
+//
+// 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_ELEMENT_HXX_
+#define CANVAS_ELEMENT_HXX_
+
+#include <simgear/props/props.hxx>
+#include <osg/MatrixTransform>
+
+namespace osg
+{
+  class Drawable;
+}
+
+namespace canvas
+{
+
+  class Element:
+    private SGPropertyChangeListener
+  {
+    public:
+      virtual ~Element() = 0;
+      SGPropertyNode* getPropertyNode();
+
+      /**
+       * Called every frame to update internal state
+       *
+       * @param dt  Frame time in seconds
+       */
+      virtual void update(double dt);
+
+      osg::ref_ptr<osg::MatrixTransform> getMatrixTransform();
+
+    protected:
+
+      enum Attributes
+      {
+        COLOR           = 0x0001,
+        COLOR_FILL      = 0x0002,
+        BOUNDING_BOX    = 0x0004
+      };
+
+      enum TransformType
+      {
+        TT_NONE,
+        TT_MATRIX,
+        TT_TRANSLATE,
+        TT_ROTATE,
+        TT_SCALE
+      };
+
+      SGPropertyNode *_node;
+      osg::Drawable  *_drawable;
+
+      uint32_t _attributes_used;
+      uint32_t _attributes_dirty;
+
+      bool _transform_dirty;
+      osg::ref_ptr<osg::MatrixTransform>    _transform;
+      std::vector<TransformType>            _transform_types;
+
+      SGPropertyNode    *_bounding_box[4]; ///<! x-min, y-min, x-max, y-max
+      SGPropertyNode    *_color[3];
+      SGPropertyNode    *_color_fill[3];
+
+      Element(SGPropertyNode* node, uint32_t attributes_used = 0);
+
+      virtual void childAdded(SGPropertyNode * child)  {}
+      virtual void childRemoved(SGPropertyNode * child){}
+      virtual void childChanged(SGPropertyNode * child){}
+
+      virtual void colorChanged(const osg::Vec4& color)  {}
+      virtual void colorFillChanged(const osg::Vec4& color){}
+
+      void linkColorNodes( const char* name,
+                           SGPropertyNode** nodes,
+                           const osg::Vec4& def = osg::Vec4(1,1,0,1) );
+
+    private:
+      Element(const Element&);// = delete
+
+      virtual void childAdded( SGPropertyNode * parent,
+                               SGPropertyNode * child );
+      virtual void childRemoved( SGPropertyNode * parent,
+                                 SGPropertyNode * child );
+      virtual void valueChanged(SGPropertyNode * child);
+  };
+
+}  // namespace canvas
+
+#endif /* CANVAS_ELEMENT_HXX_ */
diff --git a/src/Canvas/elements/group.cxx b/src/Canvas/elements/group.cxx
new file mode 100644 (file)
index 0000000..44b7166
--- /dev/null
@@ -0,0 +1,65 @@
+// A group of 2D canvas elements
+//
+// 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 "group.hxx"
+#include "text.hxx"
+
+namespace canvas
+{
+
+  //----------------------------------------------------------------------------
+  Group::Group(SGPropertyNode* node):
+    Element(node)
+  {
+
+  }
+
+  //----------------------------------------------------------------------------
+  Group::~Group()
+  {
+
+  }
+
+  //----------------------------------------------------------------------------
+  void Group::update(double dt)
+  {
+    for( size_t i = 0; i < _children.size(); ++i )
+      _children[i]->update(dt);
+
+    Element::update(dt);
+  }
+
+  //----------------------------------------------------------------------------
+  void Group::childAdded(SGPropertyNode* child)
+  {
+    if( child->getNameString() == "text" )
+    {
+      _children.push_back( boost::shared_ptr<Element>(new Text(child)) );
+      _transform->addChild( _children.back()->getMatrixTransform() );
+    }
+    else
+      std::cout << "New unknown child: " << child->getDisplayName() << std::endl;
+  }
+
+  //----------------------------------------------------------------------------
+  void Group::childRemoved(SGPropertyNode* child)
+  {
+
+  }
+
+} // namespace canvas
diff --git a/src/Canvas/elements/group.hxx b/src/Canvas/elements/group.hxx
new file mode 100644 (file)
index 0000000..ccfea07
--- /dev/null
@@ -0,0 +1,47 @@
+// A group of 2D canvas elements
+//
+// 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_GROUP_HXX_
+#define CANVAS_GROUP_HXX_
+
+#include "element.hxx"
+#include <boost/shared_ptr.hpp>
+#include <vector>
+
+namespace canvas
+{
+
+  class Group:
+    public Element
+  {
+    public:
+      Group(SGPropertyNode* node);
+      virtual ~Group();
+
+      virtual void update(double dt);
+
+    protected:
+      std::vector<boost::shared_ptr<Element> >  _children;
+
+      virtual void childAdded(SGPropertyNode * child);
+      virtual void childRemoved(SGPropertyNode * child);
+  };
+
+}  // namespace canvas
+
+#endif /* CANVAS_GROUP_HXX_ */
diff --git a/src/Canvas/elements/text-alignment.hxx b/src/Canvas/elements/text-alignment.hxx
new file mode 100644 (file)
index 0000000..0c82e7d
--- /dev/null
@@ -0,0 +1,41 @@
+// Mapping between strings and osg text alignment flags
+//
+// 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 ENUM_MAPPING
+# error "Only include with ENUM_MAPPING defined!"
+#endif
+
+ENUM_MAPPING(LEFT_TOP,      "left-top")
+ENUM_MAPPING(LEFT_CENTER,   "left-center")
+ENUM_MAPPING(LEFT_BOTTOM,   "left-bottom")
+
+ENUM_MAPPING(CENTER_TOP,    "center-top")
+ENUM_MAPPING(CENTER_CENTER, "center-center")
+ENUM_MAPPING(CENTER_BOTTOM, "center-bottom")
+
+ENUM_MAPPING(RIGHT_TOP,     "right-top")
+ENUM_MAPPING(RIGHT_CENTER,  "right-center")
+ENUM_MAPPING(RIGHT_BOTTOM,  "right-bottom")
+
+ENUM_MAPPING(LEFT_BASE_LINE,    "left-baseline")
+ENUM_MAPPING(CENTER_BASE_LINE,  "center-baseline")
+ENUM_MAPPING(RIGHT_BASE_LINE,   "right-baseline")
+
+ENUM_MAPPING(LEFT_BOTTOM_BASE_LINE,     "left-bottom-baseline")
+ENUM_MAPPING(CENTER_BOTTOM_BASE_LINE,   "center-bottom-baseline")
+ENUM_MAPPING(RIGHT_BOTTOM_BASE_LINE,    "right-bottom-baseline")
diff --git a/src/Canvas/elements/text.cxx b/src/Canvas/elements/text.cxx
new file mode 100644 (file)
index 0000000..af27cda
--- /dev/null
@@ -0,0 +1,184 @@
+// A text 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 "text.hxx"
+#include <osgText/Text>
+
+#include <Main/globals.hxx>
+#include <Main/fg_props.hxx>
+
+namespace canvas
+{
+
+  //----------------------------------------------------------------------------
+  Text::Text(SGPropertyNode* node):
+    Element(node, COLOR | COLOR_FILL | BOUNDING_BOX),
+    _text( new osgText::Text )
+  {
+    _drawable = _text;
+    _text->setCharacterSizeMode(osgText::Text::SCREEN_COORDS);
+
+    // font size and property node
+    float character_size = _node->getFloatValue("size", 32);
+    _text->setCharacterSize( character_size );
+    _node->setFloatValue("size", character_size);
+
+    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
+    geode->addDrawable(_text);
+    _transform->addChild(geode);
+
+    _node->tie
+    (
+      "alignment",
+      SGRawValueMethods<Text, const char*>
+      (
+        *this,
+        &Text::getAlignment,
+        &Text::setAlignment
+      )
+    );
+    _node->tie
+    (
+      "padding",
+      SGRawValueMethods<osgText::Text, float>
+      (
+        *_text.get(),
+        &osgText::Text::getBoundingBoxMargin,
+        &osgText::Text::setBoundingBoxMargin
+      )
+    );
+    typedef SGRawValueMethods<osgText::Text, int> TextIntMethods;
+    _node->tie
+    (
+      "draw-mode",
+      //  TEXT              = 1 default
+      //  BOUNDINGBOX       = 2
+      //  FILLEDBOUNDINGBOX = 4
+      //  ALIGNMENT         = 8
+      TextIntMethods
+      (
+        *_text.get(),
+        (TextIntMethods::getter_t)&osgText::Text::getDrawMode,
+        (TextIntMethods::setter_t)&osgText::Text::setDrawMode
+      )
+    );
+  }
+
+  //----------------------------------------------------------------------------
+  Text::~Text()
+  {
+
+  }
+
+  //----------------------------------------------------------------------------
+  void Text::setFont(const char* name)
+  {
+    _text->setFont( getFont(name) );
+  }
+
+  //----------------------------------------------------------------------------
+  void Text::setAlignment(const char* align)
+  {
+    const std::string align_string(align);
+    if( 0 ) return;
+#define ENUM_MAPPING(enum_val, string_val) \
+    else if( align_string == string_val )\
+      _text->setAlignment( osgText::Text::enum_val );
+#include "text-alignment.hxx"
+#undef ENUM_MAPPING
+    else
+      _text->setAlignment(osgText::Text::LEFT_BASE_LINE);
+  }
+
+  //----------------------------------------------------------------------------
+  const char* Text::getAlignment() const
+  {
+    switch( _text->getAlignment() )
+    {
+#define ENUM_MAPPING(enum_val, string_val) \
+      case osgText::Text::enum_val:\
+        return string_val;
+#include "text-alignment.hxx"
+#undef ENUM_MAPPING
+      default:
+        return "unknown";
+    }
+  }
+
+  //----------------------------------------------------------------------------
+  void Text::childChanged(SGPropertyNode* child)
+  {
+    if( child->getNameString() == "text" )
+      _text->setText(child->getStringValue());
+    else if( child->getNameString() == "size" )
+      _text->setCharacterSize( child->getFloatValue() );
+    else if( child->getNameString() == "font" )
+      setFont( child->getStringValue() );
+    else
+      return;
+
+    _attributes_dirty |= BOUNDING_BOX;
+  }
+
+  //----------------------------------------------------------------------------
+  void Text::colorChanged(const osg::Vec4& color)
+  {
+    _text->setColor(color);
+  }
+
+  //----------------------------------------------------------------------------
+  void Text::colorFillChanged(const osg::Vec4& color)
+  {
+    _text->setBoundingBoxColor(color);
+  }
+
+  //----------------------------------------------------------------------------
+  Text::font_ptr Text::getFont(const std::string& name)
+  {
+    SGPath path = globals->resolve_ressource_path("Fonts/" + name);
+    if( path.isNull() )
+    {
+      SG_LOG
+      (
+        SG_GL,
+        SG_ALERT,
+        "canvas::Text: No such font: " << name
+      );
+      return font_ptr();
+    }
+
+    SG_LOG
+    (
+      SG_GL,
+      SG_INFO,
+      "canvas::Text: using font file " << path.str()
+    );
+
+    font_ptr font = osgText::readFontFile(path.c_str());
+    if( !font )
+      SG_LOG
+      (
+        SG_GL,
+        SG_ALERT,
+        "canvas::Text: Failed to open font file " << path.c_str()
+      );
+
+    return font;
+  }
+
+} // namespace canvas
diff --git a/src/Canvas/elements/text.hxx b/src/Canvas/elements/text.hxx
new file mode 100644 (file)
index 0000000..38d508b
--- /dev/null
@@ -0,0 +1,56 @@
+// A text 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_TEXT_HXX_
+#define CANVAS_TEXT_HXX_
+
+#include "element.hxx"
+#include <osgText/Text>
+#include <boost/shared_ptr.hpp>
+#include <map>
+#include <vector>
+
+namespace canvas
+{
+
+  class Text:
+    public Element
+  {
+    public:
+      Text(SGPropertyNode* node);
+      virtual ~Text();
+
+      void setFont(const char* name);
+
+      void setAlignment(const char* align);
+      const char* getAlignment() const;
+
+    protected:
+      osg::ref_ptr<osgText::Text>   _text;
+
+      virtual void childChanged(SGPropertyNode * child);
+      virtual void colorChanged(const osg::Vec4& color);
+      virtual void colorFillChanged(const osg::Vec4& color);
+
+      typedef osg::ref_ptr<osgText::Font> font_ptr;
+      static font_ptr getFont(const std::string& name);
+  };
+
+}  // namespace canvas
+
+#endif /* CANVAS_TEXT_HXX_ */
index 6760bcd2244e17562d3aea218f89c3afda3a32ed..1b76593655172e49fe77c9d8dc22f404e404ef51 100644 (file)
@@ -6,6 +6,10 @@
 //
 // Ported to OSG by Tim Moore - Jun 2007
 //
+// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April 2012
+// Supports now multisampling/mipmapping, usage of the stencil buffer and placing
+// the texture in the scene by certain filter criteria
+//
 // 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
 #include <osg/StateSet>
 #include <osgDB/FileNameUtils>
 
+#include <simgear/scene/material/EffectGeode.hxx>
 #include <simgear/scene/util/RenderConstants.hxx>
-#include <simgear/debug/logstream.hxx>
 
 #include <Main/globals.hxx>
 #include <Viewer/renderer.hxx>
 #include <Scenery/scenery.hxx>
 #include "od_gauge.hxx"
 
-FGODGauge::FGODGauge() :
+#include <cassert>
+
+//------------------------------------------------------------------------------
+FGODGauge::FGODGauge():
+  _size_x( -1 ),
+  _size_y( -1 ),
+  _view_width( -1 ),
+  _view_height( -1 ),
+  _use_image_coords( false ),
+  _use_stencil( false ),
+  _use_mipmapping( false ),
+  _coverage_samples( 0 ),
+  _color_samples( 0 ),
   rtAvailable( false )
 {
 }
 
-void FGODGauge::allocRT ()
+//------------------------------------------------------------------------------
+FGODGauge::~FGODGauge()
 {
-    camera = new osg::Camera;
-    // Only the far camera should trigger this texture to be rendered.
-    camera->setNodeMask(simgear::BACKGROUND_BIT);
-    camera->setProjectionMatrix(osg::Matrix::ortho2D(-textureWH/2.0, textureWH/2.0, -textureWH/2.0, textureWH/2.0));
-    camera->setViewport(0, 0, textureWH, textureWH);
-    camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
-    camera->setRenderOrder(osg::Camera::PRE_RENDER);
-    camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-    camera->setClearColor(osg::Vec4(0.0f, 0.0f, 0.0f , 0.0f));
-    camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::FRAME_BUFFER);
-    osg::StateSet* stateSet = camera->getOrCreateStateSet();
-    stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
-    stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
-    stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
-    stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
-    stateSet->setAttributeAndModes(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK,
-            osg::PolygonMode::FILL),
-            osg::StateAttribute::ON);
-    stateSet->setAttributeAndModes(new osg::AlphaFunc(osg::AlphaFunc::GREATER,
-            0.0f),
-            osg::StateAttribute::ON);
-    stateSet->setAttribute(new osg::ShadeModel(osg::ShadeModel::FLAT));
-    stateSet->setAttributeAndModes(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA,
-            osg::BlendFunc::ONE_MINUS_SRC_ALPHA),
-            osg::StateAttribute::ON);
-    if (!texture.valid()) {
-        texture = new osg::Texture2D;
-        texture->setTextureSize(textureWH, textureWH);
-        texture->setInternalFormat(GL_RGBA);
-        texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
-        texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
-    }
-    camera->attach(osg::Camera::COLOR_BUFFER, texture.get());
-    globals->get_renderer()->addCamera(camera.get(), false);
-    rtAvailable = true;
+  if( camera.valid() )
+    globals->get_renderer()->removeCamera(camera.get());
 }
 
-FGODGauge::~FGODGauge()
+//------------------------------------------------------------------------------
+void FGODGauge::setSize(int size_x, int size_y)
 {
-    if (camera.valid()) {
-        globals->get_renderer()->removeCamera(camera.get());
-    }
+  _size_x = size_x;
+  _size_y = size_y < 0 ? size_x : size_y;
+
+  if( texture.valid() )
+    texture->setTextureSize(_size_x, _size_x);
 }
 
-void FGODGauge::setSize(int viewSize)
+//----------------------------------------------------------------------------
+void FGODGauge::setViewSize(int width, int height)
 {
-    textureWH = viewSize;
-    if (texture.valid()) {
-        texture->setTextureSize(textureWH, textureWH);
-    }
+  _view_width = width;
+  _view_height = height < 0 ? width : height;
+
+  if( camera )
+    updateCoordinateFrame();
+}
+
+//------------------------------------------------------------------------------
+void FGODGauge::useImageCoords(bool use)
+{
+  if( use == _use_image_coords )
+    return;
+
+  _use_image_coords = use;
+
+  if( texture )
+    updateCoordinateFrame();
+}
+
+//------------------------------------------------------------------------------
+void FGODGauge::useStencil(bool use)
+{
+  if( use == _use_stencil )
+    return;
+
+  _use_stencil = use;
+
+  if( texture )
+    updateStencil();
+}
+
+//------------------------------------------------------------------------------
+void FGODGauge::setSampling( bool mipmapping,
+                             int coverage_samples,
+                             int color_samples )
+{
+  if(    _use_mipmapping == mipmapping
+      && _coverage_samples == coverage_samples
+      && _color_samples == color_samples )
+    return;
+
+  _use_mipmapping = mipmapping;
+
+  if( color_samples > coverage_samples )
+  {
+    SG_LOG
+    (
+      SG_GL,
+      SG_WARN,
+      "FGODGauge::setSampling: color_samples > coverage_samples not allowed!"
+    );
+    color_samples = coverage_samples;
+  }
+
+  _coverage_samples = coverage_samples;
+  _color_samples = color_samples;
+
+  updateSampling();
 }
 
+//------------------------------------------------------------------------------
 bool FGODGauge::serviceable(void) 
 {
-    return rtAvailable;
+  return rtAvailable;
+}
+
+//------------------------------------------------------------------------------
+void FGODGauge::allocRT(osg::NodeCallback* camera_cull_callback)
+{
+  camera = new osg::Camera;
+  camera->setDataVariance(osg::Object::DYNAMIC);
+  // Only the far camera should trigger this texture to be rendered.
+  camera->setNodeMask(simgear::BACKGROUND_BIT);
+  camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
+  camera->setRenderOrder(osg::Camera::PRE_RENDER);
+  camera->setClearColor(osg::Vec4(0.0f, 0.0f, 0.0f , 0.0f));
+  camera->setClearStencil(0);
+  camera->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER_OBJECT,
+                                             osg::Camera::FRAME_BUFFER );
+
+  if( camera_cull_callback )
+    camera->setCullCallback(camera_cull_callback);
+
+  updateCoordinateFrame();
+  updateStencil();
+
+  osg::StateSet* stateSet = camera->getOrCreateStateSet();
+  stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
+  stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
+  stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
+  stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
+  stateSet->setAttributeAndModes(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK,
+          osg::PolygonMode::FILL),
+          osg::StateAttribute::ON);
+  stateSet->setAttributeAndModes(new osg::AlphaFunc(osg::AlphaFunc::GREATER,
+          0.0f),
+          osg::StateAttribute::ON);
+  stateSet->setAttribute(new osg::ShadeModel(osg::ShadeModel::FLAT));
+  stateSet->setAttributeAndModes(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA,
+          osg::BlendFunc::ONE_MINUS_SRC_ALPHA),
+          osg::StateAttribute::ON);
+  if( !texture )
+  {
+    texture = new osg::Texture2D;
+    texture->setTextureSize(_size_x, _size_y);
+    texture->setInternalFormat(GL_RGBA);
+  }
+
+  updateSampling();
+
+  globals->get_renderer()->addCamera(camera.get(), false);
+  rtAvailable = true;
+}
+
+//------------------------------------------------------------------------------
+void FGODGauge::updateCoordinateFrame()
+{
+  assert( camera );
+
+  if( _view_width < 0 )
+    _view_width = _size_x;
+  if( _view_height < 0 )
+    _view_height = _size_y;
+
+  camera->setViewport(0, 0, _size_x, _size_y);
+
+  if( _use_image_coords )
+    camera->setProjectionMatrix(
+      osg::Matrix::ortho2D(0, _view_width, _view_height, 0)
+    );
+  else
+    camera->setProjectionMatrix(
+      osg::Matrix::ortho2D( -_view_width/2.,  _view_width/2.,
+                            -_view_height/2., _view_height/2. )
+    );
+}
+
+//------------------------------------------------------------------------------
+void FGODGauge::updateStencil()
+{
+  assert( camera );
+
+  GLbitfield mask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
+
+  if( _use_stencil)
+  {
+    camera->attach( osg::Camera::PACKED_DEPTH_STENCIL_BUFFER,
+                     GL_DEPTH_STENCIL_EXT );
+    mask |= GL_STENCIL_BUFFER_BIT;
+  }
+  else
+  {
+    camera->detach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER);
+  }
+
+  camera->setClearMask(mask);
+}
+
+//------------------------------------------------------------------------------
+void FGODGauge::updateSampling()
+{
+  assert( camera );
+  assert( texture );
+
+  texture->setFilter(
+    osg::Texture2D::MIN_FILTER,
+    _use_mipmapping ? osg::Texture2D::LINEAR_MIPMAP_NEAREST
+                    : osg::Texture2D::LINEAR
+  );
+  camera->attach(
+    osg::Camera::COLOR_BUFFER,
+    texture.get(),
+    0, 0,
+    _use_mipmapping,
+    _coverage_samples,
+    _color_samples
+  );
 }
 
 /**
  * Replace a texture in the airplane model with the gauge texture.
  */
-
-class ReplaceStaticTextureVisitor : public osg::NodeVisitor
+class ReplaceStaticTextureVisitor:
+  public osg::NodeVisitor
 {
-public:
-    ReplaceStaticTextureVisitor(const std::string& name,
-        osg::Texture2D* _newTexture) :
+  public:
+
+    ReplaceStaticTextureVisitor( const char* name,
+                                 osg::Texture2D* new_texture ):
         osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
-        newTexture(_newTexture)
+        _tex_name( osgDB::getSimpleFileName(name) ),
+        _new_texture(new_texture)
+    {}
+
+    ReplaceStaticTextureVisitor( const SGPropertyNode* placement,
+                                 osg::Texture2D* new_texture,
+                                 osg::NodeCallback* cull_callback = 0 ):
+        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
+        _tex_name( osgDB::getSimpleFileName(
+          placement->getStringValue("texture"))
+        ),
+        _node_name( placement->getStringValue("node") ),
+        _parent_name( placement->getStringValue("parent") ),
+        _new_texture(new_texture),
+        _cull_callback(cull_callback)
     {
-        textureFileName = osgDB::getSimpleFileName(name);
+      if(    _tex_name.empty()
+          && _node_name.empty()
+          && _parent_name.empty() )
+        SG_LOG
+        (
+          SG_GL,
+          SG_WARN,
+          "No filter criterion for replacing texture. "
+          " Every texture will be replaced!"
+        );
     }
 
-    virtual void apply(osg::Node& node)
+    /**
+     * Get a list of groups which have been inserted into the scene graph to
+     * replace the given texture
+     */
+    Placements& getPlacements()
     {
-        osg::StateSet* ss = node.getStateSet();
-        if (ss)
-            changeStateSetTexture(ss);
-        traverse(node);
+      return _placements;
     }
 
     virtual void apply(osg::Geode& node)
     {
-        int numDrawables = node.getNumDrawables();
-        for (int i = 0; i < numDrawables; i++) {
-            osg::Drawable* drawable = node.getDrawable(i);
-            osg::StateSet* ss = drawable->getStateSet();
-            if (ss)
-                changeStateSetTexture(ss);
-        }
-        traverse(node);
-}
-protected:
-    void changeStateSetTexture(osg::StateSet *ss)
-    {
-        osg::Texture2D* tex
-                = dynamic_cast<osg::Texture2D*>(ss->getTextureAttribute(0,
-                osg::StateAttribute::TEXTURE));
-        if (!tex || tex == newTexture || !tex->getImage())
+      simgear::EffectGeode* eg = dynamic_cast<simgear::EffectGeode*>(&node);
+      if( !eg )
+        return;
+
+      osg::StateSet* ss = eg->getEffect()->getDefaultStateSet();
+      if( !ss )
+        return;
+
+      osg::Group *parent = node.getParent(0);
+      if( !_node_name.empty() && parent->getName() != _node_name )
+        return;
+
+      if( !_parent_name.empty() )
+      {
+        // Traverse nodes upwards starting at the parent node (skip current
+        // node)
+        const osg::NodePath& np = getNodePath();
+        bool found = false;
+        for( int i = static_cast<int>(np.size()) - 2; i >= 0; --i )
+        {
+          const osg::Node* path_segment = np[i];
+          const osg::Node* path_parent = path_segment->getParent(0);
+
+          // A node without a name is always the parent of the root node of
+          // the model just containing the file name
+          if( path_parent && path_parent->getName().empty() )
             return;
-        std::string fileName = tex->getImage()->getFileName();
-        std::string simpleName = osgDB::getSimpleFileName(fileName);
-        if (osgDB::equalCaseInsensitive(textureFileName, simpleName))
-            ss->setTextureAttribute(0, newTexture);
+
+          if( path_segment->getName() == _parent_name )
+          {
+            found = true;
+            break;
+          }
+        }
+
+        if( !found )
+          return;
+      }
+
+      for( size_t unit = 0; unit < ss->getNumTextureAttributeLists(); ++unit )
+      {
+        osg::Texture2D* tex = dynamic_cast<osg::Texture2D*>
+        (
+          ss->getTextureAttribute(unit, osg::StateAttribute::TEXTURE)
+        );
+
+        if( !tex || !tex->getImage() || tex == _new_texture )
+          continue;
+
+        if( !_tex_name.empty() )
+        {
+          std::string tex_name = tex->getImage()->getFileName();
+          std::string tex_name_simple = osgDB::getSimpleFileName(tex_name);
+          if( !osgDB::equalCaseInsensitive(_tex_name, tex_name_simple) )
+            continue;
+        }
+
+        // insert a new group between the geode an it's parent which overrides
+        // the texture
+        osg::ref_ptr<osg::Group> group = new osg::Group;
+        group->setName("canvas texture group");
+        group->addChild(eg);
+        parent->removeChild(eg);
+        parent->addChild(group);
+
+        if( _cull_callback )
+          group->setCullCallback(_cull_callback);
+
+        _placements.push_back(group);
+
+        osg::StateSet* stateSet = group->getOrCreateStateSet();
+        stateSet->setTextureAttribute( unit, _new_texture,
+                                             osg::StateAttribute::OVERRIDE );
+        stateSet->setTextureMode( unit, GL_TEXTURE_2D,
+                                        osg::StateAttribute::ON );
+
+        SG_LOG
+        (
+          SG_GL,
+          SG_INFO,
+             "Replaced texture '" << _tex_name << "'"
+          << " for object '" << parent->getName() << "'"
+          << (!_parent_name.empty() ? " with parent '" + _parent_name + "'"
+                                    : "")
+        );
+        return;
+      }
     }
-    std::string textureFileName;
-    osg::Texture2D* newTexture;
+
+
+
+  protected:
+
+    std::string _tex_name,      ///<! Name of texture to be replaced
+                _node_name,     ///<! Only replace if node name matches
+                _parent_name;   ///<! Only replace if any parent node matches
+                                ///   given name (all the tree upwards)
+    osg::Texture2D     *_new_texture;
+    osg::NodeCallback  *_cull_callback;
+
+    Placements _placements;
 };
 
-void FGODGauge::set_texture(const char * name, osg::Texture2D* new_texture)
+//------------------------------------------------------------------------------
+  Placements FGODGauge::set_texture(const char* name, osg::Texture2D* new_texture)
 {
-    osg::Group* root = globals->get_scenery()->get_aircraft_branch();
-    ReplaceStaticTextureVisitor visitor(name, new_texture);
-    root->accept(visitor);
+  osg::Group* root = globals->get_scenery()->get_aircraft_branch();
+  ReplaceStaticTextureVisitor visitor(name, new_texture);
+  root->accept(visitor);
+  return visitor.getPlacements();
 }
 
+//------------------------------------------------------------------------------
+Placements FGODGauge::set_texture( const SGPropertyNode* placement,
+                             osg::Texture2D* new_texture,
+                             osg::NodeCallback* cull_callback )
+{
+  osg::Group* root = globals->get_scenery()->get_aircraft_branch();
+  ReplaceStaticTextureVisitor visitor(placement, new_texture, cull_callback);
+  root->accept(visitor);
+  return visitor.getPlacements();
+}
index b867d22d47c9d8a398ffb14a844ab3522aeeae4e..902d20e6abb18bceb1bef16282509474ba534ec9 100644 (file)
@@ -6,6 +6,10 @@
 //
 // Ported to OSG by Tim Moore - Jun 2007
 //
+// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April 2012
+// Supports now multisampling/mipmapping, usage of the stencil buffer and placing
+// the texture in the scene by certain filter criteria
+//
 // 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
 #ifndef _OD_GAUGE_HXX
 #define _OD_GAUGE_HXX
 
-
-#include <osg/ref_ptr>
+#include <osg/NodeCallback>
+#include <osg/Group>
 
 namespace osg {
   class Camera;
   class Texture2D;
 }
 
+class SGPropertyNode;
+typedef std::vector<osg::ref_ptr<osg::Group> > Placements;
+
 /**
  * Owner Drawn Gauge helper class.
  */
@@ -42,11 +49,53 @@ public:
     FGODGauge();
     virtual ~FGODGauge();
 
-  
-    void setSize(int viewSize);
-    int size() const
-        { return textureWH; }
+    /**
+     * Set the size of the render target.
+     *
+     * @param size_x    X size
+     * @param size_y    Y size. Defaults to size_x if not specified
+     */
+    void setSize(int size_x, int size_y = -1);
+
+    /**
+     * Set the size of the viewport
+     *
+     * @param width
+     * @param height    Defaults to width if not specified
+     */
+    void setViewSize(int width, int height = -1);
+
+    /**
+     * DEPRECATED
+     *
+     * Get size of squared texture
+     */
+    int size() const { return _size_x; }
     
+    /**
+     * Set whether to use image coordinates or not.
+     *
+     * Default: origin == center of texture
+     * Image Coords: origin == top left corner
+     */
+    void useImageCoords(bool use = true);
+
+    /**
+     * Enable/Disable using a stencil buffer
+     */
+    void useStencil(bool use = true);
+
+    /**
+     * Set sampling parameters for mipmapping and coverage sampling
+     * antialiasing.
+     *
+     * @note color_samples is not allowed to be higher than coverage_samples
+     *
+     */
+    void setSampling( bool mipmapping,
+                      int coverage_samples = 0,
+                      int color_samples = 0 );
+
     /**
      * Say if we can render to a texture.
      * @return true if rtt is available
@@ -58,8 +107,27 @@ public:
      * This is to replace a static texture by a dynamic one
      * @param name texture filename
      * @param new_texture dynamic texture to replace the old one
+     * @return A list of groups which override the given texture
+     */
+    Placements set_texture(const char * name, osg::Texture2D* new_texture);
+
+    /**
+     * Replace an opengl texture name inside the aircraft scene graph.
+     * This is to replace a static texture by a dynamic one. The replacement
+     * is base on certain filtering criteria which have to be stored in string
+     * value childs of the placement node. Recognized nodes are:
+     *   - texture  Match the name of the texture
+     *   - node     Match the name of the object
+     *   - parent   Match any of the object parents names (all the tree upwards)
+     * @param placement the node containing the replacement criteria
+     * @param new_texture dynamic texture to replace the old one
+     * @param an optional cull callback which will be installed on any matching
+     *        object
+     * @return A list of groups which override the given texture
      */
-    void set_texture(const char * name, osg::Texture2D* new_texture);
+    Placements set_texture( const SGPropertyNode* placement,
+                            osg::Texture2D* new_texture,
+                            osg::NodeCallback* cull_callback = 0 );
 
     /**
      * Get the OSG camera for drawing this gauge.
@@ -67,17 +135,32 @@ public:
     osg::Camera* getCamera() { return camera.get(); }
 
     osg::Texture2D* getTexture() { return texture.get(); }
-    void setTexture(osg::Texture2D* t) { texture = t; }
+    //void setTexture(osg::Texture2D* t) { texture = t; }
 
     // Real initialization function. Bad name.
-    void allocRT(void);
+    void allocRT(osg::NodeCallback* camera_cull_callback = 0);
 
 private:
-    int textureWH;
+    int _size_x,
+        _size_y,
+        _view_width,
+        _view_height;
+    bool _use_image_coords,
+         _use_stencil,
+         _use_mipmapping;
+
+    // Multisampling parameters
+    int  _coverage_samples,
+         _color_samples;
+
     bool rtAvailable;
     osg::ref_ptr<osg::Camera> camera;
     osg::ref_ptr<osg::Texture2D> texture;
 
+    void updateCoordinateFrame();
+    void updateStencil();
+    void updateSampling();
+
 };
 
 #endif // _OD_GAUGE_HXX
index 5f5b5360e9ffc27a4ba7dbdf8365d7ce0adc90da..ae57786a6bb1240f47848d2bc02afb92ddfa38b9 100644 (file)
@@ -86,6 +86,7 @@
 #include <Cockpit/panel.hxx>
 #include <Cockpit/panel_io.hxx>
 
+#include <Canvas/canvas_mgr.hxx>
 #include <GUI/new_gui.hxx>
 #include <Input/input.hxx>
 #include <Instrumentation/instrument_mgr.hxx>
@@ -1158,6 +1159,11 @@ bool fgInitSubsystems() {
     ////////////////////////////////////////////////////////////////////
     fgGetBool("/sim/rendering/bump-mapping", false);
 
+    ////////////////////////////////////////////////////////////////////
+    // Initialize the canvas 2d drawing subsystem.
+    ////////////////////////////////////////////////////////////////////
+    globals->add_subsystem("Canvas2D", new CanvasMgr, SGSubsystemMgr::DISPLAY);
+
     ////////////////////////////////////////////////////////////////////
     // Initialise the ATIS Manager
     // Note that this is old stuff, but is necessary for the
index 1de5ecc7aeea40ec7e92f08ceb81aa1cfa282d1b..8c8ca52bf75b68d116154d16e079e2998807be1e 100644 (file)
@@ -335,6 +335,12 @@ SGPath FGGlobals::resolve_maybe_aircraft_path(const std::string& branch) const
   return simgear::ResourceManager::instance()->findPath(branch);
 }
 
+SGPath FGGlobals::resolve_ressource_path(const std::string& branch) const
+{
+  return simgear::ResourceManager::instance()
+    ->findPath(branch, SGPath(fgGetString("/sim/aircraft-dir")));
+}
+
 FGRenderer *
 FGGlobals::get_renderer () const
 {
index 8600e46db4f086cc8a20a0fab3743400d202d300..2ec9cc3771ff476ad6a7adfc1883fd869c4eb468 100644 (file)
@@ -215,6 +215,15 @@ public:
      */
     SGPath resolve_maybe_aircraft_path(const std::string& branch) const;
     
+    /**
+     * Search in the following directories:
+     *
+     *  1. Root directory of current aircraft (defined by /sim/aircraft-dir)
+     *  2. All aircraft directories if branch starts with Aircraft/
+     *  3. fg_data directory
+     */
+    SGPath resolve_ressource_path(const std::string& branch) const;
+
     inline const std::string &get_browser () const { return browser; }
     void set_browser (const std::string &b) { browser = b; }