diff options
Diffstat (limited to 'Runtime/Animation/GenericAnimationBindingCache.cpp')
-rw-r--r-- | Runtime/Animation/GenericAnimationBindingCache.cpp | 839 |
1 files changed, 839 insertions, 0 deletions
diff --git a/Runtime/Animation/GenericAnimationBindingCache.cpp b/Runtime/Animation/GenericAnimationBindingCache.cpp new file mode 100644 index 0000000..fe6688f --- /dev/null +++ b/Runtime/Animation/GenericAnimationBindingCache.cpp @@ -0,0 +1,839 @@ +#include "UnityPrefix.h" +#include "GenericAnimationBindingCache.h" +#include "AnimationClipBindings.h" +#include "Runtime/Mono/MonoScript.h" +#include "Runtime/Mono/MonoBehaviour.h" +#include "Runtime/Graphics/Transform.h" +#include "Runtime/mecanim/memory.h" +#include "Runtime/Serialize/TransferUtility.h" +#include "Runtime/mecanim/generic/crc32.h" +#include "Runtime/mecanim/animation/clipmuscle.h" +#include "Runtime/Misc/GameObjectUtility.h" +#include "Runtime/Interfaces/IAnimationBinding.h" +#include "Animator.h" +#include "Runtime/Utilities/InitializeAndCleanup.h" +#include "AnimatorController.h" +#include "Runtime/Core/Callbacks/GlobalCallbacks.h" + +const static char* kIsActive = "m_IsActive"; + +typedef UInt32 BindingHash; + +namespace UnityEngine +{ +namespace Animation +{ + +bool IsMuscleBinding(const GenericBinding& binding) +{ + return binding.classID == ClassID(Animator) && binding.customType == kBindMuscle; +} + +struct CachedBinding +{ + BindingHash propertyHash; + int offset; + int bindType; +}; + +struct CachedComponentBindings +{ + ScriptingClassPtr scriptingClass; + int classID; + size_t bindingSize; + CachedBinding* bindings; +}; + + +static GenericAnimationBindingCache* gGenericBindingCache = NULL; + +static CachedComponentBindings* GenerateComponentBinding (int classID, ScriptingObjectPtr scriptingInstance, ScriptingClassPtr scriptingClass, Object* targetObject); +static const CachedBinding* FindBinding (const CachedComponentBindings& componentBinding, BindingHash attribute); +static void DestroyBinding (CachedComponentBindings* binding); + +bool operator < (const CachedBinding& lhs, const CachedBinding& rhs) +{ + return lhs.propertyHash < rhs.propertyHash; +} + + +static ClassIDType BindCurve (const CachedComponentBindings& cachedBinding, const GenericBinding& inputBinding, Object* targetObject, void* targetPtr, BoundCurve& bound) +{ + const CachedBinding* found = FindBinding (cachedBinding, inputBinding.attribute); + if (found == NULL) + { + bound.targetType = kUnbound; + return ClassID(Undefined); + } + + bound.targetObject = targetObject; + bound.targetPtr = reinterpret_cast<UInt8*> (targetPtr) + found->offset; + bound.targetType = found->bindType; + + if (found->bindType == kBindFloatToBool) + return ClassID(bool); + else + return ClassID(float); +} + +static int GetTypeTreeBindType (const TypeTree& variable) +{ + if (variable.m_MetaFlag & kDontAnimate) + return kUnbound; + + if (variable.m_Type == "float") + return kBindFloat; + else if (variable.m_Type == "bool" || (variable.m_Type == "UInt8" && (variable.m_MetaFlag & kEditorDisplaysCheckBoxMask))) + return kBindFloatToBool; + else + return kUnbound; +} + +static int GetAnimatablePropertyOffset (const TypeTree* variable, ScriptingObjectPtr scriptingInstance) +{ + if (variable && scriptingInstance) + { +#if ENABLE_MONO + if (scriptingInstance) + { + UInt32 offset = reinterpret_cast<UInt8*> (variable->m_DirectPtr) - reinterpret_cast<UInt8*> (scriptingInstance); + UInt32 size = mono_class_instance_size(mono_object_get_class(scriptingInstance)); + if (offset < size) + return offset; + } +#endif + return -1; + } + else if (variable) + return variable->m_ByteOffset; + else + return -1; +} + +static void GetGenericAnimatablePropertiesRecurse (const TypeTree& typeTree, std::string& path, ScriptingObjectPtr scriptingInstance, const EditorCurveBinding& baseBinding, std::vector<EditorCurveBinding>& outProperties) +{ + size_t previousSize = path.size(); + if (!path.empty()) + path += '.'; + path += typeTree.m_Name; + + int offset = GetAnimatablePropertyOffset (&typeTree, scriptingInstance); + if (offset != -1) + { + int bindType = GetTypeTreeBindType (typeTree); + if (bindType != kUnbound) + { + outProperties.push_back(baseBinding); + outProperties.back().attribute = path; + } + } + + for (TypeTree::const_iterator i=typeTree.begin();i != typeTree.end();++i) + { + GetGenericAnimatablePropertiesRecurse (*i, path, scriptingInstance, baseBinding, outProperties); + } + + path.resize(previousSize); +} + +static void GetGenericAnimatableProperties (int classID, Object& targetObject, std::vector<EditorCurveBinding>& outProperties) +{ + //@TODO: Use temp TypeTree mem? + TypeTree typeTree; + GenerateTypeTree (targetObject, &typeTree); + + EditorCurveBinding baseBinding; + baseBinding.classID = classID; + + MonoBehaviour* behaviour = dynamic_pptr_cast<MonoBehaviour*> (&targetObject); + ScriptingObjectPtr scriptingInstance = SCRIPTING_NULL;; + if (behaviour) + { + scriptingInstance = behaviour->GetInstance(); + baseBinding.script = behaviour->GetScript(); + } + + // Create cached bindings (We don't want to include the root name, so iterate over the children directly) + std::string path; + for (TypeTree::const_iterator i=typeTree.begin();i != typeTree.end();++i) + GetGenericAnimatablePropertiesRecurse (*i, path, scriptingInstance, baseBinding, outProperties); +} + +static void GenerateBindingRecurse (const TypeTree& typeTree, ScriptingObjectPtr scriptingInstance, mecanim::crc32 attributeHash, dynamic_array<CachedBinding>& bindings) +{ + // Update hash recursively + if (attributeHash.checksum() != 0) + attributeHash.process_bytes(".", 1); + attributeHash.process_bytes(typeTree.m_Name.c_str(), strlen(typeTree.m_Name.c_str())); + + int offset = GetAnimatablePropertyOffset (&typeTree, scriptingInstance); + if (offset != -1) + { + int bindType = GetTypeTreeBindType (typeTree); + if (bindType != kUnbound) + { + CachedBinding& binding = bindings.push_back(); + binding.propertyHash = attributeHash.checksum(); + binding.offset = offset; + binding.bindType = bindType; + } + } + + for (TypeTree::const_iterator i=typeTree.begin();i != typeTree.end();++i) + GenerateBindingRecurse (*i, scriptingInstance, attributeHash, bindings); +} + + +template<class T> +bool has_duplicate_sorted (const T* begin, size_t count) +{ + if (count == 0) + return false; + + const T* previous = begin; + const T* i = begin; + const T* end = begin + count; + i++; + for (; i != end;++i) + { + if (!(*previous < *i)) + return true; + + previous = i; + } + + return false; +} + +// Find a value in a sorted array +// Returns NULL if there is no value in the array +template<class T> +const T* find_binary_search (const T* begin, size_t count, const T& value) +{ + const T* found = std::lower_bound (begin, begin + count, value); + if (found == begin + count || value < *found) + return NULL; + else + return found; +} + + +static const CachedBinding* FindBinding (const CachedComponentBindings& componentBinding, BindingHash attribute) +{ + CachedBinding proxy; + proxy.propertyHash = attribute; + + return find_binary_search(componentBinding.bindings, componentBinding.bindingSize, proxy); +} + +static void DestroyBinding (CachedComponentBindings* binding) +{ + UNITY_FREE(kMemAnimation, binding); +} + + +static CachedComponentBindings* GenerateComponentBinding (int classID, ScriptingObjectPtr scriptingInstance, ScriptingClassPtr scriptingClass, Object* targetObject) +{ + //@TODO: Use temp TypeTree mem? + TypeTree typeTree; + GenerateTypeTree (*targetObject, &typeTree); + + dynamic_array<CachedBinding> bindings (kMemTempAlloc); + + // Create cached bindings (We don't want to include the root name, so iterate over the children directly) + for (TypeTree::const_iterator i=typeTree.begin();i != typeTree.end();++i) + GenerateBindingRecurse (*i, scriptingInstance, mecanim::crc32(), bindings); + + std::sort (bindings.begin(), bindings.end()); + +#if DEBUGMODE + if (has_duplicate_sorted (bindings.begin(), bindings.size())) + { + ///@TODO: make a nice fullclassname... + + + WarningString(Format("Animation bindings for %s are not unique. Some properties might get bound incorrectly.", targetObject->GetClassName().c_str())); + } +#endif + + size_t size = sizeof (CachedComponentBindings) + sizeof (CachedBinding) * bindings.size(); + mecanim::memory::InPlaceAllocator allocator (UNITY_MALLOC(kMemAnimation, size), size); + + CachedComponentBindings* binding = allocator.Construct<CachedComponentBindings> (); + + binding->classID = classID; + binding->scriptingClass = scriptingClass; + binding->bindingSize = bindings.size(); + binding->bindings = allocator.ConstructArray<CachedBinding> (bindings.begin(), bindings.size()); + + return binding; +} + +void GenericAnimationBindingCache::DidReloadDomain () +{ + if (gGenericBindingCache != NULL) + Clear(gGenericBindingCache->m_Scripts); +} + +void InitializeGenericAnimationBindingCache () +{ + mecanim::crc32::crc32_table_type::init_table(); + + gGenericBindingCache = UNITY_NEW_AS_ROOT(GenericAnimationBindingCache, kMemAnimation, "AnimationBindingCache", ""); + + GlobalCallbacks::Get().didReloadMonoDomain.Register(GenericAnimationBindingCache::DidReloadDomain); +} + +void CleanupGenericAnimationBindingCache () +{ + UNITY_DELETE(gGenericBindingCache, kMemAnimation); + + GlobalCallbacks::Get().didReloadMonoDomain.Unregister(GenericAnimationBindingCache::DidReloadDomain); +} + +static RegisterRuntimeInitializeAndCleanup s_RegisterBindingCache (InitializeGenericAnimationBindingCache, CleanupGenericAnimationBindingCache); + +GenericAnimationBindingCache& GetGenericAnimationBindingCache () +{ + return *gGenericBindingCache; +} + +GenericAnimationBindingCache::GenericAnimationBindingCache () +{ + m_IsActiveHash = mecanim::processCRC32 (kIsActive); + m_Classes.resize_initialized (kLargestRuntimeClassID, NULL); + m_CustomBindingInterfaces.resize_initialized (kAllBindingCount, NULL); +} + +GenericAnimationBindingCache::~GenericAnimationBindingCache () +{ + Clear(m_Classes); + Clear(m_Scripts); +} + + +void CreateTransformBinding (const UnityStr& path, int bindType, GenericBinding& outputBinding) +{ + outputBinding.path = mecanim::processCRC32(path.c_str()); + outputBinding.attribute = bindType; + outputBinding.classID = ClassID(Transform); + outputBinding.customType = kUnbound; + outputBinding.isPPtrCurve = false; + outputBinding.script = NULL; +} + +void GenericAnimationBindingCache::CreateGenericBinding (const UnityStr& path, int classID, PPtr<MonoScript> script, const UnityStr& attribute, bool pptrCurve, GenericBinding& outputBinding) const +{ + outputBinding.path = mecanim::processCRC32(path.c_str()); + outputBinding.attribute = mecanim::processCRC32(attribute.c_str()); + outputBinding.classID = classID; + outputBinding.customType = kUnbound; + outputBinding.isPPtrCurve = pptrCurve; + outputBinding.script = script; + + // Pre-bind muscle indices + //@TODO: is this really worth doing? Maybe we should just make it more consistent? + if (!pptrCurve) + { + if (classID == ClassID(Animator)) + { + mecanim::int32_t muscleIndex = mecanim::animation::FindMuscleIndex(outputBinding.attribute); + if (muscleIndex != -1) + { + outputBinding.attribute = muscleIndex; + outputBinding.customType = kBindMuscle; + return; + } + } + } + + // Search custom bindings + for (int i=0;i<m_CustomBindings.size();i++) + { + int customBindingType = m_CustomBindings[i].customBindingType; + const IAnimationBinding* bindingInterface = m_CustomBindingInterfaces[customBindingType]; + if (Object::IsDerivedFromClassID (classID, m_CustomBindings[i].classID) && bindingInterface->GenerateBinding (attribute, pptrCurve, outputBinding)) + { + outputBinding.customType = customBindingType; + return; + } + } +} + +static inline MonoBehaviour* GetComponentWithScript (Transform& transform, PPtr<Object> target) +{ + MonoScript* script = dynamic_instanceID_cast<MonoScript*> (target.GetInstanceID()); + return static_cast<MonoBehaviour*> (GetComponentWithScript (transform.GetGameObject(), ClassID(MonoBehaviour), script)); +} + +#if ENABLE_MONO + +ClassIDType GenericAnimationBindingCache::BindScript (const GenericBinding& inputBinding, Transform& transform, BoundCurve& bound) +{ + MonoBehaviour* behaviour = GetComponentWithScript (transform, inputBinding.script); + + ScriptingObjectPtr instance = behaviour ? behaviour->GetInstance() : SCRIPTING_NULL; + // No valid instance -> no bound curve + if (instance == NULL) + { + bound.targetType = kUnbound; + return ClassID(Undefined); + } + + ScriptingClassPtr klass = behaviour->GetClass(); + + // Find cached binding stored by ScriptingClassPtr + CachedComponentBindings* bindings = NULL; + for (int i=0;i<m_Scripts.size();i++) + { + if (m_Scripts[i]->scriptingClass == klass) + { + bindings = m_Scripts[i]; + break; + } + } + + // Create a new binding for this ScriptingClassPtr + if (bindings == NULL) + { + bindings = GenerateComponentBinding(inputBinding.classID, instance, klass, behaviour); + m_Scripts.push_back(bindings); + } + + // Bind the specific curve + return BindCurve (*bindings, inputBinding, behaviour, instance, bound); +} +#endif + +ClassIDType GenericAnimationBindingCache::BindGenericComponent (const GenericBinding& inputBinding, Transform& transform, BoundCurve& bound) +{ + Unity::Component* target = transform.GetGameObject().QueryComponentT<Unity::Component>(inputBinding.classID); + if (target == NULL) + return ClassID(Undefined); + + if (m_Classes[inputBinding.classID] == NULL) + m_Classes[inputBinding.classID] = GenerateComponentBinding(inputBinding.classID, SCRIPTING_NULL, SCRIPTING_NULL, target); + + return BindCurve (*m_Classes[inputBinding.classID], inputBinding, target, target, bound); +} + +Object* FindAnimatedObject (Unity::GameObject& root, const EditorCurveBinding& inputBinding) +{ + Transform* transform = FindRelativeTransformWithPath (root.GetComponent(Transform), inputBinding.path.c_str()); + if (transform == NULL) + return NULL; + + if (inputBinding.classID == ClassID(GameObject)) + { + return &transform->GetGameObject(); + } + else if (inputBinding.classID == ClassID(MonoBehaviour)) + { + return GetComponentWithScript (*transform, inputBinding.script); + } + else + { + return transform->GetGameObject().QueryComponentT<Unity::Component>(inputBinding.classID); + } +} + +ClassIDType GenericAnimationBindingCache::BindPPtrGeneric (const GenericBinding& inputBinding, Transform& transform, BoundCurve& bound) +{ + if (inputBinding.customType == kUnbound) + return ClassID(Undefined); + + return BindCustom (inputBinding, transform, bound); +} + +ClassIDType GenericAnimationBindingCache::BindCustom (const GenericBinding& inputBinding, Transform& transform, BoundCurve& bound) const +{ + Assert(inputBinding.customType != kUnbound); + + Unity::Component* target; + if (inputBinding.classID == ClassID(MonoBehaviour)) + target = GetComponentWithScript (transform, inputBinding.script); + else + target = transform.GetGameObject().QueryComponentT<Unity::Component>(inputBinding.classID); + + IAnimationBinding* customBinding = m_CustomBindingInterfaces[inputBinding.customType]; + if (customBinding != NULL && target != NULL) + { + BoundCurve tempBound; + tempBound.targetType = inputBinding.customType; + tempBound.customBinding = customBinding; + tempBound.targetObject = target; + + ClassIDType type = customBinding->BindValue (*target, inputBinding, tempBound); + if (type != ClassID (Undefined)) + bound = tempBound; + + return type; + } + + return ClassID(Undefined); +} + + +ClassIDType GenericAnimationBindingCache::BindGeneric (const GenericBinding& inputBinding, Transform& transform, BoundCurve& bound) +{ + // Bind game object active state + if (inputBinding.classID == ClassID(GameObject)) + { + if (inputBinding.attribute == m_IsActiveHash && inputBinding.path != 0) + { + bound.targetPtr = NULL; + bound.targetType = kBindGameObjectActive; + bound.targetObject = transform.GetGameObjectPtr(); + return ClassID(bool); + } + + return ClassID(Undefined); + } + // Custom animator bindings + else if (inputBinding.classID == ClassID(Animator)) + { + // We bind these in mecanim internally. + return ClassID(float); + } + // Custom binding + else if (inputBinding.customType != kUnbound) + return BindCustom (inputBinding, transform, bound); + // Script bindings + else if (inputBinding.classID == ClassID(MonoBehaviour)) + { +#if ENABLE_MONO + return BindScript(inputBinding, transform, bound); +#else + return ClassID(Undefined); +#endif + } + // Generic bindings + else + { + return BindGenericComponent(inputBinding, transform, bound); + } +} + +void GenericAnimationBindingCache::RegisterIAnimationBinding (ClassIDType classID, int customBindingType, IAnimationBinding* customBinding) +{ + CustomBinding bind; + bind.classID = classID; + bind.customBindingType = customBindingType; + m_CustomBindings.push_back(bind); + + Assert(m_CustomBindingInterfaces[customBindingType] == NULL); + m_CustomBindingInterfaces[customBindingType] = customBinding; +} + +std::string GenericAnimationBindingCache::SerializedPropertyPathToCurveAttribute (Object& target, const char* propertyPath) const +{ + ClassIDType classID = target.GetClassID(); + // Search custom bindings + for (int i=0;i<m_CustomBindings.size();i++) + { + if (!Object::IsDerivedFromClassID (classID, m_CustomBindings[i].classID)) + continue; + + int customBindingType = m_CustomBindings[i].customBindingType; + const IAnimationBinding* customBinding = m_CustomBindingInterfaces[customBindingType]; + + string attributeName = customBinding->SerializedPropertyPathToCurveAttribute (target, propertyPath); + if (!attributeName.empty()) + return attributeName; + } + + return string(); +} + +std::string GenericAnimationBindingCache::CurveAttributeToSerializedPath (Unity::GameObject& root, const EditorCurveBinding& binding) const +{ + Transform* transform = FindRelativeTransformWithPath (root.GetComponent(Transform), binding.path.c_str()); + if (transform == NULL) + return std::string(); + + GenericBinding genericBinding; + CreateGenericBinding (binding.path, binding.classID, binding.script, binding.attribute, binding.isPPtrCurve, genericBinding); + + if (genericBinding.customType == kUnbound) + return std::string(); + + BoundCurve bound; + if (BindCustom (genericBinding, *transform, bound) == ClassID(Undefined)) + return std::string(); + + if (bound.customBinding != NULL) + return bound.customBinding->CurveAttributeToSerializedPath (bound); + else + return string(); +} + +void GenericAnimationBindingCache::Clear (CachedComponentBindingArray& array) +{ + for (int i=0;i<array.size();i++) + UNITY_FREE(kMemAnimation, array[i]); + array.clear(); +} + +int GetBoundCurveIntValue (const BoundCurve& bind) +{ + return bind.customBinding->GetPPtrValue (bind); +} + +void SetBoundCurveIntValue (const BoundCurve& bind, int value) +{ + bind.customBinding->SetPPtrValue (bind, value); +} + +void SetBoundCurveFloatValue (const BoundCurve& bind, float value) +{ + UInt32 targetType = bind.targetType; + Assert(bind.targetType != kUnbound && bind.targetType != kBindTransformRotation && bind.targetType != kBindTransformPosition && bind.targetType != kBindTransformScale); + + if ( targetType == kBindFloat ) + { + *reinterpret_cast<float*>(bind.targetPtr) = value; + } + else if ( targetType == kBindFloatToBool ) + { + *reinterpret_cast<UInt8*>(bind.targetPtr) = AnimationFloatToBool(value); + } + else if ( targetType == kBindGameObjectActive) + { + GameObject* go = static_cast<GameObject*> (bind.targetObject); + go->SetSelfActive (AnimationFloatToBool(value)); + } + else + bind.customBinding->SetFloatValue (bind, value); +} + +float GetBoundCurveFloatValue (const BoundCurve& bind) +{ + UInt32 targetType = bind.targetType; + Assert(bind.targetType >= kMinSinglePropertyBinding); + + if ( targetType == kBindFloat ) + { + return *reinterpret_cast<float*>(bind.targetPtr); + } + else if ( targetType == kBindFloatToBool ) + { + return AnimationBoolToFloat(*reinterpret_cast<UInt8*>(bind.targetPtr)); + } + else if ( targetType == kBindGameObjectActive ) + { + GameObject* go = static_cast<GameObject*> (bind.targetObject); + return AnimationBoolToFloat (go->IsSelfActive ()); + } + else + return bind.customBinding->GetFloatValue (bind); +} + + +bool ShouldAwakeGeneric (const BoundCurve& bind) +{ + return bind.targetType == kBindFloat || bind.targetType == kBindFloatToBool; +} + +void BoundCurveValueAwakeGeneric (Object& targetObject) +{ + targetObject.AwakeFromLoad(kDefaultAwakeFromLoad); + targetObject.SetDirty(); +} + +#if UNITY_EDITOR + +static void ExtractGameObjectIsActiveBindings (std::vector<EditorCurveBinding>& outProperties) +{ + AddBinding (outProperties, ClassID(GameObject), kIsActive); +} + +static void ExtractAllAnimatorBindings (Unity::Component& targetObject, vector<EditorCurveBinding>& attributes) +{ + Animator& animator = static_cast<Animator&> (targetObject); + if (animator.IsHuman()) + { + for(int curveIter = 0; curveIter < mecanim::animation::s_ClipMuscleCurveCount; curveIter++) + AddBinding (attributes, ClassID(Animator), mecanim::animation::GetMuscleCurveName(curveIter).c_str()); + } + + if(animator.GetAnimatorController()) + { + int eventCount = animator.GetAnimatorController()->GetParameterCount(); + + for(int eventIter = 0; eventIter < eventCount; eventIter++) + { + AnimatorControllerParameter* parameter = animator.GetAnimatorController()->GetParameter(eventIter); + if (parameter->GetType() == 1) + AddBinding (attributes, ClassID(Animator), parameter->GetName()); + } + } +} + +static void ProcessRelativePath (Unity::GameObject& gameObject, Unity::GameObject& root, std::vector<EditorCurveBinding>& outProperties) +{ + string path = CalculateTransformPath (gameObject.GetComponent(Transform), root.QueryComponent(Transform)); + for (int i=0;i<outProperties.size();i++) + outProperties[i].path = path; +} + +void GenericAnimationBindingCache::GetAllAnimatableProperties (Unity::GameObject& go, Unity::GameObject& root, std::vector<EditorCurveBinding>& outProperties) +{ + Assert(outProperties.empty()); + + if (&go != &root) + ExtractGameObjectIsActiveBindings(outProperties); + + for (int i=0;i<go.GetComponentCount ();i++) + { + Unity::Component& com = go.GetComponentAtIndex (i); + int classID = com.GetClassID(); + + // Search custom bindings + for (int c=0;c<m_CustomBindings.size();c++) + { + int customBindingType = m_CustomBindings[c].customBindingType; + if (Object::IsDerivedFromClassID (classID, m_CustomBindings[c].classID)) + m_CustomBindingInterfaces[customBindingType]->GetAllAnimatableProperties (com, outProperties); + } + + if (classID == ClassID(Animator)) + ExtractAllAnimatorBindings (com, outProperties); + + GetGenericAnimatableProperties (classID, go.GetComponentAtIndex(i), outProperties); + } + + ProcessRelativePath (go, root, outProperties); +} + +static bool GetFloatValueAnimatorBinding (Transform& transform, const EditorCurveBinding& binding, float* value) +{ + Animator* animator = transform.QueryComponent(Animator); + + if (animator == NULL) + return false; + + BindingHash hash = mecanim::processCRC32 (binding.attribute.c_str()); + + if(animator->GetMuscleValue(hash,value)) + return true; + + GetSetValueResult result = animator->GetFloat (hash, *value); + return result == kGetSetSuccess; +} + +static bool GetFloatValueTransformBinding (Transform& transform, const EditorCurveBinding& binding, float* value) +{ + int axis = -1; + char lastCharacter = binding.attribute[binding.attribute.size()-1]; + if (lastCharacter == 'w') + axis = 3; + else if (lastCharacter >= 'x' && lastCharacter <= 'z') + axis = lastCharacter - 'x'; + else + return false; + + if (BeginsWith(binding.attribute, "m_LocalPosition") && axis < 3) + { + *value = transform.GetLocalPosition()[axis]; + return true; + } + else if (BeginsWith(binding.attribute, "m_LocalScale") && axis < 3) + { + *value = transform.GetLocalScale()[axis]; + return true; + } + else if (BeginsWith(binding.attribute, "m_LocalRotation") && axis < 4) + { + *value = transform.GetLocalRotation()[axis]; + return true; + } + else if (BeginsWith(binding.attribute, "localEulerAngles") && axis < 3) + { + *value = transform.GetLocalEulerAngles()[axis]; + return true; + } + else + return false; +} + +ClassIDType GetFloatValue (Unity::GameObject& root, const EditorCurveBinding& binding, float* value) +{ + *value = 0.0F; + + if (binding.isPPtrCurve) + return ClassID(Undefined); + + Transform* transform = FindRelativeTransformWithPath (root.GetComponent(Transform), binding.path.c_str()); + if (transform == NULL) + return ClassID(Undefined); + + // Transform has a special codepath for setting T/R/S as Vector3 + if (binding.classID == ClassID(Transform)) + { + if (GetFloatValueTransformBinding (*transform, binding, value)) + return ClassID(float); + } + // Animator bindings are handled through a special more integrated code path in mecanim + else if (binding.classID == ClassID(Animator)) + { + if (GetFloatValueAnimatorBinding (*transform, binding, value)) + return ClassID(float); + } + else + { + GenericBinding genericBinding; + GetGenericAnimationBindingCache().CreateGenericBinding (binding.path, binding.classID, binding.script, binding.attribute, binding.isPPtrCurve, genericBinding); + + BoundCurve bound; + ClassIDType bindType = GetGenericAnimationBindingCache().BindGeneric (genericBinding, *transform, bound); + if (bound.targetType == kUnbound) + return ClassID(Undefined); + + *value = GetBoundCurveFloatValue (bound); + + return bindType; + } + + return ClassID(Undefined); +} + +ClassIDType GetPPtrValue (Unity::GameObject& root, const EditorCurveBinding& binding, int* instanceID) +{ + *instanceID = 0; + + if (!binding.isPPtrCurve) + return ClassID(Undefined); + + Transform* transform = FindRelativeTransformWithPath (root.GetComponent(Transform), binding.path.c_str()); + if (transform == NULL) + return ClassID(Undefined); + + GenericBinding genericBinding; + GetGenericAnimationBindingCache().CreateGenericBinding (binding.path, binding.classID, binding.script, binding.attribute, binding.isPPtrCurve, genericBinding); + + BoundCurve bound; + ClassIDType boundClassID = GetGenericAnimationBindingCache().BindPPtrGeneric (genericBinding, *transform, bound); + if (bound.targetType == kUnbound) + return ClassID(Undefined); + + *instanceID = GetBoundCurveIntValue (bound); + return boundClassID; +} + +ClassIDType GetEditorCurveValueClassID (Unity::GameObject& root, const EditorCurveBinding& binding) +{ + // Try if it's a PPtr curve + int tempInstanceID; + ClassIDType boundClassID = GetPPtrValue (root, binding, &tempInstanceID); + if (boundClassID != ClassID(Undefined)) + return boundClassID; + + // Otherwise find the type of float curve it is + float tempFloat; + return GetFloatValue (root, binding, &tempFloat); +} + +#endif + +} +} |