Issue:
Dragger receives ::PUSH event only when it is the closest object to camera. In case of more complex composite draggers this is impossible to guarantee.

File: Dragger.cpp / bool Dragger::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)

Detailed issue description:

This is original code:

if (view->computeIntersections(ea.getX(),ea.getY(),intersections))
                {
for(osgUtil::LineSegmentIntersector::Intersections::iterator hitr = intersections.begin();
                        hitr != intersections.end();
                        ++hitr)
                    {
_pointer.addIntersection(hitr->nodePath, hitr->getLocalIntersectPoint());
                    }

for (osg::NodePath::iterator itr = _pointer._hitList.front().first.begin(); itr != _pointer._hitList.front().first.end(); ++itr)

All intersections on event screen location are obtained and stored in _pointer structure. Then NodePath from *first* intersection only is scanned for node of type osgManipulator::Dragger and if such node is found and matches *this* pointer, then dragger->handle(...) is called.

Problem is when dragger is for example TranslatePlaneDragger used for manipulation of 2d text and both dragger and text are on same plane. Then dragger doesn't need to be the first intersection found and original code fails to process dragger->handle(...) even it's desired.

Solution:
It's necessary to scan all intersections and use the first/closest intersection with dragger in it's NodePath. Variable closestDraggerFound in new code guarantees that only intersection with closest dragger is evaluated and this keeps logic as it was desired in original code. Only the closest dragger gets chance to handle event.

Testing:
Code tested in our application with complex composite draggers under many conditions. Situations when more plane draggers
(from one composite gizmo) overlap tested.



/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * OpenSceneGraph Public License for more details.
*/
//osgManipulator - Copyright (C) 2007 Fugro-Jason B.V.

#include <osgManipulator/Dragger>
#include <osgManipulator/Command>
#include <osg/Material>
#include <osgGA/EventVisitor>
#include <osgViewer/View>

using namespace osgManipulator;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// computeNodePathToRoot
//
void osgManipulator::computeNodePathToRoot(osg::Node& node, osg::NodePath& np)
{
    np.clear();

    osg::NodePathList nodePaths = node.getParentalNodePaths();

    if (!nodePaths.empty())
    {
        np = nodePaths.front();
        if (nodePaths.size()>1)
        {
            OSG_NOTICE<<"osgManipulator::computeNodePathToRoot(,) taking first 
parent path, ignoring others."<<std::endl;
        }
    }
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// DraggerTransformCallback
//
DraggerTransformCallback::DraggerTransformCallback(osg::MatrixTransform* 
transform):
    _transform(transform)
{
}

bool DraggerTransformCallback::receive(const MotionCommand& command)
{
    if (!_transform) return false;

    switch (command.getStage())
    {
        case MotionCommand::START:
        {
            // Save the current matrix
            _startMotionMatrix = _transform->getMatrix();

            // Get the LocalToWorld and WorldToLocal matrix for this node.
            osg::NodePath nodePathToRoot;
            computeNodePathToRoot(*_transform,nodePathToRoot);
            _localToWorld = osg::computeLocalToWorld(nodePathToRoot);
            _worldToLocal = osg::Matrix::inverse(_localToWorld);

            return true;
        }
        case MotionCommand::MOVE:
        {
            // Transform the command's motion matrix into local motion matrix.
            osg::Matrix localMotionMatrix = _localToWorld * 
command.getWorldToLocal()
                                            * command.getMotionMatrix()
                                            * command.getLocalToWorld() * 
_worldToLocal;

            // Transform by the localMotionMatrix
            _transform->setMatrix(localMotionMatrix * _startMotionMatrix);

            return true;
        }
        case MotionCommand::FINISH:
        {
            return true;
        }
        case MotionCommand::NONE:
        default:
            return false;
    }
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// PointerInfo
//
PointerInfo::PointerInfo():
    _nearPoint(osg::Vec3d()),
    _farPoint(osg::Vec3d()),
    _eyeDir(osg::Vec3d(0,0,1))
{
    _hitIter = _hitList.begin();
}

bool PointerInfo::contains(const osg::Node* node) const
{
    if (node && _hitIter!=_hitList.end()) return 
std::find((*_hitIter).first.begin(), (*_hitIter).first.end(), node) != 
(*_hitIter).first.end();
    else return false;
}

bool PointerInfo::projectWindowXYIntoObject(const osg::Vec2d& windowCoord, 
osg::Vec3d& nearPoint, osg::Vec3d& farPoint) const
{
    nearPoint = osg::Vec3d(windowCoord.x(),windowCoord.y(),0.0)*_inverseMVPW;
    farPoint = osg::Vec3d(windowCoord.x(),windowCoord.y(),1.0)*_inverseMVPW;

    return true;
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Dragger
//
Dragger::Dragger() :
    _handleEvents(false),
    _draggerActive(false),
    _activationModKeyMask(0),
    _activationKeyEvent(0),
    _activationPermittedByModKeyMask(false),
    _activationPermittedByKeyEvent(false)
{
    _parentDragger = this;
    getOrCreateStateSet()->setDataVariance(osg::Object::DYNAMIC);

    _selfUpdater = new DraggerTransformCallback(this);

}

Dragger::Dragger(const Dragger& rhs, const osg::CopyOp& copyop):
    osg::MatrixTransform(rhs, copyop),
    _handleEvents(rhs._handleEvents),
    _draggerActive(false),
    _activationModKeyMask(rhs._activationModKeyMask),
    _activationKeyEvent(rhs._activationKeyEvent),
    _activationPermittedByModKeyMask(false),
    _activationPermittedByKeyEvent(false)
{
    OSG_NOTICE<<"CompositeDragger::CompositeDragger(const CompositeDragger& 
rhs, const osg::CopyOp& copyop) not Implemented yet."<<std::endl;
}

Dragger::~Dragger()
{
}

void Dragger::setHandleEvents(bool flag)
{
    if (_handleEvents == flag) return;

    _handleEvents = flag;

    // update the number of children that require an event traversal to make 
sure this dragger recieves events.
    if (_handleEvents) 
setNumChildrenRequiringEventTraversal(getNumChildrenRequiringEventTraversal()+1);
    else if (getNumChildrenRequiringEventTraversal()>=1) 
setNumChildrenRequiringEventTraversal(getNumChildrenRequiringEventTraversal()-1);
}

void Dragger::addConstraint(Constraint* constraint)
{
    // check to make sure constaint hasn't already been attached.
    for(Constraints::iterator itr = _constraints.begin();
        itr != _constraints.end();
        ++itr)
    {
        if (*itr == constraint) return;
    }

    _constraints.push_back(constraint);
}

void Dragger::removeConstraint(Constraint* constraint)
{
    for(Constraints::iterator itr = _constraints.begin();
        itr != _constraints.end();
        )
    {
        if (*itr == constraint)
        {
            _constraints.erase(itr);
            return;
        } else
        {
            ++itr;
        }
    }
}

void Dragger::addTransformUpdating(osg::MatrixTransform* transform)
{
    addDraggerCallback(new DraggerTransformCallback(transform));
}

void Dragger::removeTransformUpdating(osg::MatrixTransform* transform)
{
    for(Dragger::DraggerCallbacks::iterator itr = _draggerCallbacks.begin();
        itr != _draggerCallbacks.end();
        )
    {
        DraggerCallback* dc = itr->get();
        DraggerTransformCallback* dtc = 
dynamic_cast<DraggerTransformCallback*>(dc);
        if (dtc && dtc->getTransform()==transform)
        {
            itr = _draggerCallbacks.erase(itr);
        }
        else
        {
            ++itr;
        }
    }

}

void Dragger::addDraggerCallback(DraggerCallback* dc)
{
    for(DraggerCallbacks::iterator itr = _draggerCallbacks.begin();
        itr != _draggerCallbacks.end();
        ++itr)
    {
        if (*itr == dc) return;
    }

    _draggerCallbacks.push_back(dc);
}

void Dragger::removeDraggerCallback(DraggerCallback* dc)
{
}


void Dragger::traverse(osg::NodeVisitor& nv)
{
    if (_handleEvents && nv.getVisitorType()==osg::NodeVisitor::EVENT_VISITOR)
    {
        osgGA::EventVisitor* ev = dynamic_cast<osgGA::EventVisitor*>(&nv);
        if (ev)
        {
            for(osgGA::EventQueue::Events::iterator itr = 
ev->getEvents().begin();
                itr != ev->getEvents().end();
                ++itr)
            {
                osgGA::GUIEventAdapter* ea = itr->get();
                if (handle(*ea, *(ev->getActionAdapter()))) 
ea->setHandled(true);
            }
        }
        return;
    }

    MatrixTransform::traverse(nv);
}

bool Dragger::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& 
aa)
{
    if (ea.getHandled()) return false;

    osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
    if (!view) return false;

    bool handled = false;

    bool activationPermitted = true;
    if (_activationModKeyMask!=0 || _activationKeyEvent!=0)
    {
        _activationPermittedByModKeyMask = (_activationModKeyMask!=0) ?
            ((ea.getModKeyMask() & _activationModKeyMask)!=0) :
            false;

        if (_activationKeyEvent!=0)
        {
            switch (ea.getEventType())
            {
                case osgGA::GUIEventAdapter::KEYDOWN:
                {
                    if (ea.getKey()==_activationKeyEvent) 
_activationPermittedByKeyEvent = true;
                    break;
                }
                case osgGA::GUIEventAdapter::KEYUP:
                {
                    if (ea.getKey()==_activationKeyEvent) 
_activationPermittedByKeyEvent = false;
                    break;
                }
                default:
                    break;
            }
        }

        activationPermitted =  _activationPermittedByModKeyMask || 
_activationPermittedByKeyEvent;

    }

    if (activationPermitted || _draggerActive)
    {
        switch (ea.getEventType())
        {
            case osgGA::GUIEventAdapter::PUSH:
            {
                osgUtil::LineSegmentIntersector::Intersections intersections;

                _pointer.reset();

                if 
(view->computeIntersections(ea.getX(),ea.getY(),intersections))
                {
                    
for(osgUtil::LineSegmentIntersector::Intersections::iterator hitr = 
intersections.begin();
                        hitr != intersections.end();
                        ++hitr)
                    {
                        _pointer.addIntersection(hitr->nodePath, 
hitr->getLocalIntersectPoint());
                    }

                    for (PointerInfo::IntersectionList::iterator 
itrIntersection = _pointer._hitList.begin();
                          itrIntersection != _pointer._hitList.end();
                          ++itrIntersection)
                    {
                      bool  closestDraggerFound = false;

                      for (osg::NodePath::iterator itr = 
itrIntersection->first.begin();
                        itr != itrIntersection->first.end();
                        ++itr)
                      {
                        osgManipulator::Dragger* dragger = 
dynamic_cast<osgManipulator::Dragger*>(*itr);
                        if (dragger)
                        {
                          if (dragger==this)
                          {
                            osg::Camera *rootCamera = view->getCamera();
                            osg::NodePath nodePath = itrIntersection->first;
                            osg::NodePath::reverse_iterator ritr;
                            for(ritr = nodePath.rbegin();
                              ritr != nodePath.rend();
                              ++ritr)
                            {
                              osg::Camera* camera = 
dynamic_cast<osg::Camera*>(*ritr);
                              if (camera && 
(camera->getReferenceFrame()!=osg::Transform::RELATIVE_RF || 
camera->getParents().empty()))
                              {
                                rootCamera = camera;
                                break;
                              }
                            }

                            _pointer._hitIter = itrIntersection;
                            _pointer.setCamera(rootCamera);
                            _pointer.setMousePosition(ea.getX(), ea.getY());

                            dragger->handle(_pointer, ea, aa);
                            dragger->setDraggerActive(true);
                            handled = true;
                          } 
                          
                          closestDraggerFound = true;
                          break;
                        }
                      }

                      if (closestDraggerFound)
                        break;
                    }
                }
            }
            case osgGA::GUIEventAdapter::DRAG:
            case osgGA::GUIEventAdapter::RELEASE:
            {
                if (_draggerActive)
                {
//                    _pointer._hitIter = _pointer._hitList.begin();
//                    _pointer.setCamera(view->getCamera());
                    _pointer.setMousePosition(ea.getX(), ea.getY());

                    handle(_pointer, ea, aa);

                    handled = true;
                }
                break;
            }
            default:
                break;
        }

        if (_draggerActive && ea.getEventType() == 
osgGA::GUIEventAdapter::RELEASE)
        {
            setDraggerActive(false);
            _pointer.reset();
        }
    }

    return handled;
}

bool Dragger::receive(const MotionCommand& command)
{
    if (_selfUpdater.valid()) return _selfUpdater->receive(command);
    else return false;
}

void Dragger::dispatch(MotionCommand& command)
{
    // apply any constraints
    for(Constraints::iterator itr = _constraints.begin();
        itr != _constraints.end();
        ++itr)
    {
        (*itr)->constrain(command);
    }

    // move self
    getParentDragger()->receive(command);


    for(DraggerCallbacks::iterator itr = 
getParentDragger()->getDraggerCallbacks().begin();
        itr != getParentDragger()->getDraggerCallbacks().end();
        ++itr)
    {
        (*itr)->receive(command);
    }
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// CompositeDragger
//
CompositeDragger::CompositeDragger(const CompositeDragger& rhs, const 
osg::CopyOp& copyop):
    Dragger(rhs, copyop)
{
    OSG_NOTICE<<"CompositeDragger::CompositeDragger(const CompositeDragger& 
rhs, const osg::CopyOp& copyop) not Implemented yet."<<std::endl;
}

bool CompositeDragger::handle(const PointerInfo& pi, const 
osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
{
    // Check if the dragger node is in the nodepath.
    if (!pi.contains(this))
        return false;

    for (DraggerList::iterator itr=_draggerList.begin(); 
itr!=_draggerList.end(); ++itr)
    {
        if ((*itr)->handle(pi, ea, aa))
            return true;
    }
    return false;
}
bool CompositeDragger::containsDragger( const Dragger* dragger ) const
{
    for (DraggerList::const_iterator itr = _draggerList.begin(); itr != 
_draggerList.end(); ++itr)
    {
        if (itr->get() == dragger) return true;
    }
    return false;
}

CompositeDragger::DraggerList::iterator CompositeDragger::findDragger( const 
Dragger* dragger )
{
    for (DraggerList::iterator itr = _draggerList.begin(); itr != 
_draggerList.end(); ++itr)
    {
        if (itr->get() == dragger) return itr;
    }
    return _draggerList.end();
}

bool CompositeDragger::addDragger(Dragger *dragger)
{
    if (dragger && !containsDragger(dragger))
    {
        _draggerList.push_back(dragger);
        return true;
    }
    else return false;

}

bool CompositeDragger::removeDragger(Dragger *dragger)
{
    DraggerList::iterator itr = findDragger(dragger);
    if (itr != _draggerList.end())
    {
        _draggerList.erase(itr);
        return true;
    }
    else return false;

}

void CompositeDragger::setParentDragger(Dragger* dragger)
{
    for (DraggerList::iterator itr = _draggerList.begin(); itr != 
_draggerList.end(); ++itr)
    {
        (*itr)->setParentDragger(dragger);
    }
    Dragger::setParentDragger(dragger);
}

class ForceCullCallback : public osg::Drawable::CullCallback
{
    public:
        virtual bool cull(osg::NodeVisitor*, osg::Drawable*, osg::State*) const
        {
            return true;
        }
};

void osgManipulator::setDrawableToAlwaysCull(osg::Drawable& drawable)
{
    ForceCullCallback* cullCB = new ForceCullCallback;
    drawable.setCullCallback (cullCB);    
}

void osgManipulator::setMaterialColor(const osg::Vec4& color, osg::Node& node)
{
    osg::Material* mat = 
dynamic_cast<osg::Material*>(node.getOrCreateStateSet()->getAttribute(osg::StateAttribute::MATERIAL));
    if (! mat)
    {
        mat = new osg::Material;
        mat->setDataVariance(osg::Object::DYNAMIC);
        node.getOrCreateStateSet()->setAttribute(mat);
    }
    mat->setDiffuse(osg::Material::FRONT_AND_BACK, color);
}
_______________________________________________
osg-submissions mailing list
[email protected]
http://lists.openscenegraph.org/listinfo.cgi/osg-submissions-openscenegraph.org

Reply via email to