From e2ac392a4076c09aeb42d4a1f9213c83cb6d46ad Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Tue, 17 Jul 2018 21:28:05 -0500 Subject: [PATCH] Move common stuff to scene util, fix errors with 1st person meshes --- apps/opencs/view/render/actor.cpp | 245 ++++++++++++-------------- apps/openmw/mwrender/animation.cpp | 4 +- apps/openmw/mwrender/npcanimation.cpp | 33 +--- components/CMakeLists.txt | 1 + components/sceneutil/actorutil.cpp | 30 ++++ components/sceneutil/actorutil.hpp | 11 ++ components/sceneutil/visitor.cpp | 89 +++++++++- components/sceneutil/visitor.hpp | 40 ++++- 8 files changed, 289 insertions(+), 164 deletions(-) create mode 100644 components/sceneutil/actorutil.cpp create mode 100644 components/sceneutil/actorutil.hpp diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp index 1273b94c64..93fff270a0 100644 --- a/apps/opencs/view/render/actor.cpp +++ b/apps/opencs/view/render/actor.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -45,152 +46,140 @@ namespace CSVRender // Remove children mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); - // Npcs and creatures are handled differently - if (mType == CSMWorld::UniversalId::Type_Npc) + try { - auto& npc = dynamic_cast& >(referenceables.getRecord(mId)).get(); - - auto isBeast = [&](std::string race) -> bool { - int index = races.searchId(race); - if (index != -1 && !races.getRecord(index).isDeleted()) - return races.getRecord(index).get().mData.mFlags & ESM::Race::Beast; - else - return false; - }; - - // Load skeleton - std::string skeletonResource; - if (isBeast(npc.mRace)) { - std::cout << "is beast\n"; - skeletonResource = "base_animkna.nif"; - } - else if (npc.isMale()) { - std::cout << "is male\n"; - skeletonResource = "base_anim.nif"; - } - else { - std::cout << "is female\n"; - skeletonResource = "base_anim_female.nif"; - } - - std::string skeletonModel = MeshPrefix + skeletonResource; - skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); + // Npcs and creatures are handled differently + if (mType == CSMWorld::UniversalId::Type_Npc) { - osg::ref_ptr temp = sceneMgr->getInstance(skeletonModel); - mSkeleton = dynamic_cast(temp.get()); - if (!mSkeleton) - { - mSkeleton = new SceneUtil::Skeleton(); - mSkeleton->addChild(temp); - } - mBaseNode->addChild(mSkeleton); - } + auto& npc = dynamic_cast& >(referenceables.getRecord(mId)).get(); + auto& race = dynamic_cast& >(races.getRecord(npc.mRace)).get(); - // Map bone names to bones - SceneUtil::NodeMapVisitor::NodeMap nodeMap; - SceneUtil::NodeMapVisitor nmVisitor(nodeMap); - mSkeleton->accept(nmVisitor); + bool is1stPerson = false; + bool isFemale = !npc.isMale(); + bool isBeast = race.mData.mFlags & ESM::Race::Beast; + bool isWerewolf = false; - if (!npc.isMale()) { - for (auto it : nodeMap) { - std::cout << it.first << "\n"; - } - } - - - // Female mesh has some drawables attached, get rid of them - SceneUtil::HideDrawablesVisitor hdVisitor; - mSkeleton->accept(hdVisitor); - - // Convenience method to retrieve the mesh name of a body part - auto getBodyPartMesh = [&](std::string bpName) -> std::string { - int index = bodyParts.searchId(bpName); - if (index != -1 && !bodyParts.getRecord(index).isDeleted()) - return MeshPrefix + bodyParts.getRecord(index).get().mModel; - else - return ""; - }; - - using BPRaceKey = std::tuple; - using RaceToBPMap = std::map; - // Convenience method to generate a map from body part + race to mesh name - auto genRaceToBodyPartMap = [&](RaceToBPMap& bpMap) { - int size = bodyParts.getSize(); - for (int i = 0; i < size; ++i) + // Load skeleton + std::string skeletonModel = SceneUtil::getActorSkeleton(is1stPerson, isFemale, isBeast, isWerewolf); + skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); { - auto& record = bodyParts.getRecord(i); - if (!record.isDeleted()) + osg::ref_ptr temp = sceneMgr->getInstance(skeletonModel); + mSkeleton = dynamic_cast(temp.get()); + if (!mSkeleton) { - auto& bodyPart = record.get(); - bpMap.emplace( - BPRaceKey(bodyPart.mData.mPart, bodyPart.mData.mFlags & FemaleFlag ? 1 : 0, bodyPart.mRace), - MeshPrefix + bodyPart.mModel); + mSkeleton = new SceneUtil::Skeleton(); + mSkeleton->addChild(temp); } + mBaseNode->addChild(mSkeleton); } - }; - // Generate mapping - RaceToBPMap r2bpMap; - genRaceToBodyPartMap(r2bpMap); - - // Convenience method to add a body part - auto addBodyPart = [&](ESM::PartReferenceType type, std::string mesh) { - // Retrieve mesh name if necessary - if (mesh.empty()) - { - auto meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), npc.isMale() ? 0 : 1, npc.mRace)); - if (meshResult != r2bpMap.end()) + // Map bone names to bones + SceneUtil::NodeMapVisitor::NodeMap nodeMap; + SceneUtil::NodeMapVisitor nmVisitor(nodeMap); + mSkeleton->accept(nmVisitor); + + // Female mesh has some drawables attached, get rid of them + SceneUtil::CleanObjectRootVisitor cleanVisitor; + mSkeleton->accept(cleanVisitor); + cleanVisitor.remove(); + + // Convenience method to retrieve the mesh name of a body part + auto getBodyPartMesh = [&](std::string bpName) -> std::string { + int index = bodyParts.searchId(bpName); + if (index != -1 && !bodyParts.getRecord(index).isDeleted()) + return MeshPrefix + bodyParts.getRecord(index).get().mModel; + else + return ""; + }; + + using BPRaceKey = std::tuple; + using RaceToBPMap = std::map; + // Convenience method to generate a map from body part + race to mesh name + auto genRaceToBodyPartMap = [&](RaceToBPMap& bpMap) { + int size = bodyParts.getSize(); + for (int i = 0; i < size; ++i) { - mesh = meshResult->second; + auto& record = bodyParts.getRecord(i); + if (!record.isDeleted()) + { + // Method to check if 1st person part or not + auto is1stPersonPart = [](std::string name) { + return name.size() >= 4 && name.find(".1st", name.size() - 4) != std::string::npos; + }; + + auto& bodyPart = record.get(); + if (bodyPart.mData.mType != ESM::BodyPart::MT_Skin || is1stPersonPart(bodyPart.mId)) + continue; + + bpMap.emplace( + BPRaceKey(bodyPart.mData.mPart, bodyPart.mData.mFlags & FemaleFlag ? 1 : 0, bodyPart.mRace), + MeshPrefix + bodyPart.mModel); + } } - } + }; - // Attach to skeleton - std::string boneName = ESM::getBoneName(type); - auto node = nodeMap.find(boneName); - if (!mesh.empty() && node != nodeMap.end()) - { - auto instance = sceneMgr->getInstance(mesh); - if (!npc.isMale() && type == ESM::PRT_LHand) { - SceneUtil::NodeMapVisitor::NodeMap handNodeMap; - SceneUtil::NodeMapVisitor nmVisitor(handNodeMap); - instance->accept(nmVisitor); - - std::cout << "Left hand\n"; - for (auto it : handNodeMap) { - std::cout << it.first << std::endl; + // Generate mapping + RaceToBPMap r2bpMap; + genRaceToBodyPartMap(r2bpMap); + + // Convenience method to add a body part + auto addBodyPart = [&](ESM::PartReferenceType type, std::string mesh) { + // Retrieve mesh name if necessary + if (mesh.empty()) + { + auto meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), isFemale ? 1 : 0, npc.mRace)); + if (meshResult != r2bpMap.end()) + { + mesh = meshResult->second; + } + else if (isFemale){ + meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), 0, npc.mRace)); + if (meshResult != r2bpMap.end()) + mesh = meshResult->second; } } - SceneUtil::attach(instance, mSkeleton, boneName, node->second); - } - }; - // Add body parts - for (unsigned int i = 0; i < ESM::PRT_Count; ++i) - { - auto part = static_cast(i); - switch (part) + // Attach to skeleton + std::string boneName = ESM::getBoneName(type); + auto node = nodeMap.find(boneName); + if (!mesh.empty() && node != nodeMap.end()) + { + auto instance = sceneMgr->getInstance(mesh); + SceneUtil::attach(instance, mSkeleton, boneName, node->second); + } + }; + + // Add body parts + for (unsigned int i = 0; i < ESM::PRT_Count; ++i) { - case ESM::PRT_Head: - addBodyPart(part, getBodyPartMesh(npc.mHead)); - break; - case ESM::PRT_Hair: - addBodyPart(part, getBodyPartMesh(npc.mHair)); - break; - case ESM::PRT_Skirt: - case ESM::PRT_Shield: - case ESM::PRT_RPauldron: - case ESM::PRT_LPauldron: - case ESM::PRT_Weapon: - // No body part mesh associated - break; - default: - addBodyPart(part, ""); + auto part = static_cast(i); + switch (part) + { + case ESM::PRT_Head: + addBodyPart(part, getBodyPartMesh(npc.mHead)); + break; + case ESM::PRT_Hair: + addBodyPart(part, getBodyPartMesh(npc.mHair)); + break; + case ESM::PRT_Skirt: + case ESM::PRT_Shield: + case ESM::PRT_RPauldron: + case ESM::PRT_LPauldron: + case ESM::PRT_Weapon: + // No body part mesh associated + break; + default: + addBodyPart(part, ""); + } } + // Post setup + mSkeleton->markDirty(); + mSkeleton->setActive(SceneUtil::Skeleton::Active); } - // Post setup - mSkeleton->markDirty(); - mSkeleton->setActive(SceneUtil::Skeleton::Active); + } + catch (std::exception& e) + { + std::cout << "Caught exception: " << e.what() << std::endl; } } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index e4a7c94b3c..484f90ab5c 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1339,7 +1339,7 @@ namespace MWRender { osg::ref_ptr created = sceneMgr->getInstance(model); - CleanObjectRootVisitor removeDrawableVisitor; + SceneUtil::CleanObjectRootVisitor removeDrawableVisitor; created->accept(removeDrawableVisitor); removeDrawableVisitor.remove(); @@ -1408,7 +1408,7 @@ namespace MWRender if (isCreature) { - RemoveTriBipVisitor removeTriBipVisitor; + SceneUtil::RemoveTriBipVisitor removeTriBipVisitor; mObjectRoot->accept(removeTriBipVisitor); removeTriBipVisitor.remove(); } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 98f8ce892d..2716ea19ba 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -304,7 +305,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr par void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) { assert(viewMode != VM_HeadOnly); - if(mViewMode == viewMode) + if(mViewMode == viewMode) return; mViewMode = viewMode; @@ -451,37 +452,15 @@ void NpcAnimation::updateNpcBase() } } + bool is1stPerson = mViewMode == VM_FirstPerson; bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; - std::string smodel; - if (mViewMode != VM_FirstPerson) - { - if (isWerewolf) - smodel = "meshes\\wolf\\skin.nif"; - else if (isBeast) - smodel = "meshes\\base_animkna.nif"; - else if (isFemale) - smodel = "meshes\\base_anim_female.nif"; - else - smodel = "meshes\\base_anim.nif"; - } - else - { - if (isWerewolf) - smodel = "meshes\\wolf\\skin.1st.nif"; - else if (isBeast) - smodel = "meshes\\base_animkna.1st.nif"; - else if (isFemale) - smodel = "meshes\\base_anim_female.1st.nif"; - else - smodel = "meshes\\base_anim.1st.nif"; - } - + std::string smodel = SceneUtil::getActorSkeleton(is1stPerson, isFemale, isBeast, isWerewolf); smodel = Misc::ResourceHelpers::correctActorModelPath(smodel, mResourceSystem->getVFS()); setObjectRoot(smodel, true, true, false); - if(mViewMode != VM_FirstPerson) + if(!is1stPerson) { const std::string base = "meshes\\xbase_anim.nif"; if (smodel != base) @@ -675,7 +654,7 @@ PartHolderPtr NpcAnimation::insertBoundedPart(const std::string& model, const st } osg::Vec3f NpcAnimation::runAnimation(float timepassed) -{ +{ osg::Vec3f ret = Animation::runAnimation(timepassed); mHeadAnimationTime->update(timepassed); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index e7e0ea0daa..7af76137c5 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -51,6 +51,7 @@ add_component_dir (shader add_component_dir (sceneutil clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer + actorutil ) add_component_dir (nif diff --git a/components/sceneutil/actorutil.cpp b/components/sceneutil/actorutil.cpp new file mode 100644 index 0000000000..988a61f60e --- /dev/null +++ b/components/sceneutil/actorutil.cpp @@ -0,0 +1,30 @@ +#include "actorutil.hpp" + +namespace SceneUtil +{ + std::string getActorSkeleton(bool firstPerson, bool isFemale, bool isBeast, bool isWerewolf) + { + if (!firstPerson) + { + if (isWerewolf) + return "meshes\\wolf\\skin.nif"; + else if (isBeast) + return "meshes\\base_animkna.nif"; + else if (isFemale) + return "meshes\\base_anim_female.nif"; + else + return "meshes\\base_anim.nif"; + } + else + { + if (isWerewolf) + return "meshes\\wolf\\skin.1st.nif"; + else if (isBeast) + return "meshes\\base_animkna.1st.nif"; + else if (isFemale) + return "meshes\\base_anim_female.1st.nif"; + else + return "meshes\\base_anim.1st.nif"; + } + } +} diff --git a/components/sceneutil/actorutil.hpp b/components/sceneutil/actorutil.hpp new file mode 100644 index 0000000000..7bdbbaa922 --- /dev/null +++ b/components/sceneutil/actorutil.hpp @@ -0,0 +1,11 @@ +#ifndef OPENMW_COMPONENTS_SCENEUTIL_ACTORUTIL_HPP +#define OPENMW_COMPONENTS_SCENEUTIL_ACTORUTIL_HPP + +#include + +namespace SceneUtil +{ + std::string getActorSkeleton(bool firstPerson, bool female, bool beast, bool werewolf); +} + +#endif diff --git a/components/sceneutil/visitor.cpp b/components/sceneutil/visitor.cpp index 5aaeb459ee..07be1608e6 100644 --- a/components/sceneutil/visitor.cpp +++ b/components/sceneutil/visitor.cpp @@ -1,5 +1,7 @@ #include "visitor.hpp" +#include + #include #include @@ -67,8 +69,91 @@ namespace SceneUtil traverse(trans); } - void HideDrawablesVisitor::apply(osg::Drawable& drawable) + void RemoveVisitor::remove() + { + for (RemoveVec::iterator it = mToRemove.begin(); it != mToRemove.end(); ++it) + { + if (!it->second->removeChild(it->first)) + std::cerr << "error removing " << it->first->getName() << std::endl; + } + } + + void CleanObjectRootVisitor::apply(osg::Drawable& drw) + { + applyDrawable(drw); + } + + void CleanObjectRootVisitor::apply(osg::Group& node) + { + applyNode(node); + } + + void CleanObjectRootVisitor::apply(osg::MatrixTransform& node) + { + applyNode(node); + } + + void CleanObjectRootVisitor::apply(osg::Node& node) { - drawable.setNodeMask(0); + applyNode(node); + } + + void CleanObjectRootVisitor::applyNode(osg::Node& node) + { + if (node.getStateSet()) + node.setStateSet(NULL); + + if (node.getNodeMask() == 0x1 && node.getNumParents() == 1) + mToRemove.push_back(std::make_pair(&node, node.getParent(0))); + else + traverse(node); + } + + void CleanObjectRootVisitor::applyDrawable(osg::Node& node) + { + osg::NodePath::iterator parent = getNodePath().end()-2; + // We know that the parent is a Group because only Groups can have children. + osg::Group* parentGroup = static_cast(*parent); + + // Try to prune nodes that would be empty after the removal + if (parent != getNodePath().begin()) + { + // This could be extended to remove the parent's parent, and so on if they are empty as well. + // But for NIF files, there won't be a benefit since only TriShapes can be set to STATIC dataVariance. + osg::Group* parentParent = static_cast(*(parent - 1)); + if (parentGroup->getNumChildren() == 1 && parentGroup->getDataVariance() == osg::Object::STATIC) + { + mToRemove.push_back(std::make_pair(parentGroup, parentParent)); + return; + } + } + + mToRemove.push_back(std::make_pair(&node, parentGroup)); + } + + void RemoveTriBipVisitor::apply(osg::Drawable& drw) + { + applyImpl(drw); + } + + void RemoveTriBipVisitor::apply(osg::Group& node) + { + traverse(node); + } + + void RemoveTriBipVisitor::apply(osg::MatrixTransform& node) + { + traverse(node); + } + + void RemoveTriBipVisitor::applyImpl(osg::Node& node) + { + const std::string toFind = "tri bip"; + if (Misc::StringUtils::ciCompareLen(node.getName(), toFind, toFind.size()) == 0) + { + osg::Group* parent = static_cast(*(getNodePath().end()-2)); + // Not safe to remove in apply(), since the visitor is still iterating the child list + mToRemove.push_back(std::make_pair(&node, parent)); + } } } diff --git a/components/sceneutil/visitor.hpp b/components/sceneutil/visitor.hpp index bd3945296d..3e9a565c03 100644 --- a/components/sceneutil/visitor.hpp +++ b/components/sceneutil/visitor.hpp @@ -68,7 +68,8 @@ namespace SceneUtil NodeMapVisitor(NodeMap& map) : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) , mMap(map) - {} + { + } void apply(osg::MatrixTransform& trans); @@ -76,18 +77,47 @@ namespace SceneUtil NodeMap& mMap; }; - /// Hides all attached drawables - class HideDrawablesVisitor : public osg::NodeVisitor + /// @brief Base class for visitors that remove nodes from a scene graph. + /// Subclasses need to fill the mToRemove vector. + /// To use, node->accept(removeVisitor); removeVisitor.remove(); + class RemoveVisitor : public osg::NodeVisitor { public: - HideDrawablesVisitor() + RemoveVisitor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) { } - void apply(osg::Drawable& drawable) override; + void remove(); + + protected: + // + typedef std::vector > RemoveVec; + std::vector > mToRemove; + }; + + // Removes all drawables from a graph. + class CleanObjectRootVisitor : public RemoveVisitor + { + public: + virtual void apply(osg::Drawable& drw); + virtual void apply(osg::Group& node); + virtual void apply(osg::MatrixTransform& node); + virtual void apply(osg::Node& node); + + void applyNode(osg::Node& node); + void applyDrawable(osg::Node& node); }; + class RemoveTriBipVisitor : public RemoveVisitor + { + public: + virtual void apply(osg::Drawable& drw); + virtual void apply(osg::Group& node); + virtual void apply(osg::MatrixTransform& node); + + void applyImpl(osg::Node& node); + }; } #endif