1 // Owner Drawn Gauge helper class
3 // Written by Harald JOHNSEN, started May 2005.
5 // Copyright (C) 2005 Harald JOHNSEN
7 // Ported to OSG by Tim Moore - Jun 2007
9 // Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April 2012
10 // Supports now multisampling/mipmapping, usage of the stencil buffer and placing
11 // the texture in the scene by certain filter criteria
13 // This program is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU General Public License as
15 // published by the Free Software Foundation; either version 2 of the
16 // License, or (at your option) any later version.
18 // This program is distributed in the hope that it will be useful, but
19 // WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 // General Public License for more details.
23 // You should have received a copy of the GNU General Public License
24 // along with this program; if not, write to the Free Software
25 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
33 #include <osg/Texture2D>
34 #include <osg/AlphaFunc>
35 #include <osg/BlendFunc>
38 #include <osg/NodeVisitor>
40 #include <osg/PolygonMode>
41 #include <osg/ShadeModel>
42 #include <osg/StateSet>
43 #include <osg/FrameBufferObject> // for GL_DEPTH_STENCIL_EXT on Windows
45 #include <osgDB/FileNameUtils>
47 #include <simgear/scene/material/EffectGeode.hxx>
48 #include <simgear/scene/util/RenderConstants.hxx>
50 #include <Main/globals.hxx>
51 #include <Viewer/renderer.hxx>
52 #include <Scenery/scenery.hxx>
53 #include "od_gauge.hxx"
57 //------------------------------------------------------------------------------
58 FGODGauge::FGODGauge():
63 _use_image_coords( false ),
64 _use_stencil( false ),
65 _use_mipmapping( false ),
66 _coverage_samples( 0 ),
72 //------------------------------------------------------------------------------
73 FGODGauge::~FGODGauge()
76 globals->get_renderer()->removeCamera(camera.get());
79 //------------------------------------------------------------------------------
80 void FGODGauge::setSize(int size_x, int size_y)
83 _size_y = size_y < 0 ? size_x : size_y;
86 texture->setTextureSize(_size_x, _size_x);
89 //----------------------------------------------------------------------------
90 void FGODGauge::setViewSize(int width, int height)
93 _view_height = height < 0 ? width : height;
96 updateCoordinateFrame();
99 //------------------------------------------------------------------------------
100 void FGODGauge::useImageCoords(bool use)
102 if( use == _use_image_coords )
105 _use_image_coords = use;
108 updateCoordinateFrame();
111 //------------------------------------------------------------------------------
112 void FGODGauge::useStencil(bool use)
114 if( use == _use_stencil )
123 //------------------------------------------------------------------------------
124 void FGODGauge::setSampling( bool mipmapping,
125 int coverage_samples,
128 if( _use_mipmapping == mipmapping
129 && _coverage_samples == coverage_samples
130 && _color_samples == color_samples )
133 _use_mipmapping = mipmapping;
135 if( color_samples > coverage_samples )
141 "FGODGauge::setSampling: color_samples > coverage_samples not allowed!"
143 color_samples = coverage_samples;
146 _coverage_samples = coverage_samples;
147 _color_samples = color_samples;
152 //------------------------------------------------------------------------------
153 void FGODGauge::setRender(bool render)
155 // Only the far camera should trigger this texture to be rendered.
156 camera->setNodeMask(render ? simgear::BACKGROUND_BIT : 0);
159 //------------------------------------------------------------------------------
160 bool FGODGauge::serviceable(void)
165 //------------------------------------------------------------------------------
166 void FGODGauge::allocRT(osg::NodeCallback* camera_cull_callback)
168 camera = new osg::Camera;
169 camera->setDataVariance(osg::Object::DYNAMIC);
170 camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
171 camera->setRenderOrder(osg::Camera::PRE_RENDER);
172 camera->setClearColor(osg::Vec4(0.0f, 0.0f, 0.0f , 0.0f));
173 camera->setClearStencil(0);
174 camera->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER_OBJECT,
175 osg::Camera::FRAME_BUFFER );
177 if( camera_cull_callback )
178 camera->setCullCallback(camera_cull_callback);
181 updateCoordinateFrame();
184 osg::StateSet* stateSet = camera->getOrCreateStateSet();
185 stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
186 stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
187 stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
188 stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
189 stateSet->setAttributeAndModes(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK,
190 osg::PolygonMode::FILL),
191 osg::StateAttribute::ON);
192 stateSet->setAttributeAndModes(new osg::AlphaFunc(osg::AlphaFunc::GREATER,
194 osg::StateAttribute::ON);
195 stateSet->setAttribute(new osg::ShadeModel(osg::ShadeModel::FLAT));
196 stateSet->setAttributeAndModes(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA,
197 osg::BlendFunc::ONE_MINUS_SRC_ALPHA),
198 osg::StateAttribute::ON);
201 texture = new osg::Texture2D;
202 texture->setTextureSize(_size_x, _size_y);
203 texture->setInternalFormat(GL_RGBA);
208 globals->get_renderer()->addCamera(camera.get(), false);
212 //------------------------------------------------------------------------------
213 void FGODGauge::updateCoordinateFrame()
217 if( _view_width < 0 )
218 _view_width = _size_x;
219 if( _view_height < 0 )
220 _view_height = _size_y;
222 camera->setViewport(0, 0, _size_x, _size_y);
224 if( _use_image_coords )
225 camera->setProjectionMatrix(
226 osg::Matrix::ortho2D(0, _view_width, _view_height, 0)
229 camera->setProjectionMatrix(
230 osg::Matrix::ortho2D( -_view_width/2., _view_width/2.,
231 -_view_height/2., _view_height/2. )
235 //------------------------------------------------------------------------------
236 void FGODGauge::updateStencil()
240 GLbitfield mask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
244 camera->attach( osg::Camera::PACKED_DEPTH_STENCIL_BUFFER,
245 GL_DEPTH_STENCIL_EXT );
246 mask |= GL_STENCIL_BUFFER_BIT;
250 camera->detach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER);
253 camera->setClearMask(mask);
256 //------------------------------------------------------------------------------
257 void FGODGauge::updateSampling()
263 osg::Texture2D::MIN_FILTER,
264 _use_mipmapping ? osg::Texture2D::LINEAR_MIPMAP_LINEAR
265 : osg::Texture2D::LINEAR
268 osg::Camera::COLOR_BUFFER,
278 * Replace a texture in the airplane model with the gauge texture.
280 class ReplaceStaticTextureVisitor:
281 public osg::NodeVisitor
285 ReplaceStaticTextureVisitor( const char* name,
286 osg::Texture2D* new_texture ):
287 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
288 _tex_name( osgDB::getSimpleFileName(name) ),
289 _new_texture(new_texture)
292 ReplaceStaticTextureVisitor( const SGPropertyNode* placement,
293 osg::Texture2D* new_texture,
294 osg::NodeCallback* cull_callback = 0 ):
295 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
296 _tex_name( osgDB::getSimpleFileName(
297 placement->getStringValue("texture"))
299 _node_name( placement->getStringValue("node") ),
300 _parent_name( placement->getStringValue("parent") ),
301 _new_texture(new_texture),
302 _cull_callback(cull_callback)
304 if( _tex_name.empty()
305 && _node_name.empty()
306 && _parent_name.empty() )
311 "No filter criterion for replacing texture. "
312 " Every texture will be replaced!"
317 * Get a list of groups which have been inserted into the scene graph to
318 * replace the given texture
320 canvas::Placements& getPlacements()
325 virtual void apply(osg::Geode& node)
327 simgear::EffectGeode* eg = dynamic_cast<simgear::EffectGeode*>(&node);
331 osg::StateSet* ss = eg->getEffect()->getDefaultStateSet();
335 osg::Group *parent = node.getParent(0);
336 if( !_node_name.empty() && parent->getName() != _node_name )
339 if( !_parent_name.empty() )
341 // Traverse nodes upwards starting at the parent node (skip current
343 const osg::NodePath& np = getNodePath();
345 for( int i = static_cast<int>(np.size()) - 2; i >= 0; --i )
347 const osg::Node* path_segment = np[i];
348 const osg::Node* path_parent = path_segment->getParent(0);
350 // A node without a name is always the parent of the root node of
351 // the model just containing the file name
352 if( path_parent && path_parent->getName().empty() )
355 if( path_segment->getName() == _parent_name )
366 for( size_t unit = 0; unit < ss->getNumTextureAttributeLists(); ++unit )
368 osg::Texture2D* tex = dynamic_cast<osg::Texture2D*>
370 ss->getTextureAttribute(unit, osg::StateAttribute::TEXTURE)
373 if( !tex || !tex->getImage() || tex == _new_texture )
376 if( !_tex_name.empty() )
378 std::string tex_name = tex->getImage()->getFileName();
379 std::string tex_name_simple = osgDB::getSimpleFileName(tex_name);
380 if( !osgDB::equalCaseInsensitive(_tex_name, tex_name_simple) )
384 // insert a new group between the geode an it's parent which overrides
386 osg::ref_ptr<osg::Group> group = new osg::Group;
387 group->setName("canvas texture group");
389 parent->removeChild(eg);
390 parent->addChild(group);
393 group->setCullCallback(_cull_callback);
395 _placements.push_back(
396 canvas::PlacementPtr(new ObjectPlacement(group))
399 osg::StateSet* stateSet = group->getOrCreateStateSet();
400 stateSet->setTextureAttribute( unit, _new_texture,
401 osg::StateAttribute::OVERRIDE );
402 stateSet->setTextureMode( unit, GL_TEXTURE_2D,
403 osg::StateAttribute::ON );
409 "Replaced texture '" << _tex_name << "'"
410 << " for object '" << parent->getName() << "'"
411 << (!_parent_name.empty() ? " with parent '" + _parent_name + "'"
420 class ObjectPlacement:
421 public canvas::Placement
424 ObjectPlacement(osg::ref_ptr<osg::Group> group):
429 * Remove placement from the scene
431 virtual ~ObjectPlacement()
433 assert( _group->getNumChildren() == 1 );
434 osg::Node *child = _group->getChild(0);
436 if( _group->getNumParents() )
438 osg::Group *parent = _group->getParent(0);
439 parent->addChild(child);
440 parent->removeChild(_group);
443 _group->removeChild(child);
447 osg::ref_ptr<osg::Group> _group;
450 std::string _tex_name, ///<! Name of texture to be replaced
451 _node_name, ///<! Only replace if node name matches
452 _parent_name; ///<! Only replace if any parent node matches
453 /// given name (all the tree upwards)
454 osg::Texture2D *_new_texture;
455 osg::NodeCallback *_cull_callback;
457 canvas::Placements _placements;
460 //------------------------------------------------------------------------------
461 canvas::Placements FGODGauge::set_texture( const char* name,
462 osg::Texture2D* new_texture )
464 osg::Group* root = globals->get_scenery()->get_aircraft_branch();
465 ReplaceStaticTextureVisitor visitor(name, new_texture);
466 root->accept(visitor);
467 return visitor.getPlacements();
470 //------------------------------------------------------------------------------
471 canvas::Placements FGODGauge::set_texture( const SGPropertyNode* placement,
472 osg::Texture2D* new_texture,
473 osg::NodeCallback* cull_callback )
475 osg::Group* root = globals->get_scenery()->get_aircraft_branch();
476 ReplaceStaticTextureVisitor visitor(placement, new_texture, cull_callback);
477 root->accept(visitor);
478 return visitor.getPlacements();