1
0
Fork 0
flightgear/src/Canvas/canvas.cxx
Thomas Geymayer d82fd7cc5f Basic 2D canvas implementation.
Support text and transformations for the moment, more
features to follow in due course.
2012-05-18 07:05:37 +01:00

430 lines
11 KiB
C++

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