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