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

pull/1944/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/resource/resourcemanager.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/sceneutil/actorutil.hpp>
#include <components/sceneutil/attach.hpp>
#include <components/sceneutil/skeleton.hpp>
#include <components/sceneutil/visitor.hpp>
@ -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<const CSMWorld::Record<ESM::NPC>& >(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<osg::Node> temp = sceneMgr->getInstance(skeletonModel);
mSkeleton = dynamic_cast<SceneUtil::Skeleton*>(temp.get());
if (!mSkeleton)
{
mSkeleton = new SceneUtil::Skeleton();
mSkeleton->addChild(temp);
}
mBaseNode->addChild(mSkeleton);
}
auto& npc = dynamic_cast<const CSMWorld::Record<ESM::NPC>& >(referenceables.getRecord(mId)).get();
auto& race = dynamic_cast<const CSMWorld::Record<ESM::Race>& >(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<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)
// 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<osg::Node> temp = sceneMgr->getInstance(skeletonModel);
mSkeleton = dynamic_cast<SceneUtil::Skeleton*>(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<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
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<ESM::PartReferenceType>(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<ESM::PartReferenceType>(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;
}
}
}

@ -1339,7 +1339,7 @@ namespace MWRender
{
osg::ref_ptr<osg::Node> 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();
}

@ -15,6 +15,7 @@
#include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/sceneutil/actorutil.hpp>
#include <components/sceneutil/attach.hpp>
#include <components/sceneutil/visitor.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)
{
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);

@ -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

@ -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 <iostream>
#include <osg/Drawable>
#include <osg/MatrixTransform>
@ -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<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)
: 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:
// <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

Loading…
Cancel
Save