]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/Canvas.cxx
Fix #1783: repeated error message on console
[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 "CanvasPlacement.hxx"
23 #include <simgear/canvas/events/KeyboardEvent.hxx>
24 #include <simgear/canvas/events/MouseEvent.hxx>
25 #include <simgear/scene/util/parse_color.hxx>
26 #include <simgear/scene/util/RenderConstants.hxx>
27
28 #include <osg/Camera>
29 #include <osg/Geode>
30 #include <osgText/Text>
31 #include <osgViewer/Viewer>
32
33 #include <boost/algorithm/string/predicate.hpp>
34 #include <boost/foreach.hpp>
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) )
53     {
54       CanvasPtr canvas = _canvas.lock();
55       if( canvas )
56         canvas->enableRendering();
57     }
58
59     traverse(node, nv);
60   }
61
62   //----------------------------------------------------------------------------
63   Canvas::Canvas(SGPropertyNode* node):
64     PropertyBasedElement(node),
65     _canvas_mgr(0),
66     _event_manager(new EventManager),
67     _size_x(-1),
68     _size_y(-1),
69     _view_width(-1),
70     _view_height(-1),
71     _status(node, "status"),
72     _status_msg(node, "status-msg"),
73     _sampling_dirty(false),
74     _render_dirty(true),
75     _visible(true),
76     _render_always(false)
77   {
78     _status = 0;
79     setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
80
81     _root_group.reset( new Group(this, _node) );
82
83     // Remove automatically created property listener as we forward them on our
84     // own
85     _root_group->removeListener();
86     _cull_callback = new CullCallback(this);
87   }
88
89   //----------------------------------------------------------------------------
90   Canvas::~Canvas()
91   {
92
93   }
94
95   //----------------------------------------------------------------------------
96   void Canvas::onDestroy()
97   {
98     if( _root_group )
99     {
100       _root_group->clearEventListener();
101       _root_group->onDestroy();
102     }
103   }
104
105   //----------------------------------------------------------------------------
106   void Canvas::setCanvasMgr(CanvasMgr* canvas_mgr)
107   {
108     _canvas_mgr = canvas_mgr;
109   }
110
111   //----------------------------------------------------------------------------
112   CanvasMgr* Canvas::getCanvasMgr() const
113   {
114     return _canvas_mgr;
115   }
116
117   //----------------------------------------------------------------------------
118   bool Canvas::isInit() const
119   {
120     return _texture.serviceable();
121   }
122
123   //----------------------------------------------------------------------------
124   void Canvas::addParentCanvas(const CanvasWeakPtr& canvas)
125   {
126     if( canvas.expired() )
127     {
128       SG_LOG
129       (
130         SG_GENERAL,
131         SG_WARN,
132         "Canvas::addParentCanvas(" << _node->getPath(true) << "): "
133         "got an expired parent!"
134       );
135       return;
136     }
137
138     _parent_canvases.insert(canvas);
139   }
140
141   //----------------------------------------------------------------------------
142   void Canvas::addChildCanvas(const CanvasWeakPtr& canvas)
143   {
144     if( canvas.expired() )
145     {
146       SG_LOG
147       (
148         SG_GENERAL,
149         SG_WARN,
150         "Canvas::addChildCanvas(" << _node->getPath(true) << "): "
151         " got an expired child!"
152       );
153       return;
154     }
155
156     _child_canvases.insert(canvas);
157   }
158
159   //----------------------------------------------------------------------------
160   void Canvas::removeParentCanvas(const CanvasWeakPtr& canvas)
161   {
162     _parent_canvases.erase(canvas);
163   }
164
165   //----------------------------------------------------------------------------
166   void Canvas::removeChildCanvas(const CanvasWeakPtr& canvas)
167   {
168     _child_canvases.erase(canvas);
169   }
170
171   //----------------------------------------------------------------------------
172   GroupPtr Canvas::createGroup(const std::string& name)
173   {
174     return _root_group->createChild<Group>(name);
175   }
176
177   //----------------------------------------------------------------------------
178   GroupPtr Canvas::getGroup(const std::string& name)
179   {
180     return _root_group->getChild<Group>(name);
181   }
182
183   //----------------------------------------------------------------------------
184   GroupPtr Canvas::getOrCreateGroup(const std::string& name)
185   {
186     return _root_group->getOrCreateChild<Group>(name);
187   }
188
189   //----------------------------------------------------------------------------
190   GroupPtr Canvas::getRootGroup()
191   {
192     return _root_group;
193   }
194
195   //----------------------------------------------------------------------------
196   void Canvas::setLayout(const LayoutRef& layout)
197   {
198     _layout = layout;
199     _layout->setCanvas(this);
200   }
201
202   //----------------------------------------------------------------------------
203   void Canvas::setFocusElement(const ElementPtr& el)
204   {
205     if( el && el->getCanvas().lock() != this )
206     {
207       SG_LOG(SG_GUI, SG_WARN, "setFocusElement: element not from this canvas");
208       return;
209     }
210
211     // TODO focus out/in events
212     _focus_element = el;
213   }
214
215   //----------------------------------------------------------------------------
216   void Canvas::clearFocusElement()
217   {
218     _focus_element.reset();
219   }
220
221   //----------------------------------------------------------------------------
222   void Canvas::enableRendering(bool force)
223   {
224     _visible = true;
225     if( force )
226       _render_dirty = true;
227   }
228
229   //----------------------------------------------------------------------------
230   void Canvas::update(double delta_time_sec)
231   {
232     if( _status & (CREATE_FAILED | MISSING_SIZE) )
233       return;
234
235     if( _status & STATUS_DIRTY )
236     {
237       _texture.setSize(_size_x, _size_y);
238
239       if( !_texture.serviceable() )
240       {
241         _texture.useImageCoords(true);
242         _texture.useStencil(true);
243         _texture.allocRT(/*_camera_callback*/);
244       }
245       else
246       {
247         // Resizing causes a new texture to be created so we need to reapply all
248         // existing placements
249         reloadPlacements();
250       }
251
252       osg::Camera* camera = _texture.getCamera();
253
254       // TODO Allow custom render order? For now just keep in order with
255       //      property tree.
256       camera->setRenderOrder(osg::Camera::PRE_RENDER, _node->getIndex());
257
258       osg::Vec4 clear_color(0.0f, 0.0f, 0.0f , 1.0f);
259       parseColor(_node->getStringValue("background"), clear_color);
260       camera->setClearColor(clear_color);
261
262       camera->addChild(_root_group->getMatrixTransform());
263
264       if( _texture.serviceable() )
265       {
266         setStatusFlags(STATUS_OK);
267         setStatusFlags(STATUS_DIRTY, false);
268         _render_dirty = true;
269       }
270       else
271       {
272         setStatusFlags(CREATE_FAILED);
273         return;
274       }
275     }
276
277     if( _layout )
278       _layout->setGeometry(SGRecti(0, 0, _view_width, _view_height));
279
280     if( _visible || _render_always )
281     {
282       BOOST_FOREACH(CanvasWeakPtr canvas_weak, _child_canvases)
283       {
284         // TODO should we check if the image the child canvas is displayed
285         //      within is really visible?
286         CanvasPtr canvas = canvas_weak.lock();
287         if( canvas )
288           canvas->_visible = true;
289       }
290
291       if( _render_dirty )
292       {
293         // Also mark all canvases this canvas is displayed within as dirty
294         BOOST_FOREACH(CanvasWeakPtr canvas_weak, _parent_canvases)
295         {
296           CanvasPtr canvas = canvas_weak.lock();
297           if( canvas )
298             canvas->_render_dirty = true;
299         }
300       }
301
302       _texture.setRender(_render_dirty);
303
304       _render_dirty = false;
305       _visible = false;
306     }
307     else
308       _texture.setRender(false);
309
310     _root_group->update(delta_time_sec);
311
312     if( _sampling_dirty )
313     {
314       _texture.setSampling(
315         _node->getBoolValue("mipmapping"),
316         _node->getIntValue("coverage-samples"),
317         _node->getIntValue("color-samples")
318       );
319       _sampling_dirty = false;
320       _render_dirty = true;
321     }
322
323     while( !_dirty_placements.empty() )
324     {
325       SGPropertyNode *node = _dirty_placements.back();
326       _dirty_placements.pop_back();
327
328       if( node->getIndex() >= static_cast<int>(_placements.size()) )
329         // New placement
330         _placements.resize(node->getIndex() + 1);
331       else
332         // Remove possibly existing placements
333         _placements[ node->getIndex() ].clear();
334
335       // Get new placements
336       PlacementFactoryMap::const_iterator placement_factory =
337         _placement_factories.find( node->getStringValue("type", "object") );
338       if( placement_factory != _placement_factories.end() )
339       {
340         Placements& placements = _placements[ node->getIndex() ] =
341           placement_factory->second(node, this);
342         node->setStringValue
343         (
344           "status-msg",
345           placements.empty() ? "No match" : "Ok"
346         );
347       }
348       else
349         node->setStringValue("status-msg", "Unknown placement type");
350     }
351   }
352
353   //----------------------------------------------------------------------------
354   bool Canvas::addEventListener( const std::string& type,
355                                  const EventListener& cb )
356   {
357     if( !_root_group.get() )
358       throw std::runtime_error("Canvas::addEventListener: no root group!");
359
360     return _root_group->addEventListener(type, cb);
361   }
362
363   //----------------------------------------------------------------------------
364   bool Canvas::dispatchEvent(const EventPtr& event)
365   {
366     if( !_root_group.get() )
367       throw std::runtime_error("Canvas::dispatchEvent: no root group!");
368
369     return _root_group->dispatchEvent(event);
370   }
371
372   //----------------------------------------------------------------------------
373   void Canvas::setSizeX(int sx)
374   {
375     if( _size_x == sx )
376       return;
377     _size_x = sx;
378     setStatusFlags(STATUS_DIRTY);
379
380     if( _size_x <= 0 )
381       setStatusFlags(MISSING_SIZE_X);
382     else
383       setStatusFlags(MISSING_SIZE_X, false);
384
385     // reset flag to allow creation with new size
386     setStatusFlags(CREATE_FAILED, false);
387   }
388
389   //----------------------------------------------------------------------------
390   void Canvas::setSizeY(int sy)
391   {
392     if( _size_y == sy )
393       return;
394     _size_y = sy;
395     setStatusFlags(STATUS_DIRTY);
396
397     if( _size_y <= 0 )
398       setStatusFlags(MISSING_SIZE_Y);
399     else
400       setStatusFlags(MISSING_SIZE_Y, false);
401
402     // reset flag to allow creation with new size
403     setStatusFlags(CREATE_FAILED, false);
404   }
405
406   //----------------------------------------------------------------------------
407   int Canvas::getSizeX() const
408   {
409     return _size_x;
410   }
411
412   //----------------------------------------------------------------------------
413   int Canvas::getSizeY() const
414   {
415     return _size_y;
416   }
417
418   //----------------------------------------------------------------------------
419   void Canvas::setViewWidth(int w)
420   {
421     if( _view_width == w )
422       return;
423     _view_width = w;
424
425     _texture.setViewSize(_view_width, _view_height);
426   }
427
428   //----------------------------------------------------------------------------
429   void Canvas::setViewHeight(int h)
430   {
431     if( _view_height == h )
432       return;
433     _view_height = h;
434
435     _texture.setViewSize(_view_width, _view_height);
436   }
437
438   //----------------------------------------------------------------------------
439   int Canvas::getViewWidth() const
440   {
441     return _texture.getViewSize().x();
442   }
443
444   //----------------------------------------------------------------------------
445   int Canvas::getViewHeight() const
446   {
447     return _texture.getViewSize().y();
448   }
449
450   //----------------------------------------------------------------------------
451   SGRect<int> Canvas::getViewport() const
452   {
453     return SGRect<int>(0, 0, getViewWidth(), getViewHeight());
454   }
455
456   //----------------------------------------------------------------------------
457   bool Canvas::handleMouseEvent(const MouseEventPtr& event)
458   {
459     if( !_root_group )
460       return false;
461
462     EventVisitor visitor( EventVisitor::TRAVERSE_DOWN,
463                           event->getClientPos(),
464                           _root_group );
465     if( !_root_group->accept(visitor) )
466       return false;
467
468     return _event_manager->handleEvent(event, visitor.getPropagationPath());
469   }
470
471   //----------------------------------------------------------------------------
472   bool Canvas::handleKeyboardEvent(const KeyboardEventPtr& event)
473   {
474     ElementPtr target = _focus_element.lock();
475     if( !target )
476       target = _root_group;
477     if( !target )
478       return false;
479
480     return target->dispatchEvent(event);
481   }
482
483   //----------------------------------------------------------------------------
484   bool Canvas::propagateEvent( EventPtr const& event,
485                                EventPropagationPath const& path )
486   {
487     return _event_manager->propagateEvent(event, path);
488   }
489
490   //----------------------------------------------------------------------------
491   void Canvas::childAdded( SGPropertyNode * parent,
492                            SGPropertyNode * child )
493   {
494     if( parent != _node )
495       return;
496
497     if( child->getNameString() == "placement" )
498       _dirty_placements.push_back(child);
499     else if( _root_group.get() )
500       static_cast<Element*>(_root_group.get())->childAdded(parent, child);
501   }
502
503   //----------------------------------------------------------------------------
504   void Canvas::childRemoved( SGPropertyNode * parent,
505                              SGPropertyNode * child )
506   {
507     _render_dirty = true;
508
509     if( parent != _node )
510       return;
511
512     if( child->getNameString() == "placement" )
513       _placements[ child->getIndex() ].clear();
514     else if( _root_group.get() )
515       static_cast<Element*>(_root_group.get())->childRemoved(parent, child);
516   }
517
518   //----------------------------------------------------------------------------
519   void Canvas::valueChanged(SGPropertyNode* node)
520   {
521     const std::string& name = node->getNameString();
522
523     if(    boost::starts_with(name, "status")
524         || boost::starts_with(name, "data-") )
525       return;
526     _render_dirty = true;
527
528     bool handled = true;
529     if(    node->getParent()->getParent() == _node
530         && node->getParent()->getNameString() == "placement" )
531     {
532       size_t index = node->getIndex();
533       if( index < _placements.size() )
534       {
535         Placements& placements = _placements[index];
536         if( !placements.empty() )
537         {
538           bool placement_dirty = false;
539           BOOST_FOREACH(PlacementPtr& placement, placements)
540           {
541             // check if change can be directly handled by placement
542             if(    placement->getProps() == node->getParent()
543                 && !placement->childChanged(node) )
544               placement_dirty = true;
545           }
546
547           if( !placement_dirty )
548             return;
549         }
550       }
551
552       // prevent double updates...
553       for( size_t i = 0; i < _dirty_placements.size(); ++i )
554       {
555         if( node->getParent() == _dirty_placements[i] )
556           return;
557       }
558
559       _dirty_placements.push_back(node->getParent());
560     }
561     else if( node->getParent() == _node )
562     {
563       if( name == "background" )
564       {
565         osg::Vec4 color;
566         if( _texture.getCamera() && parseColor(node->getStringValue(), color) )
567         {
568           _texture.getCamera()->setClearColor(color);
569           _render_dirty = true;
570         }
571       }
572       else if(   name == "mipmapping"
573               || name == "coverage-samples"
574               || name == "color-samples" )
575       {
576         _sampling_dirty = true;
577       }
578       else if( name == "additive-blend" )
579       {
580         _texture.useAdditiveBlend( node->getBoolValue() );
581       }
582       else if( name == "render-always" )
583       {
584         _render_always = node->getBoolValue();
585       }
586       else if( name == "size" )
587       {
588         if( node->getIndex() == 0 )
589           setSizeX( node->getIntValue() );
590         else if( node->getIndex() == 1 )
591           setSizeY( node->getIntValue() );
592       }
593       else if( name == "update" )
594       {
595         if( _root_group )
596           _root_group->update(0);
597         return update(0);
598       }
599       else if( name == "view" )
600       {
601         if( node->getIndex() == 0 )
602           setViewWidth( node->getIntValue() );
603         else if( node->getIndex() == 1 )
604           setViewHeight( node->getIntValue() );
605       }
606       else if( name == "freeze" )
607         _texture.setRender( node->getBoolValue() );
608       else
609         handled = false;
610     }
611     else
612       handled = false;
613
614     if( !handled && _root_group.get() )
615       _root_group->valueChanged(node);
616   }
617
618   //----------------------------------------------------------------------------
619   osg::Texture2D* Canvas::getTexture() const
620   {
621     return _texture.getTexture();
622   }
623
624   //----------------------------------------------------------------------------
625   Canvas::CullCallbackPtr Canvas::getCullCallback() const
626   {
627     return _cull_callback;
628   }
629
630   //----------------------------------------------------------------------------
631   void Canvas::reloadPlacements(const std::string& type)
632   {
633     for(size_t i = 0; i < _placements.size(); ++i)
634     {
635       if( _placements[i].empty() )
636         continue;
637
638       SGPropertyNode* child = _placements[i].front()->getProps();
639       if(    type.empty()
640              // reload if type matches or no type specified
641           || child->getStringValue("type", type.c_str()) == type )
642       {
643         _dirty_placements.push_back(child);
644       }
645     }
646   }
647
648   //----------------------------------------------------------------------------
649   void Canvas::addPlacementFactory( const std::string& type,
650                                     PlacementFactory factory )
651   {
652     if( _placement_factories.find(type) != _placement_factories.end() )
653       SG_LOG
654       (
655         SG_GENERAL,
656         SG_WARN,
657         "Canvas::addPlacementFactory: replace existing factory '" << type << "'"
658       );
659
660     _placement_factories[type] = factory;
661   }
662
663   //----------------------------------------------------------------------------
664   void Canvas::removePlacementFactory(const std::string& type)
665   {
666     PlacementFactoryMap::iterator it = _placement_factories.find(type);
667     if( it == _placement_factories.end() )
668       SG_LOG
669       (
670         SG_GENERAL,
671         SG_WARN,
672         "Canvas::removePlacementFactory: no such factory '" << type << "'"
673       );
674     else
675       _placement_factories.erase(it);
676   }
677
678
679   //----------------------------------------------------------------------------
680   void Canvas::setSystemAdapter(const SystemAdapterPtr& system_adapter)
681   {
682     _system_adapter = system_adapter;
683   }
684
685   //----------------------------------------------------------------------------
686   SystemAdapterPtr Canvas::getSystemAdapter()
687   {
688     return _system_adapter;
689   }
690
691   //----------------------------------------------------------------------------
692   void Canvas::setStatusFlags(unsigned int flags, bool set)
693   {
694     if( set )
695       _status |= flags;
696     else
697       _status &= ~flags;
698
699     if( (_status & MISSING_SIZE_X) && (_status & MISSING_SIZE_Y) )
700       _status_msg = "Missing size";
701     else if( _status & MISSING_SIZE_X )
702       _status_msg = "Missing size-x";
703     else if( _status & MISSING_SIZE_Y )
704       _status_msg = "Missing size-y";
705     else if( _status & CREATE_FAILED )
706       _status_msg = "Creating render target failed";
707     else if( _status & STATUS_DIRTY )
708       _status_msg = "Creation pending...";
709     else
710       _status_msg = "Ok";
711   }
712
713   //----------------------------------------------------------------------------
714   Canvas::PlacementFactoryMap Canvas::_placement_factories;
715   SystemAdapterPtr Canvas::_system_adapter;
716
717 } // namespace canvas
718 } // namespace simgear