1
0
Fork 0

Basic 2D canvas implementation.

Support text and transformations for the moment, more
features to follow in due course.
This commit is contained in:
Thomas Geymayer 2012-04-21 15:31:20 +02:00 committed by James Turner
parent fe0a703a19
commit d82fd7cc5f
19 changed files with 2165 additions and 98 deletions

157
docs-mini/README.canvas Normal file
View file

@ -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>

View file

@ -8,6 +8,7 @@ foreach( mylibfolder
Aircraft
ATC
ATCDCL
Canvas
Radio
Autopilot
Cockpit

19
src/Canvas/CMakeLists.txt Normal file
View file

@ -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}")

430
src/Canvas/canvas.cxx Normal file
View file

@ -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);
}
}

101
src/Canvas/canvas.hxx Normal file
View file

@ -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_ */

144
src/Canvas/canvas_mgr.cxx Normal file
View file

@ -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);
}

76
src/Canvas/canvas_mgr.hxx Normal file
View file

@ -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_ */

View file

@ -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

View file

@ -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_ */

View file

@ -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

View file

@ -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_ */

View file

@ -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")

View file

@ -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

View file

@ -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_ */

View 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
@ -38,133 +42,399 @@
#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 ()
{
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;
}
//------------------------------------------------------------------------------
FGODGauge::~FGODGauge()
{
if (camera.valid()) {
globals->get_renderer()->removeCamera(camera.get());
}
if( camera.valid() )
globals->get_renderer()->removeCamera(camera.get());
}
void FGODGauge::setSize(int viewSize)
//------------------------------------------------------------------------------
void FGODGauge::setSize(int size_x, int size_y)
{
textureWH = viewSize;
if (texture.valid()) {
texture->setTextureSize(textureWH, textureWH);
}
_size_x = size_x;
_size_y = size_y < 0 ? size_x : size_y;
if( texture.valid() )
texture->setTextureSize(_size_x, _size_x);
}
//----------------------------------------------------------------------------
void FGODGauge::setViewSize(int width, int height)
{
_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();
}

View 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
@ -25,14 +29,17 @@
#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,10 +49,52 @@ public:
FGODGauge();
virtual ~FGODGauge();
/**
* 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);
void setSize(int viewSize);
int size() const
{ return textureWH; }
/**
* 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.
@ -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
*/
void set_texture(const char * name, osg::Texture2D* new_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
*/
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

View 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

View 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
{

View 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; }