]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/Canvas.cxx
3112fcb3b9133eb7f5628c4b10adbdf9e8b1f61c
[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 <simgear/canvas/MouseEvent.hxx>
21 #include <simgear/canvas/CanvasPlacement.hxx>
22 #include <simgear/scene/util/parse_color.hxx>
23 #include <simgear/scene/util/RenderConstants.hxx>
24
25 #include <osg/Camera>
26 #include <osg/Geode>
27 #include <osgText/Text>
28 #include <osgViewer/Viewer>
29
30 #include <boost/algorithm/string/predicate.hpp>
31 #include <boost/foreach.hpp>
32 #include <iostream>
33
34 namespace simgear
35 {
36 namespace canvas
37 {
38
39   //----------------------------------------------------------------------------
40   Canvas::CullCallback::CullCallback(const CanvasWeakPtr& canvas):
41     _canvas( canvas )
42   {
43
44   }
45
46   //----------------------------------------------------------------------------
47   void Canvas::CullCallback::operator()( osg::Node* node,
48                                          osg::NodeVisitor* nv )
49   {
50     if( (nv->getTraversalMask() & simgear::MODEL_BIT) && !_canvas.expired() )
51       _canvas.lock()->enableRendering();
52
53     traverse(node, nv);
54   }
55
56   //----------------------------------------------------------------------------
57   Canvas::Canvas(SGPropertyNode* node):
58     PropertyBasedElement(node),
59     _canvas_mgr(0),
60     _size_x(-1),
61     _size_y(-1),
62     _view_width(-1),
63     _view_height(-1),
64     _status(node, "status"),
65     _status_msg(node, "status-msg"),
66     _mouse_x(node, "mouse/x"),
67     _mouse_y(node, "mouse/y"),
68     _mouse_dx(node, "mouse/dx"),
69     _mouse_dy(node, "mouse/dy"),
70     _mouse_button(node, "mouse/button"),
71     _mouse_state(node, "mouse/state"),
72     _mouse_mod(node, "mouse/mod"),
73     _mouse_scroll(node, "mouse/scroll"),
74     _mouse_event(node, "mouse/event"),
75     _sampling_dirty(false),
76     _render_dirty(true),
77     _visible(true),
78     _render_always(false)
79   {
80     _status = 0;
81     setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
82   }
83
84   //----------------------------------------------------------------------------
85   Canvas::~Canvas()
86   {
87
88   }
89
90   //----------------------------------------------------------------------------
91   void Canvas::setSystemAdapter(const SystemAdapterPtr& system_adapter)
92   {
93     _system_adapter = system_adapter;
94     _texture.setSystemAdapter(system_adapter);
95   }
96
97   //----------------------------------------------------------------------------
98   SystemAdapterPtr Canvas::getSystemAdapter() const
99   {
100     return _system_adapter;
101   }
102
103   //----------------------------------------------------------------------------
104   void Canvas::setCanvasMgr(CanvasMgr* canvas_mgr)
105   {
106     _canvas_mgr = canvas_mgr;
107   }
108
109   //----------------------------------------------------------------------------
110   CanvasMgr* Canvas::getCanvasMgr() const
111   {
112     return _canvas_mgr;
113   }
114
115   //----------------------------------------------------------------------------
116   void Canvas::addDependentCanvas(const CanvasWeakPtr& canvas)
117   {
118     if( canvas.expired() )
119     {
120       SG_LOG
121       (
122         SG_GENERAL,
123         SG_WARN,
124         "Canvas::addDependentCanvas: got an expired Canvas dependent on "
125         << _node->getPath()
126       );
127       return;
128     }
129
130     _dependent_canvases.insert(canvas);
131   }
132
133   //----------------------------------------------------------------------------
134   void Canvas::removeDependentCanvas(const CanvasWeakPtr& canvas)
135   {
136     _dependent_canvases.erase(canvas);
137   }
138
139   //----------------------------------------------------------------------------
140   GroupPtr Canvas::createGroup(const std::string& name)
141   {
142     return boost::dynamic_pointer_cast<Group>
143     (
144       _root_group->createChild("group", name)
145     );
146   }
147
148   //----------------------------------------------------------------------------
149   void Canvas::enableRendering(bool force)
150   {
151     _visible = true;
152     if( force )
153       _render_dirty = true;
154   }
155
156   //----------------------------------------------------------------------------
157   void Canvas::update(double delta_time_sec)
158   {
159     if( !_texture.serviceable() )
160     {
161       if( _status != STATUS_OK )
162         return;
163
164       _texture.setSize(_size_x, _size_y);
165       _texture.useImageCoords(true);
166       _texture.useStencil(true);
167       _texture.allocRT(/*_camera_callback*/);
168
169       osg::Camera* camera = _texture.getCamera();
170
171       osg::Vec4 clear_color(0.0f, 0.0f, 0.0f , 1.0f);
172       parseColor(_node->getStringValue("background"), clear_color);
173       camera->setClearColor(clear_color);
174
175       camera->addChild(_root_group->getMatrixTransform());
176
177       // Ensure objects are drawn in order of traversal
178       camera->getOrCreateStateSet()->setBinName("TraversalOrderBin");
179
180       if( _texture.serviceable() )
181       {
182         setStatusFlags(STATUS_OK);
183       }
184       else
185       {
186         setStatusFlags(CREATE_FAILED);
187         return;
188       }
189     }
190
191     if( _visible || _render_always )
192     {
193       if( _render_dirty )
194       {
195         // Also mark all dependent (eg. recursively used) canvases as dirty
196         BOOST_FOREACH(CanvasWeakPtr canvas, _dependent_canvases)
197         {
198           if( !canvas.expired() )
199             canvas.lock()->_render_dirty = true;
200         }
201       }
202
203       _texture.setRender(_render_dirty);
204
205       _render_dirty = false;
206       _visible = false;
207     }
208     else
209       _texture.setRender(false);
210
211     _root_group->update(delta_time_sec);
212
213     if( _sampling_dirty )
214     {
215       _texture.setSampling(
216         _node->getBoolValue("mipmapping"),
217         _node->getIntValue("coverage-samples"),
218         _node->getIntValue("color-samples")
219       );
220       _sampling_dirty = false;
221       _render_dirty = true;
222     }
223
224     while( !_dirty_placements.empty() )
225     {
226       SGPropertyNode *node = _dirty_placements.back();
227       _dirty_placements.pop_back();
228
229       if( node->getIndex() >= static_cast<int>(_placements.size()) )
230         // New placement
231         _placements.resize(node->getIndex() + 1);
232       else
233         // Remove possibly existing placements
234         _placements[ node->getIndex() ].clear();
235
236       // Get new placements
237       PlacementFactoryMap::const_iterator placement_factory =
238         _placement_factories.find( node->getStringValue("type", "object") );
239       if( placement_factory != _placement_factories.end() )
240       {
241         Placements& placements = _placements[ node->getIndex() ] =
242           placement_factory->second
243           (
244             node,
245             boost::static_pointer_cast<Canvas>(_self.lock())
246           );
247         node->setStringValue
248         (
249           "status-msg",
250           placements.empty() ? "No match" : "Ok"
251         );
252       }
253       else
254         node->setStringValue("status-msg", "Unknown placement type");
255     }
256   }
257
258   //----------------------------------------------------------------------------
259   void Canvas::setSizeX(int sx)
260   {
261     if( _size_x == sx )
262       return;
263     _size_x = sx;
264
265     // TODO resize if texture already allocated
266
267     if( _size_x <= 0 )
268       setStatusFlags(MISSING_SIZE_X);
269     else
270       setStatusFlags(MISSING_SIZE_X, false);
271
272     // reset flag to allow creation with new size
273     setStatusFlags(CREATE_FAILED, false);
274   }
275
276   //----------------------------------------------------------------------------
277   void Canvas::setSizeY(int sy)
278   {
279     if( _size_y == sy )
280       return;
281     _size_y = sy;
282
283     // TODO resize if texture already allocated
284
285     if( _size_y <= 0 )
286       setStatusFlags(MISSING_SIZE_Y);
287     else
288       setStatusFlags(MISSING_SIZE_Y, false);
289
290     // reset flag to allow creation with new size
291     setStatusFlags(CREATE_FAILED, false);
292   }
293
294   //----------------------------------------------------------------------------
295   int Canvas::getSizeX() const
296   {
297     return _size_x;
298   }
299
300   //----------------------------------------------------------------------------
301   int Canvas::getSizeY() const
302   {
303     return _size_y;
304   }
305
306   //----------------------------------------------------------------------------
307   void Canvas::setViewWidth(int w)
308   {
309     if( _view_width == w )
310       return;
311     _view_width = w;
312
313     _texture.setViewSize(_view_width, _view_height);
314   }
315
316   //----------------------------------------------------------------------------
317   void Canvas::setViewHeight(int h)
318   {
319     if( _view_height == h )
320       return;
321     _view_height = h;
322
323     _texture.setViewSize(_view_width, _view_height);
324   }
325
326   //----------------------------------------------------------------------------
327   bool Canvas::handleMouseEvent(const MouseEvent& event)
328   {
329     _mouse_x = event.x;
330     _mouse_y = event.y;
331     _mouse_dx = event.dx;
332     _mouse_dy = event.dy;
333     _mouse_button = event.button;
334     _mouse_state = event.state;
335     _mouse_mod = event.mod;
336     _mouse_scroll = event.scroll;
337     // Always set event type last because all listeners are attached to it
338     _mouse_event = event.type;
339
340     if( _root_group.get() )
341       return _root_group->handleMouseEvent(event);
342     else
343       return false;
344   }
345
346   //----------------------------------------------------------------------------
347   void Canvas::childAdded( SGPropertyNode * parent,
348                            SGPropertyNode * child )
349   {
350     if( parent != _node )
351       return;
352
353     if( child->getNameString() == "placement" )
354       _dirty_placements.push_back(child);
355     else if( _root_group.get() )
356       static_cast<Element*>(_root_group.get())->childAdded(parent, child);
357   }
358
359   //----------------------------------------------------------------------------
360   void Canvas::childRemoved( SGPropertyNode * parent,
361                              SGPropertyNode * child )
362   {
363     _render_dirty = true;
364
365     if( parent != _node )
366       return;
367
368     if( child->getNameString() == "placement" )
369       _placements[ child->getIndex() ].clear();
370     else if( _root_group.get() )
371       static_cast<Element*>(_root_group.get())->childRemoved(parent, child);
372   }
373
374   //----------------------------------------------------------------------------
375   void Canvas::valueChanged(SGPropertyNode* node)
376   {
377     if(    boost::starts_with(node->getNameString(), "status")
378         || node->getParent()->getNameString() == "bounding-box" )
379       return;
380     _render_dirty = true;
381
382     bool handled = true;
383     if(    node->getParent()->getParent() == _node
384         && node->getParent()->getNameString() == "placement" )
385     {
386       bool placement_dirty = false;
387       BOOST_FOREACH(Placements& placements, _placements)
388       {
389         BOOST_FOREACH(PlacementPtr& placement, placements)
390         {
391           // check if change can be directly handled by placement
392           if(    placement->getProps() == node->getParent()
393               && !placement->childChanged(node) )
394             placement_dirty = true;
395         }
396       }
397
398       if( !placement_dirty )
399         return;
400
401       // prevent double updates...
402       for( size_t i = 0; i < _dirty_placements.size(); ++i )
403       {
404         if( node->getParent() == _dirty_placements[i] )
405           return;
406       }
407
408       _dirty_placements.push_back(node->getParent());
409     }
410     else if( node->getParent() == _node )
411     {
412       if( node->getNameString() == "background" )
413       {
414         osg::Vec4 color;
415         if( _texture.getCamera() && parseColor(node->getStringValue(), color) )
416         {
417           _texture.getCamera()->setClearColor(color);
418           _render_dirty = true;
419         }
420       }
421       else if(    node->getNameString() == "mipmapping"
422               || node->getNameString() == "coverage-samples"
423               || node->getNameString() == "color-samples" )
424       {
425         _sampling_dirty = true;
426       }
427       else if( node->getNameString() == "render-always" )
428       {
429         _render_always = node->getBoolValue();
430       }
431       else if( node->getNameString() == "size" )
432       {
433         if( node->getIndex() == 0 )
434           setSizeX( node->getIntValue() );
435         else if( node->getIndex() == 1 )
436           setSizeY( node->getIntValue() );
437       }
438       else if( node->getNameString() == "view" )
439       {
440         if( node->getIndex() == 0 )
441           setViewWidth( node->getIntValue() );
442         else if( node->getIndex() == 1 )
443           setViewHeight( node->getIntValue() );
444       }
445       else if( node->getNameString() == "freeze" )
446         _texture.setRender( node->getBoolValue() );
447       else
448         handled = false;
449     }
450     else
451       handled = false;
452
453     if( !handled && _root_group.get() )
454       _root_group->valueChanged(node);
455   }
456
457   //----------------------------------------------------------------------------
458   osg::Texture2D* Canvas::getTexture() const
459   {
460     return _texture.getTexture();
461   }
462
463   //----------------------------------------------------------------------------
464   Canvas::CullCallbackPtr Canvas::getCullCallback() const
465   {
466     return _cull_callback;
467   }
468
469   //----------------------------------------------------------------------------
470   void Canvas::addPlacementFactory( const std::string& type,
471                                     PlacementFactory factory )
472   {
473     if( _placement_factories.find(type) != _placement_factories.end() )
474       SG_LOG
475       (
476         SG_GENERAL,
477         SG_WARN,
478         "Canvas::addPlacementFactory: replace existing factor for type " << type
479       );
480
481     _placement_factories[type] = factory;
482   }
483
484   //----------------------------------------------------------------------------
485   void Canvas::setSelf(const PropertyBasedElementPtr& self)
486   {
487     PropertyBasedElement::setSelf(self);
488
489     CanvasPtr canvas = boost::static_pointer_cast<Canvas>(self);
490
491     _root_group.reset( new Group(canvas, _node) );
492
493     // Remove automatically created property listener as we forward them on our
494     // own
495     _root_group->removeListener();
496
497     _cull_callback = new CullCallback(canvas);
498   }
499
500   //----------------------------------------------------------------------------
501   void Canvas::setStatusFlags(unsigned int flags, bool set)
502   {
503     if( set )
504       _status = _status | flags;
505     else
506       _status = _status & ~flags;
507     // TODO maybe extend simgear::PropertyObject to allow |=, &= etc.
508
509     if( (_status & MISSING_SIZE_X) && (_status & MISSING_SIZE_Y) )
510       _status_msg = "Missing size";
511     else if( _status & MISSING_SIZE_X )
512       _status_msg = "Missing size-x";
513     else if( _status & MISSING_SIZE_Y )
514       _status_msg = "Missing size-y";
515     else if( _status & CREATE_FAILED )
516       _status_msg = "Creating render target failed";
517     else if( _status == STATUS_OK && !_texture.serviceable() )
518       _status_msg = "Creation pending...";
519     else
520       _status_msg = "Ok";
521   }
522
523   //----------------------------------------------------------------------------
524   Canvas::PlacementFactoryMap Canvas::_placement_factories;
525
526 } // namespace canvas
527 } // namespace simgear