1 ///@file 2 /// Interface for 2D Canvas element 3 // 4 // Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com> 5 // 6 // This library is free software; you can redistribute it and/or 7 // modify it under the terms of the GNU Library General Public 8 // License as published by the Free Software Foundation; either 9 // version 2 of the License, or (at your option) any later version. 10 // 11 // This library is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 // Library General Public License for more details. 15 // 16 // You should have received a copy of the GNU Library General Public 17 // License along with this library; if not, write to the Free Software 18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 20 #include <simgear_config.h> 21 22 #include "CanvasElement.hxx" 23 #include <simgear/canvas/Canvas.hxx> 24 #include <simgear/canvas/CanvasEventVisitor.hxx> 25 #include <simgear/canvas/events/MouseEvent.hxx> 26 #include <simgear/math/SGMisc.hxx> 27 #include <simgear/misc/strutils.hxx> 28 #include <simgear/scene/material/parseBlendFunc.hxx> 29 30 #include <osg/Drawable> 31 #include <osg/Geode> 32 #include <osg/StateAttribute> 33 #include <osg/Version> 34 35 #include <cassert> 36 #include <cmath> 37 #include <cstring> 38 39 namespace simgear 40 { 41 namespace canvas 42 { 43 const std::string NAME_TRANSFORM = "tf"; 44 45 /** 46 * glScissor with coordinates relative to different reference frames. 47 */ 48 class Element::RelativeScissor: 49 public osg::StateAttribute 50 { 51 public: 52 53 ReferenceFrame _coord_reference; 54 osg::observer_ptr<osg::Node> _node; 55 RelativeScissor(osg::Node * node=NULL)56 explicit RelativeScissor(osg::Node* node = NULL): 57 _coord_reference(GLOBAL), 58 _node(node), 59 _x(0), 60 _y(0), 61 _width(0), 62 _height(0) 63 { 64 65 } 66 67 /** Copy constructor using CopyOp to manage deep vs shallow copy. */ RelativeScissor(const RelativeScissor & vp,const osg::CopyOp & copyop=osg::CopyOp::SHALLOW_COPY)68 RelativeScissor( const RelativeScissor& vp, 69 const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY ): 70 StateAttribute(vp, copyop), 71 _coord_reference(vp._coord_reference), 72 _node(vp._node), 73 _x(vp._x), 74 _y(vp._y), 75 _width(vp._width), 76 _height(vp._height) 77 {} 78 79 META_StateAttribute(simgear, RelativeScissor, SCISSOR); 80 81 /** Return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs. */ compare(const StateAttribute & sa) const82 virtual int compare(const StateAttribute& sa) const 83 { 84 // check the types are equal and then create the rhs variable 85 // used by the COMPARE_StateAttribute_Parameter macros below. 86 COMPARE_StateAttribute_Types(RelativeScissor,sa) 87 88 // compare each parameter in turn against the rhs. 89 COMPARE_StateAttribute_Parameter(_x) 90 COMPARE_StateAttribute_Parameter(_y) 91 COMPARE_StateAttribute_Parameter(_width) 92 COMPARE_StateAttribute_Parameter(_height) 93 COMPARE_StateAttribute_Parameter(_coord_reference) 94 COMPARE_StateAttribute_Parameter(_node) 95 96 return 0; // passed all the above comparison macros, must be equal. 97 } 98 getModeUsage(StateAttribute::ModeUsage & usage) const99 virtual bool getModeUsage(StateAttribute::ModeUsage& usage) const 100 { 101 usage.usesMode(GL_SCISSOR_TEST); 102 return true; 103 } 104 x()105 inline float& x() { return _x; } x() const106 inline float x() const { return _x; } 107 y()108 inline float& y() { return _y; } y() const109 inline float y() const { return _y; } 110 width()111 inline float& width() { return _width; } width() const112 inline float width() const { return _width; } 113 height()114 inline float& height() { return _height; } height() const115 inline float height() const { return _height; } 116 apply(osg::State & state) const117 virtual void apply(osg::State& state) const 118 { 119 if( _width <= 0 || _height <= 0 ) 120 return; 121 122 const osg::Viewport* vp = state.getCurrentViewport(); 123 float w2 = 0.5 * vp->width(), 124 h2 = 0.5 * vp->height(); 125 126 osg::Matrix model_view 127 ( 128 w2, 0, 0, 0, 129 0, h2, 0, 0, 130 0, 0, 1, 0, 131 w2, h2, 0, 1 132 ); 133 model_view.preMult(state.getProjectionMatrix()); 134 135 if( _coord_reference != GLOBAL ) 136 { 137 osg::Node* ref_obj = _node.get(); 138 139 if( _coord_reference == PARENT ) 140 { 141 if( _node->getNumParents() < 1 ) 142 { 143 SG_LOG(SG_GL, SG_WARN, "RelativeScissor: missing parent."); 144 return; 145 } 146 147 ref_obj = _node->getParent(0); 148 } 149 150 osg::MatrixList const& parent_matrices = ref_obj->getWorldMatrices(); 151 assert( !parent_matrices.empty() ); 152 model_view.preMult(parent_matrices.front()); 153 } 154 155 const osg::Vec2 scale( model_view(0,0), model_view(1,1)), 156 offset(model_view(3,0), model_view(3,1)); 157 158 // TODO check/warn for rotation? 159 GLint x = SGMiscf::roundToInt(scale.x() * _x + offset.x()), 160 y = SGMiscf::roundToInt(scale.y() * _y + offset.y()), 161 w = SGMiscf::roundToInt(std::fabs(scale.x()) * _width), 162 h = SGMiscf::roundToInt(std::fabs(scale.y()) * _height); 163 164 if( scale.x() < 0 ) 165 x -= w; 166 if( scale.y() < 0 ) 167 y -= h; 168 169 glScissor(x, y, w, h); 170 } 171 contains(const osg::Vec2f & pos) const172 bool contains(const osg::Vec2f& pos) const 173 { 174 return _x <= pos.x() && pos.x() <= _x + _width 175 && _y <= pos.y() && pos.y() <= _y + _height; 176 } 177 contains(const osg::Vec2f & global_pos,const osg::Vec2f & parent_pos,const osg::Vec2f & local_pos) const178 bool contains( const osg::Vec2f& global_pos, 179 const osg::Vec2f& parent_pos, 180 const osg::Vec2f& local_pos ) const 181 { 182 switch( _coord_reference ) 183 { 184 case GLOBAL: return contains(global_pos); 185 case PARENT: return contains(parent_pos); 186 case LOCAL: return contains(local_pos); 187 } 188 189 return false; 190 } 191 192 protected: 193 float _x, 194 _y, 195 _width, 196 _height; 197 }; 198 199 //---------------------------------------------------------------------------- OSGUserData(ElementPtr element)200 Element::OSGUserData::OSGUserData(ElementPtr element): 201 element(element) 202 { 203 204 } 205 206 //---------------------------------------------------------------------------- ~Element()207 Element::~Element() 208 { 209 } 210 211 //---------------------------------------------------------------------------- onDestroy()212 void Element::onDestroy() 213 { 214 if( !_scene_group.valid() ) 215 return; 216 217 // The transform node keeps a reference on this element, so ensure it is 218 // deleted. 219 for(osg::Group* parent: _scene_group->getParents()) 220 { 221 parent->removeChild(_scene_group.get()); 222 } 223 224 // Hide in case someone still holds a reference 225 setVisible(false); 226 removeListener(); 227 228 _parent = nullptr; 229 _scene_group = nullptr; 230 } 231 232 //---------------------------------------------------------------------------- getParent() const233 ElementPtr Element::getParent() const 234 { 235 return _parent.lock(); 236 } 237 238 //---------------------------------------------------------------------------- getCanvas() const239 CanvasWeakPtr Element::getCanvas() const 240 { 241 return _canvas; 242 } 243 244 //---------------------------------------------------------------------------- update(double dt)245 void Element::update(double dt) 246 { 247 if( isVisible() ) 248 updateImpl(dt); 249 } 250 251 //---------------------------------------------------------------------------- addEventListener(const std::string & type_str,const EventListener & cb)252 bool Element::addEventListener( const std::string& type_str, 253 const EventListener& cb ) 254 { 255 SG_LOG 256 ( 257 SG_GENERAL, 258 SG_INFO, 259 "addEventListener(" << _node->getPath() << ", " << type_str << ")" 260 ); 261 262 _listener[ Event::getOrRegisterType(type_str) ].push_back(cb); 263 return true; 264 } 265 266 //---------------------------------------------------------------------------- clearEventListener()267 void Element::clearEventListener() 268 { 269 _listener.clear(); 270 } 271 272 //---------------------------------------------------------------------------- setFocus()273 void Element::setFocus() 274 { 275 if( auto canvas = _canvas.lock() ) 276 canvas->setFocusElement(this); 277 } 278 279 //---------------------------------------------------------------------------- accept(EventVisitor & visitor)280 bool Element::accept(EventVisitor& visitor) 281 { 282 if( !isVisible() ) 283 return false; 284 285 return visitor.apply(*this); 286 } 287 288 //---------------------------------------------------------------------------- ascend(EventVisitor & visitor)289 bool Element::ascend(EventVisitor& visitor) 290 { 291 ElementPtr parent = getParent(); 292 if( parent ) 293 return parent->accept(visitor); 294 return true; 295 } 296 297 //---------------------------------------------------------------------------- traverse(EventVisitor & visitor)298 bool Element::traverse(EventVisitor& visitor) 299 { 300 return true; 301 } 302 303 //---------------------------------------------------------------------------- numEventHandler(int type) const304 size_t Element::numEventHandler(int type) const 305 { 306 ListenerMap::const_iterator listeners = _listener.find(type); 307 if( listeners != _listener.end() ) 308 return listeners->second.size(); 309 return 0; 310 } 311 312 //---------------------------------------------------------------------------- handleEvent(const EventPtr & event)313 bool Element::handleEvent(const EventPtr& event) 314 { 315 ListenerMap::iterator listeners = _listener.find(event->getType()); 316 if( listeners == _listener.end() ) 317 return false; 318 319 for(auto const& listener: listeners->second) 320 { 321 try 322 { 323 listener(event); 324 } 325 catch( std::exception const& ex ) 326 { 327 SG_LOG( 328 SG_GENERAL, 329 SG_WARN, 330 "canvas::Element: event handler error: '" << ex.what() << "'" 331 ); 332 } 333 } 334 335 return true; 336 } 337 338 //---------------------------------------------------------------------------- dispatchEvent(const EventPtr & event)339 bool Element::dispatchEvent(const EventPtr& event) 340 { 341 EventPropagationPath path; 342 path.push_back( EventTarget(this) ); 343 344 for( ElementPtr parent = getParent(); 345 parent.valid(); 346 parent = parent->getParent() ) 347 path.push_front( EventTarget(parent) ); 348 349 CanvasPtr canvas = _canvas.lock(); 350 if( !canvas ) 351 return false; 352 353 return canvas->propagateEvent(event, path); 354 } 355 356 //---------------------------------------------------------------------------- hitBound(const osg::Vec2f & global_pos,const osg::Vec2f & parent_pos,const osg::Vec2f & local_pos) const357 bool Element::hitBound( const osg::Vec2f& global_pos, 358 const osg::Vec2f& parent_pos, 359 const osg::Vec2f& local_pos ) const 360 { 361 if( _scissor && !_scissor->contains(global_pos, parent_pos, local_pos) ) 362 return false; 363 364 const osg::Vec3f pos3(parent_pos, 0); 365 366 // Drawables have a bounding box... 367 if( _drawable ) 368 return _drawable-> 369 #if OSG_VERSION_LESS_THAN(3,3,2) 370 getBound() 371 #else 372 getBoundingBox() 373 #endif 374 .contains(osg::Vec3f(local_pos, 0)); 375 else if( _scene_group.valid() ) 376 // ... for other elements, i.e. groups only a bounding sphere is available 377 return _scene_group->getBound().contains(osg::Vec3f(parent_pos, 0)); 378 else 379 return false; 380 } 381 382 383 //---------------------------------------------------------------------------- setVisible(bool visible)384 void Element::setVisible(bool visible) 385 { 386 if( _scene_group.valid() ) 387 // TODO check if we need another nodemask 388 _scene_group->setNodeMask(visible ? 0xffffffff : 0); 389 } 390 391 //---------------------------------------------------------------------------- isVisible() const392 bool Element::isVisible() const 393 { 394 return _scene_group.valid() && _scene_group->getNodeMask() != 0; 395 } 396 397 //---------------------------------------------------------------------------- getSceneGroup() const398 osg::MatrixTransform* Element::getSceneGroup() const 399 { 400 return _scene_group.get(); 401 } 402 403 //---------------------------------------------------------------------------- posToLocal(const osg::Vec2f & pos) const404 osg::Vec2f Element::posToLocal(const osg::Vec2f& pos) const 405 { 406 if( !_scene_group ) 407 // TODO log warning? 408 return pos; 409 410 updateMatrix(); 411 const osg::Matrix& m = _scene_group->getInverseMatrix(); 412 return osg::Vec2f 413 ( 414 m(0, 0) * pos[0] + m(1, 0) * pos[1] + m(3, 0), 415 m(0, 1) * pos[0] + m(1, 1) * pos[1] + m(3, 1) 416 ); 417 } 418 419 //---------------------------------------------------------------------------- childAdded(SGPropertyNode * parent,SGPropertyNode * child)420 void Element::childAdded(SGPropertyNode* parent, SGPropertyNode* child) 421 { 422 if( parent == _node 423 && child->getNameString() == NAME_TRANSFORM ) 424 { 425 if( child->getIndex() >= static_cast<int>(_transform_types.size()) ) 426 _transform_types.resize( child->getIndex() + 1 ); 427 428 _transform_types[ child->getIndex() ] = TT_NONE; 429 _attributes_dirty |= TRANSFORM; 430 return; 431 } 432 else if( parent->getParent() == _node 433 && parent->getNameString() == NAME_TRANSFORM ) 434 { 435 assert(parent->getIndex() < static_cast<int>(_transform_types.size())); 436 437 const std::string& name = child->getNameString(); 438 439 TransformType& type = _transform_types[parent->getIndex()]; 440 441 if( name == "m" ) 442 type = TT_MATRIX; 443 else if( name == "t" ) 444 type = TT_TRANSLATE; 445 else if( name == "rot" ) 446 type = TT_ROTATE; 447 else if( name == "s" ) 448 type = TT_SCALE; 449 450 _attributes_dirty |= TRANSFORM; 451 return; 452 } 453 454 childAdded(child); 455 } 456 457 //---------------------------------------------------------------------------- childRemoved(SGPropertyNode * parent,SGPropertyNode * child)458 void Element::childRemoved(SGPropertyNode* parent, SGPropertyNode* child) 459 { 460 if( parent == _node ) 461 { 462 if( child->getNameString() == NAME_TRANSFORM ) 463 { 464 if( child->getIndex() >= static_cast<int>(_transform_types.size()) ) 465 { 466 SG_LOG 467 ( 468 SG_GENERAL, 469 SG_WARN, 470 "Element::childRemoved: unknown transform: " << child->getPath() 471 ); 472 return; 473 } 474 475 _transform_types[ child->getIndex() ] = TT_NONE; 476 477 while( !_transform_types.empty() && _transform_types.back() == TT_NONE ) 478 _transform_types.pop_back(); 479 480 _attributes_dirty |= TRANSFORM; 481 return; 482 } 483 else if( StyleInfo const* style = getStyleInfo(child->getNameString()) ) 484 { 485 if( setStyle(getParentStyle(child), style) ) 486 return; 487 } 488 } 489 490 childRemoved(child); 491 } 492 493 //---------------------------------------------------------------------------- valueChanged(SGPropertyNode * child)494 void Element::valueChanged(SGPropertyNode* child) 495 { 496 SGPropertyNode *parent = child->getParent(); 497 if( parent == _node ) 498 { 499 const std::string& name = child->getNameString(); 500 if( strutils::starts_with(name, "data-") ) 501 return; 502 else if( StyleInfo const* style_info = getStyleInfo(name) ) 503 { 504 SGPropertyNode const* style = child; 505 if( isStyleEmpty(child) ) 506 { 507 child->clearValue(); 508 style = getParentStyle(child); 509 } 510 setStyle(style, style_info); 511 return; 512 } 513 else if( name == "update" ) 514 return update(0); 515 else if( strutils::starts_with(name, "blend-") ) 516 return (void)(_attributes_dirty |= BLEND_FUNC); 517 } 518 else if( parent 519 && parent->getParent() == _node 520 && parent->getNameString() == NAME_TRANSFORM ) 521 { 522 _attributes_dirty |= TRANSFORM; 523 return; 524 } 525 526 childChanged(child); 527 } 528 529 //---------------------------------------------------------------------------- setStyle(const SGPropertyNode * child,const StyleInfo * style_info)530 bool Element::setStyle( const SGPropertyNode* child, 531 const StyleInfo* style_info ) 532 { 533 return canApplyStyle(child) && setStyleImpl(child, style_info); 534 } 535 536 //---------------------------------------------------------------------------- setClip(const std::string & clip)537 void Element::setClip(const std::string& clip) 538 { 539 if( !_scene_group ) 540 // TODO warn? 541 return; 542 543 osg::StateSet* ss = getOrCreateStateSet(); 544 if( !ss ) 545 return; 546 547 if( clip.empty() || clip == "auto" ) 548 { 549 ss->removeAttribute(osg::StateAttribute::SCISSOR); 550 _scissor = 0; 551 return; 552 } 553 554 // TODO generalize CSS property parsing 555 const std::string RECT("rect("); 556 if( !strutils::ends_with(clip, ")") 557 || !strutils::starts_with(clip, RECT) ) 558 { 559 SG_LOG(SG_GENERAL, SG_WARN, "Canvas: invalid clip: " << clip); 560 return; 561 } 562 563 const std::string sep(", \t\npx"); 564 int comp = 0; 565 float values[4]; 566 567 for(size_t pos = RECT.size(); comp < 4; ++comp) 568 { 569 pos = clip.find_first_not_of(sep, pos); 570 if( pos == std::string::npos || pos == clip.size() - 1 ) 571 break; 572 573 char *end = 0; 574 values[comp] = strtod(&clip[pos], &end); 575 if( end == &clip[pos] || !end ) 576 break; 577 578 pos = end - &clip[0]; 579 } 580 581 if( comp < 4 ) 582 { 583 SG_LOG(SG_GENERAL, SG_WARN, "Canvas: invalid clip: " << clip); 584 return; 585 } 586 587 float width = values[1] - values[3], 588 height = values[2] - values[0]; 589 590 if( width < 0 || height < 0 ) 591 { 592 SG_LOG(SG_GENERAL, SG_WARN, "Canvas: negative clip size: " << clip); 593 return; 594 } 595 596 if( !_scissor ) 597 _scissor = new RelativeScissor(_scene_group.get()); 598 599 // <top>, <right>, <bottom>, <left> 600 _scissor->x() = values[3]; 601 _scissor->y() = values[0]; 602 _scissor->width() = width; 603 _scissor->height() = height; 604 605 SGPropertyNode* clip_frame = _node->getChild("clip-frame", 0); 606 if( clip_frame ) 607 valueChanged(clip_frame); 608 else 609 _scissor->_coord_reference = GLOBAL; 610 611 ss->setAttributeAndModes(_scissor); 612 } 613 614 //---------------------------------------------------------------------------- setClipFrame(ReferenceFrame rf)615 void Element::setClipFrame(ReferenceFrame rf) 616 { 617 if( _scissor ) 618 _scissor->_coord_reference = rf; 619 } 620 621 //---------------------------------------------------------------------------- setRotation(unsigned int index,double r)622 void Element::setRotation(unsigned int index, double r) 623 { 624 _node->getChild(NAME_TRANSFORM, index, true)->setDoubleValue("rot", r); 625 } 626 627 //---------------------------------------------------------------------------- setTranslation(unsigned int index,double x,double y)628 void Element::setTranslation(unsigned int index, double x, double y) 629 { 630 SGPropertyNode* tf = _node->getChild(NAME_TRANSFORM, index, true); 631 tf->getChild("t", 0, true)->setDoubleValue(x); 632 tf->getChild("t", 1, true)->setDoubleValue(y); 633 } 634 635 //---------------------------------------------------------------------------- setTransformEnabled(unsigned int index,bool enabled)636 void Element::setTransformEnabled(unsigned int index, bool enabled) 637 { 638 SGPropertyNode* tf = _node->getChild(NAME_TRANSFORM, index, true); 639 tf->setBoolValue("enabled", enabled); 640 } 641 642 //---------------------------------------------------------------------------- getBoundingBox() const643 osg::BoundingBox Element::getBoundingBox() const 644 { 645 if( _drawable ) 646 #if OSG_VERSION_LESS_THAN(3,3,2) 647 return _drawable->getBound(); 648 #else 649 return _drawable->getBoundingBox(); 650 #endif 651 652 osg::BoundingBox bb; 653 654 if( _scene_group.valid() ) 655 bb.expandBy( _scene_group->getBound() ); 656 657 return bb; 658 } 659 660 //---------------------------------------------------------------------------- getTightBoundingBox() const661 osg::BoundingBox Element::getTightBoundingBox() const 662 { 663 return getTransformedBounds(getMatrix()); 664 } 665 666 //---------------------------------------------------------------------------- getTransformedBounds(const osg::Matrix & m) const667 osg::BoundingBox Element::getTransformedBounds(const osg::Matrix& m) const 668 { 669 if( !_drawable ) 670 return osg::BoundingBox(); 671 672 osg::BoundingBox transformed; 673 const osg::BoundingBox& bb = 674 #if OSG_VERSION_LESS_THAN(3,3,2) 675 _drawable->getBound(); 676 #else 677 _drawable->getBoundingBox(); 678 #endif 679 680 for(int i = 0; i < 4; ++i) 681 transformed.expandBy( bb.corner(i) * m ); 682 683 return transformed; 684 } 685 686 //---------------------------------------------------------------------------- getMatrix() const687 osg::Matrix Element::getMatrix() const 688 { 689 if( !_scene_group ) 690 return osg::Matrix::identity(); 691 692 updateMatrix(); 693 return _scene_group->getMatrix(); 694 } 695 696 //---------------------------------------------------------------------------- 697 Element::StyleSetters Element::_style_setters; 698 699 //---------------------------------------------------------------------------- Element(const CanvasWeakPtr & canvas,const SGPropertyNode_ptr & node,const Style & parent_style,ElementWeakPtr parent)700 Element::Element( const CanvasWeakPtr& canvas, 701 const SGPropertyNode_ptr& node, 702 const Style& parent_style, 703 ElementWeakPtr parent ): 704 PropertyBasedElement(node), 705 _canvas( canvas ), 706 _parent( parent ), 707 _scene_group( new osg::MatrixTransform ), 708 _style( parent_style ) 709 { 710 staticInit(); 711 712 SG_LOG 713 ( 714 SG_GL, 715 SG_DEBUG, 716 "New canvas element " << node->getPath() 717 ); 718 719 // Ensure elements are drawn in order they appear in the element tree 720 _scene_group 721 ->getOrCreateStateSet() 722 ->setRenderBinDetails( 723 0, 724 "PreOrderBin", 725 osg::StateSet::OVERRIDE_RENDERBIN_DETAILS 726 ); 727 728 _scene_group->setUserData( new OSGUserData(this) ); 729 } 730 731 //---------------------------------------------------------------------------- staticInit()732 void Element::staticInit() 733 { 734 if( isInit<Element>() ) 735 return; 736 737 addStyle("clip", "", &Element::setClip, false); 738 addStyle("clip-frame", "", &Element::setClipFrame, false); 739 addStyle("visible", "", &Element::setVisible, false); 740 } 741 742 //---------------------------------------------------------------------------- isStyleEmpty(const SGPropertyNode * child) const743 bool Element::isStyleEmpty(const SGPropertyNode* child) const 744 { 745 return !child 746 || simgear::strutils::strip(child->getStringValue()).empty(); 747 } 748 749 //---------------------------------------------------------------------------- canApplyStyle(const SGPropertyNode * child) const750 bool Element::canApplyStyle(const SGPropertyNode* child) const 751 { 752 if( _node == child->getParent() ) 753 return true; 754 755 // Parent values do not override if element has own value 756 return isStyleEmpty( _node->getChild(child->getName()) ); 757 } 758 759 //---------------------------------------------------------------------------- setStyleImpl(const SGPropertyNode * child,const StyleInfo * style_info)760 bool Element::setStyleImpl( const SGPropertyNode* child, 761 const StyleInfo* style_info ) 762 { 763 const StyleSetter* style_setter = style_info 764 ? &style_info->setter 765 : getStyleSetter(child->getNameString()); 766 while( style_setter ) 767 { 768 if( style_setter->func(*this, child) ) 769 return true; 770 style_setter = style_setter->next; 771 } 772 return false; 773 } 774 775 //---------------------------------------------------------------------------- 776 const Element::StyleInfo* getStyleInfo(const std::string & name) const777 Element::getStyleInfo(const std::string& name) const 778 { 779 StyleSetters::const_iterator setter = _style_setters.find(name); 780 if( setter == _style_setters.end() ) 781 return 0; 782 783 return &setter->second; 784 } 785 786 //---------------------------------------------------------------------------- 787 const Element::StyleSetter* getStyleSetter(const std::string & name) const788 Element::getStyleSetter(const std::string& name) const 789 { 790 const StyleInfo* info = getStyleInfo(name); 791 return info ? &info->setter : 0; 792 } 793 794 //---------------------------------------------------------------------------- 795 const SGPropertyNode* getParentStyle(const SGPropertyNode * child) const796 Element::getParentStyle(const SGPropertyNode* child) const 797 { 798 // Try to get value from parent... 799 ElementPtr parent = getParent(); 800 if( parent ) 801 { 802 Style::const_iterator style = 803 parent->_style.find(child->getNameString()); 804 if( style != parent->_style.end() ) 805 return style->second; 806 } 807 808 // ...or reset to default if none is available 809 return child; // TODO somehow get default value for each style? 810 } 811 812 //---------------------------------------------------------------------------- setDrawable(osg::Drawable * drawable)813 void Element::setDrawable( osg::Drawable* drawable ) 814 { 815 _drawable = drawable; 816 817 if( !_drawable ) 818 { 819 SG_LOG(SG_GL, SG_WARN, "canvas::Element::setDrawable: NULL drawable"); 820 return; 821 } 822 if( !_scene_group ) 823 { 824 SG_LOG(SG_GL, SG_WARN, "canvas::Element::setDrawable: NULL scenegroup"); 825 return; 826 } 827 828 auto geode = new osg::Geode; 829 geode->addDrawable(_drawable); 830 _scene_group->addChild(geode); 831 } 832 833 //---------------------------------------------------------------------------- getOrCreateStateSet()834 osg::StateSet* Element::getOrCreateStateSet() 835 { 836 if( _drawable.valid() ) 837 return _drawable->getOrCreateStateSet(); 838 else if( _scene_group.valid() ) 839 return _scene_group->getOrCreateStateSet(); 840 else 841 return nullptr; 842 } 843 844 //---------------------------------------------------------------------------- setupStyle()845 void Element::setupStyle() 846 { 847 for(auto const& style: _style) 848 setStyle(style.second); 849 } 850 851 //---------------------------------------------------------------------------- updateMatrix() const852 void Element::updateMatrix() const 853 { 854 if( !(_attributes_dirty & TRANSFORM) || !_scene_group ) 855 return; 856 857 osg::Matrix m; 858 for( size_t i = 0; i < _transform_types.size(); ++i ) 859 { 860 // Skip unused indizes... 861 if( _transform_types[i] == TT_NONE ) 862 continue; 863 864 SGPropertyNode* tf_node = _node->getChild("tf", i, true); 865 if (!tf_node->getBoolValue("enabled", true)) { 866 continue; // skip disabled transforms 867 } 868 869 // Build up the matrix representation of the current transform node 870 osg::Matrix tf; 871 switch( _transform_types[i] ) 872 { 873 case TT_MATRIX: 874 tf = osg::Matrix( tf_node->getDoubleValue("m[0]", 1), 875 tf_node->getDoubleValue("m[1]", 0), 876 0, 877 tf_node->getDoubleValue("m[6]", 0), 878 879 tf_node->getDoubleValue("m[2]", 0), 880 tf_node->getDoubleValue("m[3]", 1), 881 0, 882 tf_node->getDoubleValue("m[7]", 0), 883 884 0, 885 0, 886 1, 887 0, 888 889 tf_node->getDoubleValue("m[4]", 0), 890 tf_node->getDoubleValue("m[5]", 0), 891 0, 892 tf_node->getDoubleValue("m[8]", 1) ); 893 break; 894 case TT_TRANSLATE: 895 tf.makeTranslate( osg::Vec3f( tf_node->getDoubleValue("t[0]", 0), 896 tf_node->getDoubleValue("t[1]", 0), 897 0 ) ); 898 break; 899 case TT_ROTATE: 900 tf.makeRotate( tf_node->getDoubleValue("rot", 0), 0, 0, 1 ); 901 break; 902 case TT_SCALE: 903 { 904 float sx = tf_node->getDoubleValue("s[0]", 1); 905 // sy defaults to sx... 906 tf.makeScale( sx, tf_node->getDoubleValue("s[1]", sx), 1 ); 907 break; 908 } 909 default: 910 break; 911 } 912 m.postMult( tf ); 913 } 914 _scene_group->setMatrix(m); 915 _attributes_dirty &= ~TRANSFORM; 916 } 917 918 //---------------------------------------------------------------------------- updateImpl(double dt)919 void Element::updateImpl(double dt) 920 { 921 updateMatrix(); 922 923 // Update bounding box on manual update (manual updates pass zero dt) 924 if( dt == 0 && _drawable ) 925 _drawable->getBound(); 926 927 if( (_attributes_dirty & BLEND_FUNC) ) 928 { 929 parseBlendFunc( 930 _scene_group->getOrCreateStateSet(), 931 _node->getChild("blend-source"), 932 _node->getChild("blend-destination"), 933 _node->getChild("blend-source-rgb"), 934 _node->getChild("blend-destination-rgb"), 935 _node->getChild("blend-source-alpha"), 936 _node->getChild("blend-destination-alpha") 937 ); 938 _attributes_dirty &= ~BLEND_FUNC; 939 } 940 } 941 942 } // namespace canvas 943 } // namespace simgear 944