Move bone map to boneNodeName conversion to MdlReader

This commit is contained in:
Vsevolod Kremianskii 2021-05-16 09:07:03 +07:00
parent 5fce19c95b
commit bb2b5b255b
5 changed files with 43 additions and 56 deletions

View file

@ -114,7 +114,8 @@ void MdlReader::doLoad() {
readNodeNames(nameOffsets);
// Read nodes
unique_ptr<ModelNode> rootNode(readNode(offRootNode, nullptr));
shared_ptr<ModelNode> rootNode(readNode(offRootNode, nullptr));
prepareSkinMeshes();
// Load supermodel
shared_ptr<Model> superModel;
@ -158,7 +159,7 @@ void MdlReader::readNodeNames(const vector<uint32_t> &offsets) {
}
}
unique_ptr<ModelNode> MdlReader::readNode(uint32_t offset, const ModelNode *parent, bool anim) {
shared_ptr<ModelNode> MdlReader::readNode(uint32_t offset, const ModelNode *parent, bool anim) {
seek(kMdlDataOffset + offset);
uint16_t flags = readUint16();
@ -177,13 +178,10 @@ unique_ptr<ModelNode> MdlReader::readNode(uint32_t offset, const ModelNode *pare
throw runtime_error("Unsupported MDL node flags: " + to_string(flags));
}
string name(_nodeNames[nameIndex]);
if (!anim) {
_nodeFlags.insert(make_pair(name, flags));
}
glm::vec3 restPosition(glm::make_vec3(&positionValues[0]));
glm::quat restOrientation(orientationValues[0], orientationValues[1], orientationValues[2], orientationValues[3]);
auto node = make_unique<ModelNode>(
auto node = make_shared<ModelNode>(
move(name),
move(restPosition),
move(restOrientation),
@ -203,6 +201,10 @@ unique_ptr<ModelNode> MdlReader::readNode(uint32_t offset, const ModelNode *pare
if (flags & NodeFlags::reference) {
node->setReference(readReference());
}
if (!anim) {
_nodes.push_back(node);
_nodeFlags.insert(make_pair(name, flags));
}
vector<float> controllerData(readFloatArray(kMdlDataOffset + controllerDataArrayDef.offset, controllerDataArrayDef.count));
readControllers(controllerArrayDef.offset, controllerArrayDef.count, controllerData, anim, *node);
@ -297,22 +299,10 @@ shared_ptr<ModelNode::TriangleMesh> MdlReader::readMesh(int flags) {
vector<uint16_t> boneNodeSerial(readUint16Array(16));
ignore(4); // padding
// boneNodeSerial above is a more compact representation of the bone
// map, but it is limited to 16 bones, which is not enough for certain
// models
vector<float> boneMap(readFloatArray(kMdlDataOffset + offBones, numBones));
for (size_t i = 0; i < boneMap.size(); ++i) {
auto boneId = static_cast<uint16_t>(boneMap[i]);
if (boneId != 0xffff) {
if (boneId >= boneNodeSerial.size()) {
boneNodeSerial.resize(boneId + 1);
}
boneNodeSerial[boneId] = static_cast<uint16_t>(i);
}
}
skin = make_shared<ModelNode::Skin>();
skin->boneNodeSerial = move(boneNodeSerial);
skin->boneMap = move(boneMap);
attributes.offBoneIndices = offMdxBoneIndices;
attributes.offBoneWeights = offMdxBoneWeights;
@ -676,6 +666,24 @@ void MdlReader::readControllers(uint32_t keyOffset, uint32_t keyCount, const vec
}
}
void MdlReader::prepareSkinMeshes() {
for (auto &node : _nodes) {
if (!node->isSkinMesh()) continue;
shared_ptr<ModelNode::Skin> skin(node->mesh()->skin);
for (size_t i = 0; i < skin->boneMap.size(); ++i) {
auto boneIdx = static_cast<uint16_t>(skin->boneMap[i]);
if (boneIdx >= skin->boneNodeName.size()) {
skin->boneNodeName.resize(boneIdx + 1);
}
if (boneIdx != 0xffff) {
shared_ptr<ModelNode> boneNode(_nodes[i]);
skin->boneNodeName[boneIdx] = boneNode->name();
}
}
}
}
vector<shared_ptr<Animation>> MdlReader::readAnimations(const vector<uint32_t> &offsets) {
vector<shared_ptr<Animation>> anims;
anims.reserve(offsets.size());
@ -708,7 +716,7 @@ unique_ptr<Animation> MdlReader::readAnimation(uint32_t offset) {
ArrayDefinition eventArrayDef(readArrayDefinition());
ignore(4); // unknown
unique_ptr<ModelNode> rootNode(readNode(offRootNode, nullptr, true));
shared_ptr<ModelNode> rootNode(readNode(offRootNode, nullptr, true));
// Events
vector<Animation::Event> events;

View file

@ -77,6 +77,7 @@ private:
std::unique_ptr<StreamReader> _mdxReader;
bool _tsl { false }; /**< is this a TSL model? */
std::vector<std::string> _nodeNames;
std::vector<std::shared_ptr<ModelNode>> _nodes; /**< loaded model nodes (DFS ordering) */
std::map<std::string, uint16_t> _nodeFlags;
std::shared_ptr<graphics::Model> _model;
@ -84,7 +85,7 @@ private:
ArrayDefinition readArrayDefinition();
void readNodeNames(const std::vector<uint32_t> &offsets);
std::unique_ptr<graphics::ModelNode> readNode(uint32_t offset, const ModelNode *parent, bool anim = false);
std::shared_ptr<graphics::ModelNode> readNode(uint32_t offset, const ModelNode *parent, bool anim = false);
std::vector<std::shared_ptr<graphics::Animation>> readAnimations(const std::vector<uint32_t> &offsets);
std::unique_ptr<graphics::Animation> readAnimation(uint32_t offset);
void readControllers(uint32_t keyOffset, uint32_t keyCount, const std::vector<float> &data, bool anim, graphics::ModelNode &node);
@ -96,6 +97,8 @@ private:
std::shared_ptr<ModelNode::AABBTree> readAABBTree(uint32_t offset);
void prepareSkinMeshes();
// Controllers
void initControllerFn();

View file

@ -46,8 +46,7 @@ Model::Model(
throw invalid_argument("rootNode must not be null");
}
fillNodeLookups(_rootNode);
fillBoneNodeId();
fillNodeByName(_rootNode);
computeAABB();
for (auto &anim : animations) {
@ -55,33 +54,11 @@ Model::Model(
}
}
void Model::fillNodeLookups(const shared_ptr<ModelNode> &node) {
_nodes.push_back(node);
void Model::fillNodeByName(const shared_ptr<ModelNode> &node) {
_nodeByName.insert(make_pair(node->name(), node));
for (auto &child : node->children()) {
fillNodeLookups(child);
}
}
void Model::fillBoneNodeId() {
// In MDL files, bones reference node serial numbers (DFS ordering).
// We want them to reference node names instead.
for (auto &node : _nodes) {
if (!node->isSkinMesh()) continue;
shared_ptr<ModelNode::TriangleMesh> mesh(node->mesh());
mesh->skin->boneNodeName.resize(mesh->skin->boneNodeSerial.size());
for (size_t i = 0; i < mesh->skin->boneNodeSerial.size(); ++i) {
uint16_t nodeSerial = mesh->skin->boneNodeSerial[i];
if (nodeSerial < static_cast<int>(_nodes.size())) {
mesh->skin->boneNodeName[i] = _nodes[nodeSerial]->name();
} else {
mesh->skin->boneNodeName[i].clear();
}
}
fillNodeByName(child);
}
}

View file

@ -17,6 +17,7 @@
#pragma once
#include <memory>
#include <set>
#include <string>
#include <unordered_map>
@ -94,16 +95,13 @@ private:
std::shared_ptr<ModelNode> _rootNode;
std::unordered_map<std::string, std::shared_ptr<Animation>> _animations;
std::shared_ptr<Model> _superModel;
float _animationScale;
float _animationScale; /**< scales supermodel animations */
bool _affectedByFog;
AABB _aabb;
std::vector<std::shared_ptr<ModelNode>> _nodes;
bool _affectedByFog;
std::unordered_map<std::string, std::shared_ptr<ModelNode>> _nodeByName;
void fillNodeLookups(const std::shared_ptr<ModelNode> &node);
void fillBoneNodeId();
void fillNodeByName(const std::shared_ptr<ModelNode> &node);
void computeAABB();
};

View file

@ -40,13 +40,14 @@ namespace graphics {
class Model;
/**
* Model or animation node. Can be specialized to represent a triangle mesh, a light, an emitter and etc.
* Model or animation node. Can be specialized to represent a triangle mesh, a
* light, an emitter and etc.
*/
class ModelNode : boost::noncopyable {
public:
struct Skin {
std::vector<std::string> boneNodeName;
std::vector<uint16_t> boneNodeSerial; /**< temporary, used to fill boneNodeName above */
std::vector<std::string> boneNodeName; /**< node name per bone, used for skeletal animation */
std::vector<float> boneMap; /**< bone index per node (DFS ordering) */
};
struct UVAnimation {