]> git.mxchange.org Git - flightgear.git/blob - src/Canvas/canvas.cxx
Expose character-aspect-ratio and do some clean up
[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     _node->tie
143     (
144       "size[0]",
145       SGRawValueMethods<Canvas, int>( *this, &Canvas::getSizeX,
146                                              &Canvas::setSizeX )
147     );
148     _node->tie
149     (
150       "size[1]",
151       SGRawValueMethods<Canvas, int>( *this, &Canvas::getSizeY,
152                                              &Canvas::setSizeY )
153     );
154     _node->tie
155     (
156       "view[0]",
157       SGRawValueMethods<Canvas, int>( *this, &Canvas::getViewWidth,
158                                              &Canvas::setViewWidth )
159     );
160     _node->tie
161     (
162       "view[1]",
163       SGRawValueMethods<Canvas, int>( *this, &Canvas::getViewHeight,
164                                              &Canvas::setViewHeight )
165     );
166     _node->tie
167     (
168       "status",
169       SGRawValueMethods<Canvas, int>(*this, &Canvas::getStatus)
170     );
171     _node->tie
172     (
173       "status-msg",
174       SGRawValueMethods<Canvas, const char*>(*this, &Canvas::getStatusMsg)
175     );
176     _node->addChangeListener(this);
177
178     canvas::linkColorNodes
179     (
180       "color-background",
181       _node,
182       _color_background,
183       osg::Vec4f(0,0,0,1)
184     );
185   }
186 }
187
188 //------------------------------------------------------------------------------
189 void Canvas::update(double delta_time_sec)
190 {
191   if( !_texture.serviceable() )
192   {
193     if( _status != STATUS_OK )
194       return;
195
196     _texture.setSize(_size_x, _size_y);
197     _texture.useImageCoords(true);
198     _texture.useStencil(true);
199     _texture.allocRT(_camera_callback);
200     _texture.getCamera()->setClearColor(osg::Vec4(0.0f, 0.0f, 0.0f , 1.0f));
201
202     for( size_t i = 0; i < _groups.size(); ++i )
203       _texture.getCamera()->addChild( _groups[i]->getMatrixTransform() );
204
205     if( _texture.serviceable() )
206     {
207       setStatusFlags(STATUS_OK);
208     }
209     else
210     {
211       setStatusFlags(CREATE_FAILED);
212       return;
213     }
214   }
215
216   for( size_t i = 0; i < _groups.size(); ++i )
217     _groups[i]->update(delta_time_sec);
218
219   if( _sampling_dirty )
220   {
221     _texture.setSampling(
222       _node->getBoolValue("mipmapping"),
223       _node->getIntValue("coverage-samples"),
224       _node->getIntValue("color-samples")
225     );
226     _sampling_dirty = false;
227   }
228   if( _color_dirty )
229   {
230     _texture.getCamera()->setClearColor
231     (
232       osg::Vec4( _color_background[0]->getFloatValue(),
233                  _color_background[1]->getFloatValue(),
234                  _color_background[2]->getFloatValue(),
235                  _color_background[3]->getFloatValue() )
236     );
237     _color_dirty = false;
238   }
239
240   while( !_dirty_placements.empty() )
241   {
242     SGPropertyNode *node = _dirty_placements.back();
243     _dirty_placements.pop_back();
244
245     if( node->getIndex() >= static_cast<int>(_placements.size()) )
246       // New placement
247       _placements.resize(node->getIndex() + 1);
248     else
249       // Remove maybe already existing placements
250       clearPlacements(node->getIndex());
251
252     // add new placements
253     _placements[node->getIndex()] = _texture.set_texture(
254       node,
255       _texture.getTexture(),
256       _cull_callback
257     );
258   }
259 }
260
261 //------------------------------------------------------------------------------
262 void Canvas::setSizeX(int sx)
263 {
264   if( _size_x == sx )
265     return;
266   _size_x = sx;
267
268   // TODO resize if texture already allocated
269
270   if( _size_x <= 0 )
271     setStatusFlags(MISSING_SIZE_X);
272   else
273     setStatusFlags(MISSING_SIZE_X, false);
274
275   // reset flag to allow creation with new size
276   setStatusFlags(CREATE_FAILED, false);
277 }
278
279 //------------------------------------------------------------------------------
280 int Canvas::getSizeX() const
281 {
282   return _size_x;
283 }
284
285 //------------------------------------------------------------------------------
286 void Canvas::setSizeY(int sy)
287 {
288   if( _size_y == sy )
289     return;
290   _size_y = sy;
291
292   // TODO resize if texture already allocated
293
294   if( _size_y <= 0 )
295     setStatusFlags(MISSING_SIZE_Y);
296   else
297     setStatusFlags(MISSING_SIZE_Y, false);
298
299   // reset flag to allow creation with new size
300   setStatusFlags(CREATE_FAILED, false);
301 }
302
303 //------------------------------------------------------------------------------
304 int Canvas::getSizeY() const
305 {
306   return _size_y;
307 }
308
309 //------------------------------------------------------------------------------
310 void Canvas::setViewWidth(int w)
311 {
312   if( _view_width == w )
313     return;
314   _view_width = w;
315
316   _texture.setViewSize(_view_width, _view_height);
317 }
318
319 //------------------------------------------------------------------------------
320 int Canvas::getViewWidth() const
321 {
322   return _view_width;
323 }
324
325 //------------------------------------------------------------------------------
326 void Canvas::setViewHeight(int h)
327 {
328   if( _view_height == h )
329     return;
330   _view_height = h;
331
332   _texture.setViewSize(_view_width, _view_height);
333 }
334
335 //------------------------------------------------------------------------------
336 int Canvas::getViewHeight() const
337 {
338   return _view_height;
339 }
340
341 //------------------------------------------------------------------------------
342 const char* Canvas::getStatusMsg() const
343 {
344   return _status_msg.c_str();
345 }
346
347 //------------------------------------------------------------------------------
348 void Canvas::childAdded( SGPropertyNode * parent,
349                          SGPropertyNode * child )
350 {
351   if( parent != _node )
352     return;
353
354   if( child->getNameString() == "group" )
355   {
356     _groups.push_back
357     (
358       boost::shared_ptr<canvas::Group>(new canvas::Group(child))
359     );
360     if( _texture.serviceable() )
361       _texture.getCamera()->addChild( _groups.back()->getMatrixTransform() );
362   }
363   else if( child->getNameString() == "placement" )
364   {
365     _dirty_placements.push_back(child);
366   }
367 }
368
369 //------------------------------------------------------------------------------
370 void Canvas::childRemoved( SGPropertyNode * parent,
371                            SGPropertyNode * child )
372 {
373   if( parent != _node )
374     return;
375
376   if( child->getNameString() == "placement" )
377     clearPlacements(child->getIndex());
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   }
409 }
410
411 //------------------------------------------------------------------------------
412 void Canvas::setStatusFlags(unsigned int flags, bool set)
413 {
414   if( set )
415     _status |= flags;
416   else
417     _status &= ~flags;
418
419   if( (_status & MISSING_SIZE_X) && (_status & MISSING_SIZE_Y) )
420     _status_msg = "Missing size";
421   else if( _status & MISSING_SIZE_X )
422     _status_msg = "Missing size-x";
423   else if( _status & MISSING_SIZE_Y )
424     _status_msg = "Missing size-y";
425   else if( _status & CREATE_FAILED )
426     _status_msg = "Creating render target failed";
427   else if( _status == STATUS_OK && !_texture.serviceable() )
428     _status_msg = "Creation pending...";
429   else
430     _status_msg = "Ok";
431 }
432
433 //------------------------------------------------------------------------------
434 void Canvas::clearPlacements(int index)
435 {
436   Placements& placements = _placements.at(index);
437   while( !placements.empty() )
438   {
439     osg::ref_ptr<osg::Group> group = placements.back();
440     placements.pop_back();
441
442     assert( group->getNumParents() == 1 );
443     assert( group->getNumChildren() == 1 );
444
445     osg::Group *parent = group->getParent(0);
446     osg::Node *child = group->getChild(0);
447
448     parent->addChild(child);
449     group->removeChild(child);
450     parent->removeChild(group);
451   }
452 }
453
454 //------------------------------------------------------------------------------
455 void Canvas::clearPlacements()
456 {
457   for(size_t i = 0; i < _placements.size(); ++i)
458     clearPlacements(i);
459   _placements.clear();
460 }
461
462 //------------------------------------------------------------------------------
463 void Canvas::unbind()
464 {
465   if( !_node )
466     return;
467
468   _node->untie("size[0]");
469   _node->untie("size[1]");
470   _node->untie("view[0]");
471   _node->untie("view[1]");
472   _node->untie("status");
473   _node->untie("status-msg");
474   _node->removeChangeListener(this);
475 }