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