]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/Canvas.cxx
Expose reloading Canvas placements
[simgear.git] / simgear / 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 library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Library General Public
7 // License as published by the Free Software Foundation; either
8 // version 2 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // Library General Public License for more details.
14 //
15 // You should have received a copy of the GNU Library General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
18
19 #include "Canvas.hxx"
20 #include "CanvasEventManager.hxx"
21 #include "CanvasEventVisitor.hxx"
22 #include <simgear/canvas/MouseEvent.hxx>
23 #include <simgear/canvas/CanvasPlacement.hxx>
24 #include <simgear/scene/util/parse_color.hxx>
25 #include <simgear/scene/util/RenderConstants.hxx>
26
27 #include <osg/Camera>
28 #include <osg/Geode>
29 #include <osgText/Text>
30 #include <osgViewer/Viewer>
31
32 #include <boost/algorithm/string/predicate.hpp>
33 #include <boost/foreach.hpp>
34 #include <iostream>
35
36 namespace simgear
37 {
38 namespace canvas
39 {
40
41   //----------------------------------------------------------------------------
42   Canvas::CullCallback::CullCallback(const CanvasWeakPtr& canvas):
43     _canvas( canvas )
44   {
45
46   }
47
48   //----------------------------------------------------------------------------
49   void Canvas::CullCallback::operator()( osg::Node* node,
50                                          osg::NodeVisitor* nv )
51   {
52     if( (nv->getTraversalMask() & simgear::MODEL_BIT) && !_canvas.expired() )
53       _canvas.lock()->enableRendering();
54
55     traverse(node, nv);
56   }
57
58   //----------------------------------------------------------------------------
59   Canvas::Canvas(SGPropertyNode* node):
60     PropertyBasedElement(node),
61     _canvas_mgr(0),
62     _event_manager(new EventManager),
63     _size_x(-1),
64     _size_y(-1),
65     _view_width(-1),
66     _view_height(-1),
67     _status(node, "status"),
68     _status_msg(node, "status-msg"),
69     _sampling_dirty(false),
70     _render_dirty(true),
71     _visible(true),
72     _render_always(false)
73   {
74     _status = 0;
75     setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
76   }
77
78   //----------------------------------------------------------------------------
79   Canvas::~Canvas()
80   {
81
82   }
83
84   //----------------------------------------------------------------------------
85   void Canvas::setSystemAdapter(const SystemAdapterPtr& system_adapter)
86   {
87     _system_adapter = system_adapter;
88     _texture.setSystemAdapter(system_adapter);
89   }
90
91   //----------------------------------------------------------------------------
92   SystemAdapterPtr Canvas::getSystemAdapter() const
93   {
94     return _system_adapter;
95   }
96
97   //----------------------------------------------------------------------------
98   void Canvas::setCanvasMgr(CanvasMgr* canvas_mgr)
99   {
100     _canvas_mgr = canvas_mgr;
101   }
102
103   //----------------------------------------------------------------------------
104   CanvasMgr* Canvas::getCanvasMgr() const
105   {
106     return _canvas_mgr;
107   }
108
109   //----------------------------------------------------------------------------
110   void Canvas::addDependentCanvas(const CanvasWeakPtr& canvas)
111   {
112     if( canvas.expired() )
113     {
114       SG_LOG
115       (
116         SG_GENERAL,
117         SG_WARN,
118         "Canvas::addDependentCanvas: got an expired Canvas dependent on "
119         << _node->getPath()
120       );
121       return;
122     }
123
124     _dependent_canvases.insert(canvas);
125   }
126
127   //----------------------------------------------------------------------------
128   void Canvas::removeDependentCanvas(const CanvasWeakPtr& canvas)
129   {
130     _dependent_canvases.erase(canvas);
131   }
132
133   //----------------------------------------------------------------------------
134   GroupPtr Canvas::createGroup(const std::string& name)
135   {
136     return boost::dynamic_pointer_cast<Group>
137     (
138       _root_group->createChild("group", name)
139     );
140   }
141
142   //----------------------------------------------------------------------------
143   void Canvas::enableRendering(bool force)
144   {
145     _visible = true;
146     if( force )
147       _render_dirty = true;
148   }
149
150   //----------------------------------------------------------------------------
151   void Canvas::update(double delta_time_sec)
152   {
153     if(    (!_texture.serviceable() && _status != STATUS_DIRTY)
154         || (_status & CREATE_FAILED) )
155       return;
156
157     if( _status == STATUS_DIRTY )
158     {
159       _texture.setSize(_size_x, _size_y);
160
161       if( !_texture.serviceable() )
162       {
163         _texture.useImageCoords(true);
164         _texture.useStencil(true);
165         _texture.allocRT(/*_camera_callback*/);
166       }
167       else
168       {
169         // Resizing causes a new texture to be created so we need to reapply all
170         // existing placements
171         reloadPlacements();
172       }
173
174       osg::Camera* camera = _texture.getCamera();
175
176       osg::Vec4 clear_color(0.0f, 0.0f, 0.0f , 1.0f);
177       parseColor(_node->getStringValue("background"), clear_color);
178       camera->setClearColor(clear_color);
179
180       camera->addChild(_root_group->getMatrixTransform());
181
182       // Ensure objects are drawn in order of traversal
183       camera->getOrCreateStateSet()->setBinName("TraversalOrderBin");
184
185       if( _texture.serviceable() )
186       {
187         setStatusFlags(STATUS_OK);
188         setStatusFlags(STATUS_DIRTY, false);
189         _render_dirty = true;
190       }
191       else
192       {
193         setStatusFlags(CREATE_FAILED);
194         return;
195       }
196     }
197
198     if( _visible || _render_always )
199     {
200       if( _render_dirty )
201       {
202         // Also mark all dependent (eg. recursively used) canvases as dirty
203         BOOST_FOREACH(CanvasWeakPtr canvas, _dependent_canvases)
204         {
205           if( !canvas.expired() )
206             canvas.lock()->_render_dirty = true;
207         }
208       }
209
210       _texture.setRender(_render_dirty);
211
212       _render_dirty = false;
213       _visible = false;
214     }
215     else
216       _texture.setRender(false);
217
218     _root_group->update(delta_time_sec);
219
220     if( _sampling_dirty )
221     {
222       _texture.setSampling(
223         _node->getBoolValue("mipmapping"),
224         _node->getIntValue("coverage-samples"),
225         _node->getIntValue("color-samples")
226       );
227       _sampling_dirty = false;
228       _render_dirty = true;
229     }
230
231     while( !_dirty_placements.empty() )
232     {
233       SGPropertyNode *node = _dirty_placements.back();
234       _dirty_placements.pop_back();
235
236       if( node->getIndex() >= static_cast<int>(_placements.size()) )
237         // New placement
238         _placements.resize(node->getIndex() + 1);
239       else
240         // Remove possibly existing placements
241         _placements[ node->getIndex() ].clear();
242
243       // Get new placements
244       PlacementFactoryMap::const_iterator placement_factory =
245         _placement_factories.find( node->getStringValue("type", "object") );
246       if( placement_factory != _placement_factories.end() )
247       {
248         Placements& placements = _placements[ node->getIndex() ] =
249           placement_factory->second
250           (
251             node,
252             boost::static_pointer_cast<Canvas>(_self.lock())
253           );
254         node->setStringValue
255         (
256           "status-msg",
257           placements.empty() ? "No match" : "Ok"
258         );
259       }
260       else
261         node->setStringValue("status-msg", "Unknown placement type");
262     }
263   }
264
265   //----------------------------------------------------------------------------
266   naRef Canvas::addEventListener(const nasal::CallContext& ctx)
267   {
268     if( !_root_group.get() )
269       naRuntimeError(ctx.c, "Canvas: No root group!");
270
271     return _root_group->addEventListener(ctx);
272   }
273
274   //----------------------------------------------------------------------------
275   void Canvas::setSizeX(int sx)
276   {
277     if( _size_x == sx )
278       return;
279     _size_x = sx;
280     setStatusFlags(STATUS_DIRTY);
281
282     if( _size_x <= 0 )
283       setStatusFlags(MISSING_SIZE_X);
284     else
285       setStatusFlags(MISSING_SIZE_X, false);
286
287     // reset flag to allow creation with new size
288     setStatusFlags(CREATE_FAILED, false);
289   }
290
291   //----------------------------------------------------------------------------
292   void Canvas::setSizeY(int sy)
293   {
294     if( _size_y == sy )
295       return;
296     _size_y = sy;
297     setStatusFlags(STATUS_DIRTY);
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::getSizeX() const
310   {
311     return _size_x;
312   }
313
314   //----------------------------------------------------------------------------
315   int Canvas::getSizeY() const
316   {
317     return _size_y;
318   }
319
320   //----------------------------------------------------------------------------
321   void Canvas::setViewWidth(int w)
322   {
323     if( _view_width == w )
324       return;
325     _view_width = w;
326
327     _texture.setViewSize(_view_width, _view_height);
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::getViewWidth() const
342   {
343     return _view_width;
344   }
345
346   //----------------------------------------------------------------------------
347   int Canvas::getViewHeight() const
348   {
349     return _view_height;
350   }
351
352   //----------------------------------------------------------------------------
353   bool Canvas::handleMouseEvent(const MouseEventPtr& event)
354   {
355     if( !_root_group.get() )
356       return false;
357
358     EventVisitor visitor( EventVisitor::TRAVERSE_DOWN,
359                           event->getClientPos(),
360                           event->getDelta() );
361     if( !_root_group->accept(visitor) )
362       return false;
363
364     return _event_manager->handleEvent(event, visitor.getPropagationPath());
365   }
366
367   //----------------------------------------------------------------------------
368   void Canvas::childAdded( SGPropertyNode * parent,
369                            SGPropertyNode * child )
370   {
371     if( parent != _node )
372       return;
373
374     if( child->getNameString() == "placement" )
375       _dirty_placements.push_back(child);
376     else if( _root_group.get() )
377       static_cast<Element*>(_root_group.get())->childAdded(parent, child);
378   }
379
380   //----------------------------------------------------------------------------
381   void Canvas::childRemoved( SGPropertyNode * parent,
382                              SGPropertyNode * child )
383   {
384     _render_dirty = true;
385
386     if( parent != _node )
387       return;
388
389     if( child->getNameString() == "placement" )
390       _placements[ child->getIndex() ].clear();
391     else if( _root_group.get() )
392       static_cast<Element*>(_root_group.get())->childRemoved(parent, child);
393   }
394
395   //----------------------------------------------------------------------------
396   void Canvas::valueChanged(SGPropertyNode* node)
397   {
398     if(    boost::starts_with(node->getNameString(), "status")
399         || node->getParent()->getNameString() == "bounding-box" )
400       return;
401     _render_dirty = true;
402
403     bool handled = true;
404     if(    node->getParent()->getParent() == _node
405         && node->getParent()->getNameString() == "placement" )
406     {
407       bool placement_dirty = false;
408       BOOST_FOREACH(Placements& placements, _placements)
409       {
410         BOOST_FOREACH(PlacementPtr& placement, placements)
411         {
412           // check if change can be directly handled by placement
413           if(    placement->getProps() == node->getParent()
414               && !placement->childChanged(node) )
415             placement_dirty = true;
416         }
417       }
418
419       if( !placement_dirty )
420         return;
421
422       // prevent double updates...
423       for( size_t i = 0; i < _dirty_placements.size(); ++i )
424       {
425         if( node->getParent() == _dirty_placements[i] )
426           return;
427       }
428
429       _dirty_placements.push_back(node->getParent());
430     }
431     else if( node->getParent() == _node )
432     {
433       if( node->getNameString() == "background" )
434       {
435         osg::Vec4 color;
436         if( _texture.getCamera() && parseColor(node->getStringValue(), color) )
437         {
438           _texture.getCamera()->setClearColor(color);
439           _render_dirty = true;
440         }
441       }
442       else if(    node->getNameString() == "mipmapping"
443               || node->getNameString() == "coverage-samples"
444               || node->getNameString() == "color-samples" )
445       {
446         _sampling_dirty = true;
447       }
448       else if( node->getNameString() == "render-always" )
449       {
450         _render_always = node->getBoolValue();
451       }
452       else if( node->getNameString() == "size" )
453       {
454         if( node->getIndex() == 0 )
455           setSizeX( node->getIntValue() );
456         else if( node->getIndex() == 1 )
457           setSizeY( node->getIntValue() );
458       }
459       else if( node->getNameString() == "view" )
460       {
461         if( node->getIndex() == 0 )
462           setViewWidth( node->getIntValue() );
463         else if( node->getIndex() == 1 )
464           setViewHeight( node->getIntValue() );
465       }
466       else if( node->getNameString() == "freeze" )
467         _texture.setRender( node->getBoolValue() );
468       else
469         handled = false;
470     }
471     else
472       handled = false;
473
474     if( !handled && _root_group.get() )
475       _root_group->valueChanged(node);
476   }
477
478   //----------------------------------------------------------------------------
479   osg::Texture2D* Canvas::getTexture() const
480   {
481     return _texture.getTexture();
482   }
483
484   //----------------------------------------------------------------------------
485   Canvas::CullCallbackPtr Canvas::getCullCallback() const
486   {
487     return _cull_callback;
488   }
489
490   //----------------------------------------------------------------------------
491   void Canvas::reloadPlacements(const std::string& type)
492   {
493     for(size_t i = 0; i < _placements.size(); ++i)
494     {
495       if( _placements[i].empty() )
496         continue;
497
498       SGPropertyNode* child = _placements[i].front()->getProps();
499       if(    type.empty()
500              // reload if type matches or no type specified
501           || child->getStringValue("type", type.c_str()) == type )
502       {
503         _dirty_placements.push_back(child);
504       }
505     }
506   }
507
508   //----------------------------------------------------------------------------
509   void Canvas::addPlacementFactory( const std::string& type,
510                                     PlacementFactory factory )
511   {
512     if( _placement_factories.find(type) != _placement_factories.end() )
513       SG_LOG
514       (
515         SG_GENERAL,
516         SG_WARN,
517         "Canvas::addPlacementFactory: replace existing factor for type " << type
518       );
519
520     _placement_factories[type] = factory;
521   }
522
523   //----------------------------------------------------------------------------
524   void Canvas::setSelf(const PropertyBasedElementPtr& self)
525   {
526     PropertyBasedElement::setSelf(self);
527
528     CanvasPtr canvas = boost::static_pointer_cast<Canvas>(self);
529
530     _root_group.reset( new Group(canvas, _node) );
531     _root_group->setSelf(_root_group);
532
533     // Remove automatically created property listener as we forward them on our
534     // own
535     _root_group->removeListener();
536
537     _cull_callback = new CullCallback(canvas);
538   }
539
540   //----------------------------------------------------------------------------
541   void Canvas::setStatusFlags(unsigned int flags, bool set)
542   {
543     if( set )
544       _status |= flags;
545     else
546       _status &= ~flags;
547
548     if( (_status & MISSING_SIZE_X) && (_status & MISSING_SIZE_Y) )
549       _status_msg = "Missing size";
550     else if( _status & MISSING_SIZE_X )
551       _status_msg = "Missing size-x";
552     else if( _status & MISSING_SIZE_Y )
553       _status_msg = "Missing size-y";
554     else if( _status & CREATE_FAILED )
555       _status_msg = "Creating render target failed";
556     else if( _status == STATUS_DIRTY )
557       _status_msg = "Creation pending...";
558     else
559       _status_msg = "Ok";
560   }
561
562   //----------------------------------------------------------------------------
563   Canvas::PlacementFactoryMap Canvas::_placement_factories;
564
565 } // namespace canvas
566 } // namespace simgear