]> git.mxchange.org Git - flightgear.git/blob - src/Canvas/canvas.cxx
Minor main loop/init 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
22 #include <Canvas/MouseEvent.hxx>
23 #include <Canvas/property_helper.hxx>
24 #include <Main/globals.hxx>
25 #include <Viewer/CameraGroup.hxx>
26 #include <Viewer/renderer.hxx>
27
28 #include <simgear/scene/util/RenderConstants.hxx>
29
30 #include <osg/Camera>
31 #include <osg/Geode>
32 #include <osgText/Text>
33 #include <osgViewer/Viewer>
34
35 #include <boost/algorithm/string/predicate.hpp>
36 #include <iostream>
37
38 //----------------------------------------------------------------------------
39 Canvas::CameraCullCallback::CameraCullCallback():
40   _render( true ),
41   _render_frame( 0 )
42 {
43
44 }
45
46 //----------------------------------------------------------------------------
47 void Canvas::CameraCullCallback::enableRendering()
48 {
49   _render = true;
50 }
51
52 //----------------------------------------------------------------------------
53 void Canvas::CameraCullCallback::operator()( osg::Node* node,
54                                              osg::NodeVisitor* nv )
55 {
56   if( !_render && nv->getTraversalNumber() != _render_frame )
57     return;
58
59   traverse(node, nv);
60
61   _render = false;
62   _render_frame = nv->getTraversalNumber();
63 }
64
65 //----------------------------------------------------------------------------
66 Canvas::CullCallback::CullCallback(CameraCullCallback* camera_cull):
67   _camera_cull( camera_cull )
68 {
69
70 }
71
72 //----------------------------------------------------------------------------
73 void Canvas::CullCallback::operator()( osg::Node* node,
74                                        osg::NodeVisitor* nv )
75 {
76   if( (nv->getTraversalMask() & simgear::MODEL_BIT) && _camera_cull.valid() )
77     _camera_cull->enableRendering();
78
79   traverse(node, nv);
80 }
81
82 //------------------------------------------------------------------------------
83 Canvas::Canvas(SGPropertyNode* node):
84   PropertyBasedElement(node),
85   _size_x(-1),
86   _size_y(-1),
87   _view_width(-1),
88   _view_height(-1),
89   _status(node, "status"),
90   _status_msg(node, "status-msg"),
91   _mouse_x(node, "mouse/x"),
92   _mouse_y(node, "mouse/y"),
93   _mouse_dx(node, "mouse/dx"),
94   _mouse_dy(node, "mouse/dy"),
95   _mouse_button(node, "mouse/button"),
96   _mouse_state(node, "mouse/state"),
97   _mouse_mod(node, "mouse/mod"),
98   _mouse_scroll(node, "mouse/scroll"),
99   _mouse_event(node, "mouse/event"),
100   _sampling_dirty(false),
101   _color_dirty(true),
102   _root_group( new canvas::Group(node) ),
103   _render_always(false)
104 {
105   // Remove automatically created property listener as we forward them on our
106   // own
107   _root_group->removeListener();
108
109   _status = 0;
110   setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
111
112   _camera_callback = new CameraCullCallback;
113   _cull_callback = new CullCallback(_camera_callback);
114
115   canvas::linkColorNodes
116   (
117     "color-background",
118     _node,
119     _color_background,
120     osg::Vec4f(0,0,0,1)
121   );
122 }
123
124 //------------------------------------------------------------------------------
125 Canvas::~Canvas()
126 {
127
128 }
129
130 //------------------------------------------------------------------------------
131 void Canvas::update(double delta_time_sec)
132 {
133   if( !_texture.serviceable() )
134   {
135     if( _status != STATUS_OK )
136       return;
137
138     _texture.setSize(_size_x, _size_y);
139     _texture.useImageCoords(true);
140     _texture.useStencil(true);
141     _texture.allocRT(_camera_callback);
142     _texture.getCamera()->setClearColor(osg::Vec4(0.0f, 0.0f, 0.0f , 1.0f));
143     _texture.getCamera()->addChild(_root_group->getMatrixTransform());
144
145     if( _texture.serviceable() )
146     {
147       setStatusFlags(STATUS_OK);
148     }
149     else
150     {
151       setStatusFlags(CREATE_FAILED);
152       return;
153     }
154   }
155
156   _root_group->update(delta_time_sec);
157
158   if( _sampling_dirty )
159   {
160     _texture.setSampling(
161       _node->getBoolValue("mipmapping"),
162       _node->getIntValue("coverage-samples"),
163       _node->getIntValue("color-samples")
164     );
165     _sampling_dirty = false;
166   }
167   if( _color_dirty )
168   {
169     _texture.getCamera()->setClearColor
170     (
171       osg::Vec4( _color_background[0]->getFloatValue(),
172                  _color_background[1]->getFloatValue(),
173                  _color_background[2]->getFloatValue(),
174                  _color_background[3]->getFloatValue() )
175     );
176     _color_dirty = false;
177   }
178
179   while( !_dirty_placements.empty() )
180   {
181     SGPropertyNode *node = _dirty_placements.back();
182     _dirty_placements.pop_back();
183
184     if( node->getIndex() >= static_cast<int>(_placements.size()) )
185       // New placement
186       _placements.resize(node->getIndex() + 1);
187     else
188       // Remove possibly existing placements
189       _placements[ node->getIndex() ].clear();
190
191     // Get new placements
192     PlacementFactoryMap::const_iterator placement_factory =
193       _placement_factories.find( node->getStringValue("type", "object") );
194     if( placement_factory != _placement_factories.end() )
195     {
196       canvas::Placements& placements =
197         _placements[ node->getIndex() ] =
198           placement_factory->second
199           (
200             node,
201             boost::static_pointer_cast<Canvas>(_self.lock())
202           );
203       node->setStringValue
204       (
205         "status-msg",
206         placements.empty() ? "No match" : "Ok"
207       );
208     }
209     else
210       node->setStringValue("status-msg", "Unknown placement type");
211   }
212
213   if( _render_always )
214     _camera_callback->enableRendering();
215 }
216
217 //------------------------------------------------------------------------------
218 void Canvas::setSizeX(int sx)
219 {
220   if( _size_x == sx )
221     return;
222   _size_x = sx;
223
224   // TODO resize if texture already allocated
225
226   if( _size_x <= 0 )
227     setStatusFlags(MISSING_SIZE_X);
228   else
229     setStatusFlags(MISSING_SIZE_X, false);
230
231   // reset flag to allow creation with new size
232   setStatusFlags(CREATE_FAILED, false);
233 }
234
235 //------------------------------------------------------------------------------
236 void Canvas::setSizeY(int sy)
237 {
238   if( _size_y == sy )
239     return;
240   _size_y = sy;
241
242   // TODO resize if texture already allocated
243
244   if( _size_y <= 0 )
245     setStatusFlags(MISSING_SIZE_Y);
246   else
247     setStatusFlags(MISSING_SIZE_Y, false);
248
249   // reset flag to allow creation with new size
250   setStatusFlags(CREATE_FAILED, false);
251 }
252
253 //------------------------------------------------------------------------------
254 void Canvas::setViewWidth(int w)
255 {
256   if( _view_width == w )
257     return;
258   _view_width = w;
259
260   _texture.setViewSize(_view_width, _view_height);
261 }
262
263 //------------------------------------------------------------------------------
264 void Canvas::setViewHeight(int h)
265 {
266   if( _view_height == h )
267     return;
268   _view_height = h;
269
270   _texture.setViewSize(_view_width, _view_height);
271 }
272
273 //------------------------------------------------------------------------------
274 bool Canvas::handleMouseEvent(const canvas::MouseEvent& event)
275 {
276   _mouse_x = event.x;
277   _mouse_y = event.y;
278   _mouse_dx = event.dx;
279   _mouse_dy = event.dy;
280   _mouse_button = event.button;
281   _mouse_state = event.state;
282   _mouse_mod = event.mod;
283   _mouse_scroll = event.scroll;
284   // Always set event type last because all listeners are attached to it
285   _mouse_event = event.type;
286
287   return _root_group->handleMouseEvent(event);
288 }
289
290 //------------------------------------------------------------------------------
291 void Canvas::childAdded( SGPropertyNode * parent,
292                          SGPropertyNode * child )
293 {
294   if( parent != _node )
295     return;
296
297   if( child->getNameString() == "placement" )
298     _dirty_placements.push_back(child);
299   else
300     static_cast<canvas::Element*>(_root_group.get())
301       ->childAdded(parent, child);
302 }
303
304 //------------------------------------------------------------------------------
305 void Canvas::childRemoved( SGPropertyNode * parent,
306                            SGPropertyNode * child )
307 {
308   if( parent != _node )
309     return;
310
311   if( child->getNameString() == "placement" )
312     _placements[ child->getIndex() ].clear();
313   else
314     static_cast<canvas::Element*>(_root_group.get())
315       ->childRemoved(parent, child);
316 }
317
318 //----------------------------------------------------------------------------
319 void Canvas::valueChanged(SGPropertyNode* node)
320 {
321   if( boost::starts_with(node->getNameString(), "status") )
322     return;
323
324   bool handled = true;
325   if( node->getParent()->getParent() == _node )
326   {
327     if(    !_color_background.empty()
328         && _color_background[0]->getParent() == node->getParent() )
329     {
330       _color_dirty = true;
331     }
332     else if( node->getParent()->getNameString() == "placement" )
333     {
334       // prevent double updates...
335       for( size_t i = 0; i < _dirty_placements.size(); ++i )
336       {
337         if( node->getParent() == _dirty_placements[i] )
338           return;
339       }
340
341       _dirty_placements.push_back(node->getParent());
342     }
343     else
344       handled = false;
345   }
346   else if( node->getParent() == _node )
347   {
348     if(    node->getNameString() == "mipmapping"
349         || node->getNameString() == "coverage-samples"
350         || node->getNameString() == "color-samples" )
351       _sampling_dirty = true;
352     else if( node->getNameString() == "render-always" )
353       _render_always = node->getBoolValue();
354     else if( node->getNameString() == "size" )
355     {
356       if( node->getIndex() == 0 )
357         setSizeX( node->getIntValue() );
358       else if( node->getIndex() == 1 )
359         setSizeY( node->getIntValue() );
360     }
361     else if( node->getNameString() == "view" )
362     {
363       if( node->getIndex() == 0 )
364         setViewWidth( node->getIntValue() );
365       else if( node->getIndex() == 1 )
366         setViewHeight( node->getIntValue() );
367     }
368     else
369       handled = false;
370   }
371   else
372     handled = false;
373
374   if( !handled )
375     _root_group->valueChanged(node);
376 }
377
378 //------------------------------------------------------------------------------
379 osg::Texture2D* Canvas::getTexture() const
380 {
381   return _texture.getTexture();
382 }
383
384 //------------------------------------------------------------------------------
385 GLuint Canvas::getTexId() const
386 {
387   osg::Texture2D* tex = _texture.getTexture();
388   if( !tex )
389     return 0;
390
391 //  osgViewer::Viewer::Contexts contexts;
392 //  globals->get_renderer()->getViewer()->getContexts(contexts);
393 //
394 //  if( contexts.empty() )
395 //    return 0;
396
397   osg::Camera* guiCamera =
398     flightgear::getGUICamera(flightgear::CameraGroup::getDefault());
399
400   osg::State* state = guiCamera->getGraphicsContext()->getState(); //contexts[0]->getState();
401   if( !state )
402     return 0;
403
404   osg::Texture::TextureObject* tobj =
405     tex->getTextureObject( state->getContextID() );
406   if( !tobj )
407     return 0;
408
409   return tobj->_id;
410 }
411
412 //------------------------------------------------------------------------------
413 Canvas::CameraCullCallbackPtr Canvas::getCameraCullCallback() const
414 {
415   return _camera_callback;
416 }
417
418 //----------------------------------------------------------------------------
419 Canvas::CullCallbackPtr Canvas::getCullCallback() const
420 {
421   return _cull_callback;
422 }
423
424 //------------------------------------------------------------------------------
425 void Canvas::addPlacementFactory( const std::string& type,
426                                   canvas::PlacementFactory factory )
427 {
428   if( _placement_factories.find(type) != _placement_factories.end() )
429     SG_LOG
430     (
431       SG_GENERAL,
432       SG_WARN,
433       "Canvas::addPlacementFactory: replace existing factor for type " << type
434     );
435
436   _placement_factories[type] = factory;
437 }
438
439 //------------------------------------------------------------------------------
440 void Canvas::setStatusFlags(unsigned int flags, bool set)
441 {
442   if( set )
443     _status = _status | flags;
444   else
445     _status = _status & ~flags;
446   // TODO maybe extend simgear::PropertyObject to allow |=, &= etc.
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 Canvas::PlacementFactoryMap Canvas::_placement_factories;