Move common stuff to scene util, fix errors with 1st person meshes

pull/541/head
Kyle Cooley 7 years ago committed by Andrei Kortunov
parent 8444ee9981
commit e2ac392a40

@ -9,6 +9,7 @@
#include <components/misc/resourcehelpers.hpp> #include <components/misc/resourcehelpers.hpp>
#include <components/resource/resourcemanager.hpp> #include <components/resource/resourcemanager.hpp>
#include <components/resource/scenemanager.hpp> #include <components/resource/scenemanager.hpp>
#include <components/sceneutil/actorutil.hpp>
#include <components/sceneutil/attach.hpp> #include <components/sceneutil/attach.hpp>
#include <components/sceneutil/skeleton.hpp> #include <components/sceneutil/skeleton.hpp>
#include <components/sceneutil/visitor.hpp> #include <components/sceneutil/visitor.hpp>
@ -45,152 +46,140 @@ namespace CSVRender
// Remove children // Remove children
mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); mBaseNode->removeChildren(0, mBaseNode->getNumChildren());
// Npcs and creatures are handled differently try
if (mType == CSMWorld::UniversalId::Type_Npc)
{ {
auto& npc = dynamic_cast<const CSMWorld::Record<ESM::NPC>& >(referenceables.getRecord(mId)).get(); // Npcs and creatures are handled differently
if (mType == CSMWorld::UniversalId::Type_Npc)
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());
{ {
osg::ref_ptr<osg::Node> temp = sceneMgr->getInstance(skeletonModel); auto& npc = dynamic_cast<const CSMWorld::Record<ESM::NPC>& >(referenceables.getRecord(mId)).get();
mSkeleton = dynamic_cast<SceneUtil::Skeleton*>(temp.get()); auto& race = dynamic_cast<const CSMWorld::Record<ESM::Race>& >(races.getRecord(npc.mRace)).get();
if (!mSkeleton)
{
mSkeleton = new SceneUtil::Skeleton();
mSkeleton->addChild(temp);
}
mBaseNode->addChild(mSkeleton);
}
// Map bone names to bones bool is1stPerson = false;
SceneUtil::NodeMapVisitor::NodeMap nodeMap; bool isFemale = !npc.isMale();
SceneUtil::NodeMapVisitor nmVisitor(nodeMap); bool isBeast = race.mData.mFlags & ESM::Race::Beast;
mSkeleton->accept(nmVisitor); bool isWerewolf = false;
if (!npc.isMale()) { // Load skeleton
for (auto it : nodeMap) { std::string skeletonModel = SceneUtil::getActorSkeleton(is1stPerson, isFemale, isBeast, isWerewolf);
std::cout << it.first << "\n"; skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS());
}
}
// 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<int, int, std::string>;
using RaceToBPMap = std::map<BPRaceKey, std::string>;
// 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)
{ {
auto& record = bodyParts.getRecord(i); osg::ref_ptr<osg::Node> temp = sceneMgr->getInstance(skeletonModel);
if (!record.isDeleted()) mSkeleton = dynamic_cast<SceneUtil::Skeleton*>(temp.get());
if (!mSkeleton)
{ {
auto& bodyPart = record.get(); mSkeleton = new SceneUtil::Skeleton();
bpMap.emplace( mSkeleton->addChild(temp);
BPRaceKey(bodyPart.mData.mPart, bodyPart.mData.mFlags & FemaleFlag ? 1 : 0, bodyPart.mRace),
MeshPrefix + bodyPart.mModel);
} }
mBaseNode->addChild(mSkeleton);
} }
};
// Generate mapping // Map bone names to bones
RaceToBPMap r2bpMap; SceneUtil::NodeMapVisitor::NodeMap nodeMap;
genRaceToBodyPartMap(r2bpMap); SceneUtil::NodeMapVisitor nmVisitor(nodeMap);
mSkeleton->accept(nmVisitor);
// Convenience method to add a body part
auto addBodyPart = [&](ESM::PartReferenceType type, std::string mesh) { // Female mesh has some drawables attached, get rid of them
// Retrieve mesh name if necessary SceneUtil::CleanObjectRootVisitor cleanVisitor;
if (mesh.empty()) mSkeleton->accept(cleanVisitor);
{ cleanVisitor.remove();
auto meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), npc.isMale() ? 0 : 1, npc.mRace));
if (meshResult != r2bpMap.end()) // 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<int, int, std::string>;
using RaceToBPMap = std::map<BPRaceKey, std::string>;
// 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 // Generate mapping
std::string boneName = ESM::getBoneName(type); RaceToBPMap r2bpMap;
auto node = nodeMap.find(boneName); genRaceToBodyPartMap(r2bpMap);
if (!mesh.empty() && node != nodeMap.end())
{ // Convenience method to add a body part
auto instance = sceneMgr->getInstance(mesh); auto addBodyPart = [&](ESM::PartReferenceType type, std::string mesh) {
if (!npc.isMale() && type == ESM::PRT_LHand) { // Retrieve mesh name if necessary
SceneUtil::NodeMapVisitor::NodeMap handNodeMap; if (mesh.empty())
SceneUtil::NodeMapVisitor nmVisitor(handNodeMap); {
instance->accept(nmVisitor); auto meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), isFemale ? 1 : 0, npc.mRace));
if (meshResult != r2bpMap.end())
std::cout << "Left hand\n"; {
for (auto it : handNodeMap) { mesh = meshResult->second;
std::cout << it.first << std::endl; }
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 // Attach to skeleton
for (unsigned int i = 0; i < ESM::PRT_Count; ++i) std::string boneName = ESM::getBoneName(type);
{ auto node = nodeMap.find(boneName);
auto part = static_cast<ESM::PartReferenceType>(i); if (!mesh.empty() && node != nodeMap.end())
switch (part) {
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: auto part = static_cast<ESM::PartReferenceType>(i);
addBodyPart(part, getBodyPartMesh(npc.mHead)); switch (part)
break; {
case ESM::PRT_Hair: case ESM::PRT_Head:
addBodyPart(part, getBodyPartMesh(npc.mHair)); addBodyPart(part, getBodyPartMesh(npc.mHead));
break; break;
case ESM::PRT_Skirt: case ESM::PRT_Hair:
case ESM::PRT_Shield: addBodyPart(part, getBodyPartMesh(npc.mHair));
case ESM::PRT_RPauldron: break;
case ESM::PRT_LPauldron: case ESM::PRT_Skirt:
case ESM::PRT_Weapon: case ESM::PRT_Shield:
// No body part mesh associated case ESM::PRT_RPauldron:
break; case ESM::PRT_LPauldron:
default: case ESM::PRT_Weapon:
addBodyPart(part, ""); // No body part mesh associated
break;
default:
addBodyPart(part, "");
}
} }
// Post setup
mSkeleton->markDirty();
mSkeleton->setActive(SceneUtil::Skeleton::Active);
} }
// Post setup }
mSkeleton->markDirty(); catch (std::exception& e)
mSkeleton->setActive(SceneUtil::Skeleton::Active); {
std::cout << "Caught exception: " << e.what() << std::endl;
} }
} }
} }

@ -1339,7 +1339,7 @@ namespace MWRender
{ {
osg::ref_ptr<osg::Node> created = sceneMgr->getInstance(model); osg::ref_ptr<osg::Node> created = sceneMgr->getInstance(model);
CleanObjectRootVisitor removeDrawableVisitor; SceneUtil::CleanObjectRootVisitor removeDrawableVisitor;
created->accept(removeDrawableVisitor); created->accept(removeDrawableVisitor);
removeDrawableVisitor.remove(); removeDrawableVisitor.remove();
@ -1408,7 +1408,7 @@ namespace MWRender
if (isCreature) if (isCreature)
{ {
RemoveTriBipVisitor removeTriBipVisitor; SceneUtil::RemoveTriBipVisitor removeTriBipVisitor;
mObjectRoot->accept(removeTriBipVisitor); mObjectRoot->accept(removeTriBipVisitor);
removeTriBipVisitor.remove(); removeTriBipVisitor.remove();
} }

@ -15,6 +15,7 @@
#include <components/resource/resourcesystem.hpp> #include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp> #include <components/resource/scenemanager.hpp>
#include <components/sceneutil/actorutil.hpp>
#include <components/sceneutil/attach.hpp> #include <components/sceneutil/attach.hpp>
#include <components/sceneutil/visitor.hpp> #include <components/sceneutil/visitor.hpp>
#include <components/sceneutil/skeleton.hpp> #include <components/sceneutil/skeleton.hpp>
@ -304,7 +305,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, osg::ref_ptr<osg::Group> par
void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode)
{ {
assert(viewMode != VM_HeadOnly); assert(viewMode != VM_HeadOnly);
if(mViewMode == viewMode) if(mViewMode == viewMode)
return; return;
mViewMode = viewMode; mViewMode = viewMode;
@ -451,37 +452,15 @@ void NpcAnimation::updateNpcBase()
} }
} }
bool is1stPerson = mViewMode == VM_FirstPerson;
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
std::string smodel; std::string smodel = SceneUtil::getActorSkeleton(is1stPerson, isFemale, isBeast, isWerewolf);
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";
}
smodel = Misc::ResourceHelpers::correctActorModelPath(smodel, mResourceSystem->getVFS()); smodel = Misc::ResourceHelpers::correctActorModelPath(smodel, mResourceSystem->getVFS());
setObjectRoot(smodel, true, true, false); setObjectRoot(smodel, true, true, false);
if(mViewMode != VM_FirstPerson) if(!is1stPerson)
{ {
const std::string base = "meshes\\xbase_anim.nif"; const std::string base = "meshes\\xbase_anim.nif";
if (smodel != base) if (smodel != base)
@ -675,7 +654,7 @@ PartHolderPtr NpcAnimation::insertBoundedPart(const std::string& model, const st
} }
osg::Vec3f NpcAnimation::runAnimation(float timepassed) osg::Vec3f NpcAnimation::runAnimation(float timepassed)
{ {
osg::Vec3f ret = Animation::runAnimation(timepassed); osg::Vec3f ret = Animation::runAnimation(timepassed);
mHeadAnimationTime->update(timepassed); mHeadAnimationTime->update(timepassed);

@ -51,6 +51,7 @@ add_component_dir (shader
add_component_dir (sceneutil add_component_dir (sceneutil
clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller clone attach visitor util statesetupdater controller skeleton riggeometry morphgeometry lightcontroller
lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer lightmanager lightutil positionattitudetransform workqueue unrefqueue pathgridutil waterutil writescene serialize optimizer
actorutil
) )
add_component_dir (nif add_component_dir (nif

@ -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";
}
}
}

@ -0,0 +1,11 @@
#ifndef OPENMW_COMPONENTS_SCENEUTIL_ACTORUTIL_HPP
#define OPENMW_COMPONENTS_SCENEUTIL_ACTORUTIL_HPP
#include <string>
namespace SceneUtil
{
std::string getActorSkeleton(bool firstPerson, bool female, bool beast, bool werewolf);
}
#endif

@ -1,5 +1,7 @@
#include "visitor.hpp" #include "visitor.hpp"
#include <iostream>
#include <osg/Drawable> #include <osg/Drawable>
#include <osg/MatrixTransform> #include <osg/MatrixTransform>
@ -67,8 +69,91 @@ namespace SceneUtil
traverse(trans); 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<osg::Group*>(*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<osg::Group*>(*(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<osg::Group*>(*(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));
}
} }
} }

@ -68,7 +68,8 @@ namespace SceneUtil
NodeMapVisitor(NodeMap& map) NodeMapVisitor(NodeMap& map)
: osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN)
, mMap(map) , mMap(map)
{} {
}
void apply(osg::MatrixTransform& trans); void apply(osg::MatrixTransform& trans);
@ -76,18 +77,47 @@ namespace SceneUtil
NodeMap& mMap; NodeMap& mMap;
}; };
/// Hides all attached drawables /// @brief Base class for visitors that remove nodes from a scene graph.
class HideDrawablesVisitor : public osg::NodeVisitor /// Subclasses need to fill the mToRemove vector.
/// To use, node->accept(removeVisitor); removeVisitor.remove();
class RemoveVisitor : public osg::NodeVisitor
{ {
public: public:
HideDrawablesVisitor() RemoveVisitor()
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
{ {
} }
void apply(osg::Drawable& drawable) override; void remove();
protected:
// <node to remove, parent node to remove it from>
typedef std::vector<std::pair<osg::Node*, osg::Group*> > RemoveVec;
std::vector<std::pair<osg::Node*, osg::Group*> > 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 #endif

Loading…
Cancel
Save