diff options
Diffstat (limited to 'Runtime/Animation/OptimizeTransformHierarchy.cpp')
-rw-r--r-- | Runtime/Animation/OptimizeTransformHierarchy.cpp | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/Runtime/Animation/OptimizeTransformHierarchy.cpp b/Runtime/Animation/OptimizeTransformHierarchy.cpp new file mode 100644 index 0000000..85e30c5 --- /dev/null +++ b/Runtime/Animation/OptimizeTransformHierarchy.cpp @@ -0,0 +1,303 @@ +#include "UnityPrefix.h" + +#include "OptimizeTransformHierarchy.h" + +#include "Runtime/BaseClasses/GameObject.h" +#include "Runtime/Animation/Animator.h" +#include "Runtime/Filters/Deformation/SkinnedMeshFilter.h" +#include "Runtime/Filters/Mesh/LodMesh.h" +#include "Runtime/Misc/GameObjectUtility.h" +#include "Runtime/Animation/Avatar.h" +#include "Runtime/Animation/AvatarBuilder.h" +#include "Runtime/mecanim/animation/avatar.h" +#include "Runtime/mecanim/skeleton/skeleton.h" + +#include <map> + +using namespace std; +using namespace mecanim::animation; +using namespace mecanim::skeleton; +using namespace mecanim; + +// static function forward declarations +static void FlattenHierarchy (GameObject& gameObject); +static void FlattenHierarchyRecurse (Transform& transform, Transform& root); + +void OptimizeTransformHierarchy(GameObject& root, const UnityStr* exposedTransforms, size_t exposedTransformCount) +{ + Animator* animator = root.QueryComponent(Animator); + if (!animator) + return; + + if(!animator->IsOptimizable()) + return; + + Transform* rootTransform = animator->GetAvatarRoot(); + if(!rootTransform) + return; + + GameObject& effectiveRoot = rootTransform->GetGameObject(); + + // 1. Take care of the SkinnedMeshRenderers, so that the will work without the transform hierarchy. + dynamic_array<SkinnedMeshRenderer*> skins (kMemTempAlloc); + GetComponentsInChildren(effectiveRoot, true, ClassID(SkinnedMeshRenderer), reinterpret_cast<dynamic_array<Unity::Component*>&>(skins)); + for (int i=0;i<skins.size();i++) + { + SkinnedMeshRenderer& skin = *skins[i]; + + // The root bone of the SkinnedMeshRenderer will always be itself in optimized mode. + // We'll directly set the correct value to it through the transform binding. + if (skin.GetRootBone()) + { + const Transform& skinRoot = *skin.GetRootBone(); + Transform& skinTransform = skin.GetComponent(Transform); + skinTransform.SetPositionAndRotation(skinRoot.GetPosition(), skinRoot.GetRotation()); + // TODO: handle scale if needed later + } + skin.SetRootBone(NULL); + + // Clear the Transform array, we'll use skeleton indices array instead. + skin.SetBones(dynamic_array<PPtr<Transform> > ()); + } + + // 2. Flatten the transform hierarchy + FlattenHierarchy (effectiveRoot); + + // 3. Remove the unnecessary transforms + // Here, we can safely remove transforms that are: + // a) human bones, because the Avatar will take care of them + // b) referred by SkinnedMeshRenderer, because the SkinnedMeshRenderer can work with skeleton indices. + UNITY_VECTOR(kMemTempAlloc, UnityStr) exposedTransformNames(exposedTransformCount); + for (int i = 0; i < exposedTransformCount; i++) + exposedTransformNames[i] = GetLastPathNameComponent(exposedTransforms[i].c_str(), exposedTransforms[i].size()); + RemoveUnnecessaryTransforms (root, NULL, + exposedTransformCount ? &(exposedTransformNames[0]) : NULL, + exposedTransformCount, false); + + // Finally, set the Animator to be optimized mode. + animator->SetHasTransformHierarchy(false); +} + +void DeoptimizeTransformHierarchy(Unity::GameObject& root) +{ + Animator* animator = root.QueryComponent(Animator); + if (!animator) + return; + + if(!animator->IsOptimizable()) + return; + + Transform* avatarRoot = animator->GetAvatarRoot(); + if(!avatarRoot) + return; + GameObject& effectiveRoot = avatarRoot->GetGameObject(); + + // 1. Figure out the skeletonPaths from the unstripped avatar + const Avatar& unstrippedAvatar = *animator->GetAvatar(); + const TOSVector& tos = unstrippedAvatar.GetTOS(); + const AvatarConstant* avatarConstant = unstrippedAvatar.GetAsset(); + const Skeleton& skeleton = *avatarConstant->m_AvatarSkeleton; + const SkeletonPose& skeletonPose = *avatarConstant->m_DefaultPose; + + UNITY_VECTOR(kMemTempAlloc, UnityStr) skeletonPaths; + for (int i = 0; i < skeleton.m_Count; i++) + { + UnityStr path = ""; + TOSVector::const_iterator it = tos.find(skeleton.m_ID[i]); + if (it != tos.end()) + path = it->second; + skeletonPaths.push_back(path.c_str()); + } + + // 2. Restore the original transform hierarchy + // Prerequisite: skeletonPaths follow pre-order traversal + Transform& rootTransform = effectiveRoot.GetComponent(Transform); + for (int i = 1; i < skeletonPaths.size(); i++) // start from 1, skip the root transform because it will always be there. + { + const UnityStr& unflattenPath = skeletonPaths[i]; + UnityStr transformName = GetLastPathNameComponent(unflattenPath); + Transform* curTransform = FindTransformWithName(&rootTransform, transformName.c_str()); + if (curTransform == NULL) + { + // Create a new GameObject with just transform component + GameObject& go = CreateGameObjectWithHideFlags (transformName, true, 0, "Transform", NULL); + curTransform = go.QueryComponent(Transform); + } + + // insert it at the right position of the hierarchy + Transform* parentTransform = &rootTransform; + UnityStr parentPath = DeleteLastPathNameComponent(unflattenPath); + if (parentPath.length() > 0) + parentTransform = FindRelativeTransformWithPath(rootTransform, parentPath.c_str()); + curTransform->SetParent(parentTransform); + + Vector3f t, s; Quaternionf q; + xform2unityNoNormalize(skeletonPose.m_X[i], t, q, s); + curTransform->SetLocalPositionWithoutNotification(t); + curTransform->SetLocalRotationWithoutNotification(q); + curTransform->SetLocalScaleWithoutNotification(s); + } + + // 3. Restore the values in SkinnedMeshRenderer + dynamic_array<SkinnedMeshRenderer*> skins (kMemTempAlloc); + GetComponentsInChildren(effectiveRoot, true, ClassID(SkinnedMeshRenderer), reinterpret_cast<dynamic_array<Unity::Component*>&>(skins)); + for (int i = 0; i < skins.size(); i++) + { + SkinnedMeshRenderer& skin = *skins[i]; + + // a. root bone + Transform* rootBoneTransform = NULL; + BindingHash rootPathHash = 0; + const Mesh* mesh = skin.GetMesh(); + if (mesh) + rootPathHash = mesh->GetRootBonePathHash(); + if (rootPathHash) + { + int skeletonIndex = mecanim::skeleton::SkeletonFindNode(&skeleton, rootPathHash); + const UnityStr& rootPath = skeletonPaths[skeletonIndex]; + rootBoneTransform = FindRelativeTransformWithPath(rootTransform, rootPath.c_str()); + } + if (rootBoneTransform != skin.QueryComponent(Transform)) + skin.SetRootBone(rootBoneTransform); + + // b. skeleton + if (mesh) + { + const dynamic_array<BindingHash>& bonePathHashes = mesh->GetBonePathHashes(); + dynamic_array<PPtr<Transform> > boneTransforms; + boneTransforms.resize_initialized (bonePathHashes.size()); + for (int j = 0; j < boneTransforms.size(); j++) + { + BindingHash bonePathHash = bonePathHashes[j]; + int boneIndexInUnstrippedSkeleton = SkeletonFindNode(&skeleton, bonePathHash); + AssertIf(boneIndexInUnstrippedSkeleton == -1); + const UnityStr& bonePath = skeletonPaths[boneIndexInUnstrippedSkeleton]; + Transform* boneTransform = FindRelativeTransformWithPath(rootTransform, bonePath.c_str()); + boneTransforms[j] = boneTransform; + } + skin.SetBones(boneTransforms); + } + } + + // 4. Animator + animator->SetHasTransformHierarchy(true); +} + +static void FlattenHierarchyRecurse (Transform& transform, Transform& root) +{ + while (transform.GetChildrenCount () > 0) + { + Transform& child = transform.GetChild (0); + child.SetParent (&root); + FlattenHierarchyRecurse (child, root); + } +} + +static void FlattenHierarchy (GameObject& gameObject) +{ + Transform& root = gameObject.GetComponent(Transform); + int size = root.GetChildrenCount (); + dynamic_array<Transform*> children(size, kMemTempAlloc); + for (int i=0;i < size;++i) + children[i] = &root.GetChild(i); + + for (int i=0;i < size;++i) + FlattenHierarchyRecurse (*children[i], root); +} + +void RemoveUnnecessaryTransforms (Unity::GameObject& gameObject, const HumanDescription* human, const UnityStr* exposedTransforms, size_t exposedTransformCount, bool doKeepSkeleton) +{ + // false : unnecessary + map<Transform*, bool> transformToMark; + Transform& rootTransform = gameObject.GetComponent(Transform); + + // 1. Initialize the map + dynamic_array<Transform*> allTransforms (kMemTempAlloc); + GetComponentsInChildren(gameObject, true, ClassID(Transform), reinterpret_cast<dynamic_array<Unity::Component*>&>(allTransforms)); + for (int i=0; i<allTransforms.size(); i++) + { + Transform* transform = allTransforms[i]; + bool isNecessary = (transform->GetGameObject().GetComponentCount() > 1); + transformToMark.insert(pair<Transform*, bool>(transform, isNecessary)); + } + + // 2. Handle human bones + if (human) + { + for (int i=0; i<allTransforms.size(); i++) + { + Transform* transform = allTransforms[i]; + map<Transform*, bool>::iterator it = transformToMark.find(transform); + AssertIf(it == transformToMark.end()); + + // human bones should not be removed + if (std::find_if(human->m_Human.begin(), human->m_Human.end(), FindBoneName( transform->GetName() )) != human->m_Human.end() ) + it->second = true; + else if (human->m_RootMotionBoneName.length() > 0) + { + // root bone should not be removed (for Humanoid & Generic root motion) + if (human->m_RootMotionBoneName.compare(transform->GetName()) == 0) + it->second = true; + } + } + } + + // 3. Handle exposed transforms + for (int i=0; i<exposedTransformCount; i++) + { + const UnityStr& path = exposedTransforms[i]; + Transform* transform = FindRelativeTransformWithPath(rootTransform, path.c_str()); + map<Transform*, bool>::iterator it = transformToMark.find(transform); + AssertIf(it == transformToMark.end()); + it->second = true; + } + + // 4. Handle SkinnedMeshRenderers + if (doKeepSkeleton) + { + dynamic_array<SkinnedMeshRenderer*> skins (kMemTempAlloc); + GetComponentsInChildren(gameObject, true, ClassID(SkinnedMeshRenderer), reinterpret_cast<dynamic_array<Unity::Component*>&>(skins)); + + for (int i=0; i<skins.size(); i++) + { + const SkinnedMeshRenderer& skin = *skins[i]; + const dynamic_array<PPtr<Transform> >& bones = skin.GetBones(); + for (int j=0; j<bones.size(); j++) + { + Transform* transform = bones[j]; + map<Transform*, bool>::iterator it = transformToMark.find(transform); + AssertIf(it == transformToMark.end()); + it->second = true; + } + } + } + + // 5. Handle parent transforms + for (int i=allTransforms.size()-1; i>=0; i--) + { + Transform* transform = allTransforms[i]; + map<Transform*, bool>::const_iterator it = transformToMark.find(transform); + AssertIf(it == transformToMark.end()); + if (!it->second) + continue; + + Transform* parent = transform->GetParent(); + if (!parent) + continue; + map<Transform*, bool>::iterator itParent = transformToMark.find(parent); + AssertIf(itParent == transformToMark.end()); + itParent->second = true; + } + + // Remove unnecessary transforms + for (int i=allTransforms.size()-1; i>=0; i--) + { + Transform* transform = allTransforms[i]; + map<Transform*, bool>::const_iterator it = transformToMark.find(transform); + AssertIf(it == transformToMark.end()); + if (it->second) + continue; + DestroyObjectHighLevel(&(transform->GetGameObject())); + } +} + |