mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-21 06:53:53 +00:00
Centralize actor data, simplify logic
This commit is contained in:
parent
c1ec926f43
commit
f43b70d77b
5 changed files with 116 additions and 143 deletions
|
@ -6,6 +6,7 @@
|
|||
#include <components/esm/loadnpc.hpp>
|
||||
#include <components/esm/loadrace.hpp>
|
||||
#include <components/esm/mappings.hpp>
|
||||
#include <components/sceneutil/actorutil.hpp>
|
||||
|
||||
#include "data.hpp"
|
||||
|
||||
|
@ -16,6 +17,11 @@ namespace CSMWorld
|
|||
return mId;
|
||||
}
|
||||
|
||||
bool ActorAdapter::RaceData::isBeast() const
|
||||
{
|
||||
return mIsBeast;
|
||||
}
|
||||
|
||||
bool ActorAdapter::RaceData::handlesPart(ESM::PartReferenceType type) const
|
||||
{
|
||||
switch (type)
|
||||
|
@ -63,9 +69,10 @@ namespace CSMWorld
|
|||
if (!id.empty()) mDependencies.emplace(id);
|
||||
}
|
||||
|
||||
void ActorAdapter::RaceData::reset(const std::string& id)
|
||||
void ActorAdapter::RaceData::reset_data(const std::string& id, bool isBeast)
|
||||
{
|
||||
mId = id;
|
||||
mIsBeast = isBeast;
|
||||
for (auto& str : mFemaleParts)
|
||||
str.clear();
|
||||
for (auto& str : mMaleParts)
|
||||
|
@ -82,11 +89,28 @@ namespace CSMWorld
|
|||
return mId;
|
||||
}
|
||||
|
||||
bool ActorAdapter::ActorData::isCreature() const
|
||||
{
|
||||
return mCreature;
|
||||
}
|
||||
|
||||
bool ActorAdapter::ActorData::isFemale() const
|
||||
{
|
||||
return mFemale;
|
||||
}
|
||||
|
||||
std::string ActorAdapter::ActorData::getSkeleton() const
|
||||
{
|
||||
if (mCreature || !mSkeletonOverride.empty())
|
||||
return "meshes\\" + mSkeletonOverride;
|
||||
|
||||
bool firstPerson = false;
|
||||
bool beast = mRaceData ? mRaceData->isBeast() : false;
|
||||
bool werewolf = false;
|
||||
|
||||
return SceneUtil::getActorSkeleton(firstPerson, mFemale, beast, werewolf);
|
||||
}
|
||||
|
||||
const std::string& ActorAdapter::ActorData::getPart(ESM::PartReferenceType index) const
|
||||
{
|
||||
if (mParts[index].empty() && mRaceData && mRaceData->handlesPart(index))
|
||||
|
@ -112,10 +136,12 @@ namespace CSMWorld
|
|||
if (!id.empty()) mDependencies.emplace(id);
|
||||
}
|
||||
|
||||
void ActorAdapter::ActorData::reset(const std::string& id, bool isFemale, RaceDataPtr raceData)
|
||||
void ActorAdapter::ActorData::reset_data(const std::string& id, const std::string& skeleton, bool isCreature, bool isFemale, RaceDataPtr raceData)
|
||||
{
|
||||
mId = id;
|
||||
mCreature = isCreature;
|
||||
mFemale = isFemale;
|
||||
mSkeletonOverride = skeleton;
|
||||
mRaceData = raceData;
|
||||
for (auto& str : mParts)
|
||||
str.clear();
|
||||
|
@ -383,7 +409,7 @@ namespace CSMWorld
|
|||
if (index == -1)
|
||||
{
|
||||
// Record does not exist
|
||||
data->reset(id);
|
||||
data->reset_data(id);
|
||||
emit actorChanged(id);
|
||||
return;
|
||||
}
|
||||
|
@ -392,7 +418,7 @@ namespace CSMWorld
|
|||
if (record.isDeleted())
|
||||
{
|
||||
// Record is deleted and therefore not accessible
|
||||
data->reset(id);
|
||||
data->reset_data(id);
|
||||
emit actorChanged(id);
|
||||
return;
|
||||
}
|
||||
|
@ -414,20 +440,18 @@ namespace CSMWorld
|
|||
else
|
||||
{
|
||||
// Wrong record type
|
||||
data->reset(id);
|
||||
data->reset_data(id);
|
||||
emit actorChanged(id);
|
||||
}
|
||||
}
|
||||
|
||||
void ActorAdapter::setupRace(const std::string& id, RaceDataPtr data)
|
||||
{
|
||||
// Common setup
|
||||
data->reset(id);
|
||||
|
||||
int index = mRaces.searchId(id);
|
||||
if (index == -1)
|
||||
{
|
||||
// Record does not exist
|
||||
data->reset_data(id);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -435,10 +459,12 @@ namespace CSMWorld
|
|||
if (raceRecord.isDeleted())
|
||||
{
|
||||
// Record is deleted, so not accessible
|
||||
data->reset_data(id);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO move stuff in actor related to race here
|
||||
auto& race = raceRecord.get();
|
||||
data->reset_data(id, race.mData.mFlags & ESM::Race::Beast);
|
||||
|
||||
// Setup body parts
|
||||
for (int i = 0; i < mBodyParts.getSize(); ++i)
|
||||
|
@ -470,7 +496,7 @@ namespace CSMWorld
|
|||
auto& npc = dynamic_cast<const Record<ESM::NPC>&>(mReferenceables.getRecord(index)).get();
|
||||
|
||||
RaceDataPtr raceData = getRaceData(npc.mRace);
|
||||
data->reset(id, !npc.isMale(), raceData);
|
||||
data->reset_data(id, "", false, !npc.isMale(), raceData);
|
||||
|
||||
// Add inventory items
|
||||
for (auto& item : npc.mInventory.mList)
|
||||
|
@ -541,9 +567,11 @@ namespace CSMWorld
|
|||
|
||||
void ActorAdapter::setupCreature(const std::string& id, ActorDataPtr data)
|
||||
{
|
||||
data->reset(id);
|
||||
// Record is known to exist and is not deleted
|
||||
int index = mReferenceables.searchId(id);
|
||||
auto& creature = dynamic_cast<const Record<ESM::Creature>&>(mReferenceables.getRecord(index)).get();
|
||||
|
||||
// TODO move stuff from Actor here
|
||||
data->reset_data(id, creature.mModel, true);
|
||||
}
|
||||
|
||||
void ActorAdapter::markDirtyDependency(const std::string& dep)
|
||||
|
|
|
@ -44,6 +44,8 @@ namespace CSMWorld
|
|||
public:
|
||||
/// Retrieves the id of the race represented
|
||||
const std::string& getId() const;
|
||||
/// Checks if it's a beast race
|
||||
bool isBeast() const;
|
||||
/// Checks if a part could exist for the given type
|
||||
bool handlesPart(ESM::PartReferenceType type) const;
|
||||
/// Retrieves the associated body part
|
||||
|
@ -60,11 +62,12 @@ namespace CSMWorld
|
|||
/// Marks an additional dependency
|
||||
void addOtherDependency(const std::string& id);
|
||||
/// Clears parts and dependencies
|
||||
void reset(const std::string& raceId);
|
||||
void reset_data(const std::string& raceId, bool isBeast=false);
|
||||
|
||||
private:
|
||||
bool handles(ESM::PartReferenceType type) const;
|
||||
std::string mId;
|
||||
bool mIsBeast;
|
||||
RacePartList mFemaleParts;
|
||||
RacePartList mMaleParts;
|
||||
StringSet mDependencies;
|
||||
|
@ -78,8 +81,12 @@ namespace CSMWorld
|
|||
public:
|
||||
/// Retrieves the id of the actor represented
|
||||
const std::string& getId() const;
|
||||
/// Checks if the actor is a creature
|
||||
bool isCreature() const;
|
||||
/// Checks if the actor is female
|
||||
bool isFemale() const;
|
||||
/// Returns the skeleton the actor should use for attaching parts to
|
||||
std::string getSkeleton() const;
|
||||
/// Retrieves the associated actor part
|
||||
const std::string& getPart(ESM::PartReferenceType index) const;
|
||||
/// Checks if the actor has a data dependency
|
||||
|
@ -90,11 +97,13 @@ namespace CSMWorld
|
|||
/// Marks an additional dependency for the actor
|
||||
void addOtherDependency(const std::string& id);
|
||||
/// Clears race, parts, and dependencies
|
||||
void reset(const std::string& actorId, bool female=true, RaceDataPtr raceData=nullptr);
|
||||
void reset_data(const std::string& actorId, const std::string& skeleton="", bool isCreature=false, bool female=true, RaceDataPtr raceData=nullptr);
|
||||
|
||||
private:
|
||||
std::string mId;
|
||||
bool mCreature;
|
||||
bool mFemale;
|
||||
std::string mSkeletonOverride;
|
||||
RaceDataPtr mRaceData;
|
||||
ActorPartList mParts;
|
||||
StringSet mDependencies;
|
||||
|
|
|
@ -18,15 +18,15 @@ 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, CSMWorld::Data& data)
|
||||
: mId(id)
|
||||
, mInitialized(false)
|
||||
, mType(type)
|
||||
, mData(data)
|
||||
, mBaseNode(new osg::Group())
|
||||
, mSkeleton(nullptr)
|
||||
{
|
||||
mActorData = mData.getActorAdapter()->getActorData(mId);
|
||||
connect(mData.getActorAdapter(), SIGNAL(actorChanged(const std::string&)),
|
||||
this, SLOT(handleActorChanged(const std::string&)));
|
||||
}
|
||||
|
||||
osg::Group* Actor::getBaseNode()
|
||||
|
@ -36,25 +36,33 @@ namespace CSVRender
|
|||
|
||||
void Actor::update()
|
||||
{
|
||||
try
|
||||
{
|
||||
mBaseNode->removeChildren(0, mBaseNode->getNumChildren());
|
||||
mBaseNode->removeChildren(0, mBaseNode->getNumChildren());
|
||||
|
||||
if (mType == CSMWorld::UniversalId::Type_Npc)
|
||||
updateNpc();
|
||||
else if (mType == CSMWorld::UniversalId::Type_Creature)
|
||||
updateCreature();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
// Load skeleton
|
||||
std::string skeletonModel = mActorData->getSkeleton();
|
||||
skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS());
|
||||
loadSkeleton(skeletonModel);
|
||||
|
||||
if (!mActorData->isCreature())
|
||||
{
|
||||
Log(Debug::Info) << "Exception in Actor::update(): " << e.what();
|
||||
// Get rid of the extra attachments
|
||||
SceneUtil::CleanObjectRootVisitor cleanVisitor;
|
||||
mSkeleton->accept(cleanVisitor);
|
||||
cleanVisitor.remove();
|
||||
|
||||
// Attach parts to skeleton
|
||||
loadBodyParts();
|
||||
}
|
||||
else
|
||||
{
|
||||
SceneUtil::RemoveTriBipVisitor removeTriBipVisitor;
|
||||
mSkeleton->accept(removeTriBipVisitor);
|
||||
removeTriBipVisitor.remove();
|
||||
}
|
||||
|
||||
if (!mInitialized)
|
||||
{
|
||||
mInitialized = true;
|
||||
connect(mData.getActorAdapter(), SIGNAL(actorChanged(const std::string&)), this, SLOT(handleActorChanged(const std::string&)));
|
||||
}
|
||||
// Post setup
|
||||
mSkeleton->markDirty();
|
||||
mSkeleton->setActive(SceneUtil::Skeleton::Active);
|
||||
}
|
||||
|
||||
void Actor::handleActorChanged(const std::string& refId)
|
||||
|
@ -65,57 +73,6 @@ namespace CSVRender
|
|||
}
|
||||
}
|
||||
|
||||
void Actor::updateCreature()
|
||||
{
|
||||
auto& referenceables = mData.getReferenceables();
|
||||
|
||||
auto& creature = dynamic_cast<const CSMWorld::Record<ESM::Creature>& >(referenceables.getRecord(mId)).get();
|
||||
|
||||
// Load skeleton with meshes
|
||||
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()
|
||||
{
|
||||
auto& races = mData.getRaces();
|
||||
auto& referenceables = mData.getReferenceables();
|
||||
|
||||
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();
|
||||
|
||||
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());
|
||||
loadSkeleton(skeletonModel);
|
||||
|
||||
// Get rid of the extra attachments
|
||||
SceneUtil::CleanObjectRootVisitor cleanVisitor;
|
||||
mSkeleton->accept(cleanVisitor);
|
||||
cleanVisitor.remove();
|
||||
|
||||
// Attach parts to skeleton
|
||||
loadBodyParts(npc.mId);
|
||||
|
||||
// Post setup
|
||||
mSkeleton->markDirty();
|
||||
mSkeleton->setActive(SceneUtil::Skeleton::Active);
|
||||
}
|
||||
|
||||
void Actor::loadSkeleton(const std::string& model)
|
||||
{
|
||||
auto sceneMgr = mData.getResourceSystem()->getSceneManager();
|
||||
|
@ -136,7 +93,7 @@ namespace CSVRender
|
|||
|
||||
}
|
||||
|
||||
void Actor::loadBodyParts(const std::string& actorId)
|
||||
void Actor::loadBodyParts()
|
||||
{
|
||||
for (int i = 0; i < ESM::PRT_Count; ++i)
|
||||
{
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace CSVRender
|
|||
/// \param id The referenceable id
|
||||
/// \param type The record type
|
||||
/// \param data The data store
|
||||
Actor(const std::string& id, int type, CSMWorld::Data& data);
|
||||
Actor(const std::string& id, CSMWorld::Data& data);
|
||||
|
||||
/// Retrieves the base node that meshes are attached to
|
||||
osg::Group* getBaseNode();
|
||||
|
@ -50,11 +50,8 @@ namespace CSVRender
|
|||
void handleActorChanged(const std::string& refId);
|
||||
|
||||
private:
|
||||
void updateCreature();
|
||||
void updateNpc();
|
||||
|
||||
void loadSkeleton(const std::string& model);
|
||||
void loadBodyParts(const std::string& actorId);
|
||||
void loadBodyParts();
|
||||
void attachBodyPart(ESM::PartReferenceType, const std::string& mesh);
|
||||
|
||||
std::string getBodyPartMesh(const std::string& bodyPartId);
|
||||
|
@ -62,8 +59,6 @@ namespace CSVRender
|
|||
static const std::string MeshPrefix;
|
||||
|
||||
std::string mId;
|
||||
bool mInitialized;
|
||||
int mType;
|
||||
CSMWorld::Data& mData;
|
||||
CSMWorld::ActorAdapter::ActorDataPtr mActorData;
|
||||
|
||||
|
|
|
@ -83,74 +83,58 @@ void CSVRender::Object::update()
|
|||
{
|
||||
clear();
|
||||
|
||||
std::string model;
|
||||
int error = 0; // 1 referenceable does not exist, 2 referenceable does not specify a mesh
|
||||
|
||||
const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables();
|
||||
const int TypeIndex = referenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType);
|
||||
const int ModelIndex = referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Model);
|
||||
|
||||
int index = referenceables.searchId (mReferenceableId);
|
||||
int recordType = -1;
|
||||
const ESM::Light* light = NULL;
|
||||
|
||||
if (index==-1)
|
||||
error = 1;
|
||||
else
|
||||
{
|
||||
/// \todo check for Deleted state (error 1)
|
||||
|
||||
model = referenceables.getData (index,
|
||||
referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Model)).
|
||||
toString().toUtf8().constData();
|
||||
|
||||
recordType =
|
||||
referenceables.getData (index,
|
||||
referenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType)).toInt();
|
||||
if (recordType == CSMWorld::UniversalId::Type_Light)
|
||||
{
|
||||
light = &dynamic_cast<const CSMWorld::Record<ESM::Light>& >(referenceables.getRecord(index)).get();
|
||||
if (model.empty())
|
||||
model = "marker_light.nif";
|
||||
}
|
||||
|
||||
if (recordType == CSMWorld::UniversalId::Type_CreatureLevelledList)
|
||||
{
|
||||
if (model.empty())
|
||||
model = "marker_creature.nif";
|
||||
}
|
||||
|
||||
if (recordType != CSMWorld::UniversalId::Type_Npc && model.empty())
|
||||
error = 2;
|
||||
}
|
||||
|
||||
mBaseNode->removeChildren(0, mBaseNode->getNumChildren());
|
||||
|
||||
if (error)
|
||||
if (index == -1)
|
||||
{
|
||||
mBaseNode->addChild(createErrorCube());
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
/// \todo check for Deleted state (error 1)
|
||||
|
||||
int recordType = referenceables.getData(index, TypeIndex).toInt();
|
||||
std::string model = referenceables.getData(index, ModelIndex).toString().toUtf8().constData();
|
||||
|
||||
if (recordType == CSMWorld::UniversalId::Type_Light)
|
||||
{
|
||||
try
|
||||
light = &dynamic_cast<const CSMWorld::Record<ESM::Light>& >(referenceables.getRecord(index)).get();
|
||||
if (model.empty())
|
||||
model = "marker_light.nif";
|
||||
}
|
||||
|
||||
if (recordType == CSMWorld::UniversalId::Type_CreatureLevelledList)
|
||||
{
|
||||
if (model.empty())
|
||||
model = "marker_creature.nif";
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (recordType == CSMWorld::UniversalId::Type_Npc || recordType == CSMWorld::UniversalId::Type_Creature)
|
||||
{
|
||||
if (recordType == CSMWorld::UniversalId::Type_Npc || recordType == CSMWorld::UniversalId::Type_Creature)
|
||||
{
|
||||
if (!mActor)
|
||||
mActor.reset(new Actor(mReferenceableId, recordType, mData));
|
||||
mActor->update();
|
||||
mBaseNode->addChild(mActor->getBaseNode());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string path = "meshes\\" + model;
|
||||
mResourceSystem->getSceneManager()->getInstance(path, mBaseNode);
|
||||
}
|
||||
if (!mActor) mActor.reset(new Actor(mReferenceableId, mData));
|
||||
mActor->update();
|
||||
mBaseNode->addChild(mActor->getBaseNode());
|
||||
}
|
||||
catch (std::exception& e)
|
||||
else
|
||||
{
|
||||
Log(Debug::Error) << e.what();
|
||||
mBaseNode->addChild(createErrorCube());
|
||||
std::string path = "meshes\\" + model;
|
||||
mResourceSystem->getSceneManager()->getInstance(path, mBaseNode);
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
// TODO: use error marker mesh
|
||||
Log(Debug::Error) << e.what();
|
||||
}
|
||||
|
||||
if (light)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue