]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/elements/CanvasElement.cxx
Canvas::Path: reduce number of OpenGL state changes.
[simgear.git] / simgear / canvas / elements / CanvasElement.cxx
1 // Interface for 2D Canvas element
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 "CanvasElement.hxx"
20 #include <simgear/canvas/CanvasEventVisitor.hxx>
21 #include <simgear/canvas/MouseEvent.hxx>
22 #include <simgear/math/SGMisc.hxx>
23 #include <simgear/misc/strutils.hxx>
24 #include <simgear/scene/material/parseBlendFunc.hxx>
25
26 #include <osg/Drawable>
27 #include <osg/Geode>
28 #include <osg/Scissor>
29
30 #include <boost/algorithm/string/predicate.hpp>
31 #include <boost/foreach.hpp>
32 #include <boost/make_shared.hpp>
33
34 #include <cassert>
35 #include <cmath>
36 #include <cstring>
37
38 namespace simgear
39 {
40 namespace canvas
41 {
42   const std::string NAME_TRANSFORM = "tf";
43
44   /**
45    * glScissor with coordinates relative to different reference frames.
46    */
47   class Element::RelativeScissor:
48     public osg::Scissor
49   {
50     public:
51
52       ReferenceFrame    _coord_reference;
53       osg::Matrix       _parent_inverse;
54
55       RelativeScissor():
56         _coord_reference(GLOBAL)
57       {}
58
59       bool contains(const osg::Vec2f& pos) const
60       {
61         return _x <= pos.x() && pos.x() <= _x + _width
62             && _y <= pos.y() && pos.y() <= _y + _height;
63       }
64
65       bool contains( const osg::Vec2f& global_pos,
66                      const osg::Vec2f& parent_pos,
67                      const osg::Vec2f& local_pos ) const
68       {
69         switch( _coord_reference )
70         {
71           case GLOBAL: return contains(global_pos);
72           case PARENT: return contains(parent_pos);
73           case LOCAL:  return contains(local_pos);
74         }
75
76         return false;
77       }
78
79       virtual void apply(osg::State& state) const
80       {
81         const osg::Viewport* vp = state.getCurrentViewport();
82         float w2 = 0.5 * vp->width(),
83               h2 = 0.5 * vp->height();
84
85         osg::Matrix model_view
86         (
87           w2, 0,  0, 0,
88           0,  h2, 0, 0,
89           0,  0,  1, 0,
90           w2, h2, 0, 1
91         );
92         model_view.preMult(state.getProjectionMatrix());
93
94         if( _coord_reference != GLOBAL )
95         {
96           model_view.preMult(state.getModelViewMatrix());
97
98           if( _coord_reference == PARENT )
99             model_view.preMult(_parent_inverse);
100         }
101
102         const osg::Vec2 scale( model_view(0,0), model_view(1,1)),
103                         offset(model_view(3,0), model_view(3,1));
104
105         // TODO check/warn for rotation?
106
107         GLint x = SGMiscf::roundToInt(scale.x() * _x + offset.x()),
108               y = SGMiscf::roundToInt(scale.y() * _y + offset.y()),
109               w = SGMiscf::roundToInt(std::fabs(scale.x()) * _width),
110               h = SGMiscf::roundToInt(std::fabs(scale.y()) * _height);
111
112         if( scale.x() < 0 )
113           x -= w;
114         if( scale.y() < 0 )
115           y -= h;
116
117         glScissor(x, y, w, h);
118       }
119   };
120
121   //----------------------------------------------------------------------------
122   Element::OSGUserData::OSGUserData(ElementPtr element):
123     element(element)
124   {
125
126   }
127
128   //----------------------------------------------------------------------------
129   Element::~Element()
130   {
131     if( !_transform.valid() )
132       return;
133
134     for(unsigned int i = 0; i < _transform->getNumChildren(); ++i)
135     {
136       OSGUserData* ud =
137         static_cast<OSGUserData*>(_transform->getChild(i)->getUserData());
138
139       if( ud )
140         // Ensure parent is cleared to prevent accessing released memory if an
141         // element somehow survives longer than his parent.
142         ud->element->_parent = 0;
143     }
144   }
145
146   //----------------------------------------------------------------------------
147   void Element::setSelf(const PropertyBasedElementPtr& self)
148   {
149     PropertyBasedElement::setSelf(self);
150
151     _transform->setUserData
152     (
153       new OSGUserData(boost::static_pointer_cast<Element>(self))
154     );
155   }
156
157   //----------------------------------------------------------------------------
158   void Element::onDestroy()
159   {
160     if( !_transform.valid() )
161       return;
162
163     // The transform node keeps a reference on this element, so ensure it is
164     // deleted.
165     BOOST_FOREACH(osg::Group* parent, _transform->getParents())
166     {
167       parent->removeChild(_transform.get());
168     }
169   }
170
171   //----------------------------------------------------------------------------
172   ElementWeakPtr Element::getWeakPtr() const
173   {
174     return boost::static_pointer_cast<Element>(_self.lock());
175   }
176
177   //----------------------------------------------------------------------------
178   ElementPtr Element::getParent()
179   {
180     return _parent ? _parent->getWeakPtr().lock() : ElementPtr();
181   }
182
183   //----------------------------------------------------------------------------
184   void Element::update(double dt)
185   {
186     if( !isVisible() )
187       return;
188
189     // Trigger matrix update
190     getMatrix();
191
192     // TODO limit bounding box to scissor
193     if( _attributes_dirty & SCISSOR_COORDS )
194     {
195       if( _scissor && _scissor->_coord_reference != GLOBAL )
196         _scissor->_parent_inverse = _transform->getInverseMatrix();
197
198       _attributes_dirty &= ~SCISSOR_COORDS;
199     }
200
201     // Update bounding box on manual update (manual updates pass zero dt)
202     if( dt == 0 && _drawable )
203       _drawable->getBound();
204
205     if( _attributes_dirty & BLEND_FUNC )
206     {
207       parseBlendFunc(
208         _transform->getOrCreateStateSet(),
209         _node->getChild("blend-source"),
210         _node->getChild("blend-destination"),
211         _node->getChild("blend-source-rgb"),
212         _node->getChild("blend-destination-rgb"),
213         _node->getChild("blend-source-alpha"),
214         _node->getChild("blend-destination-alpha")
215       );
216       _attributes_dirty &= ~BLEND_FUNC;
217     }
218   }
219
220   //----------------------------------------------------------------------------
221   bool Element::addEventListener( const std::string& type_str,
222                                   const EventListener& cb )
223   {
224     SG_LOG
225     (
226       SG_GENERAL,
227       SG_INFO,
228       "addEventListener(" << _node->getPath() << ", " << type_str << ")"
229     );
230
231     Event::Type type = Event::strToType(type_str);
232     if( type == Event::UNKNOWN )
233     {
234       SG_LOG( SG_GENERAL,
235               SG_WARN,
236               "addEventListener: Unknown event type " << type_str );
237       return false;
238     }
239
240     _listener[ type ].push_back(cb);
241
242     return true;
243   }
244
245   //----------------------------------------------------------------------------
246   void Element::clearEventListener()
247   {
248     _listener.clear();
249   }
250
251   //----------------------------------------------------------------------------
252   bool Element::accept(EventVisitor& visitor)
253   {
254     if( !isVisible() )
255       return false;
256
257     return visitor.apply(*this);
258   }
259
260   //----------------------------------------------------------------------------
261   bool Element::ascend(EventVisitor& visitor)
262   {
263     if( _parent )
264       return _parent->accept(visitor);
265     return true;
266   }
267
268   //----------------------------------------------------------------------------
269   bool Element::traverse(EventVisitor& visitor)
270   {
271     return true;
272   }
273
274   //----------------------------------------------------------------------------
275   bool Element::handleEvent(canvas::EventPtr event)
276   {
277     ListenerMap::iterator listeners = _listener.find(event->getType());
278     if( listeners == _listener.end() )
279       return false;
280
281     BOOST_FOREACH(EventListener const& listener, listeners->second)
282       listener(event);
283
284     return true;
285   }
286
287   //----------------------------------------------------------------------------
288   bool Element::hitBound( const osg::Vec2f& global_pos,
289                           const osg::Vec2f& parent_pos,
290                           const osg::Vec2f& local_pos ) const
291   {
292     if( _scissor && !_scissor->contains(global_pos, parent_pos, local_pos) )
293       return false;
294
295     const osg::Vec3f pos3(parent_pos, 0);
296
297     // Drawables have a bounding box...
298     if( _drawable )
299       return _drawable->getBound().contains(osg::Vec3f(local_pos, 0));
300     else if( _transform.valid() )
301       // ... for other elements, i.e. groups only a bounding sphere is available
302       return _transform->getBound().contains(osg::Vec3f(parent_pos, 0));
303     else
304       return false;
305   }
306
307
308   //----------------------------------------------------------------------------
309   void Element::setVisible(bool visible)
310   {
311     if( _transform.valid() )
312       // TODO check if we need another nodemask
313       _transform->setNodeMask(visible ? 0xffffffff : 0);
314   }
315
316   //----------------------------------------------------------------------------
317   bool Element::isVisible() const
318   {
319     return _transform.valid() && _transform->getNodeMask() != 0;
320   }
321
322   //----------------------------------------------------------------------------
323   osg::MatrixTransform* Element::getMatrixTransform()
324   {
325     return _transform.get();
326   }
327
328   //----------------------------------------------------------------------------
329   osg::MatrixTransform const* Element::getMatrixTransform() const
330   {
331     return _transform.get();
332   }
333
334   //----------------------------------------------------------------------------
335   osg::Vec2f Element::posToLocal(const osg::Vec2f& pos) const
336   {
337     getMatrix();
338     const osg::Matrix& m = _transform->getInverseMatrix();
339     return osg::Vec2f
340     (
341       m(0, 0) * pos[0] + m(1, 0) * pos[1] + m(3, 0),
342       m(0, 1) * pos[0] + m(1, 1) * pos[1] + m(3, 1)
343     );
344   }
345
346   //----------------------------------------------------------------------------
347   void Element::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
348   {
349     if(    parent == _node
350         && child->getNameString() == NAME_TRANSFORM )
351     {
352       if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
353         _transform_types.resize( child->getIndex() + 1 );
354
355       _transform_types[ child->getIndex() ] = TT_NONE;
356       _attributes_dirty |= TRANSFORM;
357       return;
358     }
359     else if(    parent->getParent() == _node
360              && parent->getNameString() == NAME_TRANSFORM )
361     {
362       assert(parent->getIndex() < static_cast<int>(_transform_types.size()));
363
364       const std::string& name = child->getNameString();
365
366       TransformType& type = _transform_types[parent->getIndex()];
367
368       if(      name == "m" )
369         type = TT_MATRIX;
370       else if( name == "t" )
371         type = TT_TRANSLATE;
372       else if( name == "rot" )
373         type = TT_ROTATE;
374       else if( name == "s" )
375         type = TT_SCALE;
376
377       _attributes_dirty |= TRANSFORM;
378       return;
379     }
380
381     childAdded(child);
382   }
383
384   //----------------------------------------------------------------------------
385   void Element::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
386   {
387     if( parent == _node )
388     {
389       if( child->getNameString() == NAME_TRANSFORM )
390       {
391         if( !_transform.valid() )
392           return;
393
394         if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
395         {
396           SG_LOG
397           (
398             SG_GENERAL,
399             SG_WARN,
400             "Element::childRemoved: unknown transform: " << child->getPath()
401           );
402           return;
403         }
404
405         _transform_types[ child->getIndex() ] = TT_NONE;
406
407         while( !_transform_types.empty() && _transform_types.back() == TT_NONE )
408           _transform_types.pop_back();
409
410         _attributes_dirty |= TRANSFORM;
411         return;
412       }
413       else if( StyleInfo const* style = getStyleInfo(child->getNameString()) )
414       {
415         if( setStyle(getParentStyle(child), style) )
416           return;
417       }
418     }
419
420     childRemoved(child);
421   }
422
423   //----------------------------------------------------------------------------
424   void Element::valueChanged(SGPropertyNode* child)
425   {
426     SGPropertyNode *parent = child->getParent();
427     if( parent == _node )
428     {
429       const std::string& name = child->getNameString();
430       if( StyleInfo const* style_info = getStyleInfo(name) )
431       {
432         SGPropertyNode const* style = child;
433         if( isStyleEmpty(child) )
434         {
435           child->clearValue();
436           style = getParentStyle(child);
437         }
438         setStyle(style, style_info);
439         return;
440       }
441       else if( name == "update" )
442         return update(0);
443       else if( boost::starts_with(name, "blend-") )
444         return (void)(_attributes_dirty |= BLEND_FUNC);
445     }
446     else if(   parent->getParent() == _node
447             && parent->getNameString() == NAME_TRANSFORM )
448     {
449       _attributes_dirty |= TRANSFORM;
450       return;
451     }
452
453     childChanged(child);
454   }
455
456   //----------------------------------------------------------------------------
457   bool Element::setStyle( const SGPropertyNode* child,
458                           const StyleInfo* style_info )
459   {
460     return canApplyStyle(child) && setStyleImpl(child, style_info);
461   }
462
463   //----------------------------------------------------------------------------
464   void Element::setClip(const std::string& clip)
465   {
466     if( clip.empty() || clip == "auto" )
467     {
468       getOrCreateStateSet()->removeAttribute(osg::StateAttribute::SCISSOR);
469       _scissor = 0;
470       return;
471     }
472
473     // TODO generalize CSS property parsing
474     const std::string RECT("rect(");
475     if(    !boost::ends_with(clip, ")")
476         || !boost::starts_with(clip, RECT) )
477     {
478       SG_LOG(SG_GENERAL, SG_WARN, "Canvas: invalid clip: " << clip);
479       return;
480     }
481
482     const std::string sep(", \t\npx");
483     int comp = 0;
484     float values[4];
485
486     for(size_t pos = RECT.size(); comp < 4; ++comp)
487     {
488       pos = clip.find_first_not_of(sep, pos);
489       if( pos == std::string::npos || pos == clip.size() - 1 )
490         break;
491
492       char *end = 0;
493       values[comp] = strtod(&clip[pos], &end);
494       if( end == &clip[pos] || !end )
495         break;
496
497       pos = end - &clip[0];
498     }
499
500     if( comp < 4 )
501     {
502       SG_LOG(SG_GENERAL, SG_WARN, "Canvas: invalid clip: " << clip);
503       return;
504     }
505
506     float width = values[1] - values[3],
507           height = values[2] - values[0];
508
509     if( width < 0 || height < 0 )
510     {
511       SG_LOG(SG_GENERAL, SG_WARN, "Canvas: negative clip size: " << clip);
512       return;
513     }
514
515     _scissor = new RelativeScissor();
516     // <top>, <right>, <bottom>, <left>
517     _scissor->x() = SGMiscf::roundToInt(values[3]);
518     _scissor->y() = SGMiscf::roundToInt(values[0]);
519     _scissor->width() = SGMiscf::roundToInt(width);
520     _scissor->height() = SGMiscf::roundToInt(height);
521
522     getOrCreateStateSet()->setAttributeAndModes(_scissor);
523
524     SGPropertyNode* clip_frame = _node->getChild("clip-frame", 0);
525     if( clip_frame )
526       valueChanged(clip_frame);
527   }
528
529   //----------------------------------------------------------------------------
530   void Element::setClipFrame(ReferenceFrame rf)
531   {
532     if( _scissor )
533     {
534       _scissor->_coord_reference = rf;
535       _attributes_dirty |= SCISSOR_COORDS;
536     }
537   }
538
539   //----------------------------------------------------------------------------
540   osg::BoundingBox Element::getBoundingBox() const
541   {
542     if( _drawable )
543       return _drawable->getBound();
544
545     osg::BoundingBox bb;
546
547     if( _transform.valid() )
548       bb.expandBy(_transform->getBound());
549
550     return bb;
551   }
552
553   //----------------------------------------------------------------------------
554   osg::BoundingBox Element::getTightBoundingBox() const
555   {
556     return getTransformedBounds(getMatrix());
557   }
558
559   //----------------------------------------------------------------------------
560   osg::BoundingBox Element::getTransformedBounds(const osg::Matrix& m) const
561   {
562     if( !_drawable )
563       return osg::BoundingBox();
564
565     osg::BoundingBox transformed;
566     const osg::BoundingBox& bb = _drawable->getBound();
567     for(int i = 0; i < 4; ++i)
568       transformed.expandBy( bb.corner(i) * m );
569
570     return transformed;
571   }
572
573   //----------------------------------------------------------------------------
574   osg::Matrix Element::getMatrix() const
575   {
576     if( !_transform )
577       return osg::Matrix::identity();
578
579     if( !(_attributes_dirty & TRANSFORM) )
580       return _transform->getMatrix();
581
582     osg::Matrix m;
583     for( size_t i = 0; i < _transform_types.size(); ++i )
584     {
585       // Skip unused indizes...
586       if( _transform_types[i] == TT_NONE )
587         continue;
588
589       SGPropertyNode* tf_node = _node->getChild("tf", i, true);
590
591       // Build up the matrix representation of the current transform node
592       osg::Matrix tf;
593       switch( _transform_types[i] )
594       {
595         case TT_MATRIX:
596           tf = osg::Matrix( tf_node->getDoubleValue("m[0]", 1),
597                             tf_node->getDoubleValue("m[1]", 0),
598                             0,
599                             tf_node->getDoubleValue("m[6]", 0),
600
601                             tf_node->getDoubleValue("m[2]", 0),
602                             tf_node->getDoubleValue("m[3]", 1),
603                             0,
604                             tf_node->getDoubleValue("m[7]", 0),
605
606                             0,
607                             0,
608                             1,
609                             0,
610
611                             tf_node->getDoubleValue("m[4]", 0),
612                             tf_node->getDoubleValue("m[5]", 0),
613                             0,
614                             tf_node->getDoubleValue("m[8]", 1) );
615           break;
616         case TT_TRANSLATE:
617           tf.makeTranslate( osg::Vec3f( tf_node->getDoubleValue("t[0]", 0),
618                                         tf_node->getDoubleValue("t[1]", 0),
619                                         0 ) );
620           break;
621         case TT_ROTATE:
622           tf.makeRotate( tf_node->getDoubleValue("rot", 0), 0, 0, 1 );
623           break;
624         case TT_SCALE:
625         {
626           float sx = tf_node->getDoubleValue("s[0]", 1);
627           // sy defaults to sx...
628           tf.makeScale( sx, tf_node->getDoubleValue("s[1]", sx), 1 );
629           break;
630         }
631         default:
632           break;
633       }
634       m.postMult( tf );
635     }
636     _transform->setMatrix(m);
637     _attributes_dirty &= ~TRANSFORM;
638     _attributes_dirty |= SCISSOR_COORDS;
639
640     return m;
641   }
642
643   //----------------------------------------------------------------------------
644   Element::StyleSetters Element::_style_setters;
645
646   //----------------------------------------------------------------------------
647   Element::Element( const CanvasWeakPtr& canvas,
648                     const SGPropertyNode_ptr& node,
649                     const Style& parent_style,
650                     Element* parent ):
651     PropertyBasedElement(node),
652     _canvas( canvas ),
653     _parent( parent ),
654     _attributes_dirty( 0 ),
655     _transform( new osg::MatrixTransform ),
656     _style( parent_style ),
657     _scissor( 0 ),
658     _drawable( 0 )
659   {
660     staticInit();
661
662     SG_LOG
663     (
664       SG_GL,
665       SG_DEBUG,
666       "New canvas element " << node->getPath()
667     );
668
669     // Ensure elements are drawn in order they appear in the element tree
670     _transform->getOrCreateStateSet()
671               ->setRenderBinDetails
672               (
673                 0,
674                 "PreOrderBin",
675                 osg::StateSet::OVERRIDE_RENDERBIN_DETAILS
676               );
677   }
678
679   //----------------------------------------------------------------------------
680   void Element::staticInit()
681   {
682     if( isInit<Element>() )
683       return;
684
685     addStyle("clip", "", &Element::setClip, false);
686     addStyle("clip-frame", "", &Element::setClipFrame, false);
687     addStyle("visible", "", &Element::setVisible, false);
688   }
689
690   //----------------------------------------------------------------------------
691   bool Element::isStyleEmpty(const SGPropertyNode* child) const
692   {
693     return !child
694         || simgear::strutils::strip(child->getStringValue()).empty();
695   }
696
697   //----------------------------------------------------------------------------
698   bool Element::canApplyStyle(const SGPropertyNode* child) const
699   {
700     if( _node == child->getParent() )
701       return true;
702
703     // Parent values do not override if element has own value
704     return isStyleEmpty( _node->getChild(child->getName()) );
705   }
706
707   //----------------------------------------------------------------------------
708   bool Element::setStyleImpl( const SGPropertyNode* child,
709                               const StyleInfo* style_info )
710   {
711     const StyleSetter* style_setter = style_info
712                                     ? &style_info->setter
713                                     : getStyleSetter(child->getNameString());
714     while( style_setter )
715     {
716       if( style_setter->func(*this, child) )
717         return true;
718       style_setter = style_setter->next;
719     }
720     return false;
721   }
722
723   //----------------------------------------------------------------------------
724   const Element::StyleInfo*
725   Element::getStyleInfo(const std::string& name) const
726   {
727     StyleSetters::const_iterator setter = _style_setters.find(name);
728     if( setter == _style_setters.end() )
729       return 0;
730
731     return &setter->second;
732   }
733
734   //----------------------------------------------------------------------------
735   const Element::StyleSetter*
736   Element::getStyleSetter(const std::string& name) const
737   {
738     const StyleInfo* info = getStyleInfo(name);
739     return info ? &info->setter : 0;
740   }
741
742   //----------------------------------------------------------------------------
743   const SGPropertyNode*
744   Element::getParentStyle(const SGPropertyNode* child) const
745   {
746     // Try to get value from parent...
747     if( _parent )
748     {
749       Style::const_iterator style =
750         _parent->_style.find(child->getNameString());
751       if( style != _parent->_style.end() )
752         return style->second;
753     }
754
755     // ...or reset to default if none is available
756     return child; // TODO somehow get default value for each style?
757   }
758
759   //----------------------------------------------------------------------------
760   void Element::setDrawable( osg::Drawable* drawable )
761   {
762     _drawable = drawable;
763     assert( _drawable );
764
765     osg::ref_ptr<osg::Geode> geode = new osg::Geode;
766     geode->addDrawable(_drawable);
767     _transform->addChild(geode);
768   }
769
770   //----------------------------------------------------------------------------
771   osg::StateSet* Element::getOrCreateStateSet()
772   {
773     return _drawable ? _drawable->getOrCreateStateSet()
774                      : _transform->getOrCreateStateSet();
775   }
776
777   //----------------------------------------------------------------------------
778   void Element::setupStyle()
779   {
780     BOOST_FOREACH( Style::value_type style, _style )
781       setStyle(style.second);
782   }
783
784 } // namespace canvas
785 } // namespace simgear