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:
parent
353b7e4438
commit
38ddfab1e0
@ -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);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
@ -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_ */
|
||||
|
Loading…
Reference in New Issue
Block a user