From 6b42f37918271cdd41000e141e8f597b69b82f5b Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Sun, 22 Jul 2018 15:38:30 -0500 Subject: [PATCH] Handle creatures too --- apps/opencs/view/render/actor.cpp | 284 ++++++++++++++++------------- apps/opencs/view/render/actor.hpp | 8 + apps/opencs/view/render/object.cpp | 5 +- 3 files changed, 167 insertions(+), 130 deletions(-) diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp index 93fff270a..3691085e3 100644 --- a/apps/opencs/view/render/actor.cpp +++ b/apps/opencs/view/render/actor.cpp @@ -18,6 +18,8 @@ namespace CSVRender { + const std::string Actor::MeshPrefix = "meshes\\"; + Actor::Actor(const std::string& id, int type, CSMWorld::Data& data) : mId(id) , mType(type) @@ -34,7 +36,42 @@ namespace CSVRender void Actor::update() { - const std::string MeshPrefix = "meshes\\"; + try + { + mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); + + if (mType == CSMWorld::UniversalId::Type_Npc) + updateNpc(); + else if (mType == CSMWorld::UniversalId::Type_Creature) + updateCreature(); + } + catch (std::exception& e) + { + std::cout << "Caught exception: " << e.what() << std::endl; + } + } + + void Actor::updateCreature() + { + auto& referenceables = mData.getReferenceables(); + + auto& creature = dynamic_cast& >(referenceables.getRecord(mId)).get(); + + std::string skeletonModel = MeshPrefix + creature.mModel; + skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); + loadSkeleton(skeletonModel); + + SceneUtil::RemoveTriBipVisitor removeTriBipVisitor; + mSkeleton->accept(removeTriBipVisitor); + removeTriBipVisitor.remove(); + + // Post setup + mSkeleton->markDirty(); + mSkeleton->setActive(SceneUtil::Skeleton::Active); + } + + void Actor::updateNpc() + { const unsigned int FemaleFlag = ESM::BodyPart::BPF_Female; auto& bodyParts = mData.getBodyParts(); @@ -42,144 +79,137 @@ namespace CSVRender auto& referenceables = mData.getReferenceables(); auto sceneMgr = mData.getResourceSystem()->getSceneManager(); + auto& npc = dynamic_cast& >(referenceables.getRecord(mId)).get(); + auto& race = dynamic_cast& >(races.getRecord(npc.mRace)).get(); - // Remove children - mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); + bool is1stPerson = false; + bool isFemale = !npc.isMale(); + bool isBeast = race.mData.mFlags & ESM::Race::Beast; + bool isWerewolf = false; - try - { - // Npcs and creatures are handled differently - if (mType == CSMWorld::UniversalId::Type_Npc) + // 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(); + + // Map bone names to bones + SceneUtil::NodeMapVisitor::NodeMap nodeMap; + SceneUtil::NodeMapVisitor nmVisitor(nodeMap); + mSkeleton->accept(nmVisitor); + + using BPRaceKey = std::tuple; + using RaceToBPMap = std::map; + // Convenience method to generate a map from body part + race to mesh name + auto genRaceToBodyPartMap = [&](RaceToBPMap& bpMap) { + int size = bodyParts.getSize(); + for (int i = 0; i < size; ++i) { - auto& npc = dynamic_cast& >(referenceables.getRecord(mId)).get(); - auto& race = dynamic_cast& >(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()); + auto& record = bodyParts.getRecord(i); + if (!record.isDeleted()) { - osg::ref_ptr temp = sceneMgr->getInstance(skeletonModel); - mSkeleton = dynamic_cast(temp.get()); - if (!mSkeleton) - { - mSkeleton = new SceneUtil::Skeleton(); - mSkeleton->addChild(temp); - } - mBaseNode->addChild(mSkeleton); + // 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); } + } + }; - // Map bone names to bones - SceneUtil::NodeMapVisitor::NodeMap nodeMap; - SceneUtil::NodeMapVisitor nmVisitor(nodeMap); - mSkeleton->accept(nmVisitor); + // Generate mapping + RaceToBPMap r2bpMap; + genRaceToBodyPartMap(r2bpMap); - // Female mesh has some drawables attached, get rid of them - SceneUtil::CleanObjectRootVisitor cleanVisitor; - mSkeleton->accept(cleanVisitor); - cleanVisitor.remove(); - - // Convenience method to retrieve the mesh name of a body part - auto getBodyPartMesh = [&](std::string bpName) -> std::string { - int index = bodyParts.searchId(bpName); - if (index != -1 && !bodyParts.getRecord(index).isDeleted()) - return MeshPrefix + bodyParts.getRecord(index).get().mModel; - else - return ""; - }; - - using BPRaceKey = std::tuple; - using RaceToBPMap = std::map; - // 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) + // 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()) { - auto part = static_cast(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, ""); - } + mesh = meshResult->second; } - // Post setup - mSkeleton->markDirty(); - mSkeleton->setActive(SceneUtil::Skeleton::Active); + 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(i); + switch (part) + { + case ESM::PRT_Head: + addBodyPart(part, getBodyPartMesh(npc.mHead)); + break; + case ESM::PRT_Hair: + addBodyPart(part, getBodyPartMesh(npc.mHair)); + break; + case ESM::PRT_Skirt: + case ESM::PRT_Shield: + case ESM::PRT_RPauldron: + case ESM::PRT_LPauldron: + case ESM::PRT_Weapon: + // No body part mesh associated + break; + default: + addBodyPart(part, ""); } } - catch (std::exception& e) + + // Post setup + mSkeleton->markDirty(); + mSkeleton->setActive(SceneUtil::Skeleton::Active); + } + + void Actor::loadSkeleton(const std::string& model) + { + auto sceneMgr = mData.getResourceSystem()->getSceneManager(); + + osg::ref_ptr temp = sceneMgr->getInstance(model); + mSkeleton = dynamic_cast(temp.get()); + if (!mSkeleton) { - std::cout << "Caught exception: " << e.what() << std::endl; + mSkeleton = new SceneUtil::Skeleton(); + mSkeleton->addChild(temp); } + mBaseNode->addChild(mSkeleton); + } + + std::string Actor::getBodyPartMesh(const std::string& bodyPartId) + { + const auto& bodyParts = mData.getBodyParts(); + + int index = bodyParts.searchId(bodyPartId); + if (index != -1 && !bodyParts.getRecord(index).isDeleted()) + return MeshPrefix + bodyParts.getRecord(index).get().mModel; + else + return ""; } } diff --git a/apps/opencs/view/render/actor.hpp b/apps/opencs/view/render/actor.hpp index 10f4de558..b4e65ff2b 100644 --- a/apps/opencs/view/render/actor.hpp +++ b/apps/opencs/view/render/actor.hpp @@ -39,6 +39,14 @@ namespace CSVRender void update(); private: + void loadSkeleton(const std::string& model); + void updateCreature(); + void updateNpc(); + + std::string getBodyPartMesh(const std::string& bodyPartId); + + static const std::string MeshPrefix; + std::string mId; int mType; CSMWorld::Data& mData; diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index b8e171650..dbfd595b6 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -132,9 +132,8 @@ void CSVRender::Object::update() { try { - if (recordType == CSMWorld::UniversalId::Type_Npc) + if (recordType == CSMWorld::UniversalId::Type_Npc || recordType == CSMWorld::UniversalId::Type_Creature) { - std::cout << "recordType: Npc\n"; Actor actor(mReferenceableId, recordType, mData); actor.update(); mBaseNode->addChild(actor.getBaseNode()); @@ -147,8 +146,8 @@ void CSVRender::Object::update() } catch (std::exception& e) { - // TODO: use error marker mesh Log(Debug::Error) << e.what(); + mBaseNode->addChild(createErrorCube()); } }