Hi,
today I ask for some guidance with the following problem:
I have written a pick framework which allows fast frustum picking of polygons,
lines and points. In principle this works fine until recently. Problems show up
the moment I have introduced the first 'InverseTransform' core into the scene
graph. After some debugging I found that I have made a terrible mistake in my
"SimpleSelectionFunctor" class. Eliminating this error did reveal a weakness,
at least IMHO, of the OpenSG framework, which I would like to remove. But
maybe I did it all completely wrong and should take another route. So allow me
to lay done my design and implementation so that you can understand the core of
my problem.
User code: frustum picking
=================
void View::pick(NodePtr root, UInt32 x0, UInt32 y0, UInt32 x1, UInt32 y1)
{
graphic::Frustum frustum = calcViewFrustum(x0, y0, x1, y1);
SelectionAction* action = SelectionAction::create(frustum);
SimpleSelectionFunctor selFunc(action, SimpleSelectionFunctor::FACE);
action->apply(root);
BOOST_FOREACH(const SimpleSelectionFunctor::HitT& hit, selFunc.hits())
{
...
}
}
Implementation details
===============
// Basically the same as Action (could be improved, but is not the point here)
class SelectionAction : public Action
{
public:
static void registerEnterDefault(const FieldContainerType& type,
const Action::Functor& func);
static void registerLeaveDefault(const FieldContainerType& type,
const Action::Functor& func);
private:
std::stack<RenderEngine::graphic::Frustum> _frustums;
std::stack<NodePtr> _nodes;
std::stack<Matrix> _matrices;
FrustumVolume _frustumVolume;
SelectionFunctor* _actor;
static std::vector<Functor>* _defaultEnterFunctors;
static std::vector<Functor>* _defaultLeaveFunctors;
};
// Base class functor
class SelectionFunctor : boost::noncopyable
{
public:
enum SelectionType {
FACE = 0x0001,
EDGE = 0x0002,
VERTEX = 0x0004,
ALL = FACE | EDGE | VERTEX
};
enum HitType {
PRIMITIVE,
OBJECT_IN_VIEW_DIR,
OBJECT_AGAINST_VIEW_DIR
};
struct HitT {...}
typedef std::deque<HitT> DequeHitsT;
public:
void setAction(SelectionAction* action);
void start();
void stop();
const DequeHitsT& hits(HitType hitType = OBJECT_IN_VIEW_DIR) const;
protected:
SelectionAction* _action;
SelectionType _selectionType;
DequeHitsT _dequeHits;
};
// Functor with terrible wrong implementation
class SimpleSelectionFunctor : public SelectionFunctor
{
public:
SimpleSelectionFunctor();
private:
virtual void init();
Action::ResultE enterTransform(CNodePtr& node, Action* action);
Action::ResultE leaveTransform(CNodePtr& node, Action* action);
Action::ResultE enterAccumulateTransform(CNodePtr& node, Action* action);
Action::ResultE leaveAccumulateTransform(CNodePtr& node, Action* action);
Action::ResultE enterGeometry(CNodePtr& node, Action* action);
Action::ResultE leaveGeometry(CNodePtr& node, Action* action);
Action::ResultE enterMultiSwitch(CNodePtr& node, Action* action);
Action::ResultE leaveMultiSwitch(CNodePtr& node, Action* action);
void triangles (const Plane& plane, const GeometryPtr& geo);
void lines (const Plane& plane, const GeometryPtr& geo);
void points (const Plane& plane, const GeometryPtr& geo);
};
//
// Init: Register the handler functions
//
void SimpleSelectionFunctor::init()
{
if (!_action) return;
Base::init();
typedef Action::ResultE T1;
typedef SimpleSelectionFunctor T2;
typedef CNodePtr T3;
typedef Action* T4;
_action->registerEnterFunction(Transform::getClassType(),
osgTypedMethodFunctor2ObjPtrCPtrRef<T1, T2, T3, T4>(this,
&SimpleSelectionFunctor::enterTransform));
_action->registerLeaveFunction(Transform::getClassType(),
osgTypedMethodFunctor2ObjPtrCPtrRef<T1, T2, T3, T4>(this,
&SimpleSelectionFunctor::leaveTransform));
_action->registerEnterFunction(ComponentTransform::getClassType(),
osgTypedMethodFunctor2ObjPtrCPtrRef<T1, T2, T3, T4>(this,
&SimpleSelectionFunctor::enterTransform));
...
_action->registerEnterFunction(InverseTransform::getClassType(),
osgTypedMethodFunctor2ObjPtrCPtrRef<T1, T2, T3, T4>(this,
&SimpleSelectionFunctor::enterAccumulateTransform));
_action->registerLeaveFunction(InverseTransform::getClassType(),
osgTypedMethodFunctor2ObjPtrCPtrRef<T1, T2, T3, T4>(this,
&SimpleSelectionFunctor::leaveAccumulateTransform));
_action->registerEnterFunction(Billboard::getClassType(),
osgTypedMethodFunctor2ObjPtrCPtrRef<T1, T2, T3, T4>(this,
&SimpleSelectionFunctor::enterAccumulateTransform));
...
_action->registerEnterFunction(Geometry::getClassType(),
osgTypedMethodFunctor2ObjPtrCPtrRef<T1, T2, T3, T4>(this,
&SimpleSelectionFunctor::enterGeometry));
_action->registerLeaveFunction(Geometry::getClassType(),
osgTypedMethodFunctor2ObjPtrCPtrRef<T1, T2, T3, T4>(this,
&SimpleSelectionFunctor::leaveGeometry));
...
}
//
// My mistake the terrible wrong handler implementation for InverseTransform
and the like
//
Action::ResultE SimpleSelectionFunctor::enterAccumulateTransform(CNodePtr& cp,
Action* action)
{
NodePtr trans_node = _action->getActNode();
DynamicVolume volume = trans_node->getVolume();
if (volume.isValid() && !volume.intersect(_action->frustumVolume()))
return Action::Skip;
NodeCorePtr core = trans_node->getCore();
TransformPtr trans = TransformPtr::dcast(core);
// Hue, that is plainly wrong and will later crash! InverseTransform
// does not inherit from Transform but from Group and NodeCore.
Matrix mat;
mat.setIdentity();
trans->accumulateMatrix(mat); // crash...
mat.invert();
RenderEngine::graphic::Frustum frustum = _action->topFrustum();
frustum.multFullMatrix(mat);
mat = _action->topMatrix();
trans->accumulateMatrix(mat);
_action->pushMatrix(mat);
_action->pushFrustum(frustum);
_action->pushNode(trans_node);
return Action::Continue;
}
//
// So I tried to repair the code above with the following code
//
Action::ResultE SimpleSelectionFunctor::enterAccumulateTransform(CNodePtr& cp,
Action* action)
{
NodePtr trans_node = _action->getActNode();
DynamicVolume volume = trans_node->getVolume();
if (volume.isValid() && !volume.intersect(_action->frustumVolume()))
return Action::Skip;
NodeCorePtr core = trans_node->getCore();
Matrix mat;
mat.setIdentity();
core->accumulateMatrix(mat); // Does not compile NodeCore::accumulateMatrix
is protected
mat.invert();
RenderEngine::graphic::Frustum frustum = _action->topFrustum();
frustum.multFullMatrix(mat);
mat = _action->topMatrix();
core->accumulateMatrix(mat);
_action->pushMatrix(mat);
_action->pushFrustum(frustum);
_action->pushNode(trans_node);
return Action::Continue;
}
Now, I'm at the core of my problem. 'NodeCore' base class function
'accumulateMatrix' is protected, therefore preventing polymorphic dispatching
to the correct implementation in my client code. Of course, I could write
different handler functions for each class, like
'enterAccumulateInverseTransform', but that is cumbersome and does not scale
very well.
So my point is that NodeCore::accumulateMatrix and NodeCore::adjustVolume
should be public instead.
If there is a more elegant way to write custom actions I would be glad to learn
about them. However, until now my solution did work quite fine.
Any help and suggestions are appreciated.
With best regards,
Johannes
------------------------------------------------------------------------------
This SF.net email is sponsored by:
SourcForge Community
SourceForge wants to tell your story.
http://p.sf.net/sfu/sf-spreadtheword
_______________________________________________
Opensg-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/opensg-users