]> git.mxchange.org Git - flightgear.git/blob - src/Instrumentation/od_gauge.cxx
Basic 2D canvas implementation.
[flightgear.git] / src / Instrumentation / od_gauge.cxx
1 // Owner Drawn Gauge helper class
2 //
3 // Written by Harald JOHNSEN, started May 2005.
4 //
5 // Copyright (C) 2005  Harald JOHNSEN
6 //
7 // Ported to OSG by Tim Moore - Jun 2007
8 //
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
12 //
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.
17 //
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.
22 //
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.
26 //
27 //
28
29 #ifdef HAVE_CONFIG_H
30 #  include "config.h"
31 #endif
32
33 #include <osg/Texture2D>
34 #include <osg/AlphaFunc>
35 #include <osg/BlendFunc>
36 #include <osg/Camera>
37 #include <osg/Geode>
38 #include <osg/NodeVisitor>
39 #include <osg/Matrix>
40 #include <osg/PolygonMode>
41 #include <osg/ShadeModel>
42 #include <osg/StateSet>
43 #include <osgDB/FileNameUtils>
44
45 #include <simgear/scene/material/EffectGeode.hxx>
46 #include <simgear/scene/util/RenderConstants.hxx>
47
48 #include <Main/globals.hxx>
49 #include <Viewer/renderer.hxx>
50 #include <Scenery/scenery.hxx>
51 #include "od_gauge.hxx"
52
53 #include <cassert>
54
55 //------------------------------------------------------------------------------
56 FGODGauge::FGODGauge():
57   _size_x( -1 ),
58   _size_y( -1 ),
59   _view_width( -1 ),
60   _view_height( -1 ),
61   _use_image_coords( false ),
62   _use_stencil( false ),
63   _use_mipmapping( false ),
64   _coverage_samples( 0 ),
65   _color_samples( 0 ),
66   rtAvailable( false )
67 {
68 }
69
70 //------------------------------------------------------------------------------
71 FGODGauge::~FGODGauge()
72 {
73   if( camera.valid() )
74     globals->get_renderer()->removeCamera(camera.get());
75 }
76
77 //------------------------------------------------------------------------------
78 void FGODGauge::setSize(int size_x, int size_y)
79 {
80   _size_x = size_x;
81   _size_y = size_y < 0 ? size_x : size_y;
82
83   if( texture.valid() )
84     texture->setTextureSize(_size_x, _size_x);
85 }
86
87 //----------------------------------------------------------------------------
88 void FGODGauge::setViewSize(int width, int height)
89 {
90   _view_width = width;
91   _view_height = height < 0 ? width : height;
92
93   if( camera )
94     updateCoordinateFrame();
95 }
96
97 //------------------------------------------------------------------------------
98 void FGODGauge::useImageCoords(bool use)
99 {
100   if( use == _use_image_coords )
101     return;
102
103   _use_image_coords = use;
104
105   if( texture )
106     updateCoordinateFrame();
107 }
108
109 //------------------------------------------------------------------------------
110 void FGODGauge::useStencil(bool use)
111 {
112   if( use == _use_stencil )
113     return;
114
115   _use_stencil = use;
116
117   if( texture )
118     updateStencil();
119 }
120
121 //------------------------------------------------------------------------------
122 void FGODGauge::setSampling( bool mipmapping,
123                              int coverage_samples,
124                              int color_samples )
125 {
126   if(    _use_mipmapping == mipmapping
127       && _coverage_samples == coverage_samples
128       && _color_samples == color_samples )
129     return;
130
131   _use_mipmapping = mipmapping;
132
133   if( color_samples > coverage_samples )
134   {
135     SG_LOG
136     (
137       SG_GL,
138       SG_WARN,
139       "FGODGauge::setSampling: color_samples > coverage_samples not allowed!"
140     );
141     color_samples = coverage_samples;
142   }
143
144   _coverage_samples = coverage_samples;
145   _color_samples = color_samples;
146
147   updateSampling();
148 }
149
150 //------------------------------------------------------------------------------
151 bool FGODGauge::serviceable(void) 
152 {
153   return rtAvailable;
154 }
155
156 //------------------------------------------------------------------------------
157 void FGODGauge::allocRT(osg::NodeCallback* camera_cull_callback)
158 {
159   camera = new osg::Camera;
160   camera->setDataVariance(osg::Object::DYNAMIC);
161   // Only the far camera should trigger this texture to be rendered.
162   camera->setNodeMask(simgear::BACKGROUND_BIT);
163   camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
164   camera->setRenderOrder(osg::Camera::PRE_RENDER);
165   camera->setClearColor(osg::Vec4(0.0f, 0.0f, 0.0f , 0.0f));
166   camera->setClearStencil(0);
167   camera->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER_OBJECT,
168                                              osg::Camera::FRAME_BUFFER );
169
170   if( camera_cull_callback )
171     camera->setCullCallback(camera_cull_callback);
172
173   updateCoordinateFrame();
174   updateStencil();
175
176   osg::StateSet* stateSet = camera->getOrCreateStateSet();
177   stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
178   stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
179   stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
180   stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
181   stateSet->setAttributeAndModes(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK,
182           osg::PolygonMode::FILL),
183           osg::StateAttribute::ON);
184   stateSet->setAttributeAndModes(new osg::AlphaFunc(osg::AlphaFunc::GREATER,
185           0.0f),
186           osg::StateAttribute::ON);
187   stateSet->setAttribute(new osg::ShadeModel(osg::ShadeModel::FLAT));
188   stateSet->setAttributeAndModes(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA,
189           osg::BlendFunc::ONE_MINUS_SRC_ALPHA),
190           osg::StateAttribute::ON);
191   if( !texture )
192   {
193     texture = new osg::Texture2D;
194     texture->setTextureSize(_size_x, _size_y);
195     texture->setInternalFormat(GL_RGBA);
196   }
197
198   updateSampling();
199
200   globals->get_renderer()->addCamera(camera.get(), false);
201   rtAvailable = true;
202 }
203
204 //------------------------------------------------------------------------------
205 void FGODGauge::updateCoordinateFrame()
206 {
207   assert( camera );
208
209   if( _view_width < 0 )
210     _view_width = _size_x;
211   if( _view_height < 0 )
212     _view_height = _size_y;
213
214   camera->setViewport(0, 0, _size_x, _size_y);
215
216   if( _use_image_coords )
217     camera->setProjectionMatrix(
218       osg::Matrix::ortho2D(0, _view_width, _view_height, 0)
219     );
220   else
221     camera->setProjectionMatrix(
222       osg::Matrix::ortho2D( -_view_width/2.,  _view_width/2.,
223                             -_view_height/2., _view_height/2. )
224     );
225 }
226
227 //------------------------------------------------------------------------------
228 void FGODGauge::updateStencil()
229 {
230   assert( camera );
231
232   GLbitfield mask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
233
234   if( _use_stencil)
235   {
236     camera->attach( osg::Camera::PACKED_DEPTH_STENCIL_BUFFER,
237                      GL_DEPTH_STENCIL_EXT );
238     mask |= GL_STENCIL_BUFFER_BIT;
239   }
240   else
241   {
242     camera->detach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER);
243   }
244
245   camera->setClearMask(mask);
246 }
247
248 //------------------------------------------------------------------------------
249 void FGODGauge::updateSampling()
250 {
251   assert( camera );
252   assert( texture );
253
254   texture->setFilter(
255     osg::Texture2D::MIN_FILTER,
256     _use_mipmapping ? osg::Texture2D::LINEAR_MIPMAP_NEAREST
257                     : osg::Texture2D::LINEAR
258   );
259   camera->attach(
260     osg::Camera::COLOR_BUFFER,
261     texture.get(),
262     0, 0,
263     _use_mipmapping,
264     _coverage_samples,
265     _color_samples
266   );
267 }
268
269 /**
270  * Replace a texture in the airplane model with the gauge texture.
271  */
272 class ReplaceStaticTextureVisitor:
273   public osg::NodeVisitor
274 {
275   public:
276
277     ReplaceStaticTextureVisitor( const char* name,
278                                  osg::Texture2D* new_texture ):
279         osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
280         _tex_name( osgDB::getSimpleFileName(name) ),
281         _new_texture(new_texture)
282     {}
283
284     ReplaceStaticTextureVisitor( const SGPropertyNode* placement,
285                                  osg::Texture2D* new_texture,
286                                  osg::NodeCallback* cull_callback = 0 ):
287         osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
288         _tex_name( osgDB::getSimpleFileName(
289           placement->getStringValue("texture"))
290         ),
291         _node_name( placement->getStringValue("node") ),
292         _parent_name( placement->getStringValue("parent") ),
293         _new_texture(new_texture),
294         _cull_callback(cull_callback)
295     {
296       if(    _tex_name.empty()
297           && _node_name.empty()
298           && _parent_name.empty() )
299         SG_LOG
300         (
301           SG_GL,
302           SG_WARN,
303           "No filter criterion for replacing texture. "
304           " Every texture will be replaced!"
305         );
306     }
307
308     /**
309      * Get a list of groups which have been inserted into the scene graph to
310      * replace the given texture
311      */
312     Placements& getPlacements()
313     {
314       return _placements;
315     }
316
317     virtual void apply(osg::Geode& node)
318     {
319       simgear::EffectGeode* eg = dynamic_cast<simgear::EffectGeode*>(&node);
320       if( !eg )
321         return;
322
323       osg::StateSet* ss = eg->getEffect()->getDefaultStateSet();
324       if( !ss )
325         return;
326
327       osg::Group *parent = node.getParent(0);
328       if( !_node_name.empty() && parent->getName() != _node_name )
329         return;
330
331       if( !_parent_name.empty() )
332       {
333         // Traverse nodes upwards starting at the parent node (skip current
334         // node)
335         const osg::NodePath& np = getNodePath();
336         bool found = false;
337         for( int i = static_cast<int>(np.size()) - 2; i >= 0; --i )
338         {
339           const osg::Node* path_segment = np[i];
340           const osg::Node* path_parent = path_segment->getParent(0);
341
342           // A node without a name is always the parent of the root node of
343           // the model just containing the file name
344           if( path_parent && path_parent->getName().empty() )
345             return;
346
347           if( path_segment->getName() == _parent_name )
348           {
349             found = true;
350             break;
351           }
352         }
353
354         if( !found )
355           return;
356       }
357
358       for( size_t unit = 0; unit < ss->getNumTextureAttributeLists(); ++unit )
359       {
360         osg::Texture2D* tex = dynamic_cast<osg::Texture2D*>
361         (
362           ss->getTextureAttribute(unit, osg::StateAttribute::TEXTURE)
363         );
364
365         if( !tex || !tex->getImage() || tex == _new_texture )
366           continue;
367
368         if( !_tex_name.empty() )
369         {
370           std::string tex_name = tex->getImage()->getFileName();
371           std::string tex_name_simple = osgDB::getSimpleFileName(tex_name);
372           if( !osgDB::equalCaseInsensitive(_tex_name, tex_name_simple) )
373             continue;
374         }
375
376         // insert a new group between the geode an it's parent which overrides
377         // the texture
378         osg::ref_ptr<osg::Group> group = new osg::Group;
379         group->setName("canvas texture group");
380         group->addChild(eg);
381         parent->removeChild(eg);
382         parent->addChild(group);
383
384         if( _cull_callback )
385           group->setCullCallback(_cull_callback);
386
387         _placements.push_back(group);
388
389         osg::StateSet* stateSet = group->getOrCreateStateSet();
390         stateSet->setTextureAttribute( unit, _new_texture,
391                                              osg::StateAttribute::OVERRIDE );
392         stateSet->setTextureMode( unit, GL_TEXTURE_2D,
393                                         osg::StateAttribute::ON );
394
395         SG_LOG
396         (
397           SG_GL,
398           SG_INFO,
399              "Replaced texture '" << _tex_name << "'"
400           << " for object '" << parent->getName() << "'"
401           << (!_parent_name.empty() ? " with parent '" + _parent_name + "'"
402                                     : "")
403         );
404         return;
405       }
406     }
407
408
409
410   protected:
411
412     std::string _tex_name,      ///<! Name of texture to be replaced
413                 _node_name,     ///<! Only replace if node name matches
414                 _parent_name;   ///<! Only replace if any parent node matches
415                                 ///   given name (all the tree upwards)
416     osg::Texture2D     *_new_texture;
417     osg::NodeCallback  *_cull_callback;
418
419     Placements _placements;
420 };
421
422 //------------------------------------------------------------------------------
423   Placements FGODGauge::set_texture(const char* name, osg::Texture2D* new_texture)
424 {
425   osg::Group* root = globals->get_scenery()->get_aircraft_branch();
426   ReplaceStaticTextureVisitor visitor(name, new_texture);
427   root->accept(visitor);
428   return visitor.getPlacements();
429 }
430
431 //------------------------------------------------------------------------------
432 Placements FGODGauge::set_texture( const SGPropertyNode* placement,
433                              osg::Texture2D* new_texture,
434                              osg::NodeCallback* cull_callback )
435 {
436   osg::Group* root = globals->get_scenery()->get_aircraft_branch();
437   ReplaceStaticTextureVisitor visitor(placement, new_texture, cull_callback);
438   root->accept(visitor);
439   return visitor.getPlacements();
440 }