From 8444ee9981226e6f739b4df8ced94ef7bd9f7abe Mon Sep 17 00:00:00 2001 From: Kyle Cooley Date: Fri, 13 Jul 2018 21:48:59 -0500 Subject: [PATCH] Start rendering npc's --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/view/render/actor.cpp | 196 +++++++++++++++++++++++++++++ apps/opencs/view/render/actor.hpp | 51 ++++++++ apps/opencs/view/render/object.cpp | 25 +++- apps/openmw/mwrender/animation.cpp | 34 +---- components/CMakeLists.txt | 2 +- components/esm/mappings.cpp | 134 ++++++++++++++++++++ components/esm/mappings.hpp | 16 +++ components/sceneutil/visitor.cpp | 19 ++- components/sceneutil/visitor.hpp | 30 +++++ 10 files changed, 471 insertions(+), 38 deletions(-) create mode 100644 apps/opencs/view/render/actor.cpp create mode 100644 apps/opencs/view/render/actor.hpp create mode 100644 components/esm/mappings.cpp create mode 100644 components/esm/mappings.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index f2821f184..26713f925 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -93,7 +93,7 @@ opencs_units (view/render opencs_units_noqt (view/render lighting lightingday lightingnight lightingbright object cell terrainstorage tagbase - cellarrow cellmarker cellborder pathgrid + cellarrow cellmarker cellborder pathgrid actor ) opencs_hdrs_noqt (view/render diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp new file mode 100644 index 000000000..1273b94c6 --- /dev/null +++ b/apps/opencs/view/render/actor.cpp @@ -0,0 +1,196 @@ +#include "actor.hpp" + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../../model/world/data.hpp" + +namespace CSVRender +{ + Actor::Actor(const std::string& id, int type, CSMWorld::Data& data) + : mId(id) + , mType(type) + , mData(data) + , mSkeleton(nullptr) + , mBaseNode(new osg::Group()) + { + } + + osg::Group* Actor::getBaseNode() + { + return mBaseNode; + } + + void Actor::update() + { + const std::string MeshPrefix = "meshes\\"; + const unsigned int FemaleFlag = ESM::BodyPart::BPF_Female; + + auto& bodyParts = mData.getBodyParts(); + auto& races = mData.getRaces(); + auto& referenceables = mData.getReferenceables(); + auto sceneMgr = mData.getResourceSystem()->getSceneManager(); + + + // Remove children + mBaseNode->removeChildren(0, mBaseNode->getNumChildren()); + + // Npcs and creatures are handled differently + if (mType == CSMWorld::UniversalId::Type_Npc) + { + auto& npc = dynamic_cast& >(referenceables.getRecord(mId)).get(); + + auto isBeast = [&](std::string race) -> bool { + int index = races.searchId(race); + if (index != -1 && !races.getRecord(index).isDeleted()) + return races.getRecord(index).get().mData.mFlags & ESM::Race::Beast; + else + return false; + }; + + // Load skeleton + std::string skeletonResource; + if (isBeast(npc.mRace)) { + std::cout << "is beast\n"; + skeletonResource = "base_animkna.nif"; + } + else if (npc.isMale()) { + std::cout << "is male\n"; + skeletonResource = "base_anim.nif"; + } + else { + std::cout << "is female\n"; + skeletonResource = "base_anim_female.nif"; + } + + std::string skeletonModel = MeshPrefix + skeletonResource; + skeletonModel = Misc::ResourceHelpers::correctActorModelPath(skeletonModel, mData.getResourceSystem()->getVFS()); + { + osg::ref_ptr temp = sceneMgr->getInstance(skeletonModel); + mSkeleton = dynamic_cast(temp.get()); + if (!mSkeleton) + { + mSkeleton = new SceneUtil::Skeleton(); + mSkeleton->addChild(temp); + } + mBaseNode->addChild(mSkeleton); + } + + // Map bone names to bones + SceneUtil::NodeMapVisitor::NodeMap nodeMap; + SceneUtil::NodeMapVisitor nmVisitor(nodeMap); + mSkeleton->accept(nmVisitor); + + if (!npc.isMale()) { + for (auto it : nodeMap) { + std::cout << it.first << "\n"; + } + } + + + // Female mesh has some drawables attached, get rid of them + SceneUtil::HideDrawablesVisitor hdVisitor; + mSkeleton->accept(hdVisitor); + + // 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()) + { + auto& bodyPart = record.get(); + 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), npc.isMale() ? 0 : 1, 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); + if (!npc.isMale() && type == ESM::PRT_LHand) { + SceneUtil::NodeMapVisitor::NodeMap handNodeMap; + SceneUtil::NodeMapVisitor nmVisitor(handNodeMap); + instance->accept(nmVisitor); + + std::cout << "Left hand\n"; + for (auto it : handNodeMap) { + std::cout << it.first << std::endl; + } + } + 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, ""); + } + } + // Post setup + mSkeleton->markDirty(); + mSkeleton->setActive(SceneUtil::Skeleton::Active); + } + } +} diff --git a/apps/opencs/view/render/actor.hpp b/apps/opencs/view/render/actor.hpp new file mode 100644 index 000000000..10f4de558 --- /dev/null +++ b/apps/opencs/view/render/actor.hpp @@ -0,0 +1,51 @@ +#ifndef OPENCS_VIEW_RENDER_ACTOR_H +#define OPENCS_VIEW_RENDER_ACTOR_H + +#include + +#include + +namespace osg +{ + class Group; +} + +namespace CSMWorld +{ + class Data; +} + +namespace SceneUtil +{ + class Skeleton; +} + +namespace CSVRender +{ + /// Handles loading an npc or creature + class Actor + { + public: + /// Creates an actor. + /// \param id The referenceable id + /// \param type The record type + /// \param data The data store + Actor(const std::string& id, int type, CSMWorld::Data& data); + + /// Retrieves the base node that meshes are attached to + osg::Group* getBaseNode(); + + /// (Re)creates the npc or creature renderable + void update(); + + private: + std::string mId; + int mType; + CSMWorld::Data& mData; + + SceneUtil::Skeleton* mSkeleton; + osg::ref_ptr mBaseNode; + }; +} + +#endif diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 2b1e3adde..b8e171650 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -1,6 +1,8 @@ #include "object.hpp" #include +#include +#include #include #include @@ -11,6 +13,7 @@ #include #include #include +#include #include @@ -28,7 +31,9 @@ #include #include #include +#include +#include "actor.hpp" #include "mask.hpp" @@ -84,6 +89,7 @@ void CSVRender::Object::update() const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables(); int index = referenceables.searchId (mReferenceableId); + int recordType = -1; const ESM::Light* light = NULL; if (index==-1) @@ -96,7 +102,7 @@ void CSVRender::Object::update() referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Model)). toString().toUtf8().constData(); - int recordType = + recordType = referenceables.getData (index, referenceables.findColumnIndex(CSMWorld::Columns::ColumnId_RecordType)).toInt(); if (recordType == CSMWorld::UniversalId::Type_Light) @@ -112,7 +118,7 @@ void CSVRender::Object::update() model = "marker_creature.nif"; } - if (model.empty()) + if (recordType != CSMWorld::UniversalId::Type_Npc && model.empty()) error = 2; } @@ -126,9 +132,18 @@ void CSVRender::Object::update() { try { - std::string path = "meshes\\" + model; - - mResourceSystem->getSceneManager()->getInstance(path, mBaseNode); + if (recordType == CSMWorld::UniversalId::Type_Npc) + { + std::cout << "recordType: Npc\n"; + Actor actor(mReferenceableId, recordType, mData); + actor.update(); + mBaseNode->addChild(actor.getBaseNode()); + } + else + { + std::string path = "meshes\\" + model; + mResourceSystem->getSceneManager()->getInstance(path, mBaseNode); + } } catch (std::exception& e) { diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 7410dbf03..e4a7c94b3 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -91,32 +91,6 @@ namespace std::vector > mToRemove; }; - class NodeMapVisitor : public osg::NodeVisitor - { - public: - typedef std::map > NodeMap; - - NodeMapVisitor(NodeMap& map) - : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) - , mMap(map) - {} - - void apply(osg::MatrixTransform& trans) - { - // Take transformation for first found node in file - const std::string nodeName = Misc::StringUtils::lowerCase(trans.getName()); - if (mMap.find(nodeName) == mMap.end()) - { - mMap[nodeName] = &trans; - } - - traverse(trans); - } - - private: - NodeMap& mMap; - }; - NifOsg::TextKeyMap::const_iterator findGroupStart(const NifOsg::TextKeyMap &keys, const std::string &groupname) { NifOsg::TextKeyMap::const_iterator iter(keys.begin()); @@ -468,7 +442,7 @@ namespace MWRender } if (mDone) return; - + // Set the starting time to measure glow duration from if this is a temporary glow if ((mDuration >= 0) && mStartingTime == 0) mStartingTime = nv->getFrameStamp()->getSimulationTime(); @@ -1042,7 +1016,7 @@ namespace MWRender { if (!mNodeMapCreated && mObjectRoot) { - NodeMapVisitor visitor(mNodeMap); + SceneUtil::NodeMapVisitor visitor(mNodeMap); mObjectRoot->accept(visitor); mNodeMapCreated = true; } @@ -1313,7 +1287,7 @@ namespace MWRender if(state.getTime() >= state.mLoopStopTime) break; - } + } if(timepassed <= 0.0f) break; @@ -1528,7 +1502,7 @@ namespace MWRender osg::ref_ptr glowUpdater = new GlowUpdater(texUnit, glowColor, textures, node, glowDuration, mResourceSystem); mGlowUpdater = glowUpdater; node->addUpdateCallback(glowUpdater); - + // set a texture now so that the ShaderVisitor can find it osg::ref_ptr writableStateSet = NULL; if (!node->getStateSet()) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 2da7c80bf..e7e0ea0da 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -77,7 +77,7 @@ add_component_dir (esm loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap inventorystate containerstate npcstate creaturestate dialoguestate statstate npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile - aisequence magiceffects util custommarkerstate stolenitems transport animationstate controlsstate + aisequence magiceffects util custommarkerstate stolenitems transport animationstate controlsstate mappings ) add_component_dir (esmterrain diff --git a/components/esm/mappings.cpp b/components/esm/mappings.cpp new file mode 100644 index 000000000..440e73573 --- /dev/null +++ b/components/esm/mappings.cpp @@ -0,0 +1,134 @@ +#include "mappings.hpp" + +#include + +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); + } + } +} diff --git a/components/esm/mappings.hpp b/components/esm/mappings.hpp new file mode 100644 index 000000000..f930fef15 --- /dev/null +++ b/components/esm/mappings.hpp @@ -0,0 +1,16 @@ +#ifndef OPENMW_ESM_MAPPINGS_H +#define OPENMW_ESM_MAPPINGS_H + +#include + +#include +#include + +namespace ESM +{ + ESM::BodyPart::MeshPart getMeshPart(ESM::PartReferenceType type); + std::string getBoneName(ESM::PartReferenceType type); + std::string getMeshFilter(ESM::PartReferenceType type); +} + +#endif diff --git a/components/sceneutil/visitor.cpp b/components/sceneutil/visitor.cpp index 2f6123e34..5aaeb459e 100644 --- a/components/sceneutil/visitor.cpp +++ b/components/sceneutil/visitor.cpp @@ -1,5 +1,6 @@ #include "visitor.hpp" +#include #include #include @@ -23,7 +24,7 @@ namespace SceneUtil { if (Misc::StringUtils::ciEqual(node.className(), mNameToFind)) mFoundNodes.push_back(&node); - + traverse(node); } @@ -54,4 +55,20 @@ namespace SceneUtil partsys->setFreezeOnCull(false); } + void NodeMapVisitor::apply(osg::MatrixTransform& trans) + { + // Take transformation for first found node in file + const std::string nodeName = Misc::StringUtils::lowerCase(trans.getName()); + if (mMap.find(nodeName) == mMap.end()) + { + mMap[nodeName] = &trans; + } + + traverse(trans); + } + + void HideDrawablesVisitor::apply(osg::Drawable& drawable) + { + drawable.setNodeMask(0); + } } diff --git a/components/sceneutil/visitor.hpp b/components/sceneutil/visitor.hpp index 265fd6d02..bd3945296 100644 --- a/components/sceneutil/visitor.hpp +++ b/components/sceneutil/visitor.hpp @@ -1,6 +1,7 @@ #ifndef OPENMW_COMPONENTS_SCENEUTIL_VISITOR_H #define OPENMW_COMPONENTS_SCENEUTIL_VISITOR_H +#include #include // Commonly used scene graph visitors @@ -58,6 +59,35 @@ namespace SceneUtil virtual void apply(osg::Drawable& drw); }; + /// Maps names to nodes + class NodeMapVisitor : public osg::NodeVisitor + { + public: + typedef std::map > NodeMap; + + NodeMapVisitor(NodeMap& map) + : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) + , mMap(map) + {} + + void apply(osg::MatrixTransform& trans); + + private: + NodeMap& mMap; + }; + + /// Hides all attached drawables + class HideDrawablesVisitor : public osg::NodeVisitor + { + public: + HideDrawablesVisitor() + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + { + } + + void apply(osg::Drawable& drawable) override; + }; + } #endif