Start rendering npc's

pull/541/head
Kyle Cooley 7 years ago committed by Andrei Kortunov
parent 5fa9b32e76
commit 8444ee9981

@ -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

@ -0,0 +1,196 @@
#include "actor.hpp"
#include <iostream>
#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 <components/sceneutil/visitor.hpp>
#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<const CSMWorld::Record<ESM::NPC>& >(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<osg::Node> temp = sceneMgr->getInstance(skeletonModel);
mSkeleton = dynamic_cast<SceneUtil::Skeleton*>(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<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())
{
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<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
mSkeleton->markDirty();
mSkeleton->setActive(SceneUtil::Skeleton::Active);
}
}
}

@ -0,0 +1,51 @@
#ifndef OPENCS_VIEW_RENDER_ACTOR_H
#define OPENCS_VIEW_RENDER_ACTOR_H
#include <string>
#include <osg/ref_ptr>
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<osg::Group> mBaseNode;
};
}
#endif

@ -1,6 +1,8 @@
#include "object.hpp"
#include <stdexcept>
#include <string>
#include <iostream>
#include <osg/Depth>
#include <osg/Group>
@ -11,6 +13,7 @@
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/PrimitiveSet>
#include <osg/MatrixTransform>
#include <osgFX/Scribe>
@ -28,7 +31,9 @@
#include <components/sceneutil/lightutil.hpp>
#include <components/sceneutil/lightmanager.hpp>
#include <components/fallback/fallback.hpp>
#include <components/sceneutil/attach.hpp>
#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;
}
@ -125,11 +131,20 @@ void CSVRender::Object::update()
else
{
try
{
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)
{
// TODO: use error marker mesh

@ -91,32 +91,6 @@ namespace
std::vector<osg::ref_ptr<osg::Node> > mToRemove;
};
class NodeMapVisitor : public osg::NodeVisitor
{
public:
typedef std::map<std::string, osg::ref_ptr<osg::MatrixTransform> > 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());
@ -1042,7 +1016,7 @@ namespace MWRender
{
if (!mNodeMapCreated && mObjectRoot)
{
NodeMapVisitor visitor(mNodeMap);
SceneUtil::NodeMapVisitor visitor(mNodeMap);
mObjectRoot->accept(visitor);
mNodeMapCreated = true;
}

@ -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

@ -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

@ -1,5 +1,6 @@
#include "visitor.hpp"
#include <osg/Drawable>
#include <osg/MatrixTransform>
#include <osgParticle/ParticleSystem>
@ -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);
}
}

@ -1,6 +1,7 @@
#ifndef OPENMW_COMPONENTS_SCENEUTIL_VISITOR_H
#define OPENMW_COMPONENTS_SCENEUTIL_VISITOR_H
#include <osg/MatrixTransform>
#include <osg/NodeVisitor>
// 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<std::string, osg::ref_ptr<osg::MatrixTransform> > 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

Loading…
Cancel
Save