1 // The canvas for rendering with the 2d API
3 // Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
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.
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.
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
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>
29 #include <osgText/Text>
30 #include <osgViewer/Viewer>
32 #include <boost/algorithm/string/predicate.hpp>
33 #include <boost/foreach.hpp>
41 //----------------------------------------------------------------------------
42 Canvas::CullCallback::CullCallback(const CanvasWeakPtr& canvas):
48 //----------------------------------------------------------------------------
49 void Canvas::CullCallback::operator()( osg::Node* node,
50 osg::NodeVisitor* nv )
52 if( (nv->getTraversalMask() & simgear::MODEL_BIT) && !_canvas.expired() )
53 _canvas.lock()->enableRendering();
58 //----------------------------------------------------------------------------
59 Canvas::Canvas(SGPropertyNode* node):
60 PropertyBasedElement(node),
62 _event_manager(new EventManager),
67 _status(node, "status"),
68 _status_msg(node, "status-msg"),
69 _mouse_x(node, "mouse/x"),
70 _mouse_y(node, "mouse/y"),
71 _mouse_dx(node, "mouse/dx"),
72 _mouse_dy(node, "mouse/dy"),
73 _mouse_button(node, "mouse/button"),
74 _mouse_state(node, "mouse/state"),
75 _mouse_mod(node, "mouse/mod"),
76 _mouse_scroll(node, "mouse/scroll"),
77 _mouse_event(node, "mouse/event"),
78 _sampling_dirty(false),
84 setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
87 //----------------------------------------------------------------------------
93 //----------------------------------------------------------------------------
94 void Canvas::setSystemAdapter(const SystemAdapterPtr& system_adapter)
96 _system_adapter = system_adapter;
97 _texture.setSystemAdapter(system_adapter);
100 //----------------------------------------------------------------------------
101 SystemAdapterPtr Canvas::getSystemAdapter() const
103 return _system_adapter;
106 //----------------------------------------------------------------------------
107 void Canvas::setCanvasMgr(CanvasMgr* canvas_mgr)
109 _canvas_mgr = canvas_mgr;
112 //----------------------------------------------------------------------------
113 CanvasMgr* Canvas::getCanvasMgr() const
118 //----------------------------------------------------------------------------
119 void Canvas::addDependentCanvas(const CanvasWeakPtr& canvas)
121 if( canvas.expired() )
127 "Canvas::addDependentCanvas: got an expired Canvas dependent on "
133 _dependent_canvases.insert(canvas);
136 //----------------------------------------------------------------------------
137 void Canvas::removeDependentCanvas(const CanvasWeakPtr& canvas)
139 _dependent_canvases.erase(canvas);
142 //----------------------------------------------------------------------------
143 GroupPtr Canvas::createGroup(const std::string& name)
145 return boost::dynamic_pointer_cast<Group>
147 _root_group->createChild("group", name)
151 //----------------------------------------------------------------------------
152 void Canvas::enableRendering(bool force)
156 _render_dirty = true;
159 //----------------------------------------------------------------------------
160 void Canvas::update(double delta_time_sec)
162 if( (!_texture.serviceable() && _status != STATUS_DIRTY)
163 || (_status & CREATE_FAILED) )
166 if( _status == STATUS_DIRTY )
168 _texture.setSize(_size_x, _size_y);
170 if( !_texture.serviceable() )
172 _texture.useImageCoords(true);
173 _texture.useStencil(true);
174 _texture.allocRT(/*_camera_callback*/);
178 // Resizing causes a new texture to be created so we need to reapply all
179 // existing placements
180 for(size_t i = 0; i < _placements.size(); ++i)
182 if( !_placements[i].empty() )
183 _dirty_placements.push_back( _placements[i].front()->getProps() );
187 osg::Camera* camera = _texture.getCamera();
189 osg::Vec4 clear_color(0.0f, 0.0f, 0.0f , 1.0f);
190 parseColor(_node->getStringValue("background"), clear_color);
191 camera->setClearColor(clear_color);
193 camera->addChild(_root_group->getMatrixTransform());
195 // Ensure objects are drawn in order of traversal
196 camera->getOrCreateStateSet()->setBinName("TraversalOrderBin");
198 if( _texture.serviceable() )
200 setStatusFlags(STATUS_OK);
201 setStatusFlags(STATUS_DIRTY, false);
202 _render_dirty = true;
206 setStatusFlags(CREATE_FAILED);
211 if( _visible || _render_always )
215 // Also mark all dependent (eg. recursively used) canvases as dirty
216 BOOST_FOREACH(CanvasWeakPtr canvas, _dependent_canvases)
218 if( !canvas.expired() )
219 canvas.lock()->_render_dirty = true;
223 _texture.setRender(_render_dirty);
225 _render_dirty = false;
229 _texture.setRender(false);
231 _root_group->update(delta_time_sec);
233 if( _sampling_dirty )
235 _texture.setSampling(
236 _node->getBoolValue("mipmapping"),
237 _node->getIntValue("coverage-samples"),
238 _node->getIntValue("color-samples")
240 _sampling_dirty = false;
241 _render_dirty = true;
244 while( !_dirty_placements.empty() )
246 SGPropertyNode *node = _dirty_placements.back();
247 _dirty_placements.pop_back();
249 if( node->getIndex() >= static_cast<int>(_placements.size()) )
251 _placements.resize(node->getIndex() + 1);
253 // Remove possibly existing placements
254 _placements[ node->getIndex() ].clear();
256 // Get new placements
257 PlacementFactoryMap::const_iterator placement_factory =
258 _placement_factories.find( node->getStringValue("type", "object") );
259 if( placement_factory != _placement_factories.end() )
261 Placements& placements = _placements[ node->getIndex() ] =
262 placement_factory->second
265 boost::static_pointer_cast<Canvas>(_self.lock())
270 placements.empty() ? "No match" : "Ok"
274 node->setStringValue("status-msg", "Unknown placement type");
278 //----------------------------------------------------------------------------
279 naRef Canvas::addEventListener(const nasal::CallContext& ctx)
281 if( !_root_group.get() )
282 naRuntimeError(ctx.c, "Canvas: No root group!");
284 return _root_group->addEventListener(ctx);
287 //----------------------------------------------------------------------------
288 void Canvas::setSizeX(int sx)
293 setStatusFlags(STATUS_DIRTY);
296 setStatusFlags(MISSING_SIZE_X);
298 setStatusFlags(MISSING_SIZE_X, false);
300 // reset flag to allow creation with new size
301 setStatusFlags(CREATE_FAILED, false);
304 //----------------------------------------------------------------------------
305 void Canvas::setSizeY(int sy)
310 setStatusFlags(STATUS_DIRTY);
313 setStatusFlags(MISSING_SIZE_Y);
315 setStatusFlags(MISSING_SIZE_Y, false);
317 // reset flag to allow creation with new size
318 setStatusFlags(CREATE_FAILED, false);
321 //----------------------------------------------------------------------------
322 int Canvas::getSizeX() const
327 //----------------------------------------------------------------------------
328 int Canvas::getSizeY() const
333 //----------------------------------------------------------------------------
334 void Canvas::setViewWidth(int w)
336 if( _view_width == w )
340 _texture.setViewSize(_view_width, _view_height);
343 //----------------------------------------------------------------------------
344 void Canvas::setViewHeight(int h)
346 if( _view_height == h )
350 _texture.setViewSize(_view_width, _view_height);
353 //----------------------------------------------------------------------------
354 int Canvas::getViewWidth() const
359 //----------------------------------------------------------------------------
360 int Canvas::getViewHeight() const
365 //----------------------------------------------------------------------------
366 bool Canvas::handleMouseEvent(const MouseEventPtr& event)
368 _mouse_x = event->client_pos.x();
369 _mouse_y = event->client_pos.y();
370 _mouse_dx = event->delta.x();
371 _mouse_dy = event->delta.y();
372 _mouse_button = event->button;
373 _mouse_state = event->state;
374 _mouse_mod = event->mod;
375 //_mouse_scroll = event.scroll;
376 // Always set event type last because all listeners are attached to it
377 _mouse_event = event->type;
379 if( !_root_group.get() )
382 EventVisitor visitor( EventVisitor::TRAVERSE_DOWN,
383 event->getClientPos(),
385 if( !_root_group->accept(visitor) )
388 return _event_manager->handleEvent(event, visitor.getPropagationPath());
391 //----------------------------------------------------------------------------
392 void Canvas::childAdded( SGPropertyNode * parent,
393 SGPropertyNode * child )
395 if( parent != _node )
398 if( child->getNameString() == "placement" )
399 _dirty_placements.push_back(child);
400 else if( _root_group.get() )
401 static_cast<Element*>(_root_group.get())->childAdded(parent, child);
404 //----------------------------------------------------------------------------
405 void Canvas::childRemoved( SGPropertyNode * parent,
406 SGPropertyNode * child )
408 _render_dirty = true;
410 if( parent != _node )
413 if( child->getNameString() == "placement" )
414 _placements[ child->getIndex() ].clear();
415 else if( _root_group.get() )
416 static_cast<Element*>(_root_group.get())->childRemoved(parent, child);
419 //----------------------------------------------------------------------------
420 void Canvas::valueChanged(SGPropertyNode* node)
422 if( boost::starts_with(node->getNameString(), "status")
423 || node->getParent()->getNameString() == "bounding-box" )
425 _render_dirty = true;
428 if( node->getParent()->getParent() == _node
429 && node->getParent()->getNameString() == "placement" )
431 bool placement_dirty = false;
432 BOOST_FOREACH(Placements& placements, _placements)
434 BOOST_FOREACH(PlacementPtr& placement, placements)
436 // check if change can be directly handled by placement
437 if( placement->getProps() == node->getParent()
438 && !placement->childChanged(node) )
439 placement_dirty = true;
443 if( !placement_dirty )
446 // prevent double updates...
447 for( size_t i = 0; i < _dirty_placements.size(); ++i )
449 if( node->getParent() == _dirty_placements[i] )
453 _dirty_placements.push_back(node->getParent());
455 else if( node->getParent() == _node )
457 if( node->getNameString() == "background" )
460 if( _texture.getCamera() && parseColor(node->getStringValue(), color) )
462 _texture.getCamera()->setClearColor(color);
463 _render_dirty = true;
466 else if( node->getNameString() == "mipmapping"
467 || node->getNameString() == "coverage-samples"
468 || node->getNameString() == "color-samples" )
470 _sampling_dirty = true;
472 else if( node->getNameString() == "render-always" )
474 _render_always = node->getBoolValue();
476 else if( node->getNameString() == "size" )
478 if( node->getIndex() == 0 )
479 setSizeX( node->getIntValue() );
480 else if( node->getIndex() == 1 )
481 setSizeY( node->getIntValue() );
483 else if( node->getNameString() == "view" )
485 if( node->getIndex() == 0 )
486 setViewWidth( node->getIntValue() );
487 else if( node->getIndex() == 1 )
488 setViewHeight( node->getIntValue() );
490 else if( node->getNameString() == "freeze" )
491 _texture.setRender( node->getBoolValue() );
498 if( !handled && _root_group.get() )
499 _root_group->valueChanged(node);
502 //----------------------------------------------------------------------------
503 osg::Texture2D* Canvas::getTexture() const
505 return _texture.getTexture();
508 //----------------------------------------------------------------------------
509 Canvas::CullCallbackPtr Canvas::getCullCallback() const
511 return _cull_callback;
514 //----------------------------------------------------------------------------
515 void Canvas::addPlacementFactory( const std::string& type,
516 PlacementFactory factory )
518 if( _placement_factories.find(type) != _placement_factories.end() )
523 "Canvas::addPlacementFactory: replace existing factor for type " << type
526 _placement_factories[type] = factory;
529 //----------------------------------------------------------------------------
530 void Canvas::setSelf(const PropertyBasedElementPtr& self)
532 PropertyBasedElement::setSelf(self);
534 CanvasPtr canvas = boost::static_pointer_cast<Canvas>(self);
536 _root_group.reset( new Group(canvas, _node) );
537 _root_group->setSelf(_root_group);
539 // Remove automatically created property listener as we forward them on our
541 _root_group->removeListener();
543 _cull_callback = new CullCallback(canvas);
546 //----------------------------------------------------------------------------
547 void Canvas::setStatusFlags(unsigned int flags, bool set)
550 _status = _status | flags;
552 _status = _status & ~flags;
553 // TODO maybe extend simgear::PropertyObject to allow |=, &= etc.
555 if( (_status & MISSING_SIZE_X) && (_status & MISSING_SIZE_Y) )
556 _status_msg = "Missing size";
557 else if( _status & MISSING_SIZE_X )
558 _status_msg = "Missing size-x";
559 else if( _status & MISSING_SIZE_Y )
560 _status_msg = "Missing size-y";
561 else if( _status & CREATE_FAILED )
562 _status_msg = "Creating render target failed";
563 else if( _status == STATUS_DIRTY )
564 _status_msg = "Creation pending...";
569 //----------------------------------------------------------------------------
570 Canvas::PlacementFactoryMap Canvas::_placement_factories;
572 } // namespace canvas
573 } // namespace simgear