Exclusively use node names to bind animations to model nodes

This both makes for a cleaner API and fixes stunt animations.
This commit is contained in:
Vsevolod Kremianskii 2021-05-15 15:13:38 +07:00
parent bb9d0e1f41
commit 2d3540b73f
16 changed files with 83 additions and 138 deletions

View file

@ -78,15 +78,12 @@ shared_ptr<ModelSceneNode> Creature::buildModel() {
if (!headModelName.empty()) {
shared_ptr<Model> headModel(Models::instance().get(headModelName));
if (headModel) {
shared_ptr<ModelNode> headHook(bodyModel->getNodeByName(g_headHookNode));
if (headHook) {
auto headSceneNode = make_shared<ModelSceneNode>(headModel, ModelUsage::Creature, _sceneGraph, this);
headSceneNode->setInanimateNodes(bodyModel->getAncestorNodes(headHook->id()));
bodySceneNode->attach(headHook->id(), headSceneNode);
if (maskModel) {
auto maskSceneNode = make_shared<ModelSceneNode>(maskModel, ModelUsage::Equipment, _sceneGraph, this);
headSceneNode->attach(g_maskHookNode, maskSceneNode);
}
auto headSceneNode = make_shared<ModelSceneNode>(headModel, ModelUsage::Creature, _sceneGraph, this);
headSceneNode->setInanimateNodes(bodyModel->getAncestorNodes(g_headHookNode));
bodySceneNode->attach(g_headHookNode, headSceneNode);
if (maskModel) {
auto maskSceneNode = make_shared<ModelSceneNode>(maskModel, ModelUsage::Equipment, _sceneGraph, this);
headSceneNode->attach(g_maskHookNode, maskSceneNode);
}
}
}

View file

@ -40,10 +40,10 @@ Animation::Animation(
_rootNode(move(rootNode)),
_events(move(events)) {
fillNodeByName();
fillNodeLookups();
}
void Animation::fillNodeByName() {
void Animation::fillNodeLookups() {
stack<shared_ptr<ModelNode>> nodes;
nodes.push(_rootNode);
@ -51,7 +51,6 @@ void Animation::fillNodeByName() {
shared_ptr<ModelNode> node(nodes.top());
nodes.pop();
_nodeById.insert(make_pair(node->id(), node));
_nodeByName.insert(make_pair(node->name(), node));
for (auto &child : node->children()) {
@ -60,10 +59,6 @@ void Animation::fillNodeByName() {
}
}
shared_ptr<ModelNode> Animation::getNodeById(uint16_t nodeId) const {
return getFromLookupOrNull(_nodeById, nodeId);
}
shared_ptr<ModelNode> Animation::getNodeByName(const string &name) const {
return getFromLookupOrNull(_nodeByName, name);
}

View file

@ -41,7 +41,6 @@ public:
std::shared_ptr<ModelNode> rootNode,
std::vector<Event> events);
std::shared_ptr<ModelNode> getNodeById(uint16_t nodeId) const;
std::shared_ptr<ModelNode> getNodeByName(const std::string &name) const;
const std::string &name() const { return _name; }
@ -57,10 +56,9 @@ private:
std::shared_ptr<ModelNode> _rootNode;
std::vector<Event> _events;
std::unordered_map<uint16_t, std::shared_ptr<ModelNode>> _nodeById;
std::unordered_map<std::string, std::shared_ptr<ModelNode>> _nodeByName;
void fillNodeByName();
void fillNodeLookups();
};
} // namespace graphics

View file

@ -179,15 +179,14 @@ unique_ptr<ModelNode> MdlReader::readNode(uint32_t offset, const ModelNode *pare
if (flags & 0xf408) {
throw runtime_error("Unsupported MDL node flags: " + to_string(flags));
}
if (!anim) {
_nodeFlags.insert(make_pair(nodeId, 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>(
nodeId,
move(name),
move(restPosition),
move(restOrientation),
@ -649,7 +648,7 @@ shared_ptr<ModelNode::Reference> MdlReader::readReference() {
void MdlReader::readControllers(uint32_t keyOffset, uint32_t keyCount, const vector<float> &data, bool anim, ModelNode &node) {
uint16_t nodeFlags;
if (anim) {
nodeFlags = _nodeFlags.find(node.id())->second;
nodeFlags = _nodeFlags.find(node.name())->second;
} else {
nodeFlags = node.flags();
}

View file

@ -77,7 +77,7 @@ private:
std::unique_ptr<StreamReader> _mdxReader;
bool _tsl { false }; /**< is this a TSL model? */
std::vector<std::string> _nodeNames;
std::map<uint16_t, uint16_t> _nodeFlags;
std::map<std::string, uint16_t> _nodeFlags;
std::shared_ptr<graphics::Model> _model;
void doLoad() override;

View file

@ -124,19 +124,31 @@ static inline void ensureNumColumnsEquals(int type, int expected, int actual) {
}
}
void MdlReader::readPositionController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
void MdlReader::readFloatController(const ControllerKey &key, const vector<float> &data, AnimatedProperty<float> &prop) {
ensureNumColumnsEquals(key.type, 1, key.numColumns);
for (uint16_t i = 0; i < key.numRows; ++i) {
float time = data[key.timeIndex + i];
float value = data[key.dataIndex + i];
prop.addFrame(time, value);
}
prop.update();
}
void MdlReader::readVectorController(const ControllerKey &key, const vector<float> &data, AnimatedProperty<glm::vec3> &prop) {
bool bezier = key.numColumns & kFlagBezier;
int numColumns = key.numColumns & ~kFlagBezier;
ensureNumColumnsEquals(key.type, 3, numColumns);
for (uint16_t i = 0; i < key.numRows; ++i) {
int rowTimeIdx = key.timeIndex + i;
int rowDataIdx = key.dataIndex + (bezier ? 9 : 3) * i;
float time = data[rowTimeIdx];
glm::vec3 position(glm::make_vec3(&data[rowDataIdx]));
node.position().addFrame(time, move(position));
float time = data[key.timeIndex + i];
glm::vec3 value(glm::make_vec3(&data[key.dataIndex + (bezier ? 9 : 3) * i]));
prop.addFrame(time, value);
}
node.position().update();
prop.update();
}
void MdlReader::readPositionController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readVectorController(key, data, node.position());
}
void MdlReader::readOrientationController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
@ -191,29 +203,6 @@ void MdlReader::readOrientationController(const ControllerKey &key, const vector
node.orientation().update();
}
void MdlReader::readFloatController(const ControllerKey &key, const vector<float> &data, AnimatedProperty<float> &prop) {
ensureNumColumnsEquals(key.type, 1, key.numColumns);
for (uint16_t i = 0; i < key.numRows; ++i) {
float time = data[key.timeIndex + i];
float value = data[key.dataIndex + i];
prop.addFrame(time, value);
}
prop.update();
}
void MdlReader::readVectorController(const ControllerKey &key, const vector<float> &data, AnimatedProperty<glm::vec3> &prop) {
bool bezier = key.numColumns & kFlagBezier;
int numColumns = key.numColumns & ~kFlagBezier;
ensureNumColumnsEquals(key.type, 3, numColumns);
for (uint16_t i = 0; i < key.numRows; ++i) {
float time = data[key.timeIndex + i];
glm::vec3 value(glm::make_vec3(&data[key.dataIndex + (bezier ? 9 : 3) * i]));
prop.addFrame(time, value);
}
prop.update();
}
void MdlReader::readScaleController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.scale());
}

View file

@ -52,7 +52,6 @@ Model::Model(
void Model::fillNodeLookups(const shared_ptr<ModelNode> &node) {
_nodes.push_back(node);
_nodeById.insert(make_pair(node->id(), node));
_nodeByName.insert(make_pair(node->name(), node));
for (auto &child : node->children()) {
@ -62,20 +61,20 @@ void Model::fillNodeLookups(const shared_ptr<ModelNode> &node) {
void Model::fillBoneNodeId() {
// In MDL files, bones reference node serial numbers (DFS ordering).
// We want them to reference node identifiers, for simplicity.
// 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->boneNodeId.resize(mesh->skin->boneNodeSerial.size());
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->boneNodeId[i] = _nodes[nodeSerial]->id();
mesh->skin->boneNodeName[i] = _nodes[nodeSerial]->name();
} else {
mesh->skin->boneNodeId[i] = 0xffff;
mesh->skin->boneNodeName[i].clear();
}
}
}
@ -84,7 +83,7 @@ void Model::fillBoneNodeId() {
void Model::computeAABB() {
_aabb.reset();
for (auto &node : _nodeById) {
for (auto &node : _nodeByName) {
shared_ptr<ModelNode::TriangleMesh> mesh(node.second->mesh());
if (mesh) {
_aabb.expand(mesh->mesh->aabb() * node.second->absoluteTransform());
@ -100,10 +99,6 @@ void Model::addAnimation(shared_ptr<Animation> animation) {
_animations.insert(make_pair(animation->name(), move(animation)));
}
shared_ptr<ModelNode> Model::getNodeById(uint16_t nodeId) const {
return getFromLookupOrNull(_nodeById, nodeId);
}
shared_ptr<ModelNode> Model::getNodeByName(const string &name) const {
return getFromLookupOrNull(_nodeByName, name);
}
@ -117,7 +112,7 @@ shared_ptr<ModelNode> Model::getNodeByNameRecursive(const string &name) const {
}
shared_ptr<ModelNode> Model::getAABBNode() const {
for (auto &node : _nodeById) {
for (auto &node : _nodeByName) {
if (node.second->isAABBMesh()) return node.second;
}
return nullptr;
@ -151,13 +146,13 @@ vector<string> Model::getAnimationNames() const {
return move(result);
}
set<uint16_t> Model::getAncestorNodes(uint16_t parentId) const {
set<uint16_t> result;
set<string> Model::getAncestorNodes(const string &parentName) const {
set<string> result;
auto maybeParent = _nodeById.find(parentId);
if (maybeParent != _nodeById.end()) {
auto maybeParent = _nodeByName.find(parentName);
if (maybeParent != _nodeByName.end()) {
for (const ModelNode *node = maybeParent->second->parent(); node; node = node->parent()) {
result.insert(node->id());
result.insert(node->name());
}
}

View file

@ -64,13 +64,12 @@ public:
bool isAffectedByFog() const { return _affectedByFog; }
std::shared_ptr<ModelNode> getNodeById(uint16_t nodeId) const;
std::shared_ptr<ModelNode> getNodeByName(const std::string &name) const;
std::shared_ptr<ModelNode> getNodeByNameRecursive(const std::string &name) const;
std::shared_ptr<ModelNode> getAABBNode() const;
std::shared_ptr<Animation> getAnimation(const std::string &name) const;
std::vector<std::string> getAnimationNames() const;
std::set<uint16_t> getAncestorNodes(uint16_t parentId) const;
std::set<std::string> getAncestorNodes(const std::string &parentName) const;
const std::string &name() const { return _name; }
Classification classification() const { return _classification; }
@ -91,7 +90,6 @@ private:
bool _affectedByFog;
std::vector<std::shared_ptr<ModelNode>> _nodes;
std::unordered_map<uint16_t, std::shared_ptr<ModelNode>> _nodeById;
std::unordered_map<std::string, std::shared_ptr<ModelNode>> _nodeByName;
AABB _aabb;

View file

@ -28,13 +28,11 @@ namespace reone {
namespace graphics {
ModelNode::ModelNode(
uint16_t id,
string name,
glm::vec3 restPosition,
glm::quat restOrientation,
const ModelNode *parent
) :
_id(id),
_name(move(name)),
_restPosition(move(restPosition)),
_restOrientation(move(restOrientation)),

View file

@ -45,8 +45,8 @@ class Model;
class ModelNode : boost::noncopyable {
public:
struct Skin {
std::vector<uint16_t> boneNodeSerial; /**< temporary, used to fill boneNodeId below */
std::vector<uint16_t> boneNodeId; /**< used in skeletal animation */
std::vector<std::string> boneNodeName;
std::vector<uint16_t> boneNodeSerial; /**< temporary, used to fill boneNodeName above */
};
struct UVAnimation {
@ -171,7 +171,6 @@ public:
};
ModelNode(
uint16_t id,
std::string name,
glm::vec3 restPosition,
glm::quat restOrientation,
@ -183,7 +182,6 @@ public:
std::vector<uint32_t> getFacesByMaterial(uint32_t material) const;
uint16_t id() const { return _id; }
const std::string &name() const { return _name; }
uint16_t flags() const { return _flags; }
const ModelNode *parent() const { return _parent; }
@ -297,7 +295,6 @@ public:
// END Keyframes
private:
uint16_t _id; /**< node identifier, matches id within supermodel */
std::string _name;
const ModelNode *_parent;

View file

@ -57,10 +57,7 @@ unique_ptr<SceneGraph> SceneBuilder::build() {
} else {
shared_ptr<ModelNode> refModelNode(model->model()->getNodeByName(_cameraNodeName));
if (refModelNode) {
shared_ptr<ModelNodeSceneNode> refSceneNode(model->getNodeById(refModelNode->id()));
if (refSceneNode) {
camera->setLocalTransform(refModelNode->absoluteTransform() * _cameraTransform);
}
camera->setLocalTransform(refModelNode->absoluteTransform() * _cameraTransform);
}
}

View file

@ -47,9 +47,9 @@ LightSceneNode::LightSceneNode(const ModelSceneNode *model, shared_ptr<ModelNode
if (!model) {
throw invalid_argument("model must not be null");
}
_radius = modelNode->radius().getByFrameOrElse(0, 1.0f);
_multiplier = modelNode->multiplier().getByFrameOrElse(0, 1.0f);
_color = modelNode->color().getByFrameOrElse(0, glm::vec3(1.0f));
_radius = modelNode->radius().getByFrameOrElse(0, 0.0f);
_multiplier = modelNode->multiplier().getByFrameOrElse(0, 0.0f);
_color = modelNode->color().getByFrameOrElse(0, glm::vec3(0.0f));
}
void LightSceneNode::drawLensFlares(const ModelNode::LensFlare &flare) {

View file

@ -293,10 +293,10 @@ void MeshSceneNode::drawSingle(bool shadowPass) {
// Offset bone matrices by 1 to account for negative bone indices
uniforms.skeletal->bones[0] = glm::mat4(1.0f);
for (size_t i = 1; i < 1 + mesh->skin->boneNodeId.size() && i < kMaxBones; ++i) {
uint16_t nodeId = mesh->skin->boneNodeId[i - 1];
if (nodeId != 0xffff) {
shared_ptr<ModelNodeSceneNode> bone(_model->getNodeById(nodeId));
for (size_t i = 1; i < 1 + mesh->skin->boneNodeName.size() && i < kMaxBones; ++i) {
string nodeName = mesh->skin->boneNodeName[i - 1];
if (!nodeName.empty()) {
shared_ptr<ModelNodeSceneNode> bone(_model->getNodeByName(nodeName));
if (bone && bone->type() == SceneNodeType::Mesh) {
uniforms.skeletal->bones[i] = _modelNode->absoluteTransformInverse() * bone->boneTransform() * _modelNode->absoluteTransform();
}

View file

@ -78,12 +78,11 @@ void ModelSceneNode::buildNodeTree(shared_ptr<ModelNode> node, SceneNode *parent
sceneNode->setLocalTransform(node->localTransform());
parent->addChild(sceneNode);
}
_nodeById.insert(make_pair(node->id(), sceneNode));
_nodeByName.insert(make_pair(node->name(), sceneNode));
if (node->isReference()) {
auto model = make_shared<ModelSceneNode>(node->reference()->model, _usage, _sceneGraph, _animEventListener);
attach(node->id(), move(model));
attach(node->name(), move(model));
}
for (auto &child : node->children()) {
buildNodeTree(child, sceneNode.get());
@ -101,14 +100,14 @@ void ModelSceneNode::update(float dt) {
void ModelSceneNode::computeAABB() {
_aabb.reset();
for (auto &node : _nodeById) {
for (auto &node : _nodeByName) {
if (node.second->type() == SceneNodeType::Mesh) {
shared_ptr<ModelNode> modelNode(node.second->modelNode());
AABB modelSpaceAABB(modelNode->mesh()->mesh->aabb() * modelNode->absoluteTransform());
_aabb.expand(modelSpaceAABB);
}
}
for (auto &attachment : _attachmentByNodeId) {
for (auto &attachment : _attachments) {
if (attachment.second->type() == SceneNodeType::Model) {
AABB modelSpaceAABB(attachment.second->aabb() * attachment.second->absoluteTransform() * _absTransformInv);
_aabb.expand(modelSpaceAABB);
@ -136,7 +135,7 @@ void ModelSceneNode::signalEvent(const string &name) {
debug(boost::format("Model '%s': event '%s' signalled") % _model->name() % name, 3);
if (name == "detonate") {
for (auto &node : _nodeById) {
for (auto &node : _nodeByName) {
if (node.second->type() == SceneNodeType::Emitter) {
static_pointer_cast<EmitterSceneNode>(node.second)->detonate();
}
@ -146,36 +145,25 @@ void ModelSceneNode::signalEvent(const string &name) {
}
}
void ModelSceneNode::attach(uint16_t parentId, shared_ptr<SceneNode> node) {
auto maybeParent = _nodeById.find(parentId);
if (maybeParent == _nodeById.end()) return;
void ModelSceneNode::attach(const string &parentName, shared_ptr<SceneNode> node) {
auto maybeParent = _nodeByName.find(parentName);
if (maybeParent == _nodeByName.end()) return;
shared_ptr<ModelNodeSceneNode> parent(maybeParent->second);
parent->addChild(node);
_attachmentByNodeId.insert(make_pair(parentId, node));
_attachments.insert(make_pair(parentName, node));
computeAABB();
}
void ModelSceneNode::attach(const string &parentName, shared_ptr<SceneNode> node) {
auto parent = _model->getNodeByName(parentName);
if (parent) {
attach(parent->id(), node);
}
}
shared_ptr<ModelNodeSceneNode> ModelSceneNode::getNodeById(uint16_t nodeId) const {
return getFromLookupOrNull(_nodeById, nodeId);
}
shared_ptr<ModelNodeSceneNode> ModelSceneNode::getNodeByName(const string &name) const {
return getFromLookupOrNull(_nodeByName, name);
}
shared_ptr<SceneNode> ModelSceneNode::getAttachment(const string &parentName) const {
auto parent = _model->getNodeByName(parentName);
return parent ? getFromLookupOrNull(_attachmentByNodeId, parent->id()) : nullptr;
return parent ? getFromLookupOrNull(_attachments, parent->name()) : nullptr;
}
void ModelSceneNode::setDiffuseTexture(shared_ptr<Texture> texture) {
@ -187,12 +175,12 @@ void ModelSceneNode::setDiffuseTexture(shared_ptr<Texture> texture) {
}
void ModelSceneNode::setAppliedForce(glm::vec3 force) {
for (auto &node : _nodeById) {
for (auto &node : _nodeByName) {
if (node.second->type() == SceneNodeType::Mesh) {
static_pointer_cast<MeshSceneNode>(node.second)->setAppliedForce(force);
}
}
for (auto &attachment : _attachmentByNodeId) {
for (auto &attachment : _attachments) {
if (attachment.second->type() == SceneNodeType::Model) {
static_pointer_cast<ModelSceneNode>(attachment.second)->setAppliedForce(force);
}

View file

@ -53,7 +53,6 @@ public:
void computeAABB();
void signalEvent(const std::string &name);
std::shared_ptr<ModelNodeSceneNode> getNodeById(uint16_t nodeId) const;
std::shared_ptr<ModelNodeSceneNode> getNodeByName(const std::string &name) const;
std::shared_ptr<graphics::Model> model() const { return _model; }
@ -71,13 +70,12 @@ public:
bool isAnimationFinished() const;
void setInanimateNodes(std::set<uint16_t> nodes) { _inanimateNodes = std::move(nodes); }
void setInanimateNodes(std::set<std::string> nodes) { _inanimateNodes = std::move(nodes); }
// END Animation
// Attachments
void attach(uint16_t parentId, std::shared_ptr<SceneNode> node);
void attach(const std::string &parentName, std::shared_ptr<SceneNode> node);
std::shared_ptr<SceneNode> getAttachment(const std::string &parentName) const;
@ -109,7 +107,7 @@ private:
std::shared_ptr<graphics::LipAnimation> lipAnim;
AnimationProperties properties;
float time { 0.0f };
std::unordered_map<uint16_t, AnimationState> stateById;
std::unordered_map<std::string, AnimationState> stateByName;
// Flags
@ -128,10 +126,8 @@ private:
// Lookups
std::unordered_map<uint16_t, std::shared_ptr<ModelNodeSceneNode>> _nodeById;
std::unordered_map<std::string, std::shared_ptr<ModelNodeSceneNode>> _nodeByName;
std::unordered_map<uint16_t, std::shared_ptr<SceneNode>> _attachmentByNodeId;
std::unordered_map<std::string, std::shared_ptr<SceneNode>> _attachments;
// END Lookups
@ -139,7 +135,7 @@ private:
AnimationChannel _animChannels[kNumAnimationChannels];
AnimationBlendMode _animBlendMode { AnimationBlendMode::Single };
std::set<uint16_t> _inanimateNodes; /**< node identifiers that are not to be animated */
std::set<std::string> _inanimateNodes; /**< names of nodes that are not to be animated */
// END Animation

View file

@ -87,7 +87,7 @@ void ModelSceneNode::playAnimation(shared_ptr<Animation> anim, shared_ptr<LipAni
// Optionally propagate animation to attachments
if (properties.flags & AnimationFlags::propagate) {
for (auto &attachment : _attachmentByNodeId) {
for (auto &attachment : _attachments) {
if (attachment.second->type() == SceneNodeType::Model) {
static_pointer_cast<ModelSceneNode>(attachment.second)->playAnimation(anim, lipAnim, properties);
}
@ -106,7 +106,7 @@ void ModelSceneNode::resetAnimationChannel(AnimationChannel &channel, shared_ptr
channel.lipAnim = move(lipAnim);
channel.properties = move(properties);
channel.time = 0.0f;
channel.stateById.clear();
channel.stateByName.clear();
channel.finished = false;
channel.transition = false;
channel.freeze = false;
@ -160,14 +160,14 @@ void ModelSceneNode::updateAnimationChannel(AnimationChannel &channel, float dt)
// Compute animation states only when this model is not culled
if (!_culled) {
float time = channel.transition ? channel.anim->transitionTime() : channel.time;
channel.stateById.clear();
channel.stateByName.clear();
computeAnimationStates(channel, time, *_model->rootNode());
}
}
void ModelSceneNode::computeAnimationStates(AnimationChannel &channel, float time, const ModelNode &modelNode) {
shared_ptr<ModelNode> animNode(channel.anim->getNodeById(modelNode.id()));
if (animNode && _inanimateNodes.count(modelNode.id()) == 0) {
shared_ptr<ModelNode> animNode(channel.anim->getNodeByName(modelNode.name()));
if (animNode && _inanimateNodes.count(modelNode.name()) == 0) {
AnimationState state;
state.flags = 0;
@ -231,7 +231,7 @@ void ModelSceneNode::computeAnimationStates(AnimationChannel &channel, float tim
state.selfIllumColor = move(animSelfIllum);
}
channel.stateById.insert(make_pair(modelNode.id(), move(state)));
channel.stateByName.insert(make_pair(modelNode.name(), move(state)));
}
for (auto &child : modelNode.children()) {
@ -240,8 +240,8 @@ void ModelSceneNode::computeAnimationStates(AnimationChannel &channel, float tim
}
void ModelSceneNode::applyAnimationStates(const ModelNode &modelNode) {
auto maybeSceneNode = _nodeById.find(modelNode.id());
if (maybeSceneNode != _nodeById.end()) {
auto maybeSceneNode = _nodeByName.find(modelNode.name());
if (maybeSceneNode != _nodeByName.end()) {
shared_ptr<SceneNode> sceneNode(maybeSceneNode->second);
AnimationState combined;
@ -249,8 +249,8 @@ void ModelSceneNode::applyAnimationStates(const ModelNode &modelNode) {
case AnimationBlendMode::Single:
case AnimationBlendMode::Blend: {
bool blend = _animBlendMode == AnimationBlendMode::Blend && _animChannels[0].transition;
auto state1 = _animChannels[0].stateById.count(modelNode.id()) > 0 ? _animChannels[0].stateById[modelNode.id()] : AnimationState();
auto state2 = _animChannels[1].stateById.count(modelNode.id()) > 0 ? _animChannels[1].stateById[modelNode.id()] : AnimationState();
auto state1 = _animChannels[0].stateByName.count(modelNode.name()) > 0 ? _animChannels[0].stateByName[modelNode.name()] : AnimationState();
auto state2 = _animChannels[1].stateByName.count(modelNode.name()) > 0 ? _animChannels[1].stateByName[modelNode.name()] : AnimationState();
if (blend && state1.flags & AnimationStateFlags::transform && state2.flags & AnimationStateFlags::transform) {
float factor = glm::min(1.0f, _animChannels[0].time / _animChannels[0].anim->transitionTime());
glm::vec3 scale1, scale2, translation1, translation2, skew;
@ -278,8 +278,8 @@ void ModelSceneNode::applyAnimationStates(const ModelNode &modelNode) {
}
case AnimationBlendMode::Overlay:
for (int i = 0; i < kNumAnimationChannels; ++i) {
auto maybeState = _animChannels[i].stateById.find(modelNode.id());
if (maybeState != _animChannels[i].stateById.end()) {
auto maybeState = _animChannels[i].stateByName.find(modelNode.name());
if (maybeState != _animChannels[i].stateByName.end()) {
const AnimationState &state = maybeState->second;
if ((state.flags & AnimationStateFlags::transform) && !(combined.flags & AnimationStateFlags::transform)) {
combined.flags |= AnimationStateFlags::transform;
@ -302,8 +302,6 @@ void ModelSceneNode::applyAnimationStates(const ModelNode &modelNode) {
if (combined.flags & AnimationStateFlags::transform) {
sceneNode->setLocalTransform(combined.transform);
} else if (!modelNode.isSkinMesh()) {
sceneNode->setLocalTransform(modelNode.localTransform());
}
if (combined.flags & AnimationStateFlags::alpha) {
static_pointer_cast<MeshSceneNode>(sceneNode)->setAlpha(combined.alpha);
@ -319,7 +317,7 @@ void ModelSceneNode::applyAnimationStates(const ModelNode &modelNode) {
}
void ModelSceneNode::computeBoneTransforms() {
for (auto &node : _nodeById) {
for (auto &node : _nodeByName) {
glm::mat4 transform(1.0f);
transform = node.second->absoluteTransform() * node.second->modelNode()->absoluteTransformInverse(); // make relative to the rest pose (world space)
transform = _absTransformInv * transform; // world space to model space