mirror of
https://github.com/OpenMW/openmw.git
synced 2025-06-19 21:41:33 +00:00
Handle creatures too
This commit is contained in:
parent
e2ac392a40
commit
6b42f37918
3 changed files with 167 additions and 130 deletions
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
namespace CSVRender
|
namespace CSVRender
|
||||||
{
|
{
|
||||||
|
const std::string Actor::MeshPrefix = "meshes\\";
|
||||||
|
|
||||||
Actor::Actor(const std::string& id, int type, CSMWorld::Data& data)
|
Actor::Actor(const std::string& id, int type, CSMWorld::Data& data)
|
||||||
: mId(id)
|
: mId(id)
|
||||||
, mType(type)
|
, mType(type)
|
||||||
|
@ -34,7 +36,42 @@ namespace CSVRender
|
||||||
|
|
||||||
void Actor::update()
|
void Actor::update()
|
||||||
{
|
{
|
||||||
const std::string MeshPrefix = "meshes\\";
|
try
|
||||||
|
{
|
||||||
|
mBaseNode->removeChildren(0, mBaseNode->getNumChildren());
|
||||||
|
|
||||||
|
if (mType == CSMWorld::UniversalId::Type_Npc)
|
||||||
|
updateNpc();
|
||||||
|
else if (mType == CSMWorld::UniversalId::Type_Creature)
|
||||||
|
updateCreature();
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
std::cout << "Caught exception: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Actor::updateCreature()
|
||||||
|
{
|
||||||
|
auto& referenceables = mData.getReferenceables();
|
||||||
|
|
||||||
|
auto& creature = dynamic_cast<const CSMWorld::Record<ESM::Creature>& >(referenceables.getRecord(mId)).get();
|
||||||
|
|
||||||
|
std::string skeletonModel = MeshPrefix + creature.mModel;
|
||||||
|
skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS());
|
||||||
|
loadSkeleton(skeletonModel);
|
||||||
|
|
||||||
|
SceneUtil::RemoveTriBipVisitor removeTriBipVisitor;
|
||||||
|
mSkeleton->accept(removeTriBipVisitor);
|
||||||
|
removeTriBipVisitor.remove();
|
||||||
|
|
||||||
|
// Post setup
|
||||||
|
mSkeleton->markDirty();
|
||||||
|
mSkeleton->setActive(SceneUtil::Skeleton::Active);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Actor::updateNpc()
|
||||||
|
{
|
||||||
const unsigned int FemaleFlag = ESM::BodyPart::BPF_Female;
|
const unsigned int FemaleFlag = ESM::BodyPart::BPF_Female;
|
||||||
|
|
||||||
auto& bodyParts = mData.getBodyParts();
|
auto& bodyParts = mData.getBodyParts();
|
||||||
|
@ -42,144 +79,137 @@ namespace CSVRender
|
||||||
auto& referenceables = mData.getReferenceables();
|
auto& referenceables = mData.getReferenceables();
|
||||||
auto sceneMgr = mData.getResourceSystem()->getSceneManager();
|
auto sceneMgr = mData.getResourceSystem()->getSceneManager();
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
// Remove children
|
bool is1stPerson = false;
|
||||||
mBaseNode->removeChildren(0, mBaseNode->getNumChildren());
|
bool isFemale = !npc.isMale();
|
||||||
|
bool isBeast = race.mData.mFlags & ESM::Race::Beast;
|
||||||
|
bool isWerewolf = false;
|
||||||
|
|
||||||
try
|
// Load skeleton
|
||||||
{
|
std::string skeletonModel = SceneUtil::getActorSkeleton(is1stPerson, isFemale, isBeast, isWerewolf);
|
||||||
// Npcs and creatures are handled differently
|
skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS());
|
||||||
if (mType == CSMWorld::UniversalId::Type_Npc)
|
loadSkeleton(skeletonModel);
|
||||||
|
|
||||||
|
// Get rid of the extra attachments
|
||||||
|
SceneUtil::CleanObjectRootVisitor cleanVisitor;
|
||||||
|
mSkeleton->accept(cleanVisitor);
|
||||||
|
cleanVisitor.remove();
|
||||||
|
|
||||||
|
// Map bone names to bones
|
||||||
|
SceneUtil::NodeMapVisitor::NodeMap nodeMap;
|
||||||
|
SceneUtil::NodeMapVisitor nmVisitor(nodeMap);
|
||||||
|
mSkeleton->accept(nmVisitor);
|
||||||
|
|
||||||
|
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& npc = dynamic_cast<const CSMWorld::Record<ESM::NPC>& >(referenceables.getRecord(mId)).get();
|
auto& record = bodyParts.getRecord(i);
|
||||||
auto& race = dynamic_cast<const CSMWorld::Record<ESM::Race>& >(races.getRecord(npc.mRace)).get();
|
if (!record.isDeleted())
|
||||||
|
|
||||||
bool is1stPerson = false;
|
|
||||||
bool isFemale = !npc.isMale();
|
|
||||||
bool isBeast = race.mData.mFlags & ESM::Race::Beast;
|
|
||||||
bool isWerewolf = false;
|
|
||||||
|
|
||||||
// Load skeleton
|
|
||||||
std::string skeletonModel = SceneUtil::getActorSkeleton(is1stPerson, isFemale, isBeast, isWerewolf);
|
|
||||||
skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS());
|
|
||||||
{
|
{
|
||||||
osg::ref_ptr<osg::Node> temp = sceneMgr->getInstance(skeletonModel);
|
// Method to check if 1st person part or not
|
||||||
mSkeleton = dynamic_cast<SceneUtil::Skeleton*>(temp.get());
|
auto is1stPersonPart = [](std::string name) {
|
||||||
if (!mSkeleton)
|
return name.size() >= 4 && name.find(".1st", name.size() - 4) != std::string::npos;
|
||||||
{
|
};
|
||||||
mSkeleton = new SceneUtil::Skeleton();
|
|
||||||
mSkeleton->addChild(temp);
|
auto& bodyPart = record.get();
|
||||||
}
|
if (bodyPart.mData.mType != ESM::BodyPart::MT_Skin || is1stPersonPart(bodyPart.mId))
|
||||||
mBaseNode->addChild(mSkeleton);
|
continue;
|
||||||
|
|
||||||
|
bpMap.emplace(
|
||||||
|
BPRaceKey(bodyPart.mData.mPart, bodyPart.mData.mFlags & FemaleFlag ? 1 : 0, bodyPart.mRace),
|
||||||
|
MeshPrefix + bodyPart.mModel);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Map bone names to bones
|
// Generate mapping
|
||||||
SceneUtil::NodeMapVisitor::NodeMap nodeMap;
|
RaceToBPMap r2bpMap;
|
||||||
SceneUtil::NodeMapVisitor nmVisitor(nodeMap);
|
genRaceToBodyPartMap(r2bpMap);
|
||||||
mSkeleton->accept(nmVisitor);
|
|
||||||
|
|
||||||
// Female mesh has some drawables attached, get rid of them
|
// Convenience method to add a body part
|
||||||
SceneUtil::CleanObjectRootVisitor cleanVisitor;
|
auto addBodyPart = [&](ESM::PartReferenceType type, std::string mesh) {
|
||||||
mSkeleton->accept(cleanVisitor);
|
// Retrieve mesh name if necessary
|
||||||
cleanVisitor.remove();
|
if (mesh.empty())
|
||||||
|
{
|
||||||
// Convenience method to retrieve the mesh name of a body part
|
auto meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), isFemale ? 1 : 0, npc.mRace));
|
||||||
auto getBodyPartMesh = [&](std::string bpName) -> std::string {
|
if (meshResult != r2bpMap.end())
|
||||||
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);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
{
|
{
|
||||||
auto part = static_cast<ESM::PartReferenceType>(i);
|
mesh = meshResult->second;
|
||||||
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
|
else if (isFemale){
|
||||||
mSkeleton->markDirty();
|
meshResult = r2bpMap.find(BPRaceKey(ESM::getMeshPart(type), 0, npc.mRace));
|
||||||
mSkeleton->setActive(SceneUtil::Skeleton::Active);
|
if (meshResult != r2bpMap.end())
|
||||||
|
mesh = meshResult->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
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, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
|
||||||
|
// Post setup
|
||||||
|
mSkeleton->markDirty();
|
||||||
|
mSkeleton->setActive(SceneUtil::Skeleton::Active);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Actor::loadSkeleton(const std::string& model)
|
||||||
|
{
|
||||||
|
auto sceneMgr = mData.getResourceSystem()->getSceneManager();
|
||||||
|
|
||||||
|
osg::ref_ptr<osg::Node> temp = sceneMgr->getInstance(model);
|
||||||
|
mSkeleton = dynamic_cast<SceneUtil::Skeleton*>(temp.get());
|
||||||
|
if (!mSkeleton)
|
||||||
{
|
{
|
||||||
std::cout << "Caught exception: " << e.what() << std::endl;
|
mSkeleton = new SceneUtil::Skeleton();
|
||||||
|
mSkeleton->addChild(temp);
|
||||||
}
|
}
|
||||||
|
mBaseNode->addChild(mSkeleton);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Actor::getBodyPartMesh(const std::string& bodyPartId)
|
||||||
|
{
|
||||||
|
const auto& bodyParts = mData.getBodyParts();
|
||||||
|
|
||||||
|
int index = bodyParts.searchId(bodyPartId);
|
||||||
|
if (index != -1 && !bodyParts.getRecord(index).isDeleted())
|
||||||
|
return MeshPrefix + bodyParts.getRecord(index).get().mModel;
|
||||||
|
else
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,14 @@ namespace CSVRender
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void loadSkeleton(const std::string& model);
|
||||||
|
void updateCreature();
|
||||||
|
void updateNpc();
|
||||||
|
|
||||||
|
std::string getBodyPartMesh(const std::string& bodyPartId);
|
||||||
|
|
||||||
|
static const std::string MeshPrefix;
|
||||||
|
|
||||||
std::string mId;
|
std::string mId;
|
||||||
int mType;
|
int mType;
|
||||||
CSMWorld::Data& mData;
|
CSMWorld::Data& mData;
|
||||||
|
|
|
@ -132,9 +132,8 @@ void CSVRender::Object::update()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (recordType == CSMWorld::UniversalId::Type_Npc)
|
if (recordType == CSMWorld::UniversalId::Type_Npc || recordType == CSMWorld::UniversalId::Type_Creature)
|
||||||
{
|
{
|
||||||
std::cout << "recordType: Npc\n";
|
|
||||||
Actor actor(mReferenceableId, recordType, mData);
|
Actor actor(mReferenceableId, recordType, mData);
|
||||||
actor.update();
|
actor.update();
|
||||||
mBaseNode->addChild(actor.getBaseNode());
|
mBaseNode->addChild(actor.getBaseNode());
|
||||||
|
@ -147,8 +146,8 @@ void CSVRender::Object::update()
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
{
|
{
|
||||||
// TODO: use error marker mesh
|
|
||||||
Log(Debug::Error) << e.what();
|
Log(Debug::Error) << e.what();
|
||||||
|
mBaseNode->addChild(createErrorCube());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue