mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 02:26:41 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			213 lines
		
	
	
	
		
			6.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			213 lines
		
	
	
	
		
			6.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "attach.hpp"
 | 
						|
 | 
						|
#include <stdexcept>
 | 
						|
#include <iostream>
 | 
						|
 | 
						|
#include <osg/NodeVisitor>
 | 
						|
#include <osg/Group>
 | 
						|
#include <osg/Geode>
 | 
						|
#include <osg/FrontFace>
 | 
						|
#include <osg/PositionAttitudeTransform>
 | 
						|
 | 
						|
#include <osgAnimation/BoneMapVisitor>
 | 
						|
#include <osgAnimation/Skeleton>
 | 
						|
 | 
						|
#include <components/misc/stringops.hpp>
 | 
						|
 | 
						|
#include "visitor.hpp"
 | 
						|
 | 
						|
namespace SceneUtil
 | 
						|
{
 | 
						|
 | 
						|
    class NodeMapVisitor : public osg::NodeVisitor
 | 
						|
    {
 | 
						|
    public:
 | 
						|
        NodeMapVisitor() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}
 | 
						|
 | 
						|
        void apply(osg::MatrixTransform& trans)
 | 
						|
        {
 | 
						|
            mMap[trans.getName()] = &trans;
 | 
						|
            traverse(trans);
 | 
						|
        }
 | 
						|
 | 
						|
        typedef std::map<std::string, osg::ref_ptr<osg::MatrixTransform> > NodeMap;
 | 
						|
 | 
						|
        const NodeMap& getNodeMap() const
 | 
						|
        {
 | 
						|
            return mMap;
 | 
						|
        }
 | 
						|
 | 
						|
    private:
 | 
						|
        NodeMap mMap;
 | 
						|
    };
 | 
						|
 | 
						|
    /// Copy the matrix of a "source" node to a "dest" node (the node that the callback is attached to).
 | 
						|
    /// Must be set on a MatrixTransform.
 | 
						|
    class CopyController : public osg::NodeCallback
 | 
						|
    {
 | 
						|
    public:
 | 
						|
        CopyController(osg::MatrixTransform* copyFrom)
 | 
						|
            : mCopyFrom(copyFrom)
 | 
						|
        {
 | 
						|
        }
 | 
						|
        CopyController(const CopyController& copy, const osg::CopyOp& copyop)
 | 
						|
            : osg::NodeCallback(copy, copyop)
 | 
						|
            , mCopyFrom(copy.mCopyFrom)
 | 
						|
        {
 | 
						|
        }
 | 
						|
        CopyController()
 | 
						|
            : mCopyFrom(NULL)
 | 
						|
        {
 | 
						|
        }
 | 
						|
 | 
						|
        virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
 | 
						|
        {
 | 
						|
            osgAnimation::Bone* bone = static_cast<osgAnimation::Bone*>(node);
 | 
						|
 | 
						|
            if (mCopyFrom)
 | 
						|
            {
 | 
						|
                bone->setMatrix(mCopyFrom->getMatrix());
 | 
						|
            }
 | 
						|
 | 
						|
            traverse(node, nv);
 | 
						|
        }
 | 
						|
 | 
						|
    private:
 | 
						|
        const osg::MatrixTransform* mCopyFrom;
 | 
						|
    };
 | 
						|
 | 
						|
    class AddCopyControllerVisitor : public osg::NodeVisitor
 | 
						|
    {
 | 
						|
    public:
 | 
						|
        AddCopyControllerVisitor(const NodeMapVisitor::NodeMap& boneMap)
 | 
						|
            : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
 | 
						|
            , mNodeMap(boneMap)
 | 
						|
        {
 | 
						|
        }
 | 
						|
 | 
						|
        virtual void apply(osg::MatrixTransform &node)
 | 
						|
        {
 | 
						|
            if (osgAnimation::Bone* bone = dynamic_cast<osgAnimation::Bone*>(&node))
 | 
						|
            {
 | 
						|
                NodeMapVisitor::NodeMap::const_iterator found = mNodeMap.find(bone->getName());
 | 
						|
                if (found != mNodeMap.end())
 | 
						|
                {
 | 
						|
                    // add the CopyController at position 0 so it's executed before UpdateBone
 | 
						|
                    osg::ref_ptr<osg::NodeCallback> old = bone->getUpdateCallback();
 | 
						|
                    bone->setUpdateCallback(new CopyController(found->second.get()));
 | 
						|
                    bone->addUpdateCallback(old);
 | 
						|
                }
 | 
						|
            }
 | 
						|
            traverse(node);
 | 
						|
        }
 | 
						|
 | 
						|
    private:
 | 
						|
        const NodeMapVisitor::NodeMap& mNodeMap;
 | 
						|
    };
 | 
						|
 | 
						|
    // FIXME: would be more efficient to copy only the wanted nodes instead of deleting unwanted ones later
 | 
						|
    // copying is kinda cheap though, so don't bother for now
 | 
						|
    class FilterVisitor : public osg::NodeVisitor
 | 
						|
    {
 | 
						|
    public:
 | 
						|
        FilterVisitor(const std::string& filter)
 | 
						|
            : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
 | 
						|
            , mFilter(Misc::StringUtils::lowerCase(filter))
 | 
						|
        {
 | 
						|
        }
 | 
						|
 | 
						|
        virtual void apply(osg::Geode &node)
 | 
						|
        {
 | 
						|
            std::string lowerName = Misc::StringUtils::lowerCase(node.getName());
 | 
						|
            if (lowerName.find(mFilter) == std::string::npos)
 | 
						|
            {
 | 
						|
                mToRemove.push_back(&node);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        void removeFilteredParts()
 | 
						|
        {
 | 
						|
            for (std::vector<osg::Geode*>::iterator it = mToRemove.begin(); it != mToRemove.end(); ++it)
 | 
						|
            {
 | 
						|
                osg::Geode* geode = *it;
 | 
						|
                geode->getParent(0)->removeChild(geode);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
    private:
 | 
						|
        std::vector<osg::Geode*> mToRemove;
 | 
						|
        std::string mFilter;
 | 
						|
    };
 | 
						|
 | 
						|
    osg::ref_ptr<osg::Node> attach(osg::ref_ptr<osg::Node> toAttach, osg::Node *master, const std::string &filter, const std::string &attachNode)
 | 
						|
    {
 | 
						|
        if (osgAnimation::Skeleton* skel = dynamic_cast<osgAnimation::Skeleton*>(toAttach.get()))
 | 
						|
        {
 | 
						|
            NodeMapVisitor nodeMapVisitor;
 | 
						|
            master->accept(nodeMapVisitor);
 | 
						|
 | 
						|
            // would be more efficient if we could attach the RigGeometry directly to the master skeleton, but currently not possible
 | 
						|
            // due to a difference in binding pose of the two skeletons
 | 
						|
            AddCopyControllerVisitor visitor(nodeMapVisitor.getNodeMap());
 | 
						|
            toAttach->accept(visitor);
 | 
						|
 | 
						|
            FilterVisitor filterVisitor(filter);
 | 
						|
            toAttach->accept(filterVisitor);
 | 
						|
            filterVisitor.removeFilteredParts();
 | 
						|
 | 
						|
            master->asGroup()->addChild(skel);
 | 
						|
            return skel;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            FindByNameVisitor find(attachNode);
 | 
						|
            master->accept(find);
 | 
						|
            if (!find.mFoundNode)
 | 
						|
                throw std::runtime_error(std::string("Can't find attachment node ") + attachNode);
 | 
						|
 | 
						|
            FindByNameVisitor findBoneOffset("BoneOffset");
 | 
						|
            toAttach->accept(findBoneOffset);
 | 
						|
 | 
						|
            osg::ref_ptr<osg::PositionAttitudeTransform> trans;
 | 
						|
 | 
						|
            if (findBoneOffset.mFoundNode)
 | 
						|
            {
 | 
						|
                osg::MatrixTransform* boneOffset = dynamic_cast<osg::MatrixTransform*>(findBoneOffset.mFoundNode);
 | 
						|
                if (!boneOffset)
 | 
						|
                    throw std::runtime_error("BoneOffset must be a MatrixTransform");
 | 
						|
 | 
						|
                trans = new osg::PositionAttitudeTransform;
 | 
						|
                trans->setPosition(boneOffset->getMatrix().getTrans());
 | 
						|
                // The BoneOffset rotation seems to be incorrect
 | 
						|
                trans->setAttitude(osg::Quat(-90, osg::Vec3f(1,0,0)));
 | 
						|
            }
 | 
						|
 | 
						|
            if (attachNode.find("Left") != std::string::npos)
 | 
						|
            {
 | 
						|
                if (!trans)
 | 
						|
                    trans = new osg::PositionAttitudeTransform;
 | 
						|
                trans->setScale(osg::Vec3f(-1.f, 1.f, 1.f));
 | 
						|
 | 
						|
                // Need to invert culling because of the negative scale
 | 
						|
                // Note: for absolute correctness we would need to check the current front face for every mesh then invert it
 | 
						|
                // However MW isn't doing this either, so don't. Assuming all meshes are using backface culling is more efficient.
 | 
						|
                osg::FrontFace* frontFace = new osg::FrontFace;
 | 
						|
                frontFace->setMode(osg::FrontFace::CLOCKWISE);
 | 
						|
                trans->getOrCreateStateSet()->setAttributeAndModes(frontFace, osg::StateAttribute::ON);
 | 
						|
            }
 | 
						|
 | 
						|
            if (trans)
 | 
						|
            {
 | 
						|
                find.mFoundNode->addChild(trans);
 | 
						|
                trans->addChild(toAttach);
 | 
						|
                return trans;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                find.mFoundNode->addChild(toAttach);
 | 
						|
                return toAttach;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
}
 |