// 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>
{
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)
}
_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)
if( clip.empty() || clip == "auto" )
{
getOrCreateStateSet()->removeAttribute(osg::StateAttribute::SCISSOR);
+ _scissor = 0;
return;
}
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;
+ }
}
//----------------------------------------------------------------------------
_transform_dirty( false ),
_transform( new osg::MatrixTransform ),
_style( parent_style ),
+ _scissor( 0 ),
_drawable( 0 )
{
staticInit();
return;
addStyle("clip", "", &Element::setClip, false);
+ addStyle("clip-frame", "", &Element::setClipFrame, false);
}
//----------------------------------------------------------------------------
/// 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)
+ };
+
/**
*
*/
*/
void setClip(const std::string& clip);
+ /**
+ * Clipping coordinates reference frame
+ */
+ void setClipFrame(ReferenceFrame rf);
+
/**
* Write the given bounding box to the property tree
*/
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
TT_SCALE
};
+ class RelativeScissor;
+
CanvasWeakPtr _canvas;
Element *_parent;
Style _style;
std::vector<SGPropertyNode_ptr> _bounding_box;
+ RelativeScissor *_scissor;
typedef std::vector<EventListener> Listener;
typedef std::map<Event::Type, Listener> ListenerMap;
};
} // 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_ */