void StdMeshUpdate::Update(StdMeshInstance* instance, const StdMesh& new_mesh) const { assert(&instance->GetMesh() == OldMesh); // Update instance to represent new mesh instance->Mesh = &new_mesh; instance->BoneTransforms = std::vector<StdMeshMatrix>(new_mesh.GetSkeleton().GetNumBones(), StdMeshMatrix::Identity()); instance->BoneTransformsDirty = true; for (unsigned int i = 0; i < instance->SubMeshInstances.size(); ++i) delete instance->SubMeshInstances[i]; instance->SubMeshInstances.resize(new_mesh.GetNumSubMeshes()); for (unsigned int i = 0; i < instance->SubMeshInstances.size(); ++i) { instance->SubMeshInstances[i] = new StdSubMeshInstance(*instance, new_mesh.GetSubMesh(i), instance->GetCompletion()); // Submeshes are reset to their default material, so the submesh order is unaltered instance->SubMeshInstancesOrdered = instance->SubMeshInstances; // TODO: We might try to store the previous mesh material here } // Update child bone of attach parent. If the bone does not exist anymore // in the updated mesh, then detach the mesh from its parent if(instance->AttachParent) { if(!instance->AttachParent->SetChildBone(BoneNamesByIndex[instance->AttachParent->ChildBone])) { bool OwnChild = instance->AttachParent->OwnChild; instance->AttachParent->Parent->DetachMesh(instance->AttachParent->Number); // If the attachment owned the child instance then detach procedure // deleted the child instance. In that case we do not want to proceed // with the mesh update procedure. if(OwnChild) return; } } // Update parent bones of attach children. If a bone does not exist in the // updated mesh then detach the mesh from its parent. std::vector<unsigned int> Removal; for(StdMeshInstance::AttachedMeshIter iter = instance->AttachedMeshesBegin(); iter != instance->AttachedMeshesEnd(); ++iter) { if(!(*iter)->SetParentBone(BoneNamesByIndex[(*iter)->ParentBone])) { // Do not detach the mesh right here so we can finish iterating over // all attached meshes first. Removal.push_back((*iter)->Number); } } for(unsigned int i = 0; i < Removal.size(); ++i) instance->DetachMesh(Removal[i]); // Update the animation tree. Leaf nodes which refer to an animation that // does not exist anymore are removed. for (unsigned int i = instance->AnimationStack.size(); i > 0; --i) if(!UpdateAnimationNode(instance, new_mesh, instance->AnimationStack[i-1])) instance->StopAnimation(instance->AnimationStack[i-1]); }
bool StdMeshUpdate::UpdateAnimationNode(StdMeshInstance* instance, const StdMesh& new_mesh, StdMeshInstance::AnimationNode* node) const { switch (node->GetType()) { case StdMeshInstance::AnimationNode::LeafNode: { // Find dead animation std::map<const StdMeshAnimation*, StdCopyStrBuf>::const_iterator iter = AnimationNames.find(node->Leaf.Animation); assert(iter != AnimationNames.end()); // Update to new animation node->Leaf.Animation = new_mesh.GetSkeleton().GetAnimationByName(iter->second); if(!node->Leaf.Animation) return false; // Clamp provider value StdMeshInstance::ValueProvider* provider = node->GetPositionProvider(); C4Real min = Fix0; C4Real max = ftofix(node->GetAnimation()->Length); provider->Value = BoundBy(provider->Value, min, max); return true; } case StdMeshInstance::AnimationNode::CustomNode: { // Update bone index by bone name StdCopyStrBuf bone_name = BoneNamesByIndex[node->Custom.BoneIndex]; const StdMeshBone* bone = new_mesh.GetSkeleton().GetBoneByName(bone_name); if(!bone) return false; node->Custom.BoneIndex = bone->Index; return true; } case StdMeshInstance::AnimationNode::LinearInterpolationNode: { const bool left_result = UpdateAnimationNode(instance, new_mesh, node->GetLeftChild()); const bool right_result = UpdateAnimationNode(instance, new_mesh, node->GetRightChild()); // Remove this node completely if (!left_result && !right_result) return false; // Note that either of this also removes this node (and replaces by // the other child in the tree). if (!left_result) instance->StopAnimation(node->GetLeftChild()); if (!right_result) instance->StopAnimation(node->GetRightChild()); return true; } default: assert(false); return false; } }
void StdMeshLoader::StdMeshXML::LoadBoneAssignments(StdMesh& mesh, std::vector<StdSubMesh::Vertex>& vertices, TiXmlElement* boneassignments_elem) { for (TiXmlElement* vertexboneassignment_elem = boneassignments_elem->FirstChildElement("vertexboneassignment"); vertexboneassignment_elem != NULL; vertexboneassignment_elem = vertexboneassignment_elem->NextSiblingElement("vertexboneassignment")) { int BoneID = RequireIntAttribute(vertexboneassignment_elem, "boneindex"); int VertexIndex = RequireIntAttribute(vertexboneassignment_elem, "vertexindex"); float weight = RequireFloatAttribute(vertexboneassignment_elem, "weight"); if (VertexIndex < 0 || static_cast<unsigned int>(VertexIndex) >= vertices.size()) Error(FormatString("Vertex index in bone assignment (%d) is out of range", VertexIndex), vertexboneassignment_elem); // maybe not needed, see comment below const StdMeshBone* bone = NULL; for (unsigned int i = 0; !bone && i < mesh.GetSkeleton().GetNumBones(); ++i) if (mesh.GetSkeleton().GetBone(i).ID == BoneID) bone = &mesh.GetSkeleton().GetBone(i); if (!bone) Error(FormatString("There is no such bone with ID %d", BoneID), vertexboneassignment_elem); // Find first bone assignment with a zero weight (i.e. is unused) StdSubMesh::Vertex& vertex = vertices[VertexIndex]; // Check quickly if all weight slots are used if (vertex.bone_weight[StdMeshVertex::MaxBoneWeightCount - 1] != 0) { Error(FormatString("Vertex %d is influenced by more than %d bones", VertexIndex, static_cast<int>(StdMeshVertex::MaxBoneWeightCount)), vertexboneassignment_elem); } for (size_t weight_index = 0; weight_index < StdMeshVertex::MaxBoneWeightCount; ++weight_index) { if (vertex.bone_weight[weight_index] == 0) { vertex.bone_weight[weight_index] = weight; vertex.bone_index[weight_index] = bone->Index; break; } } } // Normalize vertex bone assignment weights (this is not guaranteed in the // Ogre file format). for (unsigned int i = 0; i < vertices.size(); ++i) { StdSubMesh::Vertex& vertex = vertices[i]; float sum = 0.0; for (float weight : vertex.bone_weight) sum += weight; if (sum != 0) for (float &weight : vertex.bone_weight) weight /= sum; else vertex.bone_weight[0] = 1.0f; } }