mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 06:56:39 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			296 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			296 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "riggeometryosgaextension.hpp"
 | 
						|
 | 
						|
#include <osgAnimation/RigGeometry>
 | 
						|
 | 
						|
#include <osg/Drawable>
 | 
						|
#include <osg/NodeVisitor>
 | 
						|
 | 
						|
#include <components/debug/debuglog.hpp>
 | 
						|
#include <components/resource/scenemanager.hpp>
 | 
						|
 | 
						|
namespace SceneUtil
 | 
						|
{
 | 
						|
 | 
						|
    OsgaRigGeometry::OsgaRigGeometry()
 | 
						|
        : osgAnimation::RigGeometry()
 | 
						|
    {
 | 
						|
        setDataVariance(osg::Object::STATIC);
 | 
						|
    }
 | 
						|
 | 
						|
    OsgaRigGeometry::OsgaRigGeometry(const osgAnimation::RigGeometry& copy, const osg::CopyOp& copyop)
 | 
						|
        : osgAnimation::RigGeometry(copy, copyop)
 | 
						|
    {
 | 
						|
        setDataVariance(osg::Object::STATIC);
 | 
						|
    }
 | 
						|
 | 
						|
    OsgaRigGeometry::OsgaRigGeometry(const OsgaRigGeometry& copy, const osg::CopyOp& copyop)
 | 
						|
        : osgAnimation::RigGeometry(copy, copyop)
 | 
						|
    {
 | 
						|
        setDataVariance(osg::Object::STATIC);
 | 
						|
    }
 | 
						|
 | 
						|
    void OsgaRigGeometry::computeMatrixFromRootSkeleton(osg::MatrixList mtxList)
 | 
						|
    {
 | 
						|
        if (!_root.valid())
 | 
						|
        {
 | 
						|
            Log(Debug::Warning) << "Warning " << className()
 | 
						|
                                << "::computeMatrixFromRootSkeleton if you have this message it means you miss to call "
 | 
						|
                                   "buildTransformer(Skeleton* root), or your RigGeometry ("
 | 
						|
                                << getName() << ") is not attached to a Skeleton subgraph";
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        osg::Matrix notRoot = _root->getMatrix();
 | 
						|
        _matrixFromSkeletonToGeometry = mtxList[0] * osg::Matrix::inverse(notRoot);
 | 
						|
        _invMatrixFromSkeletonToGeometry = osg::Matrix::inverse(_matrixFromSkeletonToGeometry);
 | 
						|
        _needToComputeMatrix = false;
 | 
						|
    }
 | 
						|
 | 
						|
    RigGeometryHolder::RigGeometryHolder()
 | 
						|
        : mBackToOrigin(nullptr)
 | 
						|
        , mLastFrameNumber(0)
 | 
						|
        , mIsBodyPart(false)
 | 
						|
    {
 | 
						|
    }
 | 
						|
 | 
						|
    RigGeometryHolder::RigGeometryHolder(const RigGeometryHolder& copy, const osg::CopyOp& copyop)
 | 
						|
        : Drawable(copy, copyop)
 | 
						|
        , mBackToOrigin(copy.mBackToOrigin)
 | 
						|
        , mLastFrameNumber(0)
 | 
						|
        , mIsBodyPart(copy.mIsBodyPart)
 | 
						|
    {
 | 
						|
        setUseVertexBufferObjects(true);
 | 
						|
 | 
						|
        if (!copy.getSourceRigGeometry())
 | 
						|
        {
 | 
						|
            Log(Debug::Error) << "copy constructor of RigGeometryHolder partially failed (no source RigGeometry)";
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        osg::ref_ptr<OsgaRigGeometry> rigGeometry = new OsgaRigGeometry(*copy.getSourceRigGeometry(), copyop);
 | 
						|
        setSourceRigGeometry(rigGeometry);
 | 
						|
    }
 | 
						|
 | 
						|
    RigGeometryHolder::RigGeometryHolder(const osgAnimation::RigGeometry& copy, const osg::CopyOp& copyop)
 | 
						|
        : mBackToOrigin(nullptr)
 | 
						|
        , mLastFrameNumber(0)
 | 
						|
        , mIsBodyPart(false)
 | 
						|
    {
 | 
						|
        setUseVertexBufferObjects(true);
 | 
						|
 | 
						|
        osg::ref_ptr<OsgaRigGeometry> rigGeometry = new OsgaRigGeometry(copy, copyop);
 | 
						|
        setSourceRigGeometry(rigGeometry);
 | 
						|
    }
 | 
						|
 | 
						|
    void RigGeometryHolder::setSourceRigGeometry(osg::ref_ptr<OsgaRigGeometry> sourceRigGeometry)
 | 
						|
    {
 | 
						|
        for (unsigned int i = 0; i < 2; ++i)
 | 
						|
            mGeometry.at(i) = nullptr;
 | 
						|
 | 
						|
        mSourceRigGeometry = sourceRigGeometry;
 | 
						|
 | 
						|
        _boundingBox = mSourceRigGeometry->getComputeBoundingBoxCallback()->computeBound(*mSourceRigGeometry);
 | 
						|
        _boundingSphere = osg::BoundingSphere(_boundingBox);
 | 
						|
 | 
						|
        for (unsigned int i = 0; i < 2; ++i)
 | 
						|
        {
 | 
						|
            const OsgaRigGeometry& from = *sourceRigGeometry;
 | 
						|
 | 
						|
            // DO NOT COPY AND PASTE THIS CODE. Cloning osg::Geometry without also cloning its contained Arrays is
 | 
						|
            // generally unsafe. In this specific case the operation is safe under the following two assumptions:
 | 
						|
            // - When Arrays are removed or replaced in the cloned geometry, the original Arrays in their place must
 | 
						|
            // outlive the cloned geometry regardless. (ensured by mSourceRigGeometry, possibly also
 | 
						|
            // RigGeometry._geometry)
 | 
						|
            // - Arrays that we add or replace in the cloned geometry must be explicitely forbidden from reusing
 | 
						|
            // BufferObjects of the original geometry.
 | 
						|
            mGeometry.at(i) = new OsgaRigGeometry(from, osg::CopyOp::SHALLOW_COPY);
 | 
						|
            mGeometry.at(i)->getOrCreateUserDataContainer()->addUserObject(
 | 
						|
                new Resource::TemplateRef(mSourceRigGeometry));
 | 
						|
 | 
						|
            OsgaRigGeometry& to = *mGeometry.at(i);
 | 
						|
            to.setSupportsDisplayList(false);
 | 
						|
            to.setUseVertexBufferObjects(true);
 | 
						|
            to.setCullingActive(false); // make sure to disable culling since that's handled by this class
 | 
						|
 | 
						|
            to.setDataVariance(osg::Object::STATIC);
 | 
						|
            to.setNeedToComputeMatrix(true);
 | 
						|
 | 
						|
            // vertices and normals are modified every frame, so we need to deep copy them.
 | 
						|
            // assign a dedicated VBO to make sure that modifications don't interfere with source geometry's VBO.
 | 
						|
            osg::ref_ptr<osg::VertexBufferObject> vbo(new osg::VertexBufferObject);
 | 
						|
            vbo->setUsage(GL_DYNAMIC_DRAW_ARB);
 | 
						|
 | 
						|
            osg::ref_ptr<osg::Array> vertexArray
 | 
						|
                = static_cast<osg::Array*>(from.getVertexArray()->clone(osg::CopyOp::DEEP_COPY_ALL));
 | 
						|
            if (vertexArray)
 | 
						|
            {
 | 
						|
                vertexArray->setVertexBufferObject(vbo);
 | 
						|
                to.setVertexArray(vertexArray);
 | 
						|
            }
 | 
						|
 | 
						|
            if (const osg::Array* normals = from.getNormalArray())
 | 
						|
            {
 | 
						|
                osg::ref_ptr<osg::Array> normalArray
 | 
						|
                    = static_cast<osg::Array*>(normals->clone(osg::CopyOp::DEEP_COPY_ALL));
 | 
						|
                if (normalArray)
 | 
						|
                {
 | 
						|
                    normalArray->setVertexBufferObject(vbo);
 | 
						|
                    to.setNormalArray(normalArray, osg::Array::BIND_PER_VERTEX);
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            if (const osg::Vec4Array* tangents = dynamic_cast<const osg::Vec4Array*>(from.getTexCoordArray(7)))
 | 
						|
            {
 | 
						|
                osg::ref_ptr<osg::Array> tangentArray
 | 
						|
                    = static_cast<osg::Array*>(tangents->clone(osg::CopyOp::DEEP_COPY_ALL));
 | 
						|
                tangentArray->setVertexBufferObject(vbo);
 | 
						|
                to.setTexCoordArray(7, tangentArray, osg::Array::BIND_PER_VERTEX);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    osg::ref_ptr<OsgaRigGeometry> RigGeometryHolder::getSourceRigGeometry() const
 | 
						|
    {
 | 
						|
        return mSourceRigGeometry;
 | 
						|
    }
 | 
						|
 | 
						|
    void RigGeometryHolder::updateRigGeometry(OsgaRigGeometry* geom, osg::NodeVisitor* nv)
 | 
						|
    {
 | 
						|
        if (!geom)
 | 
						|
            return;
 | 
						|
 | 
						|
        if (!geom->getSkeleton() && !this->getParents().empty())
 | 
						|
        {
 | 
						|
            osgAnimation::RigGeometry::FindNearestParentSkeleton finder;
 | 
						|
            if (this->getParents().size() > 1)
 | 
						|
                Log(Debug::Warning) << "A RigGeometry should not have multi parent ( " << geom->getName() << " )";
 | 
						|
            this->getParents()[0]->accept(finder);
 | 
						|
 | 
						|
            if (!finder._root.valid())
 | 
						|
            {
 | 
						|
                Log(Debug::Warning) << "A RigGeometry did not find a parent skeleton for RigGeometry ( "
 | 
						|
                                    << geom->getName() << " )";
 | 
						|
                return;
 | 
						|
            }
 | 
						|
            geom->getRigTransformImplementation()->prepareData(*geom);
 | 
						|
            geom->setSkeleton(finder._root.get());
 | 
						|
        }
 | 
						|
 | 
						|
        if (!geom->getSkeleton())
 | 
						|
            return;
 | 
						|
 | 
						|
        if (geom->getNeedToComputeMatrix())
 | 
						|
        {
 | 
						|
            osgAnimation::Skeleton* root = geom->getSkeleton();
 | 
						|
            if (!root)
 | 
						|
            {
 | 
						|
                Log(Debug::Warning)
 | 
						|
                    << "Warning: if you have this message it means you miss to call buildTransformer(Skeleton* root), "
 | 
						|
                       "or your RigGeometry is not attached to a Skeleton subgraph";
 | 
						|
                return;
 | 
						|
            }
 | 
						|
            osg::MatrixList mtxList
 | 
						|
                = root->getWorldMatrices(root); // We always assume that RigGeometries have origin at their root
 | 
						|
            geom->computeMatrixFromRootSkeleton(mtxList);
 | 
						|
 | 
						|
            if (mIsBodyPart && mBackToOrigin)
 | 
						|
                updateBackToOriginTransform(geom);
 | 
						|
        }
 | 
						|
 | 
						|
        if (geom->getSourceGeometry())
 | 
						|
        {
 | 
						|
            osg::Drawable::UpdateCallback* up
 | 
						|
                = dynamic_cast<osg::Drawable::UpdateCallback*>(geom->getSourceGeometry()->getUpdateCallback());
 | 
						|
            if (up)
 | 
						|
            {
 | 
						|
                up->update(nv, geom->getSourceGeometry());
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        geom->update();
 | 
						|
    }
 | 
						|
 | 
						|
    OsgaRigGeometry* RigGeometryHolder::getGeometry(int geometry)
 | 
						|
    {
 | 
						|
        return mGeometry.at(geometry).get();
 | 
						|
    }
 | 
						|
 | 
						|
    void RigGeometryHolder::updateBackToOriginTransform(OsgaRigGeometry* geometry)
 | 
						|
    {
 | 
						|
        osgAnimation::Skeleton* skeleton = geometry->getSkeleton();
 | 
						|
        if (skeleton)
 | 
						|
        {
 | 
						|
            osg::MatrixList mtxList = mBackToOrigin->getParents()[0]->getWorldMatrices(skeleton);
 | 
						|
            osg::Matrix skeletonMatrix = skeleton->getMatrix();
 | 
						|
            osg::Matrixf matrixFromSkeletonToGeometry = mtxList[0] * osg::Matrix::inverse(skeletonMatrix);
 | 
						|
            osg::Matrixf invMatrixFromSkeletonToGeometry = osg::Matrix::inverse(matrixFromSkeletonToGeometry);
 | 
						|
            mBackToOrigin->setMatrix(invMatrixFromSkeletonToGeometry);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    void RigGeometryHolder::accept(osg::NodeVisitor& nv)
 | 
						|
    {
 | 
						|
        if (!nv.validNodeMask(*this))
 | 
						|
            return;
 | 
						|
 | 
						|
        nv.pushOntoNodePath(this);
 | 
						|
 | 
						|
        if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR && mSourceRigGeometry.get())
 | 
						|
        {
 | 
						|
            unsigned int traversalNumber = nv.getTraversalNumber();
 | 
						|
            if (mLastFrameNumber == traversalNumber)
 | 
						|
            {
 | 
						|
                OsgaRigGeometry& geom = *getRigGeometryPerFrame(mLastFrameNumber);
 | 
						|
 | 
						|
                nv.pushOntoNodePath(&geom);
 | 
						|
                nv.apply(geom);
 | 
						|
                nv.popFromNodePath();
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                mLastFrameNumber = traversalNumber;
 | 
						|
 | 
						|
                OsgaRigGeometry& geom = *getRigGeometryPerFrame(mLastFrameNumber);
 | 
						|
 | 
						|
                if (mIsBodyPart)
 | 
						|
                {
 | 
						|
                    if (mBackToOrigin)
 | 
						|
                        updateBackToOriginTransform(&geom);
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        osg::MatrixTransform* matrixTransform
 | 
						|
                            = dynamic_cast<osg::MatrixTransform*>(this->getParents()[0]);
 | 
						|
                        if (matrixTransform)
 | 
						|
                        {
 | 
						|
                            mBackToOrigin = matrixTransform;
 | 
						|
                            updateBackToOriginTransform(&geom);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
 | 
						|
                updateRigGeometry(&geom, &nv);
 | 
						|
 | 
						|
                nv.pushOntoNodePath(&geom);
 | 
						|
                nv.apply(geom);
 | 
						|
                nv.popFromNodePath();
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else if (nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR)
 | 
						|
        {
 | 
						|
        }
 | 
						|
        else
 | 
						|
            nv.apply(*this);
 | 
						|
 | 
						|
        nv.popFromNodePath();
 | 
						|
    }
 | 
						|
 | 
						|
    void RigGeometryHolder::accept(osg::PrimitiveFunctor& func) const
 | 
						|
    {
 | 
						|
        getRigGeometryPerFrame(mLastFrameNumber)->accept(func);
 | 
						|
    }
 | 
						|
 | 
						|
    OsgaRigGeometry* RigGeometryHolder::getRigGeometryPerFrame(unsigned int frame) const
 | 
						|
    {
 | 
						|
        return mGeometry.at(frame % 2).get();
 | 
						|
    }
 | 
						|
 | 
						|
}
 |