Canvas: improved clipping and new property clip-frame.

- 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.
This commit is contained in:
Thomas Geymayer 2013-11-03 20:12:45 +01:00
parent 353b7e4438
commit 38ddfab1e0
2 changed files with 131 additions and 26 deletions

View 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);
if( canvas )
// Canvas has y axis upside down
scissor->y() = canvas->getSizeY() - scissor->y() - scissor->height();
getOrCreateStateSet()->setAttributeAndModes(_scissor);
getOrCreateStateSet()->setAttributeAndModes(scissor);
SGPropertyNode* clip_frame = _node->getChild("clip-frame", 0);
if( clip_frame )
valueChanged(clip_frame);
}
//----------------------------------------------------------------------------
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);
}
//----------------------------------------------------------------------------

View 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_ */