mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-21 08:53:52 +00:00
Move data handling out of rendering code, equip armor/clothes
This commit is contained in:
parent
6b42f37918
commit
97ac0a92dd
7 changed files with 389 additions and 93 deletions
|
@ -19,6 +19,7 @@ opencs_hdrs_noqt (model/doc
|
||||||
|
|
||||||
opencs_units (model/world
|
opencs_units (model/world
|
||||||
idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel landtexturetableproxymodel
|
idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel landtexturetableproxymodel
|
||||||
|
actoradapter
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
242
apps/opencs/model/world/actoradapter.cpp
Normal file
242
apps/opencs/model/world/actoradapter.cpp
Normal file
|
@ -0,0 +1,242 @@
|
||||||
|
#include "actoradapter.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <components/esm/loadarmo.hpp>
|
||||||
|
#include <components/esm/loadclot.hpp>
|
||||||
|
#include <components/esm/loadnpc.hpp>
|
||||||
|
#include <components/esm/loadrace.hpp>
|
||||||
|
#include <components/esm/mappings.hpp>
|
||||||
|
|
||||||
|
#include "data.hpp"
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
ActorAdapter::ActorAdapter(CSMWorld::Data& data)
|
||||||
|
: mReferenceables(data.getReferenceables())
|
||||||
|
, mRaces(data.getRaces())
|
||||||
|
, mBodyParts(data.getBodyParts())
|
||||||
|
{
|
||||||
|
connect(data.getTableModel(UniversalId::Type_Referenceable), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
|
||||||
|
this, SLOT(handleReferenceableChanged(const QModelIndex&, const QModelIndex&)));
|
||||||
|
connect(data.getTableModel(UniversalId::Type_Race), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
|
||||||
|
this, SLOT(handleRaceChanged(const QModelIndex&, const QModelIndex&)));
|
||||||
|
connect(data.getTableModel(UniversalId::Type_BodyPart), SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
|
||||||
|
this, SLOT(handleBodyPartChanged(const QModelIndex&, const QModelIndex&)));
|
||||||
|
}
|
||||||
|
|
||||||
|
const ActorAdapter::ActorPartMap* ActorAdapter::getActorPartMap(const std::string& refId)
|
||||||
|
{
|
||||||
|
auto it = mActorPartMaps.find(refId);
|
||||||
|
if (it != mActorPartMaps.end())
|
||||||
|
{
|
||||||
|
return &it->second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
updateActor(refId);
|
||||||
|
it = mActorPartMaps.find(refId);
|
||||||
|
if (it != mActorPartMaps.end())
|
||||||
|
return &it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActorAdapter::handleReferenceableChanged(const QModelIndex& topLeft, const QModelIndex& botRight)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActorAdapter::handleRaceChanged(const QModelIndex& topLeft, const QModelIndex& botRight)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActorAdapter::handleBodyPartChanged(const QModelIndex& topLeft, const QModelIndex& botRight)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
ActorAdapter::RacePartMap& ActorAdapter::getOrCreateRacePartMap(const std::string& raceId, bool isFemale)
|
||||||
|
{
|
||||||
|
auto key = std::make_pair(raceId, isFemale);
|
||||||
|
auto it = mRacePartMaps.find(key);
|
||||||
|
if (it != mRacePartMaps.end())
|
||||||
|
{
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Create and find result
|
||||||
|
updateRaceParts(raceId);
|
||||||
|
return mRacePartMaps.find(key)->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActorAdapter::updateRaceParts(const std::string& raceId)
|
||||||
|
{
|
||||||
|
// Convenience function to determine if part is for 1st person view
|
||||||
|
auto is1stPersonPart = [](std::string name) {
|
||||||
|
return name.size() >= 4 && name.find(".1st", name.size() - 4) != std::string::npos;
|
||||||
|
};
|
||||||
|
|
||||||
|
RacePartMap maleMap, femaleMap;
|
||||||
|
for (int i = 0; i < mBodyParts.getSize(); ++i)
|
||||||
|
{
|
||||||
|
auto& record = mBodyParts.getRecord(i);
|
||||||
|
if (!record.isDeleted() && record.get().mRace == raceId && record.get().mData.mType == ESM::BodyPart::MT_Skin && !is1stPersonPart(record.get().mId))
|
||||||
|
{
|
||||||
|
auto& part = record.get();
|
||||||
|
auto type = (ESM::BodyPart::MeshPart) part.mData.mPart;
|
||||||
|
// Note: Prefer the first part encountered for duplicates. emplace() does not overwrite
|
||||||
|
if (part.mData.mFlags & ESM::BodyPart::BPF_Female)
|
||||||
|
femaleMap.emplace(type, part.mId);
|
||||||
|
else
|
||||||
|
maleMap.emplace(type, part.mId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mRacePartMaps[std::make_pair(raceId, true)] = femaleMap;
|
||||||
|
mRacePartMaps[std::make_pair(raceId, false)] = maleMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActorAdapter::updateActor(const std::string& refId)
|
||||||
|
{
|
||||||
|
int index = mReferenceables.searchId(refId);
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
int typeColumn = mReferenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType);
|
||||||
|
int recordType = mReferenceables.getData(index, typeColumn).toInt();
|
||||||
|
if (recordType == CSMWorld::UniversalId::Type_Creature)
|
||||||
|
updateCreature(refId);
|
||||||
|
else if (recordType == CSMWorld::UniversalId::Type_Npc)
|
||||||
|
updateNpc(refId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActorAdapter::updateNpc(const std::string& refId)
|
||||||
|
{
|
||||||
|
auto& record = mReferenceables.getRecord(refId);
|
||||||
|
if (record.isDeleted())
|
||||||
|
{
|
||||||
|
mActorPartMaps.erase(refId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& npc = dynamic_cast<const Record<ESM::NPC>&>(record).get();
|
||||||
|
auto& femaleRacePartMap = getOrCreateRacePartMap(npc.mRace, true);
|
||||||
|
auto& maleRacePartMap = getOrCreateRacePartMap(npc.mRace, false);
|
||||||
|
|
||||||
|
ActorPartMap npcMap;
|
||||||
|
|
||||||
|
// Look at the npc's inventory first
|
||||||
|
for (auto& item : npc.mInventory.mList)
|
||||||
|
{
|
||||||
|
if (item.mCount > 0)
|
||||||
|
{
|
||||||
|
std::string itemId = item.mItem.toString();
|
||||||
|
// Handle armor, weapons, and clothing
|
||||||
|
int index = mReferenceables.searchId(itemId);
|
||||||
|
if (index != -1 && !mReferenceables.getRecord(index).isDeleted())
|
||||||
|
{
|
||||||
|
auto& itemRecord = mReferenceables.getRecord(index);
|
||||||
|
|
||||||
|
int typeColumn = mReferenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType);
|
||||||
|
int recordType = mReferenceables.getData(index, typeColumn).toInt();
|
||||||
|
if (recordType == CSMWorld::UniversalId::Type_Armor)
|
||||||
|
{
|
||||||
|
auto& armor = dynamic_cast<const Record<ESM::Armor>&>(itemRecord).get();
|
||||||
|
for (auto& part : armor.mParts.mParts)
|
||||||
|
{
|
||||||
|
std::string bodyPartId;
|
||||||
|
if (!npc.isMale())
|
||||||
|
bodyPartId = part.mFemale;
|
||||||
|
if (bodyPartId.empty())
|
||||||
|
bodyPartId = part.mMale;
|
||||||
|
|
||||||
|
if (!bodyPartId.empty())
|
||||||
|
npcMap.emplace(static_cast<ESM::PartReferenceType>(part.mPart), bodyPartId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (recordType == CSMWorld::UniversalId::Type_Clothing)
|
||||||
|
{
|
||||||
|
auto& clothing = dynamic_cast<const Record<ESM::Clothing>&>(itemRecord).get();
|
||||||
|
for (auto& part : clothing.mParts.mParts)
|
||||||
|
{
|
||||||
|
std::string bodyPartId;
|
||||||
|
if (!npc.isMale())
|
||||||
|
bodyPartId = part.mFemale;
|
||||||
|
if (bodyPartId.empty())
|
||||||
|
bodyPartId = part.mMale;
|
||||||
|
|
||||||
|
if (!bodyPartId.empty())
|
||||||
|
npcMap.emplace(static_cast<ESM::PartReferenceType>(part.mPart), bodyPartId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (recordType == CSMWorld::UniversalId::Type_Weapon)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill in the rest with body parts
|
||||||
|
for (int i = 0; i < ESM::PRT_Count; ++i)
|
||||||
|
{
|
||||||
|
auto type = static_cast<ESM::PartReferenceType>(i);
|
||||||
|
if (npcMap.find(type) == npcMap.end())
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case ESM::PRT_Head:
|
||||||
|
npcMap.emplace(type, npc.mHead);
|
||||||
|
break;
|
||||||
|
case ESM::PRT_Hair:
|
||||||
|
npcMap.emplace(type, 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 associated
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
std::string bodyPartId;
|
||||||
|
// Check female map if applicable
|
||||||
|
if (!npc.isMale())
|
||||||
|
{
|
||||||
|
auto partIt = femaleRacePartMap.find(ESM::getMeshPart(type));
|
||||||
|
if (partIt != femaleRacePartMap.end())
|
||||||
|
bodyPartId = partIt->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check male map next
|
||||||
|
if (bodyPartId.empty() || npc.isMale())
|
||||||
|
{
|
||||||
|
auto partIt = maleRacePartMap.find(ESM::getMeshPart(type));
|
||||||
|
if (partIt != maleRacePartMap.end())
|
||||||
|
bodyPartId = partIt->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to map
|
||||||
|
if (!bodyPartId.empty())
|
||||||
|
{
|
||||||
|
npcMap.emplace(type, bodyPartId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mActorPartMaps[refId] = npcMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActorAdapter::updateCreature(const std::string& refId)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
80
apps/opencs/model/world/actoradapter.hpp
Normal file
80
apps/opencs/model/world/actoradapter.hpp
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
#ifndef CSM_WOLRD_ACTORADAPTER_H
|
||||||
|
#define CSM_WOLRD_ACTORADAPTER_H
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <components/esm/loadarmo.hpp>
|
||||||
|
#include <components/esm/loadbody.hpp>
|
||||||
|
|
||||||
|
#include "refidcollection.hpp"
|
||||||
|
#include "idcollection.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
struct Race;
|
||||||
|
enum PartReferenceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
class Data;
|
||||||
|
|
||||||
|
/// Quick and dirty hashing functor.
|
||||||
|
struct StringBoolPairHash
|
||||||
|
{
|
||||||
|
size_t operator()(const std::pair<std::string, bool>& value) const noexcept
|
||||||
|
{
|
||||||
|
auto stringHash = std::hash<std::string>();
|
||||||
|
return stringHash(value.first) + value.second;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ActorAdapter : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Maps body part type to 'body part' id
|
||||||
|
using ActorPartMap = std::unordered_map<ESM::PartReferenceType, std::string>;
|
||||||
|
// Maps mesh part type to 'body part' id
|
||||||
|
using RacePartMap = std::unordered_map<ESM::BodyPart::MeshPart, std::string>;
|
||||||
|
|
||||||
|
ActorAdapter(CSMWorld::Data& data);
|
||||||
|
|
||||||
|
const ActorPartMap* getActorPartMap(const std::string& refId);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void actorChanged(const std::string& refId);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
|
||||||
|
void handleReferenceableChanged(const QModelIndex&, const QModelIndex&);
|
||||||
|
void handleRaceChanged(const QModelIndex&, const QModelIndex&);
|
||||||
|
void handleBodyPartChanged(const QModelIndex&, const QModelIndex&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
RacePartMap& getOrCreateRacePartMap(const std::string& raceId, bool isFemale);
|
||||||
|
|
||||||
|
void updateRaceParts(const std::string& raceId);
|
||||||
|
void updateActor(const std::string& refId);
|
||||||
|
void updateNpc(const std::string& refId);
|
||||||
|
void updateCreature(const std::string& refId);
|
||||||
|
|
||||||
|
RefIdCollection& mReferenceables;
|
||||||
|
IdCollection<ESM::Race>& mRaces;
|
||||||
|
IdCollection<ESM::BodyPart>& mBodyParts;
|
||||||
|
|
||||||
|
// Key: referenceable id
|
||||||
|
std::unordered_map<std::string, ActorPartMap> mActorPartMaps;
|
||||||
|
// Key: race id, is female
|
||||||
|
std::unordered_map<std::pair<std::string, bool>, RacePartMap, StringBoolPairHash> mRacePartMaps;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -572,6 +572,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat
|
||||||
UniversalId::Type_Video);
|
UniversalId::Type_Video);
|
||||||
addModel (new IdTable (&mMetaData), UniversalId::Type_MetaData);
|
addModel (new IdTable (&mMetaData), UniversalId::Type_MetaData);
|
||||||
|
|
||||||
|
mActorAdapter.reset(new ActorAdapter(*this));
|
||||||
|
|
||||||
mRefLoadCache.clear(); // clear here rather than startLoading() and continueLoading() for multiple content files
|
mRefLoadCache.clear(); // clear here rather than startLoading() and continueLoading() for multiple content files
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -912,6 +914,16 @@ QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId&
|
||||||
return iter->second;
|
return iter->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CSMWorld::ActorAdapter* CSMWorld::Data::getActorAdapter() const
|
||||||
|
{
|
||||||
|
return mActorAdapter.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::ActorAdapter* CSMWorld::Data::getActorAdapter()
|
||||||
|
{
|
||||||
|
return mActorAdapter.get();
|
||||||
|
}
|
||||||
|
|
||||||
void CSMWorld::Data::merge()
|
void CSMWorld::Data::merge()
|
||||||
{
|
{
|
||||||
mGlobals.merge();
|
mGlobals.merge();
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
|
|
||||||
#include "../doc/stage.hpp"
|
#include "../doc/stage.hpp"
|
||||||
|
|
||||||
|
#include "actoradapter.hpp"
|
||||||
#include "idcollection.hpp"
|
#include "idcollection.hpp"
|
||||||
#include "nestedidcollection.hpp"
|
#include "nestedidcollection.hpp"
|
||||||
#include "universalid.hpp"
|
#include "universalid.hpp"
|
||||||
|
@ -73,6 +74,7 @@ namespace ESM
|
||||||
|
|
||||||
namespace CSMWorld
|
namespace CSMWorld
|
||||||
{
|
{
|
||||||
|
class ActorAdapter;
|
||||||
class ResourcesManager;
|
class ResourcesManager;
|
||||||
class Resources;
|
class Resources;
|
||||||
|
|
||||||
|
@ -110,6 +112,7 @@ namespace CSMWorld
|
||||||
RefCollection mRefs;
|
RefCollection mRefs;
|
||||||
IdCollection<ESM::Filter> mFilters;
|
IdCollection<ESM::Filter> mFilters;
|
||||||
Collection<MetaData> mMetaData;
|
Collection<MetaData> mMetaData;
|
||||||
|
std::unique_ptr<ActorAdapter> mActorAdapter;
|
||||||
const Fallback::Map* mFallbackMap;
|
const Fallback::Map* mFallbackMap;
|
||||||
std::vector<QAbstractItemModel *> mModels;
|
std::vector<QAbstractItemModel *> mModels;
|
||||||
std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex;
|
std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex;
|
||||||
|
@ -287,6 +290,10 @@ namespace CSMWorld
|
||||||
/// \note The returned table may either be the model for the ID itself or the model that
|
/// \note The returned table may either be the model for the ID itself or the model that
|
||||||
/// contains the record specified by the ID.
|
/// contains the record specified by the ID.
|
||||||
|
|
||||||
|
const ActorAdapter* getActorAdapter() const;
|
||||||
|
|
||||||
|
ActorAdapter* getActorAdapter();
|
||||||
|
|
||||||
void merge();
|
void merge();
|
||||||
///< Merge modified into base.
|
///< Merge modified into base.
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
#include <components/sceneutil/actorutil.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 "../../model/world/data.hpp"
|
#include "../../model/world/data.hpp"
|
||||||
|
|
||||||
|
@ -24,8 +23,8 @@ namespace CSVRender
|
||||||
: mId(id)
|
: mId(id)
|
||||||
, mType(type)
|
, mType(type)
|
||||||
, mData(data)
|
, mData(data)
|
||||||
, mSkeleton(nullptr)
|
|
||||||
, mBaseNode(new osg::Group())
|
, mBaseNode(new osg::Group())
|
||||||
|
, mSkeleton(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +56,7 @@ namespace CSVRender
|
||||||
|
|
||||||
auto& creature = dynamic_cast<const CSMWorld::Record<ESM::Creature>& >(referenceables.getRecord(mId)).get();
|
auto& creature = dynamic_cast<const CSMWorld::Record<ESM::Creature>& >(referenceables.getRecord(mId)).get();
|
||||||
|
|
||||||
|
// Load skeleton with meshes
|
||||||
std::string skeletonModel = MeshPrefix + creature.mModel;
|
std::string skeletonModel = MeshPrefix + creature.mModel;
|
||||||
skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS());
|
skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS());
|
||||||
loadSkeleton(skeletonModel);
|
loadSkeleton(skeletonModel);
|
||||||
|
@ -65,6 +65,9 @@ namespace CSVRender
|
||||||
mSkeleton->accept(removeTriBipVisitor);
|
mSkeleton->accept(removeTriBipVisitor);
|
||||||
removeTriBipVisitor.remove();
|
removeTriBipVisitor.remove();
|
||||||
|
|
||||||
|
// Attach weapons
|
||||||
|
loadBodyParts(creature.mId);
|
||||||
|
|
||||||
// Post setup
|
// Post setup
|
||||||
mSkeleton->markDirty();
|
mSkeleton->markDirty();
|
||||||
mSkeleton->setActive(SceneUtil::Skeleton::Active);
|
mSkeleton->setActive(SceneUtil::Skeleton::Active);
|
||||||
|
@ -72,12 +75,8 @@ namespace CSVRender
|
||||||
|
|
||||||
void Actor::updateNpc()
|
void Actor::updateNpc()
|
||||||
{
|
{
|
||||||
const unsigned int FemaleFlag = ESM::BodyPart::BPF_Female;
|
|
||||||
|
|
||||||
auto& bodyParts = mData.getBodyParts();
|
|
||||||
auto& races = mData.getRaces();
|
auto& races = mData.getRaces();
|
||||||
auto& referenceables = mData.getReferenceables();
|
auto& referenceables = mData.getReferenceables();
|
||||||
auto sceneMgr = mData.getResourceSystem()->getSceneManager();
|
|
||||||
|
|
||||||
auto& npc = dynamic_cast<const CSMWorld::Record<ESM::NPC>& >(referenceables.getRecord(mId)).get();
|
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();
|
auto& race = dynamic_cast<const CSMWorld::Record<ESM::Race>& >(races.getRecord(npc.mRace)).get();
|
||||||
|
@ -97,91 +96,8 @@ namespace CSVRender
|
||||||
mSkeleton->accept(cleanVisitor);
|
mSkeleton->accept(cleanVisitor);
|
||||||
cleanVisitor.remove();
|
cleanVisitor.remove();
|
||||||
|
|
||||||
// Map bone names to bones
|
// Attach parts to skeleton
|
||||||
SceneUtil::NodeMapVisitor::NodeMap nodeMap;
|
loadBodyParts(npc.mId);
|
||||||
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& 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);
|
|
||||||
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
|
// Post setup
|
||||||
mSkeleton->markDirty();
|
mSkeleton->markDirty();
|
||||||
|
@ -200,6 +116,37 @@ namespace CSVRender
|
||||||
mSkeleton->addChild(temp);
|
mSkeleton->addChild(temp);
|
||||||
}
|
}
|
||||||
mBaseNode->addChild(mSkeleton);
|
mBaseNode->addChild(mSkeleton);
|
||||||
|
|
||||||
|
// Map bone names to bones
|
||||||
|
mNodeMap.clear();
|
||||||
|
SceneUtil::NodeMapVisitor nmVisitor(mNodeMap);
|
||||||
|
mSkeleton->accept(nmVisitor);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Actor::loadBodyParts(const std::string& actorId)
|
||||||
|
{
|
||||||
|
auto actorAdapter = mData.getActorAdapter();
|
||||||
|
auto partMap = actorAdapter->getActorPartMap(actorId);
|
||||||
|
if (partMap)
|
||||||
|
{
|
||||||
|
for (auto& pair : *partMap)
|
||||||
|
attachBodyPart(pair.first, getBodyPartMesh(pair.second));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Actor::attachBodyPart(ESM::PartReferenceType type, const std::string& mesh)
|
||||||
|
{
|
||||||
|
auto sceneMgr = mData.getResourceSystem()->getSceneManager();
|
||||||
|
|
||||||
|
// Attach to skeleton
|
||||||
|
std::string boneName = ESM::getBoneName(type);
|
||||||
|
auto node = mNodeMap.find(boneName);
|
||||||
|
if (!mesh.empty() && node != mNodeMap.end())
|
||||||
|
{
|
||||||
|
auto instance = sceneMgr->getInstance(mesh);
|
||||||
|
SceneUtil::attach(instance, mSkeleton, boneName, node->second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Actor::getBodyPartMesh(const std::string& bodyPartId)
|
std::string Actor::getBodyPartMesh(const std::string& bodyPartId)
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
|
|
||||||
#include <osg/ref_ptr>
|
#include <osg/ref_ptr>
|
||||||
|
|
||||||
|
#include <components/esm/loadarmo.hpp>
|
||||||
|
#include <components/sceneutil/visitor.hpp>
|
||||||
|
|
||||||
namespace osg
|
namespace osg
|
||||||
{
|
{
|
||||||
class Group;
|
class Group;
|
||||||
|
@ -39,10 +42,13 @@ namespace CSVRender
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loadSkeleton(const std::string& model);
|
|
||||||
void updateCreature();
|
void updateCreature();
|
||||||
void updateNpc();
|
void updateNpc();
|
||||||
|
|
||||||
|
void loadSkeleton(const std::string& model);
|
||||||
|
void loadBodyParts(const std::string& actorId);
|
||||||
|
void attachBodyPart(ESM::PartReferenceType, const std::string& mesh);
|
||||||
|
|
||||||
std::string getBodyPartMesh(const std::string& bodyPartId);
|
std::string getBodyPartMesh(const std::string& bodyPartId);
|
||||||
|
|
||||||
static const std::string MeshPrefix;
|
static const std::string MeshPrefix;
|
||||||
|
@ -51,8 +57,9 @@ namespace CSVRender
|
||||||
int mType;
|
int mType;
|
||||||
CSMWorld::Data& mData;
|
CSMWorld::Data& mData;
|
||||||
|
|
||||||
SceneUtil::Skeleton* mSkeleton;
|
|
||||||
osg::ref_ptr<osg::Group> mBaseNode;
|
osg::ref_ptr<osg::Group> mBaseNode;
|
||||||
|
SceneUtil::Skeleton* mSkeleton;
|
||||||
|
SceneUtil::NodeMapVisitor::NodeMap mNodeMap;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue