diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f1014a6a8..dd6c20ac9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -140,6 +140,7 @@ Feature #5198: Implement "Magic effect expired" event Feature #5454: Clear active spells from actor when he disappears from scene Feature #5489: MCP: Telekinesis fix for activators + Feature #5701: Convert osgAnimation::RigGeometry to double-buffered custom version Feature #5737: Handle instance move from one cell to another Feature #5996: Support Lua scripts in OpenMW Feature #6017: Separate persistent and temporary cell references when saving diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 7513ecdabe..848c27db47 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -244,6 +245,8 @@ namespace MWRender if (dynamic_cast(drawable)) return nullptr; + if (dynamic_cast(drawable)) + return nullptr; if (const SceneUtil::RigGeometry* rig = dynamic_cast(drawable)) return operator()(rig->getSourceGeometry()); if (const SceneUtil::MorphGeometry* morph = dynamic_cast(drawable)) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 779c8d28f5..2d3fc01e3b 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -61,7 +61,7 @@ add_component_dir (sceneutil clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller lightmanager lightutil positionattitudetransform workqueue pathgridutil waterutil writescene serialize optimizer actorutil detourdebugdraw navmesh agentpath shadow mwshadowtechnique recastmesh shadowsbin osgacontroller rtt - screencapture depth color + screencapture depth color riggeometryosgaextension ) add_component_dir (nif diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index a2779109ac..1bb3ee0963 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -4,9 +4,12 @@ #include #include +#include #include #include +#include + #include #include @@ -35,6 +38,7 @@ #include #include #include +#include #include #include @@ -226,11 +230,12 @@ namespace Resource }; // Check Collada extra descriptions - class ColladaAlphaTrickVisitor : public osg::NodeVisitor + class ColladaDescriptionVisitor : public osg::NodeVisitor { public: - ColladaAlphaTrickVisitor() - : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + ColladaDescriptionVisitor() + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN), + mSkeleton(nullptr) { } @@ -268,7 +273,6 @@ namespace Resource stateset->setAttributeAndModes(depth, osg::StateAttribute::ON); } } - /* Check if the has correct format for OpenMW: alphatest mode value MaterialName e.g alphatest GEQUAL 0.8 MyAlphaTestedMaterial */ @@ -299,6 +303,25 @@ namespace Resource node.getStateSet()->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON); } } + + if (descriptionParts.size() > (0) && descriptionParts.at(0) == "bodypart") + { + SceneUtil::FindByClassVisitor osgaRigFinder("RigGeometryHolder"); + node.accept(osgaRigFinder); + for(osg::Node* foundRigNode : osgaRigFinder.mFoundNodes) + { + if (SceneUtil::RigGeometryHolder* rigGeometryHolder = dynamic_cast (foundRigNode)) + mRigGeometryHolders.emplace_back(osg::ref_ptr (rigGeometryHolder)); + else Log(Debug::Error) << "Converted RigGeometryHolder is of a wrong type."; + } + + if (!mRigGeometryHolders.empty()) + { + osgAnimation::RigGeometry::FindNearestParentSkeleton skeletonFinder; + mRigGeometryHolders[0]->accept(skeletonFinder); + if (skeletonFinder._root.valid()) mSkeleton = skeletonFinder._root; + } + } } } @@ -306,6 +329,9 @@ namespace Resource } private: std::vector mDescriptions; + public: + osgAnimation::Skeleton* mSkeleton; //pointer is valid only if the model is a bodypart, osg::ref_ptr + std::vector> mRigGeometryHolders; }; SceneManager::SceneManager(const VFS::Manager *vfs, Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager) @@ -424,7 +450,7 @@ namespace Resource { return mLightingMethod; } - + void SceneManager::setConvertAlphaTestToAlphaToCoverage(bool convert) { mConvertAlphaTestToAlphaToCoverage = convert; @@ -525,11 +551,55 @@ namespace Resource if (nameFinder.mFoundNode) nameFinder.mFoundNode->setNodeMask(hiddenNodeMask); + // Recognize and convert osgAnimation::RigGeometry to OpenMW-optimized type + SceneUtil::FindByClassVisitor rigFinder("RigGeometry"); + node->accept(rigFinder); + for(osg::Node* foundRigNode : rigFinder.mFoundNodes) + { + if (foundRigNode->libraryName() == std::string("osgAnimation")) + { + osgAnimation::RigGeometry* foundRigGeometry = static_cast (foundRigNode); + osg::ref_ptr newRig = new SceneUtil::RigGeometryHolder(*foundRigGeometry, osg::CopyOp::DEEP_COPY_ALL); + + if (foundRigGeometry->getStateSet()) newRig->setStateSet(foundRigGeometry->getStateSet()); + + if (osg::Group* parent = dynamic_cast (foundRigGeometry->getParent(0))) + { + parent->removeChild(foundRigGeometry); + parent->addChild(newRig); + } + } + } + if (ext == "dae") { - // Collada alpha testing - Resource::ColladaAlphaTrickVisitor colladaAlphaTrickVisitor; - node->accept(colladaAlphaTrickVisitor); + Resource::ColladaDescriptionVisitor colladaDescriptionVisitor; + node->accept(colladaDescriptionVisitor); + + if (colladaDescriptionVisitor.mSkeleton) + { + if ( osg::Group* group = dynamic_cast (node) ) + { + group->removeChildren(0, group->getNumChildren()); + for (osg::ref_ptr newRiggeometryHolder : colladaDescriptionVisitor.mRigGeometryHolders) + { + osg::ref_ptr backToOriginTrans = new osg::MatrixTransform(); + + newRiggeometryHolder->getOrCreateUserDataContainer()->addUserObject(new TemplateRef(newRiggeometryHolder->getGeometry(0))); + backToOriginTrans->getOrCreateUserDataContainer()->addUserObject(new TemplateRef(newRiggeometryHolder->getGeometry(0))); + + newRiggeometryHolder->setBodyPart(true); + + for (int i = 0; i < 2; ++i) + { + if (newRiggeometryHolder->getGeometry(i)) newRiggeometryHolder->getGeometry(i)->setSkeleton(nullptr); + } + + backToOriginTrans->addChild(newRiggeometryHolder); + group->addChild(backToOriginTrans); + } + } + } node->getOrCreateStateSet()->addUniform(new osg::Uniform("emissiveMult", 1.f)); node->getOrCreateStateSet()->addUniform(new osg::Uniform("specStrength", 1.f)); diff --git a/components/sceneutil/clone.cpp b/components/sceneutil/clone.cpp index eaf5668c4a..99a12c1677 100644 --- a/components/sceneutil/clone.cpp +++ b/components/sceneutil/clone.cpp @@ -11,6 +11,7 @@ #include #include +#include namespace SceneUtil { @@ -41,7 +42,9 @@ namespace SceneUtil if (const osgParticle::ParticleSystem* partsys = dynamic_cast(drawable)) return operator()(partsys); - if (dynamic_cast(drawable) || dynamic_cast(drawable) || dynamic_cast(drawable) || dynamic_cast(drawable)) + if (dynamic_cast(drawable) || dynamic_cast(drawable) || + dynamic_cast(drawable) || dynamic_cast(drawable) || + dynamic_cast(drawable)) { return static_cast(drawable->clone(*this)); } diff --git a/components/sceneutil/riggeometryosgaextension.cpp b/components/sceneutil/riggeometryosgaextension.cpp new file mode 100644 index 0000000000..75af4c4b82 --- /dev/null +++ b/components/sceneutil/riggeometryosgaextension.cpp @@ -0,0 +1,281 @@ +#include "riggeometryosgaextension.hpp" + +#include + +#include +#include +#include + +#include +#include + +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 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 rigGeometry = new OsgaRigGeometry(copy, copyop); + setSourceRigGeometry(rigGeometry); +} + +void RigGeometryHolder::setSourceRigGeometry(osg::ref_ptr 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 vbo (new osg::VertexBufferObject); + vbo->setUsage(GL_DYNAMIC_DRAW_ARB); + + osg::ref_ptr vertexArray = static_cast(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 normalArray = static_cast(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(from.getTexCoordArray(7))) + { + osg::ref_ptr tangentArray = static_cast(tangents->clone(osg::CopyOp::DEEP_COPY_ALL)); + tangentArray->setVertexBufferObject(vbo); + to.setTexCoordArray(7, tangentArray, osg::Array::BIND_PER_VERTEX); + } + } + +} + +osg::ref_ptr 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(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 (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(); +} + + +} diff --git a/components/sceneutil/riggeometryosgaextension.hpp b/components/sceneutil/riggeometryosgaextension.hpp new file mode 100644 index 0000000000..6098bd9608 --- /dev/null +++ b/components/sceneutil/riggeometryosgaextension.hpp @@ -0,0 +1,72 @@ +#ifndef OPENMW_COMPONENTS_OSGAEXTENSION_RIGGEOMETRY_H +#define OPENMW_COMPONENTS_OSGAEXTENSION_RIGGEOMETRY_H + +#include + +#include +#include + +#include + +namespace SceneUtil +{ + /// @brief Custom RigGeometry-class for osgAnimation-formats (collada) + class OsgaRigGeometry : public osgAnimation::RigGeometry + { + public: + + OsgaRigGeometry(); + + OsgaRigGeometry(const osgAnimation::RigGeometry& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY); + + OsgaRigGeometry(const OsgaRigGeometry& copy, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY); + + META_Object(SceneUtil, OsgaRigGeometry); + + void computeMatrixFromRootSkeleton(osg::MatrixList mtxList); + }; + + /// @brief OpenMW-compatible double buffered static datavariance version of osgAnimation::RigGeometry + /// This class is based on osgAnimation::RigGeometry and SceneUtil::RigGeometry + class RigGeometryHolder : public osg::Drawable + { + public: + RigGeometryHolder(); + + RigGeometryHolder(const RigGeometryHolder& copy, const osg::CopyOp& copyop); + + RigGeometryHolder(const osgAnimation::RigGeometry& copy, const osg::CopyOp& copyop); + + META_Object(SceneUtil, RigGeometryHolder); + + void setSourceRigGeometry(osg::ref_ptr sourceRigGeometry); + osg::ref_ptr getSourceRigGeometry() const; + + /// @brief Modified rig update, code based on osgAnimation::UpdateRigGeometry : public osg::Drawable::UpdateCallback + void updateRigGeometry(OsgaRigGeometry* geom, osg::NodeVisitor *nv); + + OsgaRigGeometry* getGeometry(int geometry); + + void accept(osg::NodeVisitor &nv) override; + void accept(osg::PrimitiveFunctor&) const override; + bool supports(const osg::PrimitiveFunctor&) const override{ return true; } + + void setBackToOrigin(osg::MatrixTransform* backToOrigin) {mBackToOrigin = backToOrigin;} + void setBodyPart(bool isBodyPart) {mIsBodyPart = isBodyPart;} + + private: + std::array, 2> mGeometry; + osg::ref_ptr mSourceRigGeometry; + osg::MatrixTransform* mBackToOrigin; //This is used to move riggeometries from their slot locations to skeleton origin in order to get correct deformations for bodyparts + + unsigned int mLastFrameNumber; + bool mIsBodyPart; + + void updateBackToOriginTransform(OsgaRigGeometry* geometry); + + OsgaRigGeometry* getRigGeometryPerFrame(unsigned int frame) const; + }; + +} + +#endif diff --git a/components/sceneutil/serialize.cpp b/components/sceneutil/serialize.cpp index 964b8bc4c2..748b3a708d 100644 --- a/components/sceneutil/serialize.cpp +++ b/components/sceneutil/serialize.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include namespace SceneUtil @@ -49,6 +50,24 @@ public: } }; +class RigGeometryHolderSerializer : public osgDB::ObjectWrapper +{ +public: + RigGeometryHolderSerializer() + : osgDB::ObjectWrapper(createInstanceFunc, "SceneUtil::RigGeometryHolder", "osg::Object osg::Node osg::Drawable SceneUtil::RigGeometryHolder") + { + } +}; + +class OsgaRigGeometrySerializer : public osgDB::ObjectWrapper +{ +public: + OsgaRigGeometrySerializer() + : osgDB::ObjectWrapper(createInstanceFunc, "SceneUtil::OsgaRigGeometry", "osg::Object osg::Node osg::Geometry osgAnimation::RigGeometry SceneUtil::OsgaRigGeometry") + { + } +}; + class MorphGeometrySerializer : public osgDB::ObjectWrapper { public: @@ -108,6 +127,8 @@ void registerSerializers() mgr->addWrapper(new PositionAttitudeTransformSerializer); mgr->addWrapper(new SkeletonSerializer); mgr->addWrapper(new RigGeometrySerializer); + mgr->addWrapper(new RigGeometryHolderSerializer); + mgr->addWrapper(new OsgaRigGeometrySerializer); mgr->addWrapper(new MorphGeometrySerializer); mgr->addWrapper(new LightManagerSerializer); mgr->addWrapper(new CameraRelativeTransformSerializer); diff --git a/components/shader/shadervisitor.cpp b/components/shader/shadervisitor.cpp index 57fe2cb0e8..894af5c82a 100644 --- a/components/shader/shadervisitor.cpp +++ b/components/shader/shadervisitor.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include "removedalphafunc.hpp" #include "shadermanager.hpp" @@ -922,6 +923,17 @@ namespace Shader if (sourceGeometry && adjustGeometry(*sourceGeometry, reqs)) morph->setSourceGeometry(sourceGeometry); } + else if (auto osgaRig = dynamic_cast(&drawable)) + { + osg::ref_ptr sourceOsgaRigGeometry = osgaRig->getSourceRigGeometry(); + osg::ref_ptr sourceGeometry = sourceOsgaRigGeometry->getSourceGeometry(); + if (sourceGeometry && adjustGeometry(*sourceGeometry, reqs)) + { + sourceOsgaRigGeometry->setSourceGeometry(sourceGeometry); + osgaRig->setSourceRigGeometry(sourceOsgaRigGeometry); + } + } + } else ensureFFP(drawable);