From c519fc360ddaeb13872fd761c267123440a88b7e Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 18 Apr 2013 00:19:34 +0200 Subject: [PATCH] Move away from fixed record names for body parts --- apps/esmtool/record.cpp | 2 +- apps/openmw/mwgui/race.cpp | 132 +++++++------------------- apps/openmw/mwgui/race.hpp | 7 +- apps/openmw/mwrender/npcanimation.cpp | 111 ++++++++++++++-------- components/esm/loadarmo.hpp | 4 +- components/esm/loadbody.cpp | 4 +- components/esm/loadbody.hpp | 6 +- 7 files changed, 119 insertions(+), 147 deletions(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index e16ade6e2..b7cbbc4ea 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -439,7 +439,7 @@ void Record::print() template<> void Record::print() { - std::cout << " Name: " << mData.mName << std::endl; + std::cout << " Race: " << mData.mRace << std::endl; std::cout << " Model: " << mData.mModel << std::endl; std::cout << " Type: " << meshTypeLabel(mData.mData.mType) << " (" << (int)mData.mData.mType << ")" << std::endl; diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index c031ad6b6..25bae999b 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -24,36 +24,6 @@ int wrap(int index, int max) else return index; } - -int countParts(const std::string &part, const std::string &race, bool male) -{ - /// \todo loop through the whole store for appropriate bodyparts instead of looking for fixed IDs - const MWWorld::Store &store = - MWBase::Environment::get().getWorld()->getStore().get(); - - std::string prefix = - "b_n_" + race + ((male) ? "_m_" : "_f_") + part; - - std::string suffix; - suffix.reserve(prefix.size() + 3); - - int count = -1; - do { - ++count; - suffix = "_" + (boost::format("%02d") % (count + 1)).str(); - } - while (store.search(prefix + suffix) != 0); - - if (count == 0 && part == "hair") { - count = -1; - do { - ++count; - suffix = (boost::format("%02d") % (count + 1)).str(); - } - while (store.search(prefix + suffix) != 0); - } - return count; -} } RaceDialog::RaceDialog() @@ -61,8 +31,6 @@ RaceDialog::RaceDialog() , mGenderIndex(0) , mFaceIndex(0) , mHairIndex(0) - , mFaceCount(10) - , mHairCount(14) , mCurrentAngle(0) { // Centre dialog @@ -227,67 +195,28 @@ void RaceDialog::onSelectNextGender(MyGUI::Widget*) void RaceDialog::onSelectPreviousFace(MyGUI::Widget*) { - do - mFaceIndex = wrap(mFaceIndex - 1, mFaceCount); - while (!isFacePlayable()); + mFaceIndex = wrap(mFaceIndex - 1, mAvailableHeads.size()); updatePreview(); } void RaceDialog::onSelectNextFace(MyGUI::Widget*) { - do - mFaceIndex = wrap(mFaceIndex + 1, mFaceCount); - while (!isFacePlayable()); + mFaceIndex = wrap(mFaceIndex + 1, mAvailableHeads.size()); updatePreview(); } void RaceDialog::onSelectPreviousHair(MyGUI::Widget*) { - do - mHairIndex = wrap(mHairIndex - 1, mHairCount); - while (!isHairPlayable()); + mHairIndex = wrap(mHairIndex - 1, mAvailableHairs.size()); updatePreview(); } void RaceDialog::onSelectNextHair(MyGUI::Widget*) { - do - mHairIndex = wrap(mHairIndex + 1, mHairCount); - while (!isHairPlayable()); + mHairIndex = wrap(mHairIndex + 1, mAvailableHairs.size()); updatePreview(); } -bool RaceDialog::isFacePlayable() -{ - std::string prefix = - "b_n_" + mCurrentRaceId + ((mGenderIndex == 0) ? "_m_" : "_f_"); - - std::string headIndex = (boost::format("%02d") % (mFaceIndex + 1)).str(); - - const MWWorld::Store &parts = - MWBase::Environment::get().getWorld()->getStore().get(); - - if (parts.search(prefix + "head_" + headIndex) == 0) - return !(parts.find(prefix + "head" + headIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable); - else - return !(parts.find(prefix + "head_" + headIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable); -} - -bool RaceDialog::isHairPlayable() -{ - std::string prefix = - "b_n_" + mCurrentRaceId + ((mGenderIndex == 0) ? "_m_" : "_f_"); - - std::string hairIndex = (boost::format("%02d") % (mHairIndex + 1)).str(); - - const MWWorld::Store &parts = - MWBase::Environment::get().getWorld()->getStore().get(); - if (parts.search(prefix + "hair_" + hairIndex) == 0) - return !(parts.find(prefix + "hair" + hairIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable); - else - return !(parts.find(prefix + "hair_" + hairIndex)->mData.mFlags & ESM::BodyPart::BPF_NotPlayable); -} - void RaceDialog::onSelectRace(MyGUI::ListBox* _sender, size_t _index) { if (_index == MyGUI::ITEM_NONE) @@ -308,18 +237,41 @@ void RaceDialog::onSelectRace(MyGUI::ListBox* _sender, size_t _index) updateSpellPowers(); } +void RaceDialog::getBodyParts (int part, std::vector& out) +{ + out.clear(); + const MWWorld::Store &store = + MWBase::Environment::get().getWorld()->getStore().get(); + + for (MWWorld::Store::iterator it = store.begin(); it != store.end(); ++it) + { + const ESM::BodyPart& bodypart = *it; + if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable) + continue; + if (bodypart.mData.mType != ESM::BodyPart::MT_Skin) + continue; + if (bodypart.mData.mPart != static_cast(part)) + continue; + if (mGenderIndex != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female)) + continue; + bool firstPerson = (bodypart.mId.size() >= 3) + && bodypart.mId[bodypart.mId.size()-3] == '1' + && bodypart.mId[bodypart.mId.size()-2] == 's' + && bodypart.mId[bodypart.mId.size()-1] == 't'; + if (firstPerson) + continue; + if (Misc::StringUtils::ciEqual(bodypart.mRace, mCurrentRaceId)) + out.push_back(bodypart.mId); + } +} + void RaceDialog::recountParts() { - mFaceCount = countParts("head", mCurrentRaceId, mGenderIndex == 0); - mHairCount = countParts("hair", mCurrentRaceId, mGenderIndex == 0); + getBodyParts(ESM::BodyPart::MP_Hair, mAvailableHairs); + getBodyParts(ESM::BodyPart::MP_Head, mAvailableHeads); mFaceIndex = 0; mHairIndex = 0; - - while (!isHairPlayable()) - mHairIndex = wrap(mHairIndex + 1, mHairCount); - while (!isFacePlayable()) - mFaceIndex = wrap(mFaceIndex + 1, mFaceCount); } // update widget content @@ -330,21 +282,9 @@ void RaceDialog::updatePreview() record.mRace = mCurrentRaceId; record.setIsMale(mGenderIndex == 0); - std::string prefix = - "b_n_" + mCurrentRaceId + ((record.isMale()) ? "_m_" : "_f_"); + record.mHead = mAvailableHeads[mFaceIndex]; + record.mHair = mAvailableHairs[mHairIndex]; - std::string headIndex = (boost::format("%02d") % (mFaceIndex + 1)).str(); - std::string hairIndex = (boost::format("%02d") % (mHairIndex + 1)).str(); - - record.mHead = prefix + "head_" + headIndex; - record.mHair = prefix + "hair_" + hairIndex; - - const MWWorld::Store &parts = - MWBase::Environment::get().getWorld()->getStore().get(); - - if (parts.search(record.mHair) == 0) { - record.mHair = prefix + "hair" + hairIndex; - } mPreview->setPrototype(record); } diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index 893c4c90b..1d48c67cd 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -76,8 +76,10 @@ namespace MWGui void updatePreview(); void recountParts(); - bool isHairPlayable(); - bool isFacePlayable(); + void getBodyParts (int part, std::vector& out); + + std::vector mAvailableHeads; + std::vector mAvailableHairs; MyGUI::ImageBox* mPreviewImage; MyGUI::ListBox* mRaceList; @@ -90,7 +92,6 @@ namespace MWGui std::vector mSpellPowerItems; int mGenderIndex, mFaceIndex, mHairIndex; - int mFaceCount, mHairCount; std::string mCurrentRaceId; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 96220f47d..ecc0869cc 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -305,56 +305,83 @@ void NpcAnimation::updateParts(bool forceupdate) if(mViewMode == VM_HeadOnly) return; - static const struct { - ESM::PartReferenceType type; - const char name[2][12]; - } PartTypeList[] = { - { ESM::PRT_Neck, { "neck", "" } }, - { ESM::PRT_Cuirass, { "chest", "" } }, - { ESM::PRT_Groin, { "groin", "" } }, - { ESM::PRT_RHand, { "hand", "hands" } }, - { ESM::PRT_LHand, { "hand", "hands" } }, - { ESM::PRT_RWrist, { "wrist", "" } }, - { ESM::PRT_LWrist, { "wrist", "" } }, - { ESM::PRT_RForearm, { "forearm", "" } }, - { ESM::PRT_LForearm, { "forearm", "" } }, - { ESM::PRT_RUpperarm, { "upper arm", "" } }, - { ESM::PRT_LUpperarm, { "upper arm", "" } }, - { ESM::PRT_RFoot, { "foot", "feet" } }, - { ESM::PRT_LFoot, { "foot", "feet" } }, - { ESM::PRT_RAnkle, { "ankle", "" } }, - { ESM::PRT_LAnkle, { "ankle", "" } }, - { ESM::PRT_RKnee, { "knee", "" } }, - { ESM::PRT_LKnee, { "knee", "" } }, - { ESM::PRT_RLeg, { "upper leg", "" } }, - { ESM::PRT_LLeg, { "upper leg", "" } }, - { ESM::PRT_Tail, { "tail", "" } } - }; + std::map bodypartMap; + bodypartMap[ESM::PRT_Neck] = ESM::BodyPart::MP_Neck; + bodypartMap[ESM::PRT_Cuirass] = ESM::BodyPart::MP_Chest; + bodypartMap[ESM::PRT_Groin] = ESM::BodyPart::MP_Groin; + bodypartMap[ESM::PRT_RHand] = ESM::BodyPart::MP_Hand; + bodypartMap[ESM::PRT_LHand] = ESM::BodyPart::MP_Hand; + bodypartMap[ESM::PRT_RWrist] = ESM::BodyPart::MP_Wrist; + bodypartMap[ESM::PRT_LWrist] = ESM::BodyPart::MP_Wrist; + bodypartMap[ESM::PRT_RForearm] = ESM::BodyPart::MP_Forearm; + bodypartMap[ESM::PRT_LForearm] = ESM::BodyPart::MP_Forearm; + bodypartMap[ESM::PRT_RUpperarm] = ESM::BodyPart::MP_Upperarm; + bodypartMap[ESM::PRT_LUpperarm] = ESM::BodyPart::MP_Upperarm; + bodypartMap[ESM::PRT_RFoot] = ESM::BodyPart::MP_Foot; + bodypartMap[ESM::PRT_LFoot] = ESM::BodyPart::MP_Foot; + bodypartMap[ESM::PRT_RAnkle] = ESM::BodyPart::MP_Ankle; + bodypartMap[ESM::PRT_LAnkle] = ESM::BodyPart::MP_Ankle; + bodypartMap[ESM::PRT_RKnee] = ESM::BodyPart::MP_Knee; + bodypartMap[ESM::PRT_LKnee] = ESM::BodyPart::MP_Knee; + bodypartMap[ESM::PRT_RLeg] = ESM::BodyPart::MP_Upperleg; + bodypartMap[ESM::PRT_LLeg] = ESM::BodyPart::MP_Upperleg; + bodypartMap[ESM::PRT_Tail] = ESM::BodyPart::MP_Tail; - const char *ext = (mViewMode == VM_FirstPerson) ? ".1st" : ""; const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); - for(size_t i = 0;i < sizeof(PartTypeList)/sizeof(PartTypeList[0]);i++) + + const int Flag_Female = 0x01; + const int Flag_FirstPerson = 0x02; + + int flags = 0; + if (!mNpc->isMale()) + flags |= Flag_Female; + if (mViewMode == VM_FirstPerson) + flags |= Flag_FirstPerson; + + // Remember body parts so we only have to search through the store once for each race/gender/viewmode combination + static std::map< std::pair , std::vector > sRaceMapping; + std::string race = Misc::StringUtils::lowerCase(mNpc->mRace); + std::pair thisCombination = std::make_pair(race, flags); + if (sRaceMapping.find(thisCombination) == sRaceMapping.end()) { - if(mPartPriorities[PartTypeList[i].type] < 1) - { - const ESM::BodyPart *part = NULL; - const MWWorld::Store &partStore = store.get(); + sRaceMapping[thisCombination].resize(ESM::PRT_Count); + for (int i=0; iisMale()) + const MWWorld::Store &partStore = store.get(); + + for (MWWorld::Store::iterator it = partStore.begin(); it != partStore.end(); ++it) + { + const ESM::BodyPart& bodypart = *it; + if (bodypart.mData.mFlags & ESM::BodyPart::BPF_NotPlayable) + continue; + if (bodypart.mData.mType != ESM::BodyPart::MT_Skin) { - part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[0]+ext); - if(part == 0) - part = partStore.search(mBodyPrefix + "_f_" + PartTypeList[i].name[1]+ext); + continue; } - if(part == 0) - part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[0]+ext); - if(part == 0) - part = partStore.search(mBodyPrefix + "_m_" + PartTypeList[i].name[1]+ext); - - if(part) - addOrReplaceIndividualPart(PartTypeList[i].type, -1,1, "meshes\\"+part->mModel); + if (!mNpc->isMale() != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female)) + continue; + if (!Misc::StringUtils::ciEqual(bodypart.mRace, mNpc->mRace)) + continue; + + bool firstPerson = (bodypart.mId.size() >= 3) + && bodypart.mId[bodypart.mId.size()-3] == '1' + && bodypart.mId[bodypart.mId.size()-2] == 's' + && bodypart.mId[bodypart.mId.size()-1] == 't'; + if (firstPerson != (mViewMode == VM_FirstPerson)) + continue; + for (std::map::iterator bIt = bodypartMap.begin(); bIt != bodypartMap.end(); ++bIt ) + if (bIt->second == bodypart.mData.mPart) + sRaceMapping[thisCombination][bIt->first] = &*it; } } + + for (int part = ESM::PRT_Neck; part < ESM::PRT_Count; ++part) + { + const ESM::BodyPart* bodypart = sRaceMapping[thisCombination][part]; + if (mPartPriorities[part] < 1 && bodypart) + addOrReplaceIndividualPart(part, -1,1, "meshes\\"+bodypart->mModel); + } } NifOgre::ObjectList NpcAnimation::insertBoundedPart(const std::string &model, int group, const std::string &bonename) diff --git a/components/esm/loadarmo.hpp b/components/esm/loadarmo.hpp index a94ae6735..c18b4486d 100644 --- a/components/esm/loadarmo.hpp +++ b/components/esm/loadarmo.hpp @@ -38,7 +38,9 @@ enum PartReferenceType PRT_RPauldron = 23, PRT_LPauldron = 24, PRT_Weapon = 25, - PRT_Tail = 26 + PRT_Tail = 26, + + PRT_Count = 27 }; // Reference to body parts diff --git a/components/esm/loadbody.cpp b/components/esm/loadbody.cpp index 831ad8b64..e95a8a860 100644 --- a/components/esm/loadbody.cpp +++ b/components/esm/loadbody.cpp @@ -9,13 +9,13 @@ namespace ESM void BodyPart::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); - mName = esm.getHNString("FNAM"); + mRace = esm.getHNString("FNAM"); esm.getHNT(mData, "BYDT", 4); } void BodyPart::save(ESMWriter &esm) { esm.writeHNCString("MODL", mModel); - esm.writeHNCString("FNAM", mName); + esm.writeHNCString("FNAM", mRace); esm.writeHNT("BYDT", mData, 4); } diff --git a/components/esm/loadbody.hpp b/components/esm/loadbody.hpp index c467b3625..3ad9b1b95 100644 --- a/components/esm/loadbody.hpp +++ b/components/esm/loadbody.hpp @@ -27,7 +27,9 @@ struct BodyPart MP_Knee = 11, MP_Upperleg = 12, MP_Clavicle = 13, - MP_Tail = 14 + MP_Tail = 14, + + MP_Count = 15 }; enum Flags @@ -52,7 +54,7 @@ struct BodyPart }; BYDTstruct mData; - std::string mId, mModel, mName; + std::string mId, mModel, mRace; void load(ESMReader &esm); void save(ESMWriter &esm);