Merged pull request #1944
commit
38758a9555
@ -0,0 +1,686 @@
|
||||
#include "actoradapter.hpp"
|
||||
|
||||
#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 <components/sceneutil/actorutil.hpp>
|
||||
|
||||
#include "data.hpp"
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
const std::string& ActorAdapter::RaceData::getId() const
|
||||
{
|
||||
return mId;
|
||||
}
|
||||
|
||||
bool ActorAdapter::RaceData::isBeast() const
|
||||
{
|
||||
return mIsBeast;
|
||||
}
|
||||
|
||||
bool ActorAdapter::RaceData::handlesPart(ESM::PartReferenceType type) const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ESM::PRT_Skirt:
|
||||
case ESM::PRT_Shield:
|
||||
case ESM::PRT_RPauldron:
|
||||
case ESM::PRT_LPauldron:
|
||||
case ESM::PRT_Weapon:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& ActorAdapter::RaceData::getFemalePart(ESM::PartReferenceType index) const
|
||||
{
|
||||
return mFemaleParts[ESM::getMeshPart(index)];
|
||||
}
|
||||
|
||||
const std::string& ActorAdapter::RaceData::getMalePart(ESM::PartReferenceType index) const
|
||||
{
|
||||
return mMaleParts[ESM::getMeshPart(index)];
|
||||
}
|
||||
|
||||
bool ActorAdapter::RaceData::hasDependency(const std::string& id) const
|
||||
{
|
||||
return mDependencies.find(id) != mDependencies.end();
|
||||
}
|
||||
|
||||
void ActorAdapter::RaceData::setFemalePart(ESM::BodyPart::MeshPart index, const std::string& partId)
|
||||
{
|
||||
mFemaleParts[index] = partId;
|
||||
addOtherDependency(partId);
|
||||
}
|
||||
|
||||
void ActorAdapter::RaceData::setMalePart(ESM::BodyPart::MeshPart index, const std::string& partId)
|
||||
{
|
||||
mMaleParts[index] = partId;
|
||||
addOtherDependency(partId);
|
||||
}
|
||||
|
||||
void ActorAdapter::RaceData::addOtherDependency(const std::string& id)
|
||||
{
|
||||
if (!id.empty()) mDependencies.emplace(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)
|
||||
str.clear();
|
||||
mDependencies.clear();
|
||||
|
||||
// Mark self as a dependency
|
||||
addOtherDependency(id);
|
||||
}
|
||||
|
||||
|
||||
const std::string& ActorAdapter::ActorData::getId() const
|
||||
{
|
||||
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
|
||||
{
|
||||
auto it = mParts.find(index);
|
||||
if (it == mParts.end() && mRaceData && mRaceData->handlesPart(index))
|
||||
{
|
||||
if (mFemale)
|
||||
{
|
||||
// Note: we should use male parts for females as fallback
|
||||
const std::string femalePart = mRaceData->getFemalePart(index);
|
||||
if (!femalePart.empty())
|
||||
return femalePart;
|
||||
}
|
||||
|
||||
return mRaceData->getMalePart(index);
|
||||
}
|
||||
|
||||
const std::string& partName = it->second.first;
|
||||
return partName;
|
||||
}
|
||||
|
||||
bool ActorAdapter::ActorData::hasDependency(const std::string& id) const
|
||||
{
|
||||
return mDependencies.find(id) != mDependencies.end();
|
||||
}
|
||||
|
||||
void ActorAdapter::ActorData::setPart(ESM::PartReferenceType index, const std::string& partId, int priority)
|
||||
{
|
||||
auto it = mParts.find(index);
|
||||
if (it != mParts.end())
|
||||
{
|
||||
if (it->second.second >= priority)
|
||||
return;
|
||||
}
|
||||
|
||||
mParts[index] = std::make_pair(partId, priority);
|
||||
addOtherDependency(partId);
|
||||
}
|
||||
|
||||
void ActorAdapter::ActorData::addOtherDependency(const std::string& id)
|
||||
{
|
||||
if (!id.empty()) mDependencies.emplace(id);
|
||||
}
|
||||
|
||||
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;
|
||||
mParts.clear();
|
||||
mDependencies.clear();
|
||||
|
||||
// Mark self and race as a dependency
|
||||
addOtherDependency(id);
|
||||
if (raceData) addOtherDependency(raceData->getId());
|
||||
}
|
||||
|
||||
|
||||
ActorAdapter::ActorAdapter(Data& data)
|
||||
: mReferenceables(data.getReferenceables())
|
||||
, mRaces(data.getRaces())
|
||||
, mBodyParts(data.getBodyParts())
|
||||
{
|
||||
// Setup qt slots and signals
|
||||
QAbstractItemModel* refModel = data.getTableModel(UniversalId::Type_Referenceable);
|
||||
connect(refModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)),
|
||||
this, SLOT(handleReferenceablesInserted(const QModelIndex&, int, int)));
|
||||
connect(refModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
|
||||
this, SLOT(handleReferenceableChanged(const QModelIndex&, const QModelIndex&)));
|
||||
connect(refModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)),
|
||||
this, SLOT(handleReferenceablesAboutToBeRemoved(const QModelIndex&, int, int)));
|
||||
|
||||
QAbstractItemModel* raceModel = data.getTableModel(UniversalId::Type_Race);
|
||||
connect(raceModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)),
|
||||
this, SLOT(handleRacesAboutToBeRemoved(const QModelIndex&, int, int)));
|
||||
connect(raceModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
|
||||
this, SLOT(handleRaceChanged(const QModelIndex&, const QModelIndex&)));
|
||||
connect(raceModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)),
|
||||
this, SLOT(handleRacesAboutToBeRemoved(const QModelIndex&, int, int)));
|
||||
|
||||
QAbstractItemModel* partModel = data.getTableModel(UniversalId::Type_BodyPart);
|
||||
connect(partModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)),
|
||||
this, SLOT(handleBodyPartsInserted(const QModelIndex&, int, int)));
|
||||
connect(partModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
|
||||
this, SLOT(handleBodyPartChanged(const QModelIndex&, const QModelIndex&)));
|
||||
connect(partModel, SIGNAL(rowsAboutToBeRemoved(const QModelIndex&, int, int)),
|
||||
this, SLOT(handleBodyPartsAboutToBeRemoved(const QModelIndex&, int, int)));
|
||||
}
|
||||
|
||||
ActorAdapter::ActorDataPtr ActorAdapter::getActorData(const std::string& id)
|
||||
{
|
||||
// Return cached actor data if it exists
|
||||
ActorDataPtr data = mCachedActors.get(id);
|
||||
if (data)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
// Create the actor data
|
||||
data.reset(new ActorData());
|
||||
setupActor(id, data);
|
||||
mCachedActors.insert(id, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
void ActorAdapter::handleReferenceablesInserted(const QModelIndex& parent, int start, int end)
|
||||
{
|
||||
// Only rows added at the top level are pertinent. Others are caught by dataChanged handler.
|
||||
if (!parent.isValid())
|
||||
{
|
||||
for (int row = start; row <= end; ++row)
|
||||
{
|
||||
std::string refId = mReferenceables.getId(row);
|
||||
markDirtyDependency(refId);
|
||||
}
|
||||
}
|
||||
|
||||
// Update affected
|
||||
updateDirty();
|
||||
}
|
||||
|
||||
void ActorAdapter::handleReferenceableChanged(const QModelIndex& topLeft, const QModelIndex& botRight)
|
||||
{
|
||||
int start = getHighestIndex(topLeft).row();
|
||||
int end = getHighestIndex(botRight).row();
|
||||
|
||||
// A change to record status (ex. Deleted) returns an invalid botRight
|
||||
if (end == -1)
|
||||
end = start;
|
||||
|
||||
// Handle each record
|
||||
for (int row = start; row <= end; ++row)
|
||||
{
|
||||
std::string refId = mReferenceables.getId(row);
|
||||
markDirtyDependency(refId);
|
||||
}
|
||||
|
||||
// Update affected
|
||||
updateDirty();
|
||||
}
|
||||
|
||||
void ActorAdapter::handleReferenceablesAboutToBeRemoved(const QModelIndex& parent, int start, int end)
|
||||
{
|
||||
// Only rows at the top are pertinent.
|
||||
if (!parent.isValid())
|
||||
{
|
||||
for (int row = start; row <= end; ++row)
|
||||
{
|
||||
std::string refId = mReferenceables.getId(row);
|
||||
markDirtyDependency(refId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActorAdapter::handleReferenceablesRemoved(const QModelIndex& parent, int start, int end)
|
||||
{
|
||||
// Changes specified in handleReferenceablesAboutToBeRemoved
|
||||
updateDirty();
|
||||
}
|
||||
|
||||
void ActorAdapter::handleRacesInserted(const QModelIndex& parent, int start, int end)
|
||||
{
|
||||
// Only rows added at the top are pertinent.
|
||||
if (!parent.isValid())
|
||||
{
|
||||
for (int row = start; row <= end; ++row)
|
||||
{
|
||||
std::string raceId = mReferenceables.getId(row);
|
||||
markDirtyDependency(raceId);
|
||||
}
|
||||
}
|
||||
|
||||
// Update affected
|
||||
updateDirty();
|
||||
}
|
||||
|
||||
void ActorAdapter::handleRaceChanged(const QModelIndex& topLeft, const QModelIndex& botRight)
|
||||
{
|
||||
int start = getHighestIndex(topLeft).row();
|
||||
int end = getHighestIndex(botRight).row();
|
||||
|
||||
// A change to record status (ex. Deleted) returns an invalid botRight
|
||||
if (end == -1)
|
||||
end = start;
|
||||
|
||||
for (int row = start; row <= end; ++row)
|
||||
{
|
||||
std::string raceId = mRaces.getId(row);
|
||||
markDirtyDependency(raceId);
|
||||
}
|
||||
|
||||
// Update affected
|
||||
updateDirty();
|
||||
}
|
||||
|
||||
void ActorAdapter::handleRacesAboutToBeRemoved(const QModelIndex& parent, int start, int end)
|
||||
{
|
||||
// Only changes at the top are pertinent.
|
||||
if (!parent.isValid())
|
||||
{
|
||||
for (int row = start; row <= end; ++row)
|
||||
{
|
||||
std::string raceId = mRaces.getId(row);
|
||||
markDirtyDependency(raceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActorAdapter::handleRacesRemoved(const QModelIndex& parent, int start, int end)
|
||||
{
|
||||
// Changes specified in handleRacesAboutToBeRemoved
|
||||
updateDirty();
|
||||
}
|
||||
|
||||
void ActorAdapter::handleBodyPartsInserted(const QModelIndex& parent, int start, int end)
|
||||
{
|
||||
// Only rows added at the top are pertinent.
|
||||
if (!parent.isValid())
|
||||
{
|
||||
for (int row = start; row <= end; ++row)
|
||||
{
|
||||
// Race specified by part may need update
|
||||
auto& record = mBodyParts.getRecord(row);
|
||||
if (!record.isDeleted())
|
||||
{
|
||||
markDirtyDependency(record.get().mRace);
|
||||
}
|
||||
|
||||
std::string partId = mBodyParts.getId(row);
|
||||
markDirtyDependency(partId);
|
||||
}
|
||||
}
|
||||
|
||||
// Update affected
|
||||
updateDirty();
|
||||
}
|
||||
|
||||
void ActorAdapter::handleBodyPartChanged(const QModelIndex& topLeft, const QModelIndex& botRight)
|
||||
{
|
||||
int start = getHighestIndex(topLeft).row();
|
||||
int end = getHighestIndex(botRight).row();
|
||||
|
||||
// A change to record status (ex. Deleted) returns an invalid botRight
|
||||
if (end == -1)
|
||||
end = start;
|
||||
|
||||
for (int row = start; row <= end; ++row)
|
||||
{
|
||||
// Race specified by part may need update
|
||||
auto& record = mBodyParts.getRecord(row);
|
||||
if (!record.isDeleted())
|
||||
{
|
||||
markDirtyDependency(record.get().mRace);
|
||||
}
|
||||
|
||||
// Update entries with a tracked dependency
|
||||
std::string partId = mBodyParts.getId(row);
|
||||
markDirtyDependency(partId);
|
||||
}
|
||||
|
||||
// Update affected
|
||||
updateDirty();
|
||||
}
|
||||
|
||||
void ActorAdapter::handleBodyPartsAboutToBeRemoved(const QModelIndex& parent, int start, int end)
|
||||
{
|
||||
// Only changes at the top are pertinent.
|
||||
if (!parent.isValid())
|
||||
{
|
||||
for (int row = start; row <= end; ++row)
|
||||
{
|
||||
std::string partId = mBodyParts.getId(row);
|
||||
markDirtyDependency(partId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActorAdapter::handleBodyPartsRemoved(const QModelIndex& parent, int start, int end)
|
||||
{
|
||||
// Changes specified in handleBodyPartsAboutToBeRemoved
|
||||
updateDirty();
|
||||
}
|
||||
|
||||
QModelIndex ActorAdapter::getHighestIndex(QModelIndex index) const
|
||||
{
|
||||
while (index.parent().isValid())
|
||||
index = index.parent();
|
||||
return index;
|
||||
}
|
||||
|
||||
bool ActorAdapter::is1stPersonPart(const std::string& name) const
|
||||
{
|
||||
return name.size() >= 4 && name.find(".1st", name.size() - 4) != std::string::npos;
|
||||
}
|
||||
|
||||
ActorAdapter::RaceDataPtr ActorAdapter::getRaceData(const std::string& id)
|
||||
{
|
||||
// Return cached race data if it exists
|
||||
RaceDataPtr data = mCachedRaces.get(id);
|
||||
if (data) return data;
|
||||
|
||||
// Create the race data
|
||||
data.reset(new RaceData());
|
||||
setupRace(id, data);
|
||||
mCachedRaces.insert(id, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
void ActorAdapter::setupActor(const std::string& id, ActorDataPtr data)
|
||||
{
|
||||
int index = mReferenceables.searchId(id);
|
||||
if (index == -1)
|
||||
{
|
||||
// Record does not exist
|
||||
data->reset_data(id);
|
||||
emit actorChanged(id);
|
||||
return;
|
||||
}
|
||||
|
||||
auto& record = mReferenceables.getRecord(index);
|
||||
if (record.isDeleted())
|
||||
{
|
||||
// Record is deleted and therefore not accessible
|
||||
data->reset_data(id);
|
||||
emit actorChanged(id);
|
||||
return;
|
||||
}
|
||||
|
||||
const int TypeColumn = mReferenceables.findColumnIndex(Columns::ColumnId_RecordType);
|
||||
int type = mReferenceables.getData(index, TypeColumn).toInt();
|
||||
if (type == UniversalId::Type_Creature)
|
||||
{
|
||||
// Valid creature record
|
||||
setupCreature(id, data);
|
||||
emit actorChanged(id);
|
||||
}
|
||||
else if (type == UniversalId::Type_Npc)
|
||||
{
|
||||
// Valid npc record
|
||||
setupNpc(id, data);
|
||||
emit actorChanged(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wrong record type
|
||||
data->reset_data(id);
|
||||
emit actorChanged(id);
|
||||
}
|
||||
}
|
||||
|
||||
void ActorAdapter::setupRace(const std::string& id, RaceDataPtr data)
|
||||
{
|
||||
int index = mRaces.searchId(id);
|
||||
if (index == -1)
|
||||
{
|
||||
// Record does not exist
|
||||
data->reset_data(id);
|
||||
return;
|
||||
}
|
||||
|
||||
auto& raceRecord = mRaces.getRecord(index);
|
||||
if (raceRecord.isDeleted())
|
||||
{
|
||||
// Record is deleted, so not accessible
|
||||
data->reset_data(id);
|
||||
return;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
std::string partId = mBodyParts.getId(i);
|
||||
auto& partRecord = mBodyParts.getRecord(i);
|
||||
|
||||
if (partRecord.isDeleted())
|
||||
{
|
||||
// Record is deleted, so not accessible.
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& part = partRecord.get();
|
||||
if (part.mRace == id && part.mData.mType == ESM::BodyPart::MT_Skin && !is1stPersonPart(part.mId))
|
||||
{
|
||||
auto type = (ESM::BodyPart::MeshPart) part.mData.mPart;
|
||||
bool female = part.mData.mFlags & ESM::BodyPart::BPF_Female;
|
||||
if (female) data->setFemalePart(type, part.mId);
|
||||
else data->setMalePart(type, part.mId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ActorAdapter::setupNpc(const std::string& id, ActorDataPtr data)
|
||||
{
|
||||
// Common setup, record is known to exist and is not deleted
|
||||
int index = mReferenceables.searchId(id);
|
||||
auto& npc = dynamic_cast<const Record<ESM::NPC>&>(mReferenceables.getRecord(index)).get();
|
||||
|
||||
RaceDataPtr raceData = getRaceData(npc.mRace);
|
||||
data->reset_data(id, "", false, !npc.isMale(), raceData);
|
||||
|
||||
// Add inventory items
|
||||
for (auto& item : npc.mInventory.mList)
|
||||
{
|
||||
if (item.mCount <= 0) continue;
|
||||
std::string itemId = item.mItem.toString();
|
||||
addNpcItem(itemId, data);
|
||||
}
|
||||
|
||||
// Add head and hair
|
||||
data->setPart(ESM::PRT_Head, npc.mHead, 0);
|
||||
data->setPart(ESM::PRT_Hair, npc.mHair, 0);
|
||||
}
|
||||
|
||||
void ActorAdapter::addNpcItem(const std::string& itemId, ActorDataPtr data)
|
||||
{
|
||||
int index = mReferenceables.searchId(itemId);
|
||||
if (index == -1)
|
||||
{
|
||||
// Item does not exist yet
|
||||
data->addOtherDependency(itemId);
|
||||
return;
|
||||
}
|
||||
|
||||
auto& record = mReferenceables.getRecord(index);
|
||||
if (record.isDeleted())
|
||||
{
|
||||
// Item cannot be accessed yet
|
||||
data->addOtherDependency(itemId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Convenience function to add a parts list to actor data
|
||||
auto addParts = [&](const std::vector<ESM::PartReference>& list, int priority) {
|
||||
for (auto& part : list)
|
||||
{
|
||||
std::string partId;
|
||||
auto partType = (ESM::PartReferenceType) part.mPart;
|
||||
|
||||
if (data->isFemale())
|
||||
partId = part.mFemale;
|
||||
if (partId.empty())
|
||||
partId = part.mMale;
|
||||
|
||||
data->setPart(partType, partId, priority);
|
||||
}
|
||||
};
|
||||
|
||||
int TypeColumn = mReferenceables.findColumnIndex(Columns::ColumnId_RecordType);
|
||||
int type = mReferenceables.getData(index, TypeColumn).toInt();
|
||||
if (type == UniversalId::Type_Armor)
|
||||
{
|
||||
auto& armor = dynamic_cast<const Record<ESM::Armor>&>(record).get();
|
||||
addParts(armor.mParts.mParts, 1);
|
||||
|
||||
// Changing parts could affect what is picked for rendering
|
||||
data->addOtherDependency(itemId);
|
||||
}
|
||||
else if (type == UniversalId::Type_Clothing)
|
||||
{
|
||||
int priority = 0;
|
||||
// TODO: reserve bodyparts for robes and skirts
|
||||
auto& clothing = dynamic_cast<const Record<ESM::Clothing>&>(record).get();
|
||||
|
||||
if (clothing.mData.mType == ESM::Clothing::Robe)
|
||||
{
|
||||
auto reservedList = std::vector<ESM::PartReference>();
|
||||
|
||||
ESM::PartReference pr;
|
||||
pr.mMale = "";
|
||||
pr.mFemale = "";
|
||||
|
||||
ESM::PartReferenceType parts[] = {
|
||||
ESM::PRT_Groin, ESM::PRT_Skirt, ESM::PRT_RLeg, ESM::PRT_LLeg,
|
||||
ESM::PRT_RUpperarm, ESM::PRT_LUpperarm, ESM::PRT_RKnee, ESM::PRT_LKnee,
|
||||
ESM::PRT_RForearm, ESM::PRT_LForearm
|
||||
};
|
||||
size_t parts_size = sizeof(parts)/sizeof(parts[0]);
|
||||
for(size_t p = 0;p < parts_size;++p)
|
||||
{
|
||||
pr.mPart = parts[p];
|
||||
reservedList.push_back(pr);
|
||||
}
|
||||
|
||||
priority = parts_size;
|
||||
addParts(reservedList, priority);
|
||||
}
|
||||
else if (clothing.mData.mType == ESM::Clothing::Skirt)
|
||||
{
|
||||
auto reservedList = std::vector<ESM::PartReference>();
|
||||
|
||||
ESM::PartReference pr;
|
||||
pr.mMale = "";
|
||||
pr.mFemale = "";
|
||||
|
||||
ESM::PartReferenceType parts[] = {
|
||||
ESM::PRT_Groin, ESM::PRT_RLeg, ESM::PRT_LLeg
|
||||
};
|
||||
size_t parts_size = sizeof(parts)/sizeof(parts[0]);
|
||||
for(size_t p = 0;p < parts_size;++p)
|
||||
{
|
||||
pr.mPart = parts[p];
|
||||
reservedList.push_back(pr);
|
||||
}
|
||||
|
||||
priority = parts_size;
|
||||
addParts(reservedList, priority);
|
||||
}
|
||||
|
||||
addParts(clothing.mParts.mParts, priority);
|
||||
|
||||
// Changing parts could affect what is picked for rendering
|
||||
data->addOtherDependency(itemId);
|
||||
}
|
||||
}
|
||||
|
||||
void ActorAdapter::setupCreature(const std::string& id, ActorDataPtr data)
|
||||
{
|
||||
// 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();
|
||||
|
||||
data->reset_data(id, creature.mModel, true);
|
||||
}
|
||||
|
||||
void ActorAdapter::markDirtyDependency(const std::string& dep)
|
||||
{
|
||||
for (auto raceIt : mCachedRaces)
|
||||
{
|
||||
if (raceIt->hasDependency(dep))
|
||||
mDirtyRaces.emplace(raceIt->getId());
|
||||
}
|
||||
for (auto actorIt : mCachedActors)
|
||||
{
|
||||
if (actorIt->hasDependency(dep))
|
||||
mDirtyActors.emplace(actorIt->getId());
|
||||
}
|
||||
}
|
||||
|
||||
void ActorAdapter::updateDirty()
|
||||
{
|
||||
// Handle races before actors, since actors are dependent on race
|
||||
for (auto& race : mDirtyRaces)
|
||||
{
|
||||
RaceDataPtr data = mCachedRaces.get(race);
|
||||
if (data)
|
||||
{
|
||||
setupRace(race, data);
|
||||
// Race was changed. Need to mark actor dependencies as dirty.
|
||||
// Cannot use markDirtyDependency because that would invalidate
|
||||
// the current iterator.
|
||||
for (auto actorIt : mCachedActors)
|
||||
{
|
||||
if (actorIt->hasDependency(race))
|
||||
mDirtyActors.emplace(actorIt->getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
mDirtyRaces.clear();
|
||||
|
||||
for (auto& actor : mDirtyActors)
|
||||
{
|
||||
ActorDataPtr data = mCachedActors.get(actor);
|
||||
if (data)
|
||||
{
|
||||
setupActor(actor, data);
|
||||
}
|
||||
}
|
||||
mDirtyActors.clear();
|
||||
}
|
||||
}
|
@ -0,0 +1,174 @@
|
||||
#ifndef CSM_WOLRD_ACTORADAPTER_H
|
||||
#define CSM_WOLRD_ACTORADAPTER_H
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <QObject>
|
||||
#include <QModelIndex>
|
||||
|
||||
#include <components/esm/loadarmo.hpp>
|
||||
#include <components/esm/loadbody.hpp>
|
||||
#include <components/misc/weakcache.hpp>
|
||||
|
||||
#include "refidcollection.hpp"
|
||||
#include "idcollection.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
struct Race;
|
||||
}
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class Data;
|
||||
|
||||
/// Adapts multiple collections to provide the data needed to render
|
||||
/// an npc or creature.
|
||||
class ActorAdapter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
/// A list indexed by ESM::PartReferenceType
|
||||
using ActorPartList = std::map<ESM::PartReferenceType, std::pair<std::string, int>>;
|
||||
/// A list indexed by ESM::BodyPart::MeshPart
|
||||
using RacePartList = std::array<std::string, ESM::BodyPart::MP_Count>;
|
||||
/// Tracks unique strings
|
||||
using StringSet = std::unordered_set<std::string>;
|
||||
|
||||
|
||||
/// Contains base race data shared between actors
|
||||
class RaceData
|
||||
{
|
||||
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
|
||||
const std::string& getFemalePart(ESM::PartReferenceType index) const;
|
||||
/// Retrieves the associated body part
|
||||
const std::string& getMalePart(ESM::PartReferenceType index) const;
|
||||
/// Checks if the race has a data dependency
|
||||
bool hasDependency(const std::string& id) const;
|
||||
|
||||
/// Sets the associated part if it's empty and marks a dependency
|
||||
void setFemalePart(ESM::BodyPart::MeshPart partIndex, const std::string& partId);
|
||||
/// Sets the associated part if it's empty and marks a dependency
|
||||
void setMalePart(ESM::BodyPart::MeshPart partIndex, const std::string& partId);
|
||||
/// Marks an additional dependency
|
||||
void addOtherDependency(const std::string& id);
|
||||
/// Clears parts and dependencies
|
||||
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;
|
||||
};
|
||||
using RaceDataPtr = std::shared_ptr<RaceData>;
|
||||
|
||||
/// Contains all the data needed to render an actor. Tracks dependencies
|
||||
/// so that pertinent data changes can be checked.
|
||||
class ActorData
|
||||
{
|
||||
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
|
||||
bool hasDependency(const std::string& id) const;
|
||||
|
||||
/// Sets the actor part used and marks a dependency
|
||||
void setPart(ESM::PartReferenceType partIndex, const std::string& partId, int priority);
|
||||
/// Marks an additional dependency for the actor
|
||||
void addOtherDependency(const std::string& id);
|
||||
/// Clears race, parts, and dependencies
|
||||
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;
|
||||
};
|
||||
using ActorDataPtr = std::shared_ptr<ActorData>;
|
||||
|
||||
|
||||
ActorAdapter(Data& data);
|
||||
|
||||
/// Obtains the shared data for a given actor
|
||||
ActorDataPtr getActorData(const std::string& refId);
|
||||
|
||||
signals:
|
||||
|
||||
void actorChanged(const std::string& refId);
|
||||
|
||||
public slots:
|
||||
|
||||
void handleReferenceablesInserted(const QModelIndex&, int, int);
|
||||
void handleReferenceableChanged(const QModelIndex&, const QModelIndex&);
|
||||
void handleReferenceablesAboutToBeRemoved(const QModelIndex&, int, int);
|
||||
void handleReferenceablesRemoved(const QModelIndex&, int, int);
|
||||
|
||||
void handleRacesInserted(const QModelIndex&, int, int);
|
||||
void handleRaceChanged(const QModelIndex&, const QModelIndex&);
|
||||
void handleRacesAboutToBeRemoved(const QModelIndex&, int, int);
|
||||
void handleRacesRemoved(const QModelIndex&, int, int);
|
||||
|
||||
void handleBodyPartsInserted(const QModelIndex&, int, int);
|
||||
void handleBodyPartChanged(const QModelIndex&, const QModelIndex&);
|
||||
void handleBodyPartsAboutToBeRemoved(const QModelIndex&, int, int);
|
||||
void handleBodyPartsRemoved(const QModelIndex&, int, int);
|
||||
|
||||
private:
|
||||
|
||||
ActorAdapter(const ActorAdapter&) = delete;
|
||||
ActorAdapter& operator=(const ActorAdapter&) = delete;
|
||||
|
||||
QModelIndex getHighestIndex(QModelIndex) const;
|
||||
bool is1stPersonPart(const std::string& id) const;
|
||||
|
||||
RaceDataPtr getRaceData(const std::string& raceId);
|
||||
|
||||
void setupActor(const std::string& id, ActorDataPtr data);
|
||||
void setupRace(const std::string& id, RaceDataPtr data);
|
||||
|
||||
void setupNpc(const std::string& id, ActorDataPtr data);
|
||||
void addNpcItem(const std::string& itemId, ActorDataPtr data);
|
||||
|
||||
void setupCreature(const std::string& id, ActorDataPtr data);
|
||||
|
||||
void markDirtyDependency(const std::string& dependency);
|
||||
void updateDirty();
|
||||
|
||||
RefIdCollection& mReferenceables;
|
||||
IdCollection<ESM::Race>& mRaces;
|
||||
IdCollection<ESM::BodyPart>& mBodyParts;
|
||||
|
||||
Misc::WeakCache<std::string, ActorData> mCachedActors; // Key: referenceable id
|
||||
Misc::WeakCache<std::string, RaceData> mCachedRaces; // Key: race id
|
||||
|
||||
StringSet mDirtyActors; // Actors that need updating
|
||||
StringSet mDirtyRaces; // Races that need updating
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,128 @@
|
||||
#include "actor.hpp"
|
||||
|
||||
#include <osg/Group>
|
||||
#include <osg/Node>
|
||||
|
||||
#include <components/esm/mappings.hpp>
|
||||
#include <components/misc/resourcehelpers.hpp>
|
||||
#include <components/resource/resourcemanager.hpp>
|
||||
#include <components/resource/scenemanager.hpp>
|
||||
#include <components/sceneutil/attach.hpp>
|
||||
#include <components/sceneutil/skeleton.hpp>
|
||||
|
||||
#include "../../model/world/data.hpp"
|
||||
|
||||
namespace CSVRender
|
||||
{
|
||||
const std::string Actor::MeshPrefix = "meshes\\";
|
||||
|
||||
Actor::Actor(const std::string& id, CSMWorld::Data& data)
|
||||
: mId(id)
|
||||
, 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()
|
||||
{
|
||||
return mBaseNode;
|
||||
}
|
||||
|
||||
void Actor::update()
|
||||
{
|
||||
mBaseNode->removeChildren(0, mBaseNode->getNumChildren());
|
||||
|
||||
// Load skeleton
|
||||
std::string skeletonModel = mActorData->getSkeleton();
|
||||
skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS());
|
||||
loadSkeleton(skeletonModel);
|
||||
|
||||
if (!mActorData->isCreature())
|
||||
{
|
||||
// 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();
|
||||
}
|
||||
|
||||
// Post setup
|
||||
mSkeleton->markDirty();
|
||||
mSkeleton->setActive(SceneUtil::Skeleton::Active);
|
||||
}
|
||||
|
||||
void Actor::handleActorChanged(const std::string& refId)
|
||||
{
|
||||
if (mId == refId)
|
||||
{
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
mSkeleton = new SceneUtil::Skeleton();
|
||||
mSkeleton->addChild(temp);
|
||||
}
|
||||
mBaseNode->addChild(mSkeleton);
|
||||
|
||||
// Map bone names to bones
|
||||
mNodeMap.clear();
|
||||
SceneUtil::NodeMapVisitor nmVisitor(mNodeMap);
|
||||
mSkeleton->accept(nmVisitor);
|
||||
|
||||
}
|
||||
|
||||
void Actor::loadBodyParts()
|
||||
{
|
||||
for (int i = 0; i < ESM::PRT_Count; ++i)
|
||||
{
|
||||
auto type = (ESM::PartReferenceType) i;
|
||||
std::string partId = mActorData->getPart(type);
|
||||
attachBodyPart(type, getBodyPartMesh(partId));
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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 "";
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
#ifndef OPENCS_VIEW_RENDER_ACTOR_H
|
||||
#define OPENCS_VIEW_RENDER_ACTOR_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <components/esm/loadarmo.hpp>
|
||||
#include <components/sceneutil/visitor.hpp>
|
||||
|
||||
#include "../../model/world/actoradapter.hpp"
|
||||
|
||||
namespace osg
|
||||
{
|
||||
class Group;
|
||||
}
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class Data;
|
||||
}
|
||||
|
||||
namespace SceneUtil
|
||||
{
|
||||
class Skeleton;
|
||||
}
|
||||
|
||||
namespace CSVRender
|
||||
{
|
||||
/// Handles loading an npc or creature
|
||||
class Actor : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/// Creates an actor.
|
||||
/// \param id The referenceable id
|
||||
/// \param type The record type
|
||||
/// \param data The data store
|
||||
Actor(const std::string& id, CSMWorld::Data& data);
|
||||
|
||||
/// Retrieves the base node that meshes are attached to
|
||||
osg::Group* getBaseNode();
|
||||
|
||||
/// (Re)creates the npc or creature renderable
|
||||
void update();
|
||||
|
||||
private slots:
|
||||
void handleActorChanged(const std::string& refId);
|
||||
|
||||
private:
|
||||
void loadSkeleton(const std::string& model);
|
||||
void loadBodyParts();
|
||||
void attachBodyPart(ESM::PartReferenceType, const std::string& mesh);
|
||||
|
||||
std::string getBodyPartMesh(const std::string& bodyPartId);
|
||||
|
||||
static const std::string MeshPrefix;
|
||||
|
||||
std::string mId;
|
||||
CSMWorld::Data& mData;
|
||||
CSMWorld::ActorAdapter::ActorDataPtr mActorData;
|
||||
|
||||
osg::ref_ptr<osg::Group> mBaseNode;
|
||||
SceneUtil::Skeleton* mSkeleton;
|
||||
SceneUtil::NodeMapVisitor::NodeMap mNodeMap;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,134 @@
|
||||
#include "mappings.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
ESM::BodyPart::MeshPart getMeshPart(ESM::PartReferenceType type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ESM::PRT_Head:
|
||||
return ESM::BodyPart::MP_Head;
|
||||
case ESM::PRT_Hair:
|
||||
return ESM::BodyPart::MP_Hair;
|
||||
case ESM::PRT_Neck:
|
||||
return ESM::BodyPart::MP_Neck;
|
||||
case ESM::PRT_Cuirass:
|
||||
return ESM::BodyPart::MP_Chest;
|
||||
case ESM::PRT_Groin:
|
||||
return ESM::BodyPart::MP_Groin;
|
||||
case ESM::PRT_RHand:
|
||||
return ESM::BodyPart::MP_Hand;
|
||||
case ESM::PRT_LHand:
|
||||
return ESM::BodyPart::MP_Hand;
|
||||
case ESM::PRT_RWrist:
|
||||
return ESM::BodyPart::MP_Wrist;
|
||||
case ESM::PRT_LWrist:
|
||||
return ESM::BodyPart::MP_Wrist;
|
||||
case ESM::PRT_RForearm:
|
||||
return ESM::BodyPart::MP_Forearm;
|
||||
case ESM::PRT_LForearm:
|
||||
return ESM::BodyPart::MP_Forearm;
|
||||
case ESM::PRT_RUpperarm:
|
||||
return ESM::BodyPart::MP_Upperarm;
|
||||
case ESM::PRT_LUpperarm:
|
||||
return ESM::BodyPart::MP_Upperarm;
|
||||
case ESM::PRT_RFoot:
|
||||
return ESM::BodyPart::MP_Foot;
|
||||
case ESM::PRT_LFoot:
|
||||
return ESM::BodyPart::MP_Foot;
|
||||
case ESM::PRT_RAnkle:
|
||||
return ESM::BodyPart::MP_Ankle;
|
||||
case ESM::PRT_LAnkle:
|
||||
return ESM::BodyPart::MP_Ankle;
|
||||
case ESM::PRT_RKnee:
|
||||
return ESM::BodyPart::MP_Knee;
|
||||
case ESM::PRT_LKnee:
|
||||
return ESM::BodyPart::MP_Knee;
|
||||
case ESM::PRT_RLeg:
|
||||
return ESM::BodyPart::MP_Upperleg;
|
||||
case ESM::PRT_LLeg:
|
||||
return ESM::BodyPart::MP_Upperleg;
|
||||
case ESM::PRT_Tail:
|
||||
return ESM::BodyPart::MP_Tail;
|
||||
default:
|
||||
throw std::runtime_error("PartReferenceType " +
|
||||
std::to_string(type) + " not associated with a mesh part");
|
||||
}
|
||||
}
|
||||
|
||||
std::string getBoneName(ESM::PartReferenceType type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ESM::PRT_Head:
|
||||
return "head";
|
||||
case ESM::PRT_Hair:
|
||||
return "head"; // This is purposeful.
|
||||
case ESM::PRT_Neck:
|
||||
return "neck";
|
||||
case ESM::PRT_Cuirass:
|
||||
return "chest";
|
||||
case ESM::PRT_Groin:
|
||||
return "groin";
|
||||
case ESM::PRT_Skirt:
|
||||
return "groin";
|
||||
case ESM::PRT_RHand:
|
||||
return "right hand";
|
||||
case ESM::PRT_LHand:
|
||||
return "left hand";
|
||||
case ESM::PRT_RWrist:
|
||||
return "right wrist";
|
||||
case ESM::PRT_LWrist:
|
||||
return "left wrist";
|
||||
case ESM::PRT_Shield:
|
||||
return "shield bone";
|
||||
case ESM::PRT_RForearm:
|
||||
return "right forearm";
|
||||
case ESM::PRT_LForearm:
|
||||
return "left forearm";
|
||||
case ESM::PRT_RUpperarm:
|
||||
return "right upper arm";
|
||||
case ESM::PRT_LUpperarm:
|
||||
return "left upper arm";
|
||||
case ESM::PRT_RFoot:
|
||||
return "right foot";
|
||||
case ESM::PRT_LFoot:
|
||||
return "left foot";
|
||||
case ESM::PRT_RAnkle:
|
||||
return "right ankle";
|
||||
case ESM::PRT_LAnkle:
|
||||
return "left ankle";
|
||||
case ESM::PRT_RKnee:
|
||||
return "right knee";
|
||||
case ESM::PRT_LKnee:
|
||||
return "left knee";
|
||||
case ESM::PRT_RLeg:
|
||||
return "right upper leg";
|
||||
case ESM::PRT_LLeg:
|
||||
return "left upper leg";
|
||||
case ESM::PRT_RPauldron:
|
||||
return "right clavicle";
|
||||
case ESM::PRT_LPauldron:
|
||||
return "left clavicle";
|
||||
case ESM::PRT_Weapon:
|
||||
return "weapon bone";
|
||||
case ESM::PRT_Tail:
|
||||
return "tail";
|
||||
default:
|
||||
throw std::runtime_error("unknown PartReferenceType");
|
||||
}
|
||||
}
|
||||
|
||||
std::string getMeshFilter(ESM::PartReferenceType type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ESM::PRT_Hair:
|
||||
return "hair";
|
||||
default:
|
||||
return getBoneName(type);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
#ifndef OPENMW_ESM_MAPPINGS_H
|
||||
#define OPENMW_ESM_MAPPINGS_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <components/esm/loadarmo.hpp>
|
||||
#include <components/esm/loadbody.hpp>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
ESM::BodyPart::MeshPart getMeshPart(ESM::PartReferenceType type);
|
||||
std::string getBoneName(ESM::PartReferenceType type);
|
||||
std::string getMeshFilter(ESM::PartReferenceType type);
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,138 @@
|
||||
#ifndef OPENMW_COMPONENTS_WEAKCACHE_HPP
|
||||
#define OPENMW_COMPONENTS_WEAKCACHE_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace Misc
|
||||
{
|
||||
/// \class WeakCache
|
||||
/// Provides a container to weakly store pointers to shared data.
|
||||
template <typename Key, typename T>
|
||||
class WeakCache
|
||||
{
|
||||
public:
|
||||
using WeakPtr = std::weak_ptr<T>;
|
||||
using StrongPtr = std::shared_ptr<T>;
|
||||
using Map = std::unordered_map<Key, WeakPtr>;
|
||||
|
||||
class iterator
|
||||
{
|
||||
public:
|
||||
iterator(WeakCache* cache, typename Map::iterator current, typename Map::iterator end);
|
||||
iterator& operator++();
|
||||
bool operator==(const iterator& other);
|
||||
bool operator!=(const iterator& other);
|
||||
StrongPtr operator*();
|
||||
private:
|
||||
WeakCache* mCache;
|
||||
typename Map::iterator mCurrent, mEnd;
|
||||
StrongPtr mPtr;
|
||||
};
|
||||
|
||||
/// Stores a weak pointer to the item.
|
||||
void insert(Key key, StrongPtr value, bool prune=true);
|
||||
|
||||
/// Retrieves the item associated with the key.
|
||||
/// \return An item or null.
|
||||
StrongPtr get(Key key);
|
||||
|
||||
iterator begin();
|
||||
iterator end();
|
||||
|
||||
/// Removes known invalid entries
|
||||
void prune();
|
||||
|
||||
private:
|
||||
Map mData;
|
||||
std::vector<Key> mDirty;
|
||||
};
|
||||
|
||||
|
||||
template <typename Key, typename T>
|
||||
WeakCache<Key, T>::iterator::iterator(WeakCache* cache, typename Map::iterator current, typename Map::iterator end)
|
||||
: mCache(cache)
|
||||
, mCurrent(current)
|
||||
, mEnd(end)
|
||||
{
|
||||
// Move to 1st available valid item
|
||||
for ( ; mCurrent != mEnd; ++mCurrent)
|
||||
{
|
||||
mPtr = mCurrent->second.lock();
|
||||
if (mPtr) break;
|
||||
else mCache->mDirty.push_back(mCurrent->first);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Key, typename T>
|
||||
typename WeakCache<Key, T>::iterator& WeakCache<Key, T>::iterator::operator++()
|
||||
{
|
||||
auto next = mCurrent;
|
||||
++next;
|
||||
return *this = iterator(mCache, next, mEnd);
|
||||
}
|
||||
|
||||
template <typename Key, typename T>
|
||||
bool WeakCache<Key, T>::iterator::operator==(const iterator& other)
|
||||
{
|
||||
return mCurrent == other.mCurrent;
|
||||
}
|
||||
|
||||
template <typename Key, typename T>
|
||||
bool WeakCache<Key, T>::iterator::operator!=(const iterator& other)
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
template <typename Key, typename T>
|
||||
typename WeakCache<Key, T>::StrongPtr WeakCache<Key, T>::iterator::operator*()
|
||||
{
|
||||
return mPtr;
|
||||
}
|
||||
|
||||
|
||||
template <typename Key, typename T>
|
||||
void WeakCache<Key, T>::insert(Key key, StrongPtr value, bool shouldPrune)
|
||||
{
|
||||
mData[key] = WeakPtr(value);
|
||||
if (shouldPrune) prune();
|
||||
}
|
||||
|
||||
template <typename Key, typename T>
|
||||
typename WeakCache<Key, T>::StrongPtr WeakCache<Key, T>::get(Key key)
|
||||
{
|
||||
auto searchIt = mData.find(key);
|
||||
if (searchIt != mData.end())
|
||||
return searchIt->second.lock();
|
||||
else
|
||||
return StrongPtr();
|
||||
}
|
||||
|
||||
template <typename Key, typename T>
|
||||
typename WeakCache<Key, T>::iterator WeakCache<Key, T>::begin()
|
||||
{
|
||||
return iterator(this, mData.begin(), mData.end());
|
||||
}
|
||||
|
||||
template <typename Key, typename T>
|
||||
typename WeakCache<Key, T>::iterator WeakCache<Key, T>::end()
|
||||
{
|
||||
return iterator(this, mData.end(), mData.end());
|
||||
}
|
||||
|
||||
template <typename Key, typename T>
|
||||
void WeakCache<Key, T>::prune()
|
||||
{
|
||||
// Remove empty entries
|
||||
for (auto& key : mDirty)
|
||||
{
|
||||
auto it = mData.find(key);
|
||||
if (it != mData.end() && it->second.use_count() == 0)
|
||||
mData.erase(it);
|
||||
}
|
||||
mDirty.clear();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -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
|
Loading…
Reference in New Issue