void AnimatedModel::SetSkeleton(const Skeleton& skeleton, bool createBones) { if (!node_ && createBones) { URHO3D_LOGERROR("AnimatedModel not attached to a scene node, can not create bone nodes"); return; } if (isMaster_) { // Check if bone structure has stayed compatible (reloading the model.) In that case retain the old bones and animations if (skeleton_.GetNumBones() == skeleton.GetNumBones()) { Vector<Bone>& destBones = skeleton_.GetModifiableBones(); const Vector<Bone>& srcBones = skeleton.GetBones(); bool compatible = true; for (unsigned i = 0; i < destBones.Size(); ++i) { if (destBones[i].node_ && destBones[i].name_ == srcBones[i].name_ && destBones[i].parentIndex_ == srcBones[i].parentIndex_) { // If compatible, just copy the values and retain the old node and animated status Node* boneNode = destBones[i].node_; bool animated = destBones[i].animated_; destBones[i] = srcBones[i]; destBones[i].node_ = boneNode; destBones[i].animated_ = animated; } else { compatible = false; break; } } if (compatible) return; } RemoveAllAnimationStates(); // Detach the rootbone of the previous model if any if (createBones) RemoveRootBone(); skeleton_.Define(skeleton); // Merge bounding boxes from non-master models FinalizeBoneBoundingBoxes(); Vector<Bone>& bones = skeleton_.GetModifiableBones(); // Create scene nodes for the bones if (createBones) { for (Vector<Bone>::Iterator i = bones.Begin(); i != bones.End(); ++i) { // Create bones as local, as they are never to be directly synchronized over the network Node* boneNode = node_->CreateChild(i->name_, LOCAL); boneNode->AddListener(this); boneNode->SetTransform(i->initialPosition_, i->initialRotation_, i->initialScale_); // Copy the model component's temporary status boneNode->SetTemporary(IsTemporary()); i->node_ = boneNode; } for (unsigned i = 0; i < bones.Size(); ++i) { unsigned parentIndex = bones[i].parentIndex_; if (parentIndex != i && parentIndex < bones.Size()) bones[parentIndex].node_->AddChild(bones[i].node_); } } using namespace BoneHierarchyCreated; VariantMap& eventData = GetEventDataMap(); eventData[P_NODE] = node_; node_->SendEvent(E_BONEHIERARCHYCREATED, eventData); } else { // For non-master models: use the bone nodes of the master model skeleton_.Define(skeleton); // Instruct the master model to refresh (merge) its bone bounding boxes auto* master = node_->GetComponent<AnimatedModel>(); if (master && master != this) master->FinalizeBoneBoundingBoxes(); if (createBones) { Vector<Bone>& bones = skeleton_.GetModifiableBones(); for (Vector<Bone>::Iterator i = bones.Begin(); i != bones.End(); ++i) { Node* boneNode = node_->GetChild(i->name_, true); if (boneNode) boneNode->AddListener(this); i->node_ = boneNode; } } } assignBonesPending_ = !createBones; }
void AnimatedModel::SetSkeleton(const Skeleton& skeleton, bool createBones) { if (!node_ && createBones) { LOGERROR("AnimatedModel not attached to a scene node, can not create bone nodes"); return; } if (isMaster_) { // Check if bone structure has stayed compatible (reloading the model.) In that case retain the old bones and animations if (skeleton_.GetNumBones() == skeleton.GetNumBones()) { Vector<Bone>& destBones = skeleton_.GetModifiableBones(); const Vector<Bone>& srcBones = skeleton.GetBones(); bool compatible = true; for (unsigned i = 0; i < destBones.Size(); ++i) { if (destBones[i].node_ && destBones[i].name_ == srcBones[i].name_ && destBones[i].parentIndex_ == srcBones[i].parentIndex_) { // If compatible, just copy the values and retain the old node and animated status Node* boneNode = destBones[i].node_; bool animated = destBones[i].animated_; destBones[i] = srcBones[i]; destBones[i].node_ = boneNode; destBones[i].animated_ = animated; } else { compatible = false; break; } } if (compatible) return; } RemoveAllAnimationStates(); // Detach the rootbone of the previous model if any if (createBones) RemoveRootBone(); skeleton_.Define(skeleton); // Remove collision information from dummy bones that do not affect skinning, to prevent them from being merged // to the bounding box Vector<Bone>& bones = skeleton_.GetModifiableBones(); for (Vector<Bone>::Iterator i = bones.Begin(); i != bones.End(); ++i) { if (i->collisionMask_ & BONECOLLISION_BOX && i->boundingBox_.Size().Length() < M_EPSILON) i->collisionMask_ &= ~BONECOLLISION_BOX; if (i->collisionMask_ & BONECOLLISION_SPHERE && i->radius_ < M_EPSILON) i->collisionMask_ &= ~BONECOLLISION_SPHERE; } // Create scene nodes for the bones if (createBones) { for (Vector<Bone>::Iterator i = bones.Begin(); i != bones.End(); ++i) { // Create bones as local, as they are never to be directly synchronized over the network Node* boneNode = node_->CreateChild(i->name_, LOCAL); boneNode->AddListener(this); boneNode->SetTransform(i->initialPosition_, i->initialRotation_, i->initialScale_); i->node_ = boneNode; } for (unsigned i = 0; i < bones.Size(); ++i) { unsigned parentIndex = bones[i].parentIndex_; if (parentIndex != i && parentIndex < bones.Size()) bones[parentIndex].node_->AddChild(bones[i].node_); } } MarkAnimationDirty(); using namespace BoneHierarchyCreated; VariantMap& eventData = GetEventDataMap(); eventData[P_NODE] = (void*)node_; node_->SendEvent(E_BONEHIERARCHYCREATED, eventData); } else { // For non-master models: use the bone nodes of the master model skeleton_.Define(skeleton); if (createBones) { Vector<Bone>& bones = skeleton_.GetModifiableBones(); for (Vector<Bone>::Iterator i = bones.Begin(); i != bones.End(); ++i) { Node* boneNode = node_->GetChild(i->name_, true); if (boneNode) boneNode->AddListener(this); i->node_ = boneNode; } } } // Reserve space for skinning matrices skinMatrices_.Resize(skeleton_.GetNumBones()); SetGeometryBoneMappings(); assignBonesPending_ = !createBones; }