]> git.mxchange.org Git - flightgear.git/blob - src/Canvas/canvas.cxx
Canvas: Add new element type map for geo mapping.
[flightgear.git] / src / Canvas / canvas.cxx
1 // The canvas for rendering with the 2d api
2 //
3 // Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18
19 #include "canvas.hxx"
20 #include "elements/group.hxx"
21
22 #include <Canvas/property_helper.hxx>
23 #include <Main/globals.hxx>
24 #include <Viewer/renderer.hxx>
25
26 #include <osg/Camera>
27 #include <osg/Geode>
28 #include <osgText/Text>
29 #include <osgViewer/Viewer>
30
31 #include <iostream>
32
33 //------------------------------------------------------------------------------
34 /**
35  * Callback used to disable/enable rendering to the texture if it is not
36  * visible
37  */
38 class CameraCullCallback:
39   public osg::NodeCallback
40 {
41   public:
42
43     CameraCullCallback():
44       _render( true )
45     {}
46
47     /**
48      * Enable rendering for the next frame
49      */
50     void enableRendering()
51     {
52       _render = true;
53     }
54
55   private:
56
57     bool _render;
58
59     virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
60     {
61       if( _render )
62       {
63         traverse(node, nv);
64         _render = false;
65       }
66     }
67 };
68
69 /**
70  * This callback is installed on every placement of the canvas in the scene to
71  * only render the canvas if at least one placement is visible
72  */
73 class PlacementCullCallback:
74   public osg::NodeCallback
75 {
76   public:
77
78     PlacementCullCallback(Canvas* canvas, CameraCullCallback* camera_cull):
79       _canvas( canvas ),
80       _camera_cull( camera_cull )
81     {}
82
83   private:
84
85     Canvas *_canvas;
86     CameraCullCallback *_camera_cull;
87
88     virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
89     {
90       _camera_cull->enableRendering();
91       traverse(node, nv);
92     }
93 };
94
95 //------------------------------------------------------------------------------
96 Canvas::Canvas():
97   _size_x(-1),
98   _size_y(-1),
99   _view_width(-1),
100   _view_height(-1),
101   _status(0),
102   _sampling_dirty(false),
103   _color_dirty(true),
104   _node(0),
105   _render_always(false)
106 {
107   setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
108
109   _camera_callback = new CameraCullCallback;
110   _cull_callback = new PlacementCullCallback(this, _camera_callback);
111 }
112
113 //------------------------------------------------------------------------------
114 Canvas::~Canvas()
115 {
116   clearPlacements();
117
118   unbind();
119   _node = 0;
120 }
121
122 //------------------------------------------------------------------------------
123 int Canvas::getStatus() const
124 {
125   return _status;
126 }
127
128 //------------------------------------------------------------------------------
129 void Canvas::reset(SGPropertyNode* node)
130 {
131   if( node )
132     SG_LOG
133     (
134       SG_GL,
135       SG_INFO,
136       "Canvas::reset() texture[" << node->getIndex() << "]"
137     );
138
139   unbind();
140
141   _node = node;
142   setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
143
144   if( _node )
145   {
146     _root_group.reset( new canvas::Group(_node) );
147     _node->tie
148     (
149       "size[0]",
150       SGRawValueMethods<Canvas, int>( *this, &Canvas::getSizeX,
151                                              &Canvas::setSizeX )
152     );
153     _node->tie
154     (
155       "size[1]",
156       SGRawValueMethods<Canvas, int>( *this, &Canvas::getSizeY,
157                                              &Canvas::setSizeY )
158     );
159     _node->tie
160     (
161       "view[0]",
162       SGRawValueMethods<Canvas, int>( *this, &Canvas::getViewWidth,
163                                              &Canvas::setViewWidth )
164     );
165     _node->tie
166     (
167       "view[1]",
168       SGRawValueMethods<Canvas, int>( *this, &Canvas::getViewHeight,
169                                              &Canvas::setViewHeight )
170     );
171     _node->tie
172     (
173       "status",
174       SGRawValueMethods<Canvas, int>(*this, &Canvas::getStatus)
175     );
176     _node->tie
177     (
178       "status-msg",
179       SGRawValueMethods<Canvas, const char*>(*this, &Canvas::getStatusMsg)
180     );
181     _node->addChangeListener(this);
182
183     canvas::linkColorNodes
184     (
185       "color-background",
186       _node,
187       _color_background,
188       osg::Vec4f(0,0,0,1)
189     );
190   }
191 }
192
193 //------------------------------------------------------------------------------
194 void Canvas::update(double delta_time_sec)
195 {
196   if( !_texture.serviceable() )
197   {
198     if( _status != STATUS_OK )
199       return;
200
201     _texture.setSize(_size_x, _size_y);
202     _texture.useImageCoords(true);
203     _texture.useStencil(true);
204     _texture.allocRT(_camera_callback);
205     _texture.getCamera()->setClearColor(osg::Vec4(0.0f, 0.0f, 0.0f , 1.0f));
206     _texture.getCamera()->addChild(_root_group->getMatrixTransform());
207
208     if( _texture.serviceable() )
209     {
210       setStatusFlags(STATUS_OK);
211     }
212     else
213     {
214       setStatusFlags(CREATE_FAILED);
215       return;
216     }
217   }
218
219   _root_group->update(delta_time_sec);
220
221   if( _sampling_dirty )
222   {
223     _texture.setSampling(
224       _node->getBoolValue("mipmapping"),
225       _node->getIntValue("coverage-samples"),
226       _node->getIntValue("color-samples")
227     );
228     _sampling_dirty = false;
229   }
230   if( _color_dirty )
231   {
232     _texture.getCamera()->setClearColor
233     (
234       osg::Vec4( _color_background[0]->getFloatValue(),
235                  _color_background[1]->getFloatValue(),
236                  _color_background[2]->getFloatValue(),
237                  _color_background[3]->getFloatValue() )
238     );
239     _color_dirty = false;
240   }
241
242   while( !_dirty_placements.empty() )
243   {
244     SGPropertyNode *node = _dirty_placements.back();
245     _dirty_placements.pop_back();
246
247     if( node->getIndex() >= static_cast<int>(_placements.size()) )
248       // New placement
249       _placements.resize(node->getIndex() + 1);
250     else
251       // Remove maybe already existing placements
252       clearPlacements(node->getIndex());
253
254     // add new placements
255     _placements[node->getIndex()] = _texture.set_texture(
256       node,
257       _texture.getTexture(),
258       _cull_callback
259     );
260   }
261
262   if( _render_always )
263     _camera_callback->enableRendering();
264 }
265
266 //------------------------------------------------------------------------------
267 void Canvas::setSizeX(int sx)
268 {
269   if( _size_x == sx )
270     return;
271   _size_x = sx;
272
273   // TODO resize if texture already allocated
274
275   if( _size_x <= 0 )
276     setStatusFlags(MISSING_SIZE_X);
277   else
278     setStatusFlags(MISSING_SIZE_X, false);
279
280   // reset flag to allow creation with new size
281   setStatusFlags(CREATE_FAILED, false);
282 }
283
284 //------------------------------------------------------------------------------
285 int Canvas::getSizeX() const
286 {
287   return _size_x;
288 }
289
290 //------------------------------------------------------------------------------
291 void Canvas::setSizeY(int sy)
292 {
293   if( _size_y == sy )
294     return;
295   _size_y = sy;
296
297   // TODO resize if texture already allocated
298
299   if( _size_y <= 0 )
300     setStatusFlags(MISSING_SIZE_Y);
301   else
302     setStatusFlags(MISSING_SIZE_Y, false);
303
304   // reset flag to allow creation with new size
305   setStatusFlags(CREATE_FAILED, false);
306 }
307
308 //------------------------------------------------------------------------------
309 int Canvas::getSizeY() const
310 {
311   return _size_y;
312 }
313
314 //------------------------------------------------------------------------------
315 void Canvas::setViewWidth(int w)
316 {
317   if( _view_width == w )
318     return;
319   _view_width = w;
320
321   _texture.setViewSize(_view_width, _view_height);
322 }
323
324 //------------------------------------------------------------------------------
325 int Canvas::getViewWidth() const
326 {
327   return _view_width;
328 }
329
330 //------------------------------------------------------------------------------
331 void Canvas::setViewHeight(int h)
332 {
333   if( _view_height == h )
334     return;
335   _view_height = h;
336
337   _texture.setViewSize(_view_width, _view_height);
338 }
339
340 //------------------------------------------------------------------------------
341 int Canvas::getViewHeight() const
342 {
343   return _view_height;
344 }
345
346 //------------------------------------------------------------------------------
347 const char* Canvas::getStatusMsg() const
348 {
349   return _status_msg.c_str();
350 }
351
352 //------------------------------------------------------------------------------
353 void Canvas::childAdded( SGPropertyNode * parent,
354                          SGPropertyNode * child )
355 {
356   if( parent != _node )
357     return;
358
359   if( child->getNameString() == "placement" )
360     _dirty_placements.push_back(child);
361   else
362     static_cast<canvas::Element*>(_root_group.get())
363       ->childAdded(parent, child);
364 }
365
366 //------------------------------------------------------------------------------
367 void Canvas::childRemoved( SGPropertyNode * parent,
368                            SGPropertyNode * child )
369 {
370   if( parent != _node )
371     return;
372
373   if( child->getNameString() == "placement" )
374     clearPlacements(child->getIndex());
375   else
376     static_cast<canvas::Element*>(_root_group.get())
377       ->childRemoved(parent, child);
378 }
379
380 //----------------------------------------------------------------------------
381 void Canvas::valueChanged(SGPropertyNode* node)
382 {
383   if( node->getParent()->getParent() == _node )
384   {
385     if(    !_color_background.empty()
386         && _color_background[0]->getParent() == node->getParent() )
387     {
388       _color_dirty = true;
389     }
390     else if( node->getParent()->getNameString() == "placement" )
391     {
392       // prevent double updates...
393       for( size_t i = 0; i < _dirty_placements.size(); ++i )
394       {
395         if( node->getParent() == _dirty_placements[i] )
396           return;
397       }
398
399       _dirty_placements.push_back(node->getParent());
400     }
401   }
402   else if( node->getParent() == _node )
403   {
404     if(    node->getNameString() == "mipmapping"
405         || node->getNameString() == "coverage-samples"
406         || node->getNameString() == "color-samples" )
407       _sampling_dirty = true;
408     else if( node->getNameString() == "render-always" )
409       _render_always = node->getBoolValue();
410   }
411
412   _root_group->valueChanged(node);
413 }
414
415 //------------------------------------------------------------------------------
416 GLuint Canvas::getTexId() const
417 {
418   osg::Texture2D* tex = _texture.getTexture();
419   if( !tex )
420     return 0;
421
422   osgViewer::Viewer::Contexts contexts;
423   globals->get_renderer()->getViewer()->getContexts(contexts);
424
425   if( contexts.empty() )
426     return 0;
427
428   osg::State* state = contexts[0]->getState();
429   if( !state )
430     return 0;
431
432   osg::Texture::TextureObject* tobj =
433     tex->getTextureObject( state->getContextID() );
434   if( !tobj )
435     return 0;
436
437   return tobj->_id;
438 }
439
440 //------------------------------------------------------------------------------
441 void Canvas::setStatusFlags(unsigned int flags, bool set)
442 {
443   if( set )
444     _status |= flags;
445   else
446     _status &= ~flags;
447
448   if( (_status & MISSING_SIZE_X) && (_status & MISSING_SIZE_Y) )
449     _status_msg = "Missing size";
450   else if( _status & MISSING_SIZE_X )
451     _status_msg = "Missing size-x";
452   else if( _status & MISSING_SIZE_Y )
453     _status_msg = "Missing size-y";
454   else if( _status & CREATE_FAILED )
455     _status_msg = "Creating render target failed";
456   else if( _status == STATUS_OK && !_texture.serviceable() )
457     _status_msg = "Creation pending...";
458   else
459     _status_msg = "Ok";
460 }
461
462 //------------------------------------------------------------------------------
463 void Canvas::clearPlacements(int index)
464 {
465   Placements& placements = _placements.at(index);
466   while( !placements.empty() )
467   {
468     osg::ref_ptr<osg::Group> group = placements.back();
469     placements.pop_back();
470
471     assert( group->getNumChildren() == 1 );
472     osg::Node *child = group->getChild(0);
473
474     if( group->getNumParents() )
475     {
476       osg::Group *parent = group->getParent(0);
477       parent->addChild(child);
478       parent->removeChild(group);
479     }
480
481     group->removeChild(child);
482   }
483 }
484
485 //------------------------------------------------------------------------------
486 void Canvas::clearPlacements()
487 {
488   for(size_t i = 0; i < _placements.size(); ++i)
489     clearPlacements(i);
490   _placements.clear();
491 }
492
493 //------------------------------------------------------------------------------
494 void Canvas::unbind()
495 {
496   if( !_node )
497     return;
498
499   _node->untie("size[0]");
500   _node->untie("size[1]");
501   _node->untie("view[0]");
502   _node->untie("view[1]");
503   _node->untie("status");
504   _node->untie("status-msg");
505   _node->removeChangeListener(this);
506 }