]> git.mxchange.org Git - simgear.git/commitdiff
Canvas: improved clipping and new property clip-frame.
authorThomas Geymayer <tomgey@gmail.com>
Sun, 3 Nov 2013 19:12:45 +0000 (20:12 +0100)
committerThomas Geymayer <tomgey@gmail.com>
Sun, 3 Nov 2013 19:12:45 +0000 (20:12 +0100)
 - Update clipping rect if canvas view or texture size
   changes.
 - Add new property "clip-frame" to specify coordinate
   frame for "clip" coordinates. Coordinates can be
   global, relative to the parent or relative to the
   element itself.

simgear/canvas/elements/CanvasElement.cxx
simgear/canvas/elements/CanvasElement.hxx

index f4eacd1f41c864798d3b442e1503c3974ce4db8d..30601e77999ca0ceb428bc62c6eb860a7f4ccac0 100644 (file)
@@ -17,7 +17,6 @@
 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
 
 #include "CanvasElement.hxx"
-#include <simgear/canvas/Canvas.hxx>
 #include <simgear/canvas/CanvasEventVisitor.hxx>
 #include <simgear/canvas/MouseEvent.hxx>
 #include <simgear/math/SGMisc.hxx>
@@ -41,6 +40,63 @@ namespace canvas
 {
   const std::string NAME_TRANSFORM = "tf";
 
+  /**
+   * glScissor with coordinates relative to different reference frames.
+   */
+  class Element::RelativeScissor:
+    public osg::Scissor
+  {
+    public:
+
+      ReferenceFrame    _coord_reference;
+      osg::Matrix       _parent_inverse;
+
+      RelativeScissor():
+        _coord_reference(GLOBAL)
+      {}
+
+      virtual void apply(osg::State& state) const
+      {
+        const osg::Viewport* vp = state.getCurrentViewport();
+        float w2 = 0.5 * vp->width(),
+              h2 = 0.5 * vp->height();
+
+        osg::Matrix model_view
+        (
+          w2, 0,  0, 0,
+          0,  h2, 0, 0,
+          0,  0,  1, 0,
+          w2, h2, 0, 1
+        );
+        model_view.preMult(state.getProjectionMatrix());
+
+        if( _coord_reference != GLOBAL )
+        {
+          model_view.preMult(state.getModelViewMatrix());
+
+          if( _coord_reference == PARENT )
+            model_view.preMult(_parent_inverse);
+        }
+
+        const osg::Vec2 scale( model_view(0,0), model_view(1,1)),
+                        offset(model_view(3,0), model_view(3,1));
+
+        // TODO check/warn for rotation?
+
+        GLint x = SGMiscf::roundToInt(scale.x() * _x + offset.x()),
+              y = SGMiscf::roundToInt(scale.y() * _y + offset.y()),
+              w = SGMiscf::roundToInt(std::fabs(scale.x()) * _width),
+              h = SGMiscf::roundToInt(std::fabs(scale.y()) * _height);
+
+        if( scale.x() < 0 )
+          x -= w;
+        if( scale.y() < 0 )
+          y -= h;
+
+        glScissor(x, y, w, h);
+      }
+  };
+
   //----------------------------------------------------------------------------
   Element::OSGUserData::OSGUserData(ElementPtr element):
     element(element)
@@ -156,6 +212,15 @@ namespace canvas
       }
       _transform->setMatrix(m);
       _transform_dirty = false;
+      _attributes_dirty |= SCISSOR_COORDS;
+    }
+
+    if( _attributes_dirty & SCISSOR_COORDS )
+    {
+      if( _scissor && _scissor->_coord_reference != GLOBAL )
+        _scissor->_parent_inverse = _transform->getInverseMatrix();
+
+      _attributes_dirty &= ~SCISSOR_COORDS;
     }
 
     // Update bounding box on manual update (manual updates pass zero dt)
@@ -402,6 +467,7 @@ namespace canvas
     if( clip.empty() || clip == "auto" )
     {
       getOrCreateStateSet()->removeAttribute(osg::StateAttribute::SCISSOR);
+      _scissor = 0;
       return;
     }
 
@@ -447,32 +513,28 @@ namespace canvas
       return;
     }
 
-    float scale_x = 1,
-          scale_y = 1;
-
-    CanvasPtr canvas = _canvas.lock();
-    if( canvas )
-    {
-      // The scissor rectangle isn't affected by any transformation, so we need
-      // to convert to image/canvas coordinates on our selves.
-      scale_x = canvas->getSizeX()
-              / static_cast<float>(canvas->getViewWidth());
-      scale_y = canvas->getSizeY()
-              / static_cast<float>(canvas->getViewHeight());
-    }
-
-    osg::Scissor* scissor = new osg::Scissor();
+    _scissor = new RelativeScissor();
     // <top>, <right>, <bottom>, <left>
-    scissor->x() = SGMiscf::roundToInt(scale_x * values[3]);
-    scissor->y() = SGMiscf::roundToInt(scale_y * values[0]);
-    scissor->width() = SGMiscf::roundToInt(scale_x * width);
-    scissor->height() = SGMiscf::roundToInt(scale_y * height);
+    _scissor->x() = SGMiscf::roundToInt(values[3]);
+    _scissor->y() = SGMiscf::roundToInt(values[0]);
+    _scissor->width() = SGMiscf::roundToInt(width);
+    _scissor->height() = SGMiscf::roundToInt(height);
+
+    getOrCreateStateSet()->setAttributeAndModes(_scissor);
 
-    if( canvas )
-      // Canvas has y axis upside down
-      scissor->y() = canvas->getSizeY() - scissor->y() - scissor->height();
+    SGPropertyNode* clip_frame = _node->getChild("clip-frame", 0);
+    if( clip_frame )
+      valueChanged(clip_frame);
+  }
 
-    getOrCreateStateSet()->setAttributeAndModes(scissor);
+  //----------------------------------------------------------------------------
+  void Element::setClipFrame(ReferenceFrame rf)
+  {
+    if( _scissor )
+    {
+      _scissor->_coord_reference = rf;
+      _attributes_dirty |= SCISSOR_COORDS;
+    }
   }
 
   //----------------------------------------------------------------------------
@@ -523,6 +585,7 @@ namespace canvas
     _transform_dirty( false ),
     _transform( new osg::MatrixTransform ),
     _style( parent_style ),
+    _scissor( 0 ),
     _drawable( 0 )
   {
     staticInit();
@@ -551,6 +614,7 @@ namespace canvas
       return;
 
     addStyle("clip", "", &Element::setClip, false);
+    addStyle("clip-frame", "", &Element::setClipFrame, false);
   }
 
   //----------------------------------------------------------------------------
index 53357f1c7ce709b8c0f3a0188e7a0ac5f20c82d6..93108255483152a9d28d1bb025610a352834fd5d 100644 (file)
@@ -75,6 +75,17 @@ namespace canvas
                             ///   their parents
       };
 
+      /**
+       * Coordinate reference frame (eg. "clip" property)
+       */
+      enum ReferenceFrame
+      {
+        GLOBAL, ///!< Global coordinates
+        PARENT, ///!< Coordinates relative to parent coordinate frame
+        LOCAL   ///!< Coordinates relative to local coordinates (parent
+                ///   coordinates with local transformations applied)
+      };
+
       /**
        *
        */
@@ -131,6 +142,11 @@ namespace canvas
        */
       void setClip(const std::string& clip);
 
+      /**
+       * Clipping coordinates reference frame
+       */
+      void setClipFrame(ReferenceFrame rf);
+
       /**
        * Write the given bounding box to the property tree
        */
@@ -165,8 +181,9 @@ namespace canvas
 
       enum Attributes
       {
-        BLEND_FUNC = 0x0001,
-        LAST_ATTRIBUTE  = BLEND_FUNC << 1
+        BLEND_FUNC      = 1,
+        SCISSOR_COORDS  = BLEND_FUNC << 1,
+        LAST_ATTRIBUTE  = SCISSOR_COORDS << 1
       };
 
       enum TransformType
@@ -178,6 +195,8 @@ namespace canvas
         TT_SCALE
       };
 
+      class RelativeScissor;
+
       CanvasWeakPtr _canvas;
       Element      *_parent;
 
@@ -189,6 +208,7 @@ namespace canvas
 
       Style                             _style;
       std::vector<SGPropertyNode_ptr>   _bounding_box;
+      RelativeScissor                  *_scissor;
 
       typedef std::vector<EventListener> Listener;
       typedef std::map<Event::Type, Listener> ListenerMap;
@@ -555,6 +575,27 @@ namespace canvas
   };
 
 } // namespace canvas
+
+  template<>
+  struct enum_traits<canvas::Element::ReferenceFrame>
+  {
+    static const char* name()
+    {
+      return "canvas::Element::ReferenceFrame";
+    }
+
+    static canvas::Element::ReferenceFrame defVal()
+    {
+      return canvas::Element::GLOBAL;
+    }
+
+    static bool validate(int frame)
+    {
+      return frame >= canvas::Element::GLOBAL
+          && frame <= canvas::Element::LOCAL;
+    }
+  };
+
 } // namespace simgear
 
 #endif /* CANVAS_ELEMENT_HXX_ */