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