summaryrefslogtreecommitdiff
path: root/Runtime/Animation/Animator.cpp
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2019-08-14 22:50:43 +0800
committerchai <chaifix@163.com>2019-08-14 22:50:43 +0800
commit15740faf9fe9fe4be08965098bbf2947e096aeeb (patch)
treea730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/Animation/Animator.cpp
+Unity Runtime codeHEADmaster
Diffstat (limited to 'Runtime/Animation/Animator.cpp')
-rw-r--r--Runtime/Animation/Animator.cpp2674
1 files changed, 2674 insertions, 0 deletions
diff --git a/Runtime/Animation/Animator.cpp b/Runtime/Animation/Animator.cpp
new file mode 100644
index 0000000..a8db798
--- /dev/null
+++ b/Runtime/Animation/Animator.cpp
@@ -0,0 +1,2674 @@
+#include "UnityPrefix.h"
+
+#include "Animator.h"
+
+#include "Runtime/Animation/OptimizeTransformHierarchy.h"
+
+#include "Runtime/mecanim/animation/avatar.h"
+#include "Runtime/mecanim/animation/damp.h"
+#include "Runtime/mecanim/generic/stringtable.h"
+
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+#include "Runtime/Input/TimeManager.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/BaseClasses/IsPlaying.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "AnimatorManager.h"
+#include "Runtime/Threads/JobScheduler.h"
+#include "AnimationClip.h"
+#include "AnimationSetBinding.h"
+#include "MecanimAnimation.h"
+
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Filters/Renderer.h"
+#include "Runtime/GameCode/RootMotionData.h"
+#include "Runtime/BaseClasses/SupportedMessageOptimization.h"
+#include "Runtime/BaseClasses/EventIDs.h"
+
+#include "Runtime/Graphics/Transform.h"
+
+#include "Runtime/Math/Vector4.h"
+
+#include "AnimatorController.h"
+#include "GenericAnimationBindingCache.h"
+#include "Avatar.h"
+#include "AnimatorOverrideController.h"
+
+#include "Runtime/mecanim/generic/typetraits.h"
+
+#include "Runtime/Serialize/SerializeTraits.h"
+#include "AnimatorGenericBindings.h"
+
+#include "Runtime/Animation/AnimationClip.h"
+#include "Runtime/Animation/AnimationUtility.h"
+
+#define ENABLE_DETAILED_SINGLE_THREAD_PROFILER 0
+
+#define ENABLE_MULTITHREADED_ANIMATION ENABLE_MULTITHREADED_CODE && (!ENABLE_DETAILED_SINGLE_THREAD_PROFILER)
+using namespace UnityEngine::Animation;
+Animator::Animator(MemLabelId label, ObjectCreationMode mode)
+: Super(label, mode),
+m_BehaviourIndex(-1),
+m_FixedBehaviourIndex(-1),
+m_DeltaPosition(Vector3f::zero),
+m_DeltaRotation(Quaternionf::identity()),
+m_PivotPosition(0,0,0),
+m_MatchPosition(Vector3f::zero),
+m_MatchRotation(Quaternionf::identity()),
+m_MatchStartTime(-1),
+m_MatchStateID(-1),
+m_MustCompleteMatch(false),
+m_MatchTargetMask(Vector3f::one, 0),
+m_ApplyRootMotion(true),
+m_AnimatePhysics(false),
+m_Visible(false),
+m_CullingMode (kCullAlwaysAnimate),
+m_Speed(1),
+m_FireEvents(true),
+m_LogWarnings(true),
+m_AvatarPlayback(label),
+m_RecorderMode(eNormal),
+m_PlaybackDeltaTime(0),
+m_PlaybackTime(0),
+m_HasTransformHierarchy(true),
+m_AnimatorAvatarNode(this),
+m_AnimatorControllerNode(this),
+m_SamplingDataSet(256*1024),
+mAlloc(kMemAnimation)
+{
+
+}
+
+Animator::~Animator()
+{
+ Assert(m_EvaluationDataSet.m_AvatarMemory == NULL && m_EvaluationDataSet.m_ControllerConstant == NULL && m_EvaluationDataSet.m_GenericBindingConstant == NULL && m_ContainedRenderers.size() == 0);
+}
+
+void Animator::AwakeFromLoad(AwakeFromLoadMode mode)
+{
+ Super::AwakeFromLoad(mode);
+ CreateObject();
+ InitializeVisibilityCulling();
+
+ UpdateInManager();
+}
+
+void Animator::CheckConsistency()
+{
+}
+
+void Animator::Reset ()
+{
+ Super::Reset();
+
+ m_CullingMode = kCullAlwaysAnimate;
+ m_ApplyRootMotion = true;
+ m_AnimatePhysics = false;
+ m_HasTransformHierarchy = true;
+}
+
+IMPLEMENT_OBJECT_SERIALIZE (Animator)
+IMPLEMENT_CLASS_HAS_INIT (Animator)
+
+template<class TransferFunction>
+void Animator::Transfer (TransferFunction& transfer)
+{
+ transfer.SetVersion(2);
+
+ Super::Transfer (transfer);
+
+ transfer.Transfer (m_Avatar, "m_Avatar");
+ transfer.Transfer (m_Controller, "m_Controller");
+
+ TRANSFER_ENUM (m_CullingMode);
+ transfer.Transfer (m_ApplyRootMotion, "m_ApplyRootMotion", kDontAnimate);
+ transfer.Transfer (m_AnimatePhysics, "m_AnimatePhysics", kDontAnimate);
+ transfer.Transfer (m_HasTransformHierarchy, "m_HasTransformHierarchy", kDontAnimate);
+}
+
+void Animator::AddToManager ()
+{
+ GetAnimatorManager().AddAnimator(*this);
+}
+
+void Animator::RemoveFromManager ()
+{
+ GetAnimatorManager().RemoveAnimator(*this);
+}
+
+void Animator::Deactivate (DeactivateOperation operation)
+{
+ Super::Deactivate(operation);
+ ClearObject();
+ ClearContainedRenderers();
+}
+
+
+void Animator::TransformChanged (int change)
+{
+ // No need to initialize it
+ if (!IsInitialize())
+ return;
+
+ // Teleport
+ Transform& avatarTransform = GetComponent(Transform);
+ if (change & Transform::kPositionChanged)
+ SetAvatarPosition(avatarTransform.GetPosition());
+
+ if (change & Transform::kRotationChanged)
+ SetAvatarRotation(avatarTransform.GetRotation());
+
+ if(change & Transform::kScaleChanged)
+ SetAvatarScale(avatarTransform.GetWorldScaleLossy());
+}
+
+void Animator::OnAddComponent(Component* com)
+{
+ Renderer* renderer = dynamic_pptr_cast<Renderer*>(com);
+ if(renderer)
+ InitializeVisibilityCulling ();
+}
+
+void Animator::InitializeClass ()
+{
+ mecanim::memory::Profiler::StaticInitialize();
+
+ REGISTER_MESSAGE (Animator, kTransformChanged, TransformChanged, int);
+ REGISTER_MESSAGE_VOID(Animator, kDidModifyAnimatorController, ClearObject);
+ REGISTER_MESSAGE_VOID(Animator, kDidModifyMotion, ClearObject); // an animationClip sends this when deleted
+ REGISTER_MESSAGE_VOID(Animator, kDidModifyAvatar, ClearObject);
+
+ ///@TODO: This doesn't really cover any real world cases...
+ /// adding new chidlren is not covered, adding component to childeren is not covered.
+ REGISTER_MESSAGE_PTR (Animator, kDidAddComponent, OnAddComponent, Component);
+
+ AnimatorManager::InitializeClass();
+ MecanimAnimation::InitializeClass();
+ mecanim::animation::ControllerConstant::InitializeClass();
+ mecanim::animation::AvatarConstant::InitializeClass();
+ mecanim::statemachine::StateConstant::InitializeClass();
+
+ mecanim::crc32::crc32_table_type::init_table();
+ mecanim::animation::InitializeMuscleClipTables ();
+
+ Assert(mecanim::animation::FindMuscleIndex(0) == -1);
+ Assert(mecanim::animation::FindMuscleIndex(mecanim::processCRC32 ("MotionT.x")) == 0);
+}
+
+void Animator::CleanupClass ()
+{
+ AnimatorManager::CleanupClass();
+ MecanimAnimation::CleanupClass();
+ mecanim::memory::Profiler::StaticDestroy();
+}
+
+PROFILER_INFORMATION (gAnimatorUpdate, "Animator.Update", kProfilerAnimation);
+PROFILER_INFORMATION (gAnimatorInitialize, "Animator.Initialize", kProfilerAnimation);
+
+PROFILER_INFORMATION (gProfileApplyRootMotion, "Apply Root Motion", kProfilerAnimation);
+PROFILER_INFORMATION (gProfileFKStep, "FK & Statemachine", kProfilerAnimation);
+PROFILER_INFORMATION (gProfileRetarget, "Retarget", kProfilerAnimation);
+PROFILER_INFORMATION (gRetargetAndPrepareIK, "Retarget", kProfilerAnimation);
+PROFILER_INFORMATION (gProfileAvatarIK, "IK & Final Pose Computation", kProfilerAnimation);
+PROFILER_INFORMATION (gProfileAvatarWrite, "Write", kProfilerAnimation);
+
+PROFILER_INFORMATION (gAnimatorSendTransformChanged, "Animator.SendTransformChanged", kProfilerAnimation);
+PROFILER_INFORMATION (gAnimatorSetGenericProperties, "Animator.ApplyGenericAnimatedProperties", kProfilerAnimation);
+PROFILER_INFORMATION (gAnimatorSetTransformDirty, "Animator.EditorOnlySetDirty", kProfilerAnimation);
+PROFILER_INFORMATION (gAnimatorWriteSkeletonPose, "Animator.WriteSkeletonPose", kProfilerAnimation);
+
+PROFILER_INFORMATION (gProfileDetailSM, "EvaluateAvatarSM", kProfilerAnimation);
+PROFILER_INFORMATION (gProfileDetailFK, "EvaluateAvatarFK", kProfilerAnimation);
+PROFILER_INFORMATION (gProfileDetailAvatarIK, "EvaluateAvatarIK", kProfilerAnimation);
+PROFILER_INFORMATION (gProfileDetailAvatarEnd, "EvaluateAvatarEnd", kProfilerAnimation);
+PROFILER_INFORMATION (gProfileSample, "Animator.Sample", kProfilerAnimation);
+
+PROFILER_INFORMATION (gProfileSetupDataSet, "Animator.SetupDataSet", kProfilerAnimation);
+
+#if ENABLE_DETAILED_SINGLE_THREAD_PROFILER
+#define PROFILER_AUTO_DETAIL(x,o) PROFILER_AUTO(x, o)
+#else
+#define PROFILER_AUTO_DETAIL(x,o)
+#endif
+
+
+static bool DoesLayerHaveIKPass(int layerIndex, const mecanim::animation::ControllerConstant& controller)
+{
+ return layerIndex < controller.m_LayerCount && controller.m_LayerArray[layerIndex]->m_IKPass;
+}
+
+// lazy search to find the right evaluation context for the given clip
+static bool FindClipInController(AnimationClip &clip, AnimationClipVector const& clips, AnimationSetBindings const& animationSetBindings, int &layerIndex, int &clipLayerIndex)
+{
+ layerIndex = -1;
+ clipLayerIndex = -1;
+
+
+ int clipIndex = -1;
+ for(int clipIter = 0; clipIndex == -1 && clipIter < clips.size(); clipIter++)
+ {
+ AnimationClip* currentClip = clips[clipIter];
+ if (&clip == currentClip)
+ {
+ clipIndex = clipIter;
+ break;
+ }
+ }
+
+ if (clipIndex == -1)
+ return false;
+
+ for(int layerIter = 0; layerIndex == -1 && layerIter < animationSetBindings.animationSet->m_LayerCount; layerIter++)
+ {
+ for(int clipLayerIter = 0; clipLayerIndex == -1 && clipLayerIter < animationSetBindings.animationSet->m_ClipPerLayer[layerIter]; clipLayerIter++)
+ {
+ const mecanim::animation::AnimationSet::Clip& clip = animationSetBindings.animationSet->m_ClipConstant[layerIter][clipLayerIter];
+ if (clip.m_ClipIndex == clipIndex && clip.m_Clip != NULL)
+ {
+ layerIndex = layerIter;
+ clipLayerIndex = clipLayerIter;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+namespace
+{
+ mecanim::animation::ControllerConstant* BuildController(AnimationClip const& clip, mecanim::memory::Allocator& allocator)
+ {
+ mecanim::uint32_t id = mecanim::processCRC32(clip.GetName());
+ mecanim::animation::BlendTreeConstant* blendTreeCst = mecanim::animation::CreateBlendTreeConstant(id, allocator);
+ mecanim::statemachine::StateConstant* stateCst = mecanim::statemachine::CreateStateConstant(0, 0, 1, true,false, 0, &blendTreeCst, 1, id, id, 0, true, allocator);
+ mecanim::statemachine::StateMachineConstant* stateMachinCst = CreateStateMachineConstant(&stateCst,1 , 0, 0, 0, 1, allocator);
+ mecanim::animation::LayerConstant* layer = mecanim::animation::CreateLayerConstant(0, 0, allocator);
+ layer->m_BodyMask = mecanim::human::FullBodyMask();
+ layer->m_SkeletonMask = 0;
+
+ mecanim::ValueArrayConstant* values = mecanim::CreateValueArrayConstant(0, 0, allocator);
+ mecanim::ValueArray* defaultValues = mecanim::CreateValueArray(values, allocator);
+
+ return mecanim::animation::CreateControllerConstant(1, &layer,1, &stateMachinCst, values, defaultValues, allocator);
+ }
+}
+
+Animator::AutoMecanimDataSet::~AutoMecanimDataSet()
+{
+ Reset();
+}
+
+void Animator::AutoMecanimDataSet::Reset()
+{
+ if(m_MecanimDataSet.m_AvatarBindingConstant != NULL)
+ UnregisterAvatarBindingObjects(m_MecanimDataSet.m_AvatarBindingConstant);
+ if(m_MecanimDataSet.m_GenericBindingConstant != NULL)
+ UnregisterGenericBindingObjects(m_MecanimDataSet.m_GenericBindingConstant);
+
+ m_MecanimDataSet.Reset();
+ m_Alloc.Reset();
+}
+
+bool Animator::Sample(AnimationClip& clip, float inTime)
+{
+ PROFILER_AUTO(gProfileSample, this);
+
+ if(!clip.IsAnimatorMotion())
+ return NULL;
+
+
+ m_SamplingDataSet.Reset();
+
+ SetupAvatarMecanimDataSet(m_Avatar.IsValid () ? m_Avatar->GetAsset() : NULL, m_SamplingDataSet.m_Alloc, *m_SamplingDataSet);
+
+ // muscle clip can be NULL when there is not curve at all in the clip.
+ mecanim::animation::ClipMuscleConstant* muscleConstant = clip.GetRuntimeAsset();
+ if(muscleConstant == NULL)
+ {
+ // in this case we want to set a human rig into relax pose to start keyframing
+ if(m_SamplingDataSet->m_AvatarConstant->isHuman())
+ {
+ mecanim::human::HumanPose pose;
+
+ mecanim::human::Human const* human = m_SamplingDataSet->m_AvatarConstant->m_Human.Get();
+ mecanim::human::RetargetTo(human, &pose, 0, math::xformIdentity(), m_SamplingDataSet->m_AvatarOutput->m_HumanPoseOutput, m_SamplingDataSet->m_AvatarWorkspace->m_BodySkeletonPoseWs, m_SamplingDataSet->m_AvatarWorkspace->m_BodySkeletonPoseWsA);
+ mecanim::animation::EvaluateAvatarEnd(m_SamplingDataSet->m_AvatarConstant, m_SamplingDataSet->m_AvatarInput, m_SamplingDataSet->m_AvatarOutput, m_SamplingDataSet->m_AvatarMemory, m_SamplingDataSet->m_AvatarWorkspace, NULL);
+
+ SetHumanTransformPropertyValues (*m_SamplingDataSet->m_AvatarBindingConstant, *m_SamplingDataSet->m_AvatarOutput->m_SkeletonPoseOutput);
+ SetTransformPropertyApplyMainThread (GetComponent (Transform), *m_SamplingDataSet->m_AvatarBindingConstant, false); // when sampling we don't skip apply root
+ }
+ return false;
+ }
+
+
+ AnimationClipVector clips;
+ clips.push_back( PPtr<AnimationClip>(&clip) );
+
+ mecanim::animation::ControllerConstant const* controllerConstant = ::BuildController(clip, m_SamplingDataSet.m_Alloc);
+ UnityEngine::Animation::AnimationSetBindings const* animationSetBindings = UnityEngine::Animation::CreateAnimationSetBindings(controllerConstant, clips, m_SamplingDataSet.m_Alloc);
+
+ SetupControllerMecanimDataSet(controllerConstant, animationSetBindings, m_SamplingDataSet.m_Alloc, *m_SamplingDataSet);
+
+ int layerIndex = 0;
+ int clipLayerIndex = 0;
+
+ // prepare the evaluation context for clip and evaluate in float array
+ mecanim::ValueArray* valuesDefault = m_SamplingDataSet->m_GenericBindingConstant->controllerBindingConstant->m_DynamicValuesDefault;
+ mecanim::ValueArrayConstant* valuesDefaultConstant = m_SamplingDataSet->m_GenericBindingConstant->controllerBindingConstant->m_DynamicValuesConstant;
+ mecanim::ValueArrayMask& readMask = *m_SamplingDataSet->m_AvatarWorkspace->m_ControllerWorkspace->m_ReadMask;
+
+ mecanim::animation::AnimationSet::Clip* setClip = &animationSetBindings->animationSet->m_ClipConstant[layerIndex][clipLayerIndex];
+ mecanim::animation::ClipMemory* clipMemory = m_SamplingDataSet->m_AnimationSetMemory->m_ClipMemory[layerIndex][clipLayerIndex];
+ mecanim::animation::ClipOutput* clipOutput = m_SamplingDataSet->m_AnimationSetMemory->m_ClipOutput;
+
+ mecanim::animation::ClipInput in;
+ in.m_Time = inTime;
+
+ mecanim::animation::EvaluateClip (muscleConstant->m_Clip.Get(), &in, clipMemory, clipOutput);
+
+ // load values and retarget
+ mecanim::SetValueMask (&readMask, false);
+ ValuesFromClip (*valuesDefault, *muscleConstant, *clipOutput, setClip->m_Bindings, animationSetBindings->animationSet->m_IntegerRemapStride, *m_SamplingDataSet->m_AvatarOutput->m_DynamicValuesOutput, readMask);
+ if (m_SamplingDataSet->m_AvatarConstant->isHuman())
+ {
+ mecanim::animation::GetHumanPose (*muscleConstant,clipOutput->m_Values,*m_SamplingDataSet->m_AvatarOutput->m_HumanPoseBaseOutput);
+ mecanim::human::HumanPoseCopy (*m_SamplingDataSet->m_AvatarOutput->m_HumanPoseOutput,*m_SamplingDataSet->m_AvatarOutput->m_HumanPoseBaseOutput);
+ mecanim::animation::EvaluateAvatarRetarget (m_SamplingDataSet->m_AvatarConstant, m_SamplingDataSet->m_AvatarInput, m_SamplingDataSet->m_AvatarOutput, m_SamplingDataSet->m_AvatarMemory, m_SamplingDataSet->m_AvatarWorkspace, m_SamplingDataSet->m_ControllerConstant);
+ mecanim::animation::EvaluateAvatarEnd (m_SamplingDataSet->m_AvatarConstant, m_SamplingDataSet->m_AvatarInput, m_SamplingDataSet->m_AvatarOutput, m_SamplingDataSet->m_AvatarMemory, m_SamplingDataSet->m_AvatarWorkspace, m_SamplingDataSet->m_ControllerConstant);
+ }
+
+ // Controller animated values
+ ValueArrayCopy (valuesDefaultConstant, m_SamplingDataSet->m_AvatarOutput->m_DynamicValuesOutput, m_SamplingDataSet->m_ControllerConstant->m_Values.Get(), m_SamplingDataSet->m_AvatarMemory->m_ControllerMemory->m_Values.Get(), animationSetBindings->animationSet->m_AdditionalIndexArray);
+
+ // set transform values
+ if (m_SamplingDataSet->m_AvatarConstant->isHuman())
+ SetHumanTransformPropertyValues (*m_SamplingDataSet->m_AvatarBindingConstant, *m_SamplingDataSet->m_AvatarOutput->m_SkeletonPoseOutput);
+ SetGenericTransformPropertyValues (*m_SamplingDataSet->m_GenericBindingConstant, *m_SamplingDataSet->m_AvatarOutput->m_DynamicValuesOutput,0); // when sampling set root transform if needed
+
+ SetTransformPropertyApplyMainThread (GetComponent (Transform), *m_SamplingDataSet->m_GenericBindingConstant, *m_SamplingDataSet->m_AvatarBindingConstant, false); // when sampling we don't skip apply root
+
+ // set generic values
+ SetGenericFloatPropertyValues (*m_SamplingDataSet->m_GenericBindingConstant, *m_SamplingDataSet->m_AvatarOutput->m_DynamicValuesOutput);
+ SetGenericPPtrPropertyValues (*m_SamplingDataSet->m_GenericBindingConstant, *m_SamplingDataSet->m_AvatarOutput->m_DynamicValuesOutput);
+
+ return true;
+}
+
+void Animator::UpdateAvatars (Animator** inputAvatars, size_t inputSize, float deltaTime, bool doFKMove, bool doRetargetIKWrite)
+{
+ PROFILER_AUTO(gAnimatorUpdate, NULL)
+
+ #if ENABLE_MULTITHREADED_ANIMATION
+
+ JobScheduler& scheduler = GetJobScheduler();
+ JobScheduler::JobGroupID jobGroup;
+
+ #define AVATAR_LOOP(x,list,profile) \
+ { \
+ PROFILER_AUTO(profile, NULL); \
+ size_t avatarJobCount = list.size(); \
+ jobGroup = scheduler.BeginGroup(avatarJobCount); \
+ for (size_t i = 0; i < avatarJobCount; ++i) \
+ { Animator& avatar = *list[i]; scheduler.SubmitJob (jobGroup, x, &avatar, NULL); } \
+ scheduler.WaitForGroup (jobGroup); \
+ }
+ #else
+
+ #define AVATAR_LOOP(x,list, profile) \
+ for (size_t i=0;i<list.size();i++) \
+ { Animator& avatar = *list[i]; PROFILER_AUTO(profile, &avatar); x (&avatar); }
+
+ #endif
+
+ if(doFKMove)
+ {
+ // invisible & visible animators
+ dynamic_array<Animator*> activeAvatars (kMemTempAlloc);
+ activeAvatars.reserve(inputSize);
+
+ for (size_t i=0;i<inputSize;i++)
+ {
+ Animator& avatar = *inputAvatars[i];
+
+ if (avatar.Prepare())
+ {
+ activeAvatars.push_back(&avatar);
+ }
+ }
+
+ // Init delta time
+ for (size_t i=0;i<activeAvatars.size();i++)
+ {
+ Animator& avatar = *activeAvatars[i];
+ avatar.InitStep(deltaTime);
+ }
+
+
+ // Multithreaded FK Step
+ AVATAR_LOOP(FKStepStatic, activeAvatars, gProfileFKStep)
+
+ // MainThread AnimationEvents and OnAnimatorMove script call
+ for (size_t i=0;i<activeAvatars.size();i++)
+ {
+ Animator& avatar = *activeAvatars[i];
+ avatar.FireAnimationEvents();
+ avatar.ApplyOnAnimatorMove();
+ }
+ }
+
+ if (doRetargetIKWrite)
+ {
+ int layerCount = 0;
+
+ // only visible animators
+ dynamic_array<Animator*> visibleAvatars (kMemTempAlloc);
+ visibleAvatars.reserve(inputSize);
+
+ // human visible animators
+ dynamic_array<Animator*> humanAvatars (kMemTempAlloc);
+ humanAvatars.reserve(inputSize);
+
+ for (size_t i=0;i<inputSize;i++)
+ {
+ Animator& avatar = *inputAvatars[i];
+
+ if (avatar.Prepare() && avatar.m_Visible)
+ {
+ layerCount = avatar.GetLayerCount() > layerCount ? avatar.GetLayerCount() : layerCount;
+ visibleAvatars.push_back(&avatar);
+
+ if(avatar.IsHuman())
+ {
+ humanAvatars.push_back(&avatar);
+
+ avatar.m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_DoIK = true;
+ }
+
+ avatar.m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_DoWrite = true;
+ }
+ }
+
+
+ // Multithreaded Retarget & PrepareIK
+ AVATAR_LOOP(RetargetStepStatic, humanAvatars, gProfileRetarget)
+
+ // Default IK Pass
+ AVATAR_LOOP(AvatarIKAndEndStepStatic, humanAvatars, gProfileAvatarIK)
+ AVATAR_LOOP(AvatarWriteStepStatic, humanAvatars, gProfileAvatarWrite)
+
+ // Layered IK Pass
+ for(int layerIter = 0; layerIter < layerCount; layerIter++)
+ {
+ // MainThread OnApplyAvatarIK
+ for (size_t i = 0; i < humanAvatars.size(); i++)
+ {
+ Animator& avatar = *humanAvatars[i];
+
+ bool ikPass = DoesLayerHaveIKPass (layerIter, *avatar.m_EvaluationDataSet.m_ControllerConstant);
+ if (ikPass)
+ {
+ avatar.ApplyOnAnimatorIK (layerIter);
+ }
+
+ avatar.m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_DoIK = ikPass;
+ avatar.m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_DoWrite = ikPass;
+ }
+
+ AVATAR_LOOP(AvatarIKAndEndStepStatic,humanAvatars, gProfileAvatarIK)
+ AVATAR_LOOP(AvatarWriteStepStatic, humanAvatars, gProfileAvatarWrite)
+ }
+
+ // Write to those that were not written already
+ AVATAR_LOOP(AvatarWriteStepStatic, visibleAvatars, gProfileAvatarWrite)
+
+ // MainThread Apply skeleton to transform components
+ for (size_t i=0;i<visibleAvatars.size();i++)
+ {
+ Animator& animator = *visibleAvatars[i];
+ if(animator.IsActive()) // animator can be turned Inactive by a parent animator -> 566794
+ {
+ {
+ if (animator.m_HasTransformHierarchy)
+ {
+ PROFILER_AUTO(gAnimatorSendTransformChanged, &animator)
+ SetTransformPropertyApplyMainThread (animator.GetComponent(Transform), *animator.m_EvaluationDataSet.m_GenericBindingConstant, *animator.m_EvaluationDataSet.m_AvatarBindingConstant, animator.IsHuman() || animator.HasRootMotion()); // if it has root motion we skip root
+ }
+ else
+ {
+ const mecanim::skeleton::SkeletonPose* pose = animator.GetGlobalSpaceSkeletonPose();
+ if (pose && animator.m_EvaluationDataSet.m_AvatarConstant)
+ SetFlattenedSkeletonTransformsMainThread (*animator.m_EvaluationDataSet.m_AvatarBindingConstant, *pose, *animator.m_EvaluationDataSet.m_AvatarConstant);
+ }
+ }
+
+ {
+
+ PROFILER_AUTO(gAnimatorSetGenericProperties, &animator)
+ SetGenericFloatPropertyValues (*animator.m_EvaluationDataSet.m_GenericBindingConstant, *animator.m_EvaluationDataSet.m_AvatarOutput->m_DynamicValuesOutput);
+ SetGenericPPtrPropertyValues (*animator.m_EvaluationDataSet.m_GenericBindingConstant, *animator.m_EvaluationDataSet.m_AvatarOutput->m_DynamicValuesOutput);
+ }
+
+ animator.Record(deltaTime);
+
+ UInt8 deltaTimeIs0 = (deltaTime==0) ? 1 : 0;
+ animator.m_EvaluationDataSet.m_AvatarMemory->m_FirstEval &= deltaTimeIs0;
+ }
+ }
+ }
+}
+
+bool Animator::Prepare()
+{
+ if (!IsInitialize())
+ CreateObject();
+
+ return IsInitialize();
+}
+
+void Animator::InitStep(float deltaTime)
+{
+ if(IsAutoPlayingBack())
+ {
+ SetPlaybackTimeInternal(m_AvatarPlayback.CursorTime() + (deltaTime * GetSpeed()));
+ }
+ else
+ {
+ m_EvaluationDataSet.m_AvatarInput->m_DeltaTime = deltaTime * GetSpeed();
+ }
+
+ if(IsPlayingBack())
+ {
+ m_EvaluationDataSet.m_AvatarInput->m_DeltaTime = m_PlaybackDeltaTime;
+ m_PlaybackDeltaTime = 0;
+ }
+}
+
+void Animator::EvaluateSM()
+{
+ if(Prepare())
+ {
+ mecanim::animation::EvaluateAvatarSM(m_EvaluationDataSet.m_AvatarConstant, m_EvaluationDataSet.m_AvatarInput, m_EvaluationDataSet.m_AvatarOutput, m_EvaluationDataSet.m_AvatarMemory, m_EvaluationDataSet.m_AvatarWorkspace, m_EvaluationDataSet.m_ControllerConstant);
+}
+}
+
+void Animator::FKStep()
+{
+ m_DeltaPosition = Vector3f::zero;
+ m_DeltaRotation = Quaternionf::identity();
+
+ {
+ PROFILER_AUTO_DETAIL (gProfileDetailSM, this)
+ mecanim::animation::EvaluateAvatarSM(m_EvaluationDataSet.m_AvatarConstant, m_EvaluationDataSet.m_AvatarInput, m_EvaluationDataSet.m_AvatarOutput, m_EvaluationDataSet.m_AvatarMemory, m_EvaluationDataSet.m_AvatarWorkspace, m_EvaluationDataSet.m_ControllerConstant);
+ }
+
+ {
+ PROFILER_AUTO_DETAIL(gProfileDetailFK, this);
+
+ mecanim::animation::EvaluateAvatarLayers(m_EvaluationDataSet.m_GenericBindingConstant->controllerBindingConstant, m_EvaluationDataSet.m_AvatarInput, m_EvaluationDataSet.m_AvatarOutput, m_EvaluationDataSet.m_AvatarMemory, m_EvaluationDataSet.m_AvatarWorkspace, m_EvaluationDataSet.m_AnimationSetMemory);
+
+ if(m_EvaluationDataSet.m_AvatarConstant->isHuman())
+ {
+ // Prepare for OnAnimatorMove
+ m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_TargetX.t *= math::float1(m_EvaluationDataSet.m_AvatarConstant->m_Human->m_Scale);
+
+ TargetMatch(m_MustCompleteMatch);
+ m_MustCompleteMatch = false;
+
+ m_DeltaPosition = RotateVectorByQuat(GetAvatarRotation(), float4ToVector3f(m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_DX.t*m_EvaluationDataSet.m_AvatarMemory->m_AvatarX.s*math::float1(m_EvaluationDataSet.m_AvatarConstant->m_Human->m_Scale)));
+ m_DeltaRotation = float4ToQuaternionf(m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_DX.q);
+ }
+ else if(m_EvaluationDataSet.m_AvatarConstant->m_RootMotionBoneIndex != -1)
+ {
+ m_DeltaPosition = RotateVectorByQuat(GetAvatarRotation(), float4ToVector3f(m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_DX.t*m_EvaluationDataSet.m_AvatarMemory->m_AvatarX.s));
+ m_DeltaRotation = float4ToQuaternionf(m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_DX.q);
+ }
+
+ if(m_EvaluationDataSet.m_AvatarConstant->isHuman() || m_EvaluationDataSet.m_AvatarConstant->m_RootMotionBoneIndex != -1)
+ {
+ mecanim::animation::EvaluateAvatarX(m_EvaluationDataSet.m_AvatarConstant, m_EvaluationDataSet.m_AvatarInput, m_EvaluationDataSet.m_AvatarOutput, m_EvaluationDataSet.m_AvatarMemory, m_EvaluationDataSet.m_AvatarWorkspace);
+ }
+
+ if(m_EvaluationDataSet.m_AvatarConstant->isHuman())
+ {
+ m_TargetPosition = float4ToVector3f(math::xformMulVec(m_EvaluationDataSet.m_AvatarMemory->m_AvatarX, m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_TargetX.t));
+ m_TargetRotation = float4ToQuaternionf(math::normalize(math::quatMul(m_EvaluationDataSet.m_AvatarMemory->m_AvatarX.q, m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_TargetX.q)));
+ m_PivotPosition = float4ToVector3f(math::xformMulVec(m_EvaluationDataSet.m_AvatarMemory->m_AvatarX, m_EvaluationDataSet.m_AvatarMemory->m_Pivot));
+ }
+ }
+}
+
+void Animator::RetargetStep()
+{
+ mecanim::animation::EvaluateAvatarRetarget(m_EvaluationDataSet.m_AvatarConstant, m_EvaluationDataSet.m_AvatarInput, m_EvaluationDataSet.m_AvatarOutput, m_EvaluationDataSet.m_AvatarMemory, m_EvaluationDataSet.m_AvatarWorkspace, m_EvaluationDataSet.m_ControllerConstant);
+}
+
+void Animator::AvatarIKAndEndStep()
+{
+ if (!m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_DoIK)
+ return;
+
+ {
+ PROFILER_AUTO_DETAIL(gProfileDetailAvatarIK, this)
+ mecanim::animation::EvaluateAvatarIK(m_EvaluationDataSet.m_AvatarConstant, m_EvaluationDataSet.m_AvatarInput, m_EvaluationDataSet.m_AvatarOutput, m_EvaluationDataSet.m_AvatarMemory, m_EvaluationDataSet.m_AvatarWorkspace, m_EvaluationDataSet.m_ControllerConstant);
+ }
+
+ {
+ PROFILER_AUTO_DETAIL(gProfileDetailAvatarEnd, this)
+
+ mecanim::animation::EvaluateAvatarEnd(m_EvaluationDataSet.m_AvatarConstant, m_EvaluationDataSet.m_AvatarInput, m_EvaluationDataSet.m_AvatarOutput, m_EvaluationDataSet.m_AvatarMemory, m_EvaluationDataSet.m_AvatarWorkspace, m_EvaluationDataSet.m_ControllerConstant);
+ }
+}
+
+void Animator::AvatarWriteStep()
+{
+ if (!m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_DoWrite)
+ return;
+
+ m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_DoWrite = false;
+
+ const bool hasRootMotion = m_EvaluationDataSet.m_AvatarConstant->isHuman() || (m_EvaluationDataSet.m_AvatarConstant->m_RootMotionBoneIndex != -1) ;
+
+ if (m_HasTransformHierarchy)
+ {
+ // Write Transforms from humanoid skeleton
+ if (m_EvaluationDataSet.m_AvatarConstant->isHuman())
+ /// review this function to just write what it needs
+ SetHumanTransformPropertyValues (*m_EvaluationDataSet.m_AvatarBindingConstant, *m_EvaluationDataSet.m_AvatarOutput->m_SkeletonPoseOutput);
+
+ // Write Transforms for generic binding
+ SetGenericTransformPropertyValues (*m_EvaluationDataSet.m_GenericBindingConstant, *m_EvaluationDataSet.m_AvatarOutput->m_DynamicValuesOutput, hasRootMotion ? &GetComponent(Transform) : 0);
+ }
+ else if (m_EvaluationDataSet.m_AvatarConstant->m_AvatarSkeleton->m_Count > 0)
+ {
+ mecanim::animation::SkeletonPoseFromValue( *m_EvaluationDataSet.m_AvatarConstant->m_AvatarSkeleton.Get(),
+ *m_EvaluationDataSet.m_AvatarConstant->m_AvatarSkeletonPose.Get(),
+ *m_EvaluationDataSet.m_AvatarOutput->m_DynamicValuesOutput,
+ m_EvaluationDataSet.m_GenericBindingConstant->controllerBindingConstant->m_SkeletonTQSMap,
+ *m_EvaluationDataSet.m_AvatarOutput->m_SkeletonPoseOutput,
+ m_EvaluationDataSet.m_AvatarConstant->isHuman() ? m_EvaluationDataSet.m_AvatarConstant->m_HumanSkeletonReverseIndexArray.Get() : 0,
+ hasRootMotion );
+
+ if (!m_EvaluationDataSet.m_AvatarConstant->isHuman())
+ {
+ m_EvaluationDataSet.m_AvatarOutput->m_SkeletonPoseOutput->m_X[0] = m_EvaluationDataSet.m_AvatarMemory->m_AvatarX;
+ }
+
+ mecanim::skeleton::SkeletonPoseComputeGlobal (m_EvaluationDataSet.m_AvatarConstant->m_AvatarSkeleton.Get(), m_EvaluationDataSet.m_AvatarOutput->m_SkeletonPoseOutput, m_EvaluationDataSet.m_AvatarOutput->m_SkeletonPoseOutput);
+
+ m_EvaluationDataSet.m_AvatarMemory->m_SkeletonPoseOutputReady = true;
+ }
+}
+
+const mecanim::skeleton::SkeletonPose* Animator::GetGlobalSpaceSkeletonPose() const
+{
+ if (m_EvaluationDataSet.m_AvatarConstant && m_EvaluationDataSet.m_AvatarMemory->m_SkeletonPoseOutputReady)
+ return m_EvaluationDataSet.m_AvatarOutput->m_SkeletonPoseOutput;
+ return NULL;
+}
+
+void Animator::ApplyOnAnimatorMove()
+{
+ if(!IsPlayingBack())
+ {
+ if (SupportsOnAnimatorMove ())
+ {
+ SendMessage (kAnimatorMove);
+ }
+ else
+ {
+ if (m_ApplyRootMotion)
+ {
+ ApplyBuiltinRootMotion();
+ }
+ }
+
+ // force feedback of transform in avatarX
+ // avatarX is always modified by deltaTransform in FK step
+ // if transform is not modified then avatarX won't be sync with it
+ // case 498101
+ // TODO: we could skip this most of the time with a simple dirty check
+ Transform& avatarTransform = GetComponent(Transform);
+ SetAvatarPosition(avatarTransform.GetPosition());
+ SetAvatarRotation(avatarTransform.GetRotation());
+ }
+ else
+ {
+ Transform& avatarTransform = GetComponent(Transform);
+ avatarTransform.SetPositionAndRotation(GetAvatarPosition(), GetAvatarRotation());
+ }
+}
+
+void Animator::ApplyBuiltinRootMotion()
+{
+ PROFILER_AUTO(gProfileApplyRootMotion, this);
+
+ // Builtin move (For example rigidbodies & Character Controllers)
+ RootMotionData motionData;
+ motionData.deltaPosition = GetDeltaPosition();
+ motionData.targetRotation = GetAvatarRotation();
+ motionData.gravityWeight = GetGravityWeight();
+ motionData.didApply = false;
+ SendMessage (kAnimatorMoveBuiltin, &motionData, ClassID(RootMotionData));
+
+ // Fallback to just moving the transform
+ if (!motionData.didApply)
+ {
+ Transform& avatarTransform = GetComponent(Transform);
+ avatarTransform.SetPositionAndRotation(GetAvatarPosition(), motionData.targetRotation);
+ }
+}
+
+void Animator::FireAnimationEvents()
+{
+ if(!m_FireEvents) return; // we dont want to fire events in previewers
+
+ AnimationClipVector const allClips = GetAnimationClips();
+ for(int i = 0; i < m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_BlendingClipCount; i++)
+ {
+ mecanim::animation::BlendingClip &blendingClip = m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_BlendingClipArray[i];
+
+ AnimationClip *clip = allClips[blendingClip.m_ClipIndex];
+
+ if (clip->HasAnimationEvents())
+ {
+ float prevTime = blendingClip.m_Reverse ? blendingClip.m_Time : blendingClip.m_PrevTime;
+ float time = blendingClip.m_Reverse ? blendingClip.m_PrevTime : blendingClip.m_Time;
+
+ clip->FireAnimationEvents (prevTime, time, *this);
+ }
+ }
+}
+
+bool Animator::SupportsOnAnimatorMove ()
+{
+ return GetGameObject().GetSupportedMessages() & kHasOnAnimatorMove;
+}
+
+void Animator::ApplyOnAnimatorIK(int layerIndex)
+{
+ if (GetGameObject().GetSupportedMessages() & kHasOnAnimatorIK)
+ SendMessage (kAnimatorIK, layerIndex, ClassID(int));
+}
+
+void Animator::Record(float deltaTime)
+{
+ if(m_RecorderMode == eRecord && GetSpeed() >= 0)
+ {
+ m_AvatarPlayback.RecordFrame(deltaTime*GetSpeed(), m_EvaluationDataSet.m_AvatarMemory);
+ }
+}
+
+void* Animator::FKStepStatic (void* userData)
+{
+ PROFILER_AUTO(gProfileFKStep,NULL);
+ static_cast<Animator*> (userData)->FKStep();
+ return NULL;
+}
+
+void* Animator::RetargetStepStatic (void* userData)
+{
+ PROFILER_AUTO(gProfileRetarget,NULL);
+ static_cast<Animator*> (userData)->RetargetStep();
+ return NULL;
+}
+
+void* Animator::AvatarIKAndEndStepStatic (void* userData)
+{
+ PROFILER_AUTO(gProfileAvatarIK,NULL);
+ static_cast<Animator*> (userData)->AvatarIKAndEndStep();
+ return NULL;
+}
+
+void* Animator::AvatarWriteStepStatic (void* userData)
+{
+ PROFILER_AUTO(gProfileAvatarWrite,NULL);
+ static_cast<Animator*> (userData)->AvatarWriteStep();
+ return NULL;
+}
+
+void Animator::Update(float deltaTime)
+{
+ Animator* avatar = this;
+ UpdateAvatars (&avatar, 1, deltaTime, true, true);
+}
+
+bool Animator::IsAvatarInitialize() const
+{
+ return m_EvaluationDataSet.m_AvatarMemory;
+}
+
+bool Animator::IsInitialize() const
+{
+ return IsAvatarInitialize() && m_EvaluationDataSet.m_ControllerMemory;
+}
+
+void Animator::SetLayersAffectMassCenter(bool value)
+{
+ if(!IsAvatarInitialize())
+ return;
+
+ m_EvaluationDataSet.m_AvatarInput->m_LayersAffectMassCenter = value;
+}
+
+bool Animator::GetLayersAffectMassCenter() const
+{
+ if(!IsAvatarInitialize())
+ return false;
+
+ return m_EvaluationDataSet.m_AvatarInput->m_LayersAffectMassCenter ;
+}
+
+bool Animator::IsInManagerList() const
+{
+ return m_BehaviourIndex != -1 || m_FixedBehaviourIndex != -1;
+}
+
+bool Animator::IsValid() const
+{
+ return m_Controller.GetInstanceID() != 0;
+}
+
+
+RuntimeAnimatorController* Animator::GetRuntimeAnimatorController() const
+{
+ return m_Controller;
+}
+
+void Animator::SetRuntimeAnimatorController(RuntimeAnimatorController* controller)
+{
+ if(m_Controller != PPtr<RuntimeAnimatorController>(controller))
+ {
+ m_Controller = controller;
+
+ UpdateInManager();
+
+ CreateObject();
+ SetDirty();
+ }
+}
+
+
+AnimatorController* Animator::GetAnimatorController()const
+{
+ return dynamic_pptr_cast<AnimatorController*>(m_Controller);
+}
+
+
+AnimatorOverrideController* Animator::GetAnimatorOverrideController()const
+{
+ return dynamic_pptr_cast<AnimatorOverrideController*>(m_Controller);
+}
+
+void Animator::UpdateInManager()
+{
+ bool isValid = IsValid() && IsAddedToManager() ;
+
+ bool isEffectivelyAddedToManager = IsInManagerList();
+
+ if(!isValid && isEffectivelyAddedToManager)
+ {
+ RemoveFromManager();
+ }
+ else if(isValid && !isEffectivelyAddedToManager)
+ {
+ AddToManager();
+ }
+}
+
+
+
+
+Avatar* Animator::GetAvatar()
+{
+ return m_Avatar;
+}
+
+void Animator::SetAvatar(Avatar* avatar)
+{
+ if(m_Avatar != PPtr<Avatar>(avatar))
+ {
+ m_Avatar = avatar;
+
+ UpdateInManager();
+
+ CreateObject();
+ SetDirty();
+ }
+}
+
+const mecanim::animation::AvatarConstant* Animator::GetAvatarConstant()
+{
+ if (!IsAvatarInitialize())
+ CreateObject();
+ return m_EvaluationDataSet.m_AvatarConstant;
+}
+
+
+void Animator::SetCullingMode (CullingMode mode)
+{
+ if (m_CullingMode == mode)
+ return;
+
+ m_CullingMode = mode;
+
+ InitializeVisibilityCulling ();
+}
+
+void Animator::SetVisibleRenderers(bool visible)
+{
+ Assert(m_CullingMode == kCullBasedOnRenderers);
+
+ bool becameVisible = visible && !m_Visible;
+ m_Visible = visible;
+
+ if (!IsWorldPlaying())
+ return;
+
+ // Perform Retarget & IK step during culling to ensure the animator
+ // will be rendered correctly in the same frame.
+ if (becameVisible )
+ {
+ float deltaTime = GetDeltaTime();
+
+ if(!IsInManagerList() || deltaTime == 0)
+ return;
+
+ if(Prepare())
+ {
+ Animator* animator[1] = { this };
+
+ bool firstEval = m_EvaluationDataSet.m_AvatarMemory->m_FirstEval;
+ m_EvaluationDataSet.m_AvatarMemory->m_FirstEval = 1;
+ m_EvaluationDataSet.m_AvatarMemory->m_SkeletonPoseOutputReady = 0;
+ UpdateAvatars (animator, 1, deltaTime, firstEval, true);
+ }
+ }
+}
+
+void Animator::AnimatorVisibilityCallback (void* userData, void* renderer, int visibilityEvent)
+{
+ Animator& animator = *reinterpret_cast<Animator*> (userData);
+
+ if (visibilityEvent == kBecameVisibleEvent)
+ animator.SetVisibleRenderers(true);
+ else if (visibilityEvent == kBecameInvisibleEvent)
+ {
+ animator.SetVisibleRenderers(animator.IsAnyRendererVisible ());
+ }
+ else if (visibilityEvent == kWillDestroyEvent)
+ {
+ animator.RemoveContainedRenderer(renderer);
+
+ // Check if we are visible
+ animator.SetVisibleRenderers(animator.IsAnyRendererVisible ());
+ }
+}
+
+void Animator::RemoveContainedRenderer (void* renderer)
+{
+ for (int i=0;i<m_ContainedRenderers.size();i++)
+ {
+ if (m_ContainedRenderers[i] == renderer)
+ {
+ m_ContainedRenderers[i] = m_ContainedRenderers.back();
+ m_ContainedRenderers.pop_back();
+ return;
+ }
+ }
+}
+
+bool Animator::IsAnyRendererVisible () const
+{
+ Assert(m_CullingMode == kCullBasedOnRenderers);
+
+ ContainedRenderers::const_iterator end = m_ContainedRenderers.end();
+ for (ContainedRenderers::const_iterator i = m_ContainedRenderers.begin();i != end;++i)
+ {
+ const Renderer* renderer = *i;
+ Assert(renderer->HasEvent(AnimatorVisibilityCallback, this));
+
+ if (renderer->IsVisibleInScene())
+ return true;
+ }
+
+ return false;
+}
+
+void Animator::ClearContainedRenderers ()
+{
+ ContainedRenderers::iterator end = m_ContainedRenderers.end();
+ for (ContainedRenderers::iterator i = m_ContainedRenderers.begin();i != end;++i)
+ {
+ Renderer* renderer = *i;
+ renderer->RemoveEvent(AnimatorVisibilityCallback, this);
+ }
+ m_ContainedRenderers.clear();
+}
+
+void Animator::RecomputeContainedRenderersRecurse (Transform& transform)
+{
+
+ Renderer* renderer = transform.QueryComponent(Renderer);
+ if (renderer)
+ {
+ m_ContainedRenderers.push_back(renderer);
+ renderer->AddEvent(AnimatorVisibilityCallback, this);
+ }
+ Transform::iterator end = transform.end();
+ for (Transform::iterator i = transform.begin();i != end;++i)
+ {
+ RecomputeContainedRenderersRecurse(**i);
+ }
+}
+
+void Animator::InitializeVisibilityCulling ()
+{
+ if(!IsActive())
+ return;
+
+ ClearContainedRenderers ();
+
+ if (m_CullingMode == kCullBasedOnRenderers)
+ {
+ Transform& transform = GetComponent (Transform);
+ RecomputeContainedRenderersRecurse(transform);
+
+ if (m_ContainedRenderers.empty())
+ m_Visible = IsAnyRendererVisible();
+ }
+ else
+ {
+ m_Visible = true;
+ }
+}
+
+
+void Animator::ClearObject()
+{
+ InvokeEvent(kAnimatorClearEvent);
+
+ mecanim::animation::DestroyAnimationSetMemory(m_EvaluationDataSet.m_AnimationSetMemory, mAlloc);
+ mecanim::animation::DestroyControllerMemory(m_EvaluationDataSet.m_ControllerMemory, mAlloc);
+ DestroyAnimatorGenericBindings (m_EvaluationDataSet.m_GenericBindingConstant, mAlloc);
+ DestroyAvatarBindingConstant (m_EvaluationDataSet.m_AvatarBindingConstant, mAlloc);
+ if(m_EvaluationDataSet.m_AvatarWorkspace)
+ mecanim::animation::DestroyControllerWorkspace(m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace, mAlloc);
+ mecanim::animation::DestroyAvatarOutput(m_EvaluationDataSet.m_AvatarOutput, mAlloc);
+ mecanim::animation::DestroyAvatarInput(m_EvaluationDataSet.m_AvatarInput, mAlloc);
+ mecanim::animation::DestroyAvatarMemory(m_EvaluationDataSet.m_AvatarMemory, mAlloc);
+
+ mecanim::animation::DestroyAvatarWorkspace(m_EvaluationDataSet.m_AvatarWorkspace, mAlloc);
+
+ if(m_EvaluationDataSet.m_OwnsAvatar)
+ {
+ mecanim::animation::DestroyAvatarConstant( const_cast<mecanim::animation::AvatarConstant*>(m_EvaluationDataSet.m_AvatarConstant), mAlloc);
+ m_EvaluationDataSet.m_OwnsAvatar = false;
+ }
+
+ m_EvaluationDataSet.m_AvatarConstant = 0;
+ m_EvaluationDataSet.m_AvatarMemory = 0;
+ // It is very important to reset the memory size to zero because we use this member to determine if the data is blobified and ready to be copy by recorder
+ m_EvaluationDataSet.m_AvatarMemorySize = 0;
+ m_EvaluationDataSet.m_AvatarInput = 0;
+ m_EvaluationDataSet.m_AvatarOutput = 0;
+ m_EvaluationDataSet.m_AvatarWorkspace = 0;
+ m_EvaluationDataSet.m_ControllerConstant = 0;
+
+ m_EvaluationDataSet.m_GenericBindingConstant = 0;
+ m_EvaluationDataSet.m_AvatarBindingConstant = 0;
+
+ m_EvaluationDataSet.m_ControllerMemory = 0;
+ m_EvaluationDataSet.m_AnimationSetMemory = 0;
+
+ m_AnimatorControllerNode.Clear();
+ m_AnimatorAvatarNode.Clear();
+
+ m_SamplingDataSet.Reset();
+}
+
+void Animator::SetupAvatarMecanimDataSet(mecanim::animation::AvatarConstant const* avatarConstant, mecanim::memory::Allocator& allocator, Animator::MecanimDataSet& outMecanimDataSet)
+{
+ PROFILER_AUTO(gProfileSetupDataSet, this);
+
+ outMecanimDataSet.m_AvatarConstant = avatarConstant;
+
+ if (outMecanimDataSet.m_AvatarConstant == NULL)
+ {
+ outMecanimDataSet.m_OwnsAvatar = true;
+ outMecanimDataSet.m_AvatarConstant = mecanim::animation::CreateAvatarConstant(0,0,0,0,0,-1,math::xformIdentity(), allocator);
+ }
+
+ // It is very important to reset the memory size to zero because we use this member to determine if the data is blobified and ready to be copy by recorder
+ outMecanimDataSet.m_AvatarMemorySize = 0;
+ outMecanimDataSet.m_AvatarMemory = mecanim::animation::CreateAvatarMemory(outMecanimDataSet.m_AvatarConstant, allocator);
+ outMecanimDataSet.m_AvatarInput = mecanim::animation::CreateAvatarInput(outMecanimDataSet.m_AvatarConstant, allocator);
+ outMecanimDataSet.m_AvatarWorkspace = mecanim::animation::CreateAvatarWorkspace(outMecanimDataSet.m_AvatarConstant, allocator);
+ outMecanimDataSet.m_AvatarOutput = mecanim::animation::CreateAvatarOutput(outMecanimDataSet.m_AvatarConstant, m_HasTransformHierarchy, allocator);
+
+ Transform *effectiveRoot = GetAvatarRoot();
+ if (m_HasTransformHierarchy)
+ outMecanimDataSet.m_AvatarBindingConstant = CreateAvatarBindingConstant (*effectiveRoot, outMecanimDataSet.m_AvatarConstant, allocator);
+ else
+ outMecanimDataSet.m_AvatarBindingConstant = CreateAvatarBindingConstantOpt (*effectiveRoot, outMecanimDataSet.m_AvatarConstant, allocator);
+
+ // Setup AvatarX based on current transform
+ Transform& transform = GetComponent(Transform);
+ SetAvatarPosition(transform.GetPosition());
+ SetAvatarRotation(transform.GetRotation());
+ SetAvatarScale(transform.GetWorldScaleLossy());
+}
+
+void Animator::SetupControllerMecanimDataSet(mecanim::animation::ControllerConstant const* controllerConstant, UnityEngine::Animation::AnimationSetBindings const* animationSetBindings, mecanim::memory::Allocator& allocator, Animator::MecanimDataSet& outMecanimDataSet)
+{
+ outMecanimDataSet.m_ControllerConstant = controllerConstant;
+
+ Transform *effectiveRoot = GetAvatarRoot();
+ if (m_HasTransformHierarchy)
+ outMecanimDataSet.m_GenericBindingConstant = CreateAnimatorGenericBindings (*animationSetBindings, *effectiveRoot, outMecanimDataSet.m_AvatarConstant, outMecanimDataSet.m_ControllerConstant, allocator);
+ else
+ outMecanimDataSet.m_GenericBindingConstant = CreateAnimatorGenericBindingsOpt (*animationSetBindings, *effectiveRoot, outMecanimDataSet.m_AvatarConstant, outMecanimDataSet.m_ControllerConstant, allocator);
+
+ const mecanim::ValueArrayConstant* dynamicValuesConstant = outMecanimDataSet.m_GenericBindingConstant->controllerBindingConstant->m_DynamicValuesConstant;
+
+ outMecanimDataSet.m_ControllerMemory = mecanim::animation::CreateControllerMemory(outMecanimDataSet.m_ControllerConstant, outMecanimDataSet.m_AvatarConstant, animationSetBindings->animationSet, dynamicValuesConstant, allocator);
+ outMecanimDataSet.m_AvatarMemory->m_ControllerMemory = outMecanimDataSet.m_ControllerMemory;
+
+ outMecanimDataSet.m_AnimationSetMemory = mecanim::animation::CreateAnimationSetMemory(animationSetBindings->animationSet, outMecanimDataSet.m_GenericBindingConstant->allowConstantClipSamplingOptimization, allocator);
+
+ outMecanimDataSet.m_AvatarWorkspace->m_ControllerWorkspace = mecanim::animation::CreateControllerWorkspace(outMecanimDataSet.m_ControllerConstant, outMecanimDataSet.m_AvatarConstant, animationSetBindings->animationSet, dynamicValuesConstant, allocator);
+ outMecanimDataSet.m_AvatarOutput->m_DynamicValuesOutput = mecanim::CreateValueArray(dynamicValuesConstant, allocator);
+ outMecanimDataSet.m_AvatarInput->m_GotoStateInfos = allocator.ConstructArray<mecanim::statemachine::GotoStateInfo>(outMecanimDataSet.m_ControllerConstant->m_LayerCount);
+
+ UpdateLeafNodeDuration(*outMecanimDataSet.m_ControllerConstant,*animationSetBindings->animationSet,*outMecanimDataSet.m_ControllerMemory);
+}
+
+void Animator::CreateObject()
+{
+ if(!IsActive())
+ return;
+
+ SET_ALLOC_OWNER(this);
+ PROFILER_AUTO(gAnimatorInitialize, this);
+
+ ClearObject();
+
+ SETPROFILERLABEL(Animator);
+
+ // ///////////////////////////////////////////////////
+ // Setup is split in two part: Avatar and controller
+ // both part are independant
+
+ // ///////////////////////////////////////////////////
+ // 1. Avatar setup and binding
+ mecanim::animation::AvatarConstant* avatarConstant = NULL;
+ if(m_Avatar.IsValid())
+ {
+ avatarConstant = m_Avatar->GetAsset();
+ m_Avatar->AddObjectUser(m_AnimatorAvatarNode);
+ }
+ SetupAvatarMecanimDataSet(avatarConstant, mAlloc, m_EvaluationDataSet);
+
+ // ///////////////////////////////////////////////////
+ // 2. Controller setup and binding
+ if(m_Controller.IsNull())
+ return;
+
+ m_Controller->AddObjectUser(m_AnimatorControllerNode);
+
+ mecanim::animation::ControllerConstant* controllerConstant = m_Controller->GetAsset();
+
+ UnityEngine::Animation::AnimationSetBindings* animationSetBindings = m_Controller->GetAnimationSetBindings();
+ if (animationSetBindings == NULL)
+ return;
+
+ SetupControllerMecanimDataSet(controllerConstant, animationSetBindings, mAlloc, m_EvaluationDataSet);
+}
+
+std::string Animator::GetPerformanceHints()
+{
+ if (m_EvaluationDataSet.m_GenericBindingConstant == NULL)
+ return "Not initialized";
+
+ std::string info;
+ ///@TODO: more accuaracy...
+
+// if (!m_EvaluationDataSet.m_GenericBindingConstant->allowConstantClipSamplingOptimization)
+// info += "Constant curve optimization disabled (Instance default values differ from constant curve values)\n";
+// else
+// info += Format ("Constant curve optimization enabled. %d of %d were eliminated.\n", m_EvaluationDataSet.m_GenericBindingConstant->controllerBindingConstant->m_AnimationSet->m_DynamicReducedValuesConstant->m_Count, GetAnimationSet()->m_DynamicFullValuesConstant->m_Count);
+
+ //@TODO: Bound curves overview like animationclip stats...
+
+ info += "Instance memory: " + FormatBytes (GetRuntimeMemorySize());
+
+ return info;
+}
+
+void Animator::PrepareForPlayback()
+{
+ if(m_EvaluationDataSet.m_AvatarMemorySize == 0)
+ {
+ /// Blobify memory to be able to use inplace allocator during playback
+ mecanim::animation::AvatarMemory *mem = m_EvaluationDataSet.m_AvatarMemory;
+ m_EvaluationDataSet.m_AvatarMemory = CopyBlob(*mem,mAlloc,m_EvaluationDataSet.m_AvatarMemorySize);
+ mecanim::animation::DestroyAvatarMemory(mem, mAlloc);
+ }
+}
+
+void Animator::StartRecording(int frameCount)
+{
+ PrepareForPlayback();
+
+ if(m_RecorderMode == ePlayback)
+ {
+ WarningStringIfLoggingActive("Can't call StartRecording while in playback mode. You must call StopPlayback.");
+ return;
+ }
+
+ m_AvatarPlayback.Init(frameCount);
+ m_RecorderMode = eRecord;
+}
+void Animator::StopRecording()
+{
+ m_RecorderMode = eNormal;
+}
+
+float Animator::GetRecorderStartTime()
+{
+ return m_AvatarPlayback.StartTime();
+}
+
+float Animator::GetRecorderStopTime()
+{
+ return m_AvatarPlayback.StopTime();
+}
+
+void Animator::StartPlayback()
+{
+ if(m_RecorderMode == eRecord)
+ {
+ WarningStringIfLoggingActive("Can't call StartPlayback while in record mode. You must call StopRecording.");
+ return;
+ }
+
+ m_RecorderMode = ePlayback;
+}
+
+
+void Animator::SetPlaybackTimeInternal(float time)
+{
+ float effectiveTime = 0;
+ mecanim::animation::AvatarMemory* memory = m_AvatarPlayback.PlayFrame(time, effectiveTime);
+
+ if(memory != 0 )
+ {
+ if(effectiveTime > time )
+ {
+ if(IsAutoPlayingBack())
+ WarningStringIfLoggingActive ("Cannot rewind Animator Recorder anymore. No more recorded data");
+ else
+ WarningStringIfLoggingActive("Animator Recorder does not have recorded data at given time");
+ }
+ else if(time > m_AvatarPlayback.StopTime())
+ {
+ WarningStringIfLoggingActive("Animator Recorder does not have recorded data at given time, Animator will update based on current AnimatorParameters");
+ }
+
+ PrepareForPlayback();
+
+ m_PlaybackTime = time;
+ mecanim::memory::InPlaceAllocator inPlaceAlloc(m_EvaluationDataSet.m_AvatarMemory, m_EvaluationDataSet.m_AvatarMemorySize);
+ mecanim::animation::AvatarMemory* memoryCopy = CopyBlob(*memory, inPlaceAlloc, m_EvaluationDataSet.m_AvatarMemorySize);
+ if(memoryCopy != 0)
+ m_EvaluationDataSet.m_AvatarMemory = memoryCopy;
+ else
+ {
+ // We don't have enough memory to fulfill the request with the current m_EvaluationDataSet.m_AvatarMemory memory block
+ // Let's try another time with a memory block big enough.
+ mecanim::animation::DestroyAvatarMemory(m_EvaluationDataSet.m_AvatarMemory, mAlloc );
+
+ UInt8* ptr = reinterpret_cast<UInt8*>(mAlloc.Allocate(m_EvaluationDataSet.m_AvatarMemorySize, ALIGN_OF(mecanim::animation::AvatarMemory)));
+ mecanim::memory::InPlaceAllocator inPlaceAlloc(ptr, m_EvaluationDataSet.m_AvatarMemorySize);
+ m_EvaluationDataSet.m_AvatarMemory = CopyBlob(*memory, inPlaceAlloc, m_EvaluationDataSet.m_AvatarMemorySize);
+ if(m_EvaluationDataSet.m_AvatarMemory == 0 )
+ {
+ WarningStringIfLoggingActive ("Can't playback from recorder, cannot allocate memory for recorded data.");
+ m_PlaybackDeltaTime = 0;
+ m_PlaybackTime = 0;
+ return;
+ }
+ }
+ m_PlaybackDeltaTime = time-effectiveTime;
+ }
+ else
+ {
+ WarningStringIfLoggingActive ("Can't playback from recorder, no recorded data found");
+ m_PlaybackDeltaTime = 0;
+ m_PlaybackTime = 0;
+ }
+}
+
+float Animator::GetPlaybackTime()
+{
+
+ if(m_RecorderMode != ePlayback)
+ {
+ WarningStringIfLoggingActive("Can't call GetPlaybackTime while not in playback mode. You must call StartPlayback before.");
+ return -1;
+ }
+
+ return m_PlaybackTime;
+}
+
+void Animator::SetPlaybackTime(float time)
+{
+ if(m_RecorderMode != ePlayback)
+ {
+ WarningStringIfLoggingActive("Can't call SetPlaybackTime while not in playback mode. You must call StartPlayback before.");
+ return ;
+ }
+
+ SetPlaybackTimeInternal(time);
+}
+
+void Animator::StopPlayback()
+{
+ m_RecorderMode = eNormal;
+}
+
+bool Animator::IsOptimizable() const
+{
+ return m_Avatar.IsValid();
+}
+
+bool Animator::IsHuman() const
+{
+ return m_Avatar.IsValid() && m_Avatar->IsHuman();
+}
+
+bool Animator::HasRootMotion() const
+{
+ return m_Avatar.IsValid() && m_Avatar->HasRootMotion();
+}
+
+float Animator::GetHumanScale() const
+{
+ return m_Avatar.IsValid() ? m_Avatar->GetHumanScale() : 1.f;
+}
+
+void Animator::SetApplyRootMotion (bool rootMotion)
+{
+ if (m_ApplyRootMotion != rootMotion)
+ {
+ m_ApplyRootMotion = rootMotion;
+ SetDirty();
+ }
+}
+
+void Animator::SetAnimatePhysics (bool animatePhysics)
+{
+ if (m_AnimatePhysics != animatePhysics)
+ {
+ m_AnimatePhysics = animatePhysics;
+ RemoveFromManager();
+ AddToManager();
+ SetDirty();
+ }
+}
+
+void Animator::SetHasTransformHierarchy (bool value)
+{
+ if (m_HasTransformHierarchy != value)
+ {
+ m_HasTransformHierarchy = value;
+
+ // I need to validate this
+ CreateObject();
+ InitializeVisibilityCulling();
+
+ SetDirty();
+ }
+}
+
+GetSetValueResult Animator::SetFloatDamp(int id, float value, float dampTime, float deltaTime)
+{
+ if (dampTime > 0)
+ {
+ mecanim::dynamics::ScalDamp damper;
+ GetValue(id, damper.m_Value);
+ damper.m_DampTime = dampTime;
+ damper.Evaluate(value, deltaTime);
+
+ return SetValue(id, damper.m_Value);
+ }
+ else
+ {
+ return SetValue(id, value);
+ }
+}
+
+GetSetValueResult Animator::SetFloat(int id, float value)
+{
+ return SetValue(id, value);
+}
+
+GetSetValueResult Animator::SetBool(int id, bool value)
+{
+ return SetValue(id, value);
+}
+
+GetSetValueResult Animator::SetInteger(int id, int value)
+{
+ return SetValue(id, (mecanim::int32_t)value);
+}
+
+GetSetValueResult Animator::GetFloat(int id, float& output)
+{
+ return GetValue(id, output);
+}
+
+GetSetValueResult Animator::GetBool(int id, bool& output)
+{
+ return GetValue(id, output);
+}
+
+GetSetValueResult Animator::GetInteger(int id, int& output)
+{
+ mecanim::int32_t temp;
+ GetSetValueResult res = GetValue(id, temp);
+ output = temp;
+ return res;
+}
+
+GetSetValueResult Animator::ResetTrigger(int id)
+{
+ return SetValue(id, false);
+}
+
+GetSetValueResult Animator::SetTrigger(int id)
+{
+ return SetValue(id, true);
+}
+
+bool Animator::HasParameter(int id)
+{
+ if(!IsInitialize())
+ return false;
+
+ mecanim::int32_t i = mecanim::FindValueIndex(m_EvaluationDataSet.m_ControllerConstant->m_Values.Get(), id);
+ return i != -1;
+}
+
+bool Animator::GetMuscleValue(int id, float *value)
+{
+ *value = 0;
+ if (this->m_Avatar.IsNull() || !this->m_Avatar->IsHuman())
+ return false;
+
+ // it is the desire behavior to return true if the Animator is not initialized correctly because the AnimationWindows is using this function to
+ // find what is the newly added curve type.
+ // Also has a side effect adding any kind of curve like MotionT trigger a ClearObject() on the animator because we are adding new curve to a clip
+ // used by the animator.
+ mecanim::int32_t muscleIndex = mecanim::animation::FindMuscleIndex(id);
+ bool ret = muscleIndex != -1;
+
+ if(m_SamplingDataSet->m_GenericBindingConstant == NULL)
+ return ret;
+
+ if (ret)
+ *value = mecanim::animation::GetMuscleCurveValue(*m_SamplingDataSet->m_AvatarOutput->m_HumanPoseOutput, m_SamplingDataSet->m_AvatarOutput->m_MotionOutput->m_MotionX,muscleIndex);
+
+ return ret;
+}
+
+GetSetValueResult Animator::ParameterControlledByCurve(int id)
+{
+ if (!IsInitialize())
+ return kAnimatorNotInitialized;
+
+ mecanim::int32_t index = mecanim::FindValueIndex(m_EvaluationDataSet.m_ControllerConstant->m_Values.Get(), id);
+ if (index == -1)
+ return kParameterDoesNotExist;
+
+ if (m_EvaluationDataSet.m_GenericBindingConstant->controllerBindingConstant->m_AnimationSet->m_AdditionalIndexArray[index] != -1)
+ return kParameterIsControlledByCurve;
+ else
+ return kGetSetSuccess;
+}
+
+Vector3f Animator::GetAvatarPosition()
+{
+ if(!IsAvatarInitialize())
+ return Vector3f(0,0,0);
+
+ return float4ToVector3f(m_EvaluationDataSet.m_AvatarMemory->m_AvatarX.t);
+}
+
+void Animator::SetAvatarPosition(const Vector3f& pos)
+{
+ ///@TODO: Give error messages...
+ if (!IsAvatarInitialize())
+ return ;
+
+ m_EvaluationDataSet.m_AvatarMemory->m_AvatarX.t = Vector3fTofloat4(pos);
+}
+
+Quaternionf Animator::GetAvatarRotation()
+{
+ if(!IsAvatarInitialize())
+ return Quaternionf(0,0,0,1);
+
+
+ return float4ToQuaternionf(m_EvaluationDataSet.m_AvatarMemory->m_AvatarX.q);
+}
+
+void Animator::SetAvatarRotation(const Quaternionf& q)
+{
+ if(!IsAvatarInitialize())
+ return;
+
+ m_EvaluationDataSet.m_AvatarMemory->m_AvatarX.q = QuaternionfTofloat4(q);
+}
+
+Vector3f Animator::GetAvatarScale()
+{
+ if(!IsAvatarInitialize())
+ return Vector3f(1,1,1);
+
+ return float4ToVector3f(m_EvaluationDataSet.m_AvatarMemory->m_AvatarX.s);
+}
+
+void Animator::SetAvatarScale(const Vector3f& scale)
+{
+ if (!IsAvatarInitialize())
+ return ;
+
+ m_EvaluationDataSet.m_AvatarMemory->m_AvatarX.s = Vector3fTofloat4(scale,1);
+}
+
+Vector3f Animator::GetDeltaPosition()
+{
+ return m_DeltaPosition;
+}
+
+Quaternionf Animator::GetDeltaRotation()
+{
+ return m_DeltaRotation;
+}
+
+Vector3f Animator::GetBodyPosition()
+{
+ if(!IsAvatarInitialize())
+ return Vector3f();
+
+ mecanim::animation::AvatarConstant const* avatar = m_EvaluationDataSet.m_AvatarConstant;
+
+ if(avatar->isHuman())
+ {
+ return float4ToVector3f(m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_RootX.t);
+ }
+
+ return Vector3f();
+}
+
+Quaternionf Animator::GetBodyRotation()
+{
+ if(!IsAvatarInitialize())
+ return Quaternionf();
+
+ mecanim::animation::AvatarConstant const * avatar = m_EvaluationDataSet.m_AvatarConstant;
+
+ if(avatar->isHuman())
+ {
+ return float4ToQuaternionf(m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_RootX.q);
+ }
+
+ return Quaternionf();
+}
+
+void Animator::SetBodyPosition(Vector3f const& bodyPosition)
+{
+ if(!IsAvatarInitialize() || !m_EvaluationDataSet.m_AvatarConstant->isHuman())
+ return;
+
+ m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_RootX.t = Vector3fTofloat4(bodyPosition);
+}
+
+void Animator::SetBodyRotation(Quaternionf const& bodyRotation)
+{
+ if(!IsAvatarInitialize() || !m_EvaluationDataSet.m_AvatarConstant->isHuman())
+ return;
+
+ m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_RootX.q = QuaternionfTofloat4(bodyRotation);
+}
+
+Vector3f Animator::GetPivotPosition()
+{
+ return m_PivotPosition;
+}
+
+Vector3f Animator::GetTargetPosition()
+{
+ return m_TargetPosition;
+}
+
+Quaternionf Animator::GetTargetRotation()
+{
+ if (m_EvaluationDataSet.m_AvatarInput->m_TargetIndex >= mecanim::animation::kTargetLeftFoot && m_EvaluationDataSet.m_AvatarInput->m_TargetIndex <= mecanim::animation::kTargetRightHand)
+ {
+ return m_TargetRotation * float4ToQuaternionf(mecanim::human::HumanGetGoalOrientationOffset(mecanim::human::Goal(m_EvaluationDataSet.m_AvatarInput->m_TargetIndex - mecanim::animation::kTargetLeftFoot)));
+ }
+
+ return m_TargetRotation;
+}
+
+float Animator::GetGravityWeight()
+{
+ if(m_EvaluationDataSet.m_AvatarOutput && m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput)
+ {
+ return m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_GravityWeight;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+bool Animator::IsBoneTransform(Transform *transform)
+{
+ if(!IsAvatarInitialize())
+ return false;
+
+ bool ret = false;
+
+ if (m_HasTransformHierarchy)
+ {
+ for(int boneIter = 0; !ret && boneIter < m_EvaluationDataSet.m_AvatarBindingConstant->skeletonBindingsCount; boneIter++)
+ {
+ ret = m_EvaluationDataSet.m_AvatarBindingConstant->skeletonBindings[boneIter] == transform;
+ }
+ }
+ else
+ {
+ for (int exposedIndex = 0; !ret && exposedIndex < m_EvaluationDataSet.m_AvatarBindingConstant->exposedTransformCount; exposedIndex++)
+ {
+ ret = (m_EvaluationDataSet.m_AvatarBindingConstant->exposedTransforms[exposedIndex].transform == transform) &&
+ (m_EvaluationDataSet.m_AvatarBindingConstant->exposedTransforms[exposedIndex].skeletonIndex != -1);
+ }
+ }
+
+ return ret;
+}
+
+Transform* Animator::GetBoneTransform(int humanId)
+{
+ if(!IsAvatarInitialize())
+ return 0;
+
+ Transform *ret = 0;
+
+ mecanim::animation::AvatarConstant* cst = GetAvatar()->GetAsset();
+
+ if(cst && cst->isHuman())
+ {
+ int humanBoneId = HumanTrait::GetBoneId(*GetAvatar(),humanId);
+
+ if( humanBoneId == -1)
+ return NULL;
+
+ if (m_HasTransformHierarchy)
+ {
+ ret = m_EvaluationDataSet.m_AvatarBindingConstant->skeletonBindings[cst->m_HumanSkeletonIndexArray[humanBoneId]];
+ }
+ else
+ {
+ int skeletonIndex = cst->m_HumanSkeletonIndexArray[humanBoneId];
+ for (int exposedIndex = 0; exposedIndex < m_EvaluationDataSet.m_AvatarBindingConstant->exposedTransformCount; exposedIndex++)
+ {
+ const ExposedTransform& exposedTransform = m_EvaluationDataSet.m_AvatarBindingConstant->exposedTransforms[exposedIndex];
+ if (exposedTransform.skeletonIndex == skeletonIndex)
+ {
+ ret = exposedTransform.transform;
+ break;
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+void Animator::MatchTarget(Vector3f const& matchPosition, Quaternionf const& matchRotation, int targetIndex, const MatchTargetWeightMask & mask, float startNormalizedTime, float targetNormalizedTime)
+{
+ // It's only possible to match a target on the first layer
+ const int layerIndex = 0;
+
+ if(!ValidateTargetIndex(targetIndex) || IsMatchingTarget() || !IsInitialize())
+ return;
+
+ if(IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1) && IsInTransitionInternal(layerIndex))
+ {
+ WarningStringIfLoggingActive("Calling Animator.MatchTarget while in transition does not have any effect");
+ return;
+ }
+
+ mecanim::uint32_t stateMachineIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_StateMachineIndex;
+ mecanim::statemachine::StateMachineMemory const* apStateMachineMem = m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_StateMachineMemory[stateMachineIndex].Get();
+
+ float internalTime = apStateMachineMem->m_StateMemoryArray[apStateMachineMem->m_CurrentStateIndex]->m_PreviousTime;
+
+ float intTime = 0 ;
+ float currentStateTime = math::modf(internalTime, intTime);
+
+ float effectiveStartTime = 0;
+ float effectiveTargetTime = targetNormalizedTime + intTime;
+
+ if(currentStateTime <= startNormalizedTime)
+ effectiveStartTime = startNormalizedTime + intTime;
+ else if( currentStateTime > startNormalizedTime && currentStateTime < targetNormalizedTime)
+ effectiveStartTime = currentStateTime + intTime;
+ else
+ {
+ // Passed target time, wait for next loop
+ effectiveStartTime = startNormalizedTime + intTime + 1.0f;
+ effectiveTargetTime += 1.0f;
+ }
+
+ AnimatorStateInfo animatorInfo;
+ GetAnimatorStateInfo(layerIndex, true, animatorInfo);
+ if (!animatorInfo.loop != 0 && targetNormalizedTime < effectiveStartTime )
+ return;
+
+ m_MatchTargetMask = mask;
+
+ m_MatchStartTime = effectiveStartTime;
+ m_MatchStateID = animatorInfo.nameHash;
+ m_MatchPosition = matchPosition;
+ m_MatchRotation = SqrMagnitude(matchRotation) > 0 ? matchRotation : Quaternionf::identity();
+ m_EvaluationDataSet.m_AvatarInput->m_TargetIndex = targetIndex;
+ m_EvaluationDataSet.m_AvatarInput->m_TargetTime = effectiveTargetTime < effectiveStartTime ? effectiveTargetTime + 1.f : effectiveTargetTime;
+}
+
+void Animator::InterruptMatchTarget(bool completeMatch)
+{
+ if(completeMatch)
+ m_MustCompleteMatch = true;
+ else
+ {
+ m_MatchStartTime = -1;
+ m_MatchStateID = -1;
+ }
+}
+
+bool Animator::IsMatchingTarget()const
+{
+ if(!IsInitialize())
+ return false;
+
+ mecanim::uint32_t stateMachineIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[0]->m_StateMachineIndex;
+ mecanim::statemachine::StateMachineConstant const* apStateMachineConst = m_EvaluationDataSet.m_ControllerConstant->m_StateMachineArray[stateMachineIndex].Get();
+ mecanim::statemachine::StateMachineMemory const* apStateMachineMem = m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_StateMachineMemory[stateMachineIndex].Get();
+ mecanim::statemachine::StateConstant const* state = apStateMachineConst->m_StateConstantArray[apStateMachineMem->m_CurrentStateIndex].Get();
+
+ return m_MatchStartTime >= 0 && CompareStateID (state, m_MatchStateID);
+}
+
+void Animator::SetTarget(int targetIndex, float targetNormalizedTime)
+{
+ if(!ValidateTargetIndex(targetIndex) || !IsInitialize())
+ return;
+
+ if(IsMatchingTarget())
+ {
+ ErrorString("Calling Animator::SetTarget while already Matching Target does not have any effect");
+ }
+
+ m_EvaluationDataSet.m_AvatarInput->m_TargetIndex = targetIndex;
+ m_EvaluationDataSet.m_AvatarInput->m_TargetTime = targetNormalizedTime;
+}
+
+void Animator::SetSpeed(float speed)
+{
+
+ m_Speed = speed;
+}
+
+float Animator::GetSpeed() const
+{
+ return m_Speed;
+}
+
+bool GetLayerAndStateIndex(mecanim::animation::ControllerConstant const* controllerConstant, mecanim::uint32_t id, int* outLayer, int* outStateIndex)
+{
+ for (int i=0;i < controllerConstant->m_LayerCount;i++)
+ {
+ int index = controllerConstant->m_LayerArray[i]->m_StateMachineIndex;
+
+ // Ignore synced layers
+ if (controllerConstant->m_LayerArray[i]->m_StateMachineMotionSetIndex != 0)
+ continue;
+
+ mecanim::int32_t stateIndex = mecanim::statemachine::GetStateIndex(controllerConstant->m_StateMachineArray[index].Get(), id);
+ if (stateIndex != -1)
+ {
+ *outStateIndex = stateIndex;
+ *outLayer = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+void Animator::GotoState(int layerIndex, int stateId, float normalizedTime, float transitionDuration, float transitionTime)
+{
+ if(!IsInitialize() )
+ return ;
+
+ // Automatically find a good layer index
+ if (layerIndex == -1)
+ {
+ int stateIndex;
+ // StateId = 0 means active state
+ if (stateId == 0)
+ layerIndex = 0;
+ else if (!GetLayerAndStateIndex(m_EvaluationDataSet.m_ControllerConstant, stateId, &layerIndex, &stateIndex))
+ {
+ ErrorString("Animator.GotoState: State could not be found");
+ }
+ }
+
+ if(!ValidateLayerIndex(layerIndex))
+ return;
+
+ const mecanim::uint32_t stateMachineIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_StateMachineIndex;
+
+ if(stateMachineIndex == -1)
+ return;
+
+ if(stateMachineIndex >= m_EvaluationDataSet.m_ControllerConstant->m_StateMachineCount)
+ {
+ ErrorString("Animator.GotoState: Cannot find statemachine");
+ return;
+ }
+
+ const mecanim::uint32_t motionSetIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_StateMachineMotionSetIndex;
+ if(motionSetIndex != 0)
+ {
+ ErrorString("Calling Animator.GotoState on Synchronize layer");
+ return;
+ }
+
+ #if DEBUGMODE
+ if (stateId != 0 && mecanim::statemachine::GetStateIndex(m_EvaluationDataSet.m_ControllerConstant->m_StateMachineArray[stateMachineIndex].Get(), stateId) == -1)
+ {
+ ErrorString("Animator.GotoState: State could not be found");
+ }
+ #endif
+
+ // When no explicit normalizedTime is specified we will simply start the clip at the start if it is not already playing.
+ if (normalizedTime == -std::numeric_limits<float>::infinity())
+ {
+ AnimatorStateInfo info;
+ GetAnimatorStateInfo(layerIndex, true, info);
+
+ // If the state is not currently playing -> Start Playing it
+ bool nameMatches = info.pathHash == stateId || info.nameHash == stateId;
+ if (nameMatches)
+ return;
+ normalizedTime = 0.0F;
+ }
+
+ m_EvaluationDataSet.m_ControllerMemory->m_StateMachineMemory[stateMachineIndex]->m_ActiveGotoState = true;
+
+ m_EvaluationDataSet.m_AvatarInput->m_GotoStateInfos[layerIndex].m_StateID = stateId;
+ m_EvaluationDataSet.m_AvatarInput->m_GotoStateInfos[layerIndex].m_NormalizedTime = normalizedTime;
+ m_EvaluationDataSet.m_AvatarInput->m_GotoStateInfos[layerIndex].m_TransitionDuration = transitionDuration;
+ m_EvaluationDataSet.m_AvatarInput->m_GotoStateInfos[layerIndex].m_TransitionTime = transitionTime;
+}
+
+#define VALIDATE_IK_GOAL(x) \
+if (!GetBuildSettings().hasAdvancedVersion) \
+ return x; \
+if(!ValidateGoalIndex(index) || !IsAvatarInitialize()) \
+ return x; \
+
+#define VALIDATE_IK_GOAL_VOID() \
+if (!GetBuildSettings().hasAdvancedVersion)\
+ return; \
+if(!ValidateGoalIndex(index) || !IsAvatarInitialize()) \
+ return; \
+
+
+Vector3f Animator::GetGoalPosition(int index)
+{
+ VALIDATE_IK_GOAL(Vector3f::zero)
+
+ return float4ToVector3f(m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_GoalArray[index].m_X.t);
+}
+
+void Animator::SetGoalPosition(int index, Vector3f const& pos)
+{
+ VALIDATE_IK_GOAL_VOID()
+
+ m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_GoalArray[index].m_X.t = Vector3fTofloat4(pos);
+}
+
+Quaternionf Animator::GetGoalRotation(int index)
+{
+ VALIDATE_IK_GOAL(Quaternionf::identity())
+
+ return float4ToQuaternionf(math::normalize(math::quatMul(m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_GoalArray[index].m_X.q,mecanim::human::HumanGetGoalOrientationOffset(mecanim::human::Goal(index)))));
+}
+
+void Animator::SetGoalRotation(int index, Quaternionf const& rot)
+{
+ VALIDATE_IK_GOAL_VOID()
+
+ m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_GoalArray[index].m_X.q = math::normalize(math::quatMul(QuaternionfTofloat4(rot),math::quatConj(mecanim::human::HumanGetGoalOrientationOffset(mecanim::human::Goal(index)))));
+}
+
+void Animator::SetGoalWeightPosition(int index, float value)
+{
+ VALIDATE_IK_GOAL_VOID()
+
+ m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_GoalArray[index].m_WeightT = value;
+}
+
+void Animator::SetGoalWeightRotation(int index, float value)
+{
+ VALIDATE_IK_GOAL_VOID()
+
+ m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_GoalArray[index].m_WeightR = value;
+}
+
+float Animator::GetGoalWeightPosition(int index)
+{
+ VALIDATE_IK_GOAL(0.0F)
+
+ return m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_GoalArray[index].m_WeightT;
+}
+
+float Animator::GetGoalWeightRotation(int index)
+{
+ VALIDATE_IK_GOAL(0.0F)
+
+ return m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_GoalArray[index].m_WeightR;
+}
+
+#define VALIDATE_LOOKAT if (!GetBuildSettings().hasAdvancedVersion || !IsAvatarInitialize()) return;
+
+
+void Animator::SetLookAtPosition(Vector3f lookAtPosition)
+{
+ VALIDATE_LOOKAT
+
+ m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_LookAtPosition = Vector3fTofloat4(lookAtPosition);
+}
+
+void Animator::SetLookAtClampWeight(float weight)
+{
+ VALIDATE_LOOKAT
+
+ m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_LookAtWeight.x() = weight;
+}
+
+void Animator::SetLookAtBodyWeight(float weight)
+{
+ VALIDATE_LOOKAT
+
+ m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_LookAtWeight.y() = weight;
+}
+
+void Animator::SetLookAtHeadWeight(float weight)
+{
+ VALIDATE_LOOKAT
+
+ m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_LookAtWeight.z() = weight;
+}
+
+void Animator::SetLookAtEyesWeight(float weight)
+{
+ VALIDATE_LOOKAT
+
+ m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_LookAtWeight.w() = weight;
+}
+
+int Animator::GetLayerCount()const
+{
+ if(!IsInitialize())
+ return 0;
+
+ return m_EvaluationDataSet.m_ControllerConstant->m_LayerCount;
+}
+
+std::string Animator::GetLayerName(int layerIndex)
+{
+ if(!ValidateLayerIndex(layerIndex))
+ return "";
+
+ return m_Controller->StringFromID(m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_Binding);
+}
+
+float Animator::GetLayerWeight(int layerIndex)
+{
+ if(!ValidateLayerIndex(layerIndex))
+ return layerIndex == 0 ? 1 : 0 ;
+
+ return m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_LayerWeights[layerIndex];
+}
+
+void Animator::SetLayerWeight(int layerIndex, float w)
+{
+ if(!ValidateLayerIndex(layerIndex))
+ return;
+
+ m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_LayerWeights[layerIndex] = w;
+}
+
+float Animator::GetPivotWeight()
+{
+ if(!IsAvatarInitialize())
+ return 0;
+
+ return m_EvaluationDataSet.m_AvatarMemory->m_PivotWeight;
+}
+
+bool Animator::IsInTransitionInternal(int layerIndex)const
+{
+ mecanim::uint32_t stateMachineIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_StateMachineIndex;
+
+ if(stateMachineIndex == mecanim::DISABLED_SYNCED_LAYER_IN_NON_PRO)
+ return false;
+
+ mecanim::statemachine::StateMachineMemory const* apStateMachineMem = m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_StateMachineMemory[stateMachineIndex].Get();
+ return apStateMachineMem->m_InTransition;
+}
+
+bool Animator::IsInTransition(int layerIndex)const
+{
+ if(!ValidateLayerIndex(layerIndex) )
+ return false;
+
+ return IsInTransitionInternal(layerIndex);
+}
+
+AnimationClipVector Animator::GetAnimationClips()const
+{
+ return m_Controller->GetAnimationClips();
+}
+
+UnityEngine::Animation::AnimationSetBindings* Animator::GetAnimationSetBindings()const
+{
+ return m_Controller->GetAnimationSetBindings();
+}
+
+bool Animator::GetAnimationClipState (int layerIndex, bool currentState, dynamic_array<AnimationInfo>& output)
+{
+ if (!ValidateLayerIndex(layerIndex))
+ return false;
+
+ mecanim::uint32_t stateMachineIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_StateMachineIndex;
+
+ mecanim::uint32_t motionSetIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_StateMachineMotionSetIndex;
+ mecanim::statemachine::StateMachineMemory const* apStateMachineMem = m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_StateMachineMemory[stateMachineIndex].Get();
+
+ float blendFactor = currentState ? 1 - m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_StateMachineOutput[stateMachineIndex]->m_BlendFactor : m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_StateMachineOutput[stateMachineIndex]->m_BlendFactor;
+
+ mecanim::statemachine::BlendNodeLayer *blendNodeLayer = currentState ? &m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_StateMachineOutput[stateMachineIndex]->m_Left.m_BlendNodeLayer[motionSetIndex] :
+ apStateMachineMem->m_InTransition ? &m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_StateMachineOutput[stateMachineIndex]->m_Right.m_BlendNodeLayer[motionSetIndex] : 0;
+
+ int clipCount = blendNodeLayer ? blendNodeLayer->m_OutputCount : 0 ;
+
+ AnimationClipVector const allClips = GetAnimationClips();
+
+ int layerClipOffset = GetLayerClipOffset(layerIndex);
+
+ output.resize_uninitialized(clipCount);
+ for(int i = 0 ; i < clipCount ; i++)
+ {
+ int index = blendNodeLayer->m_OutputIndexArray[i];
+
+ AnimationInfo& animInfo = output[i];
+ animInfo.clip = allClips[index+layerClipOffset];
+ animInfo.weight = blendNodeLayer->m_OutputBlendArray[i] * blendFactor;
+ }
+
+ return true;
+}
+
+
+bool Animator::GetAnimatorStateInfo (int layerIndex, bool currentState, AnimatorStateInfo& output)
+{
+ if (!ValidateLayerIndex(layerIndex))
+ return false;
+
+ mecanim::uint32_t stateMachineIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_StateMachineIndex;
+ const mecanim::statemachine::StateMachineConstant* apStateMachineConst = m_EvaluationDataSet.m_ControllerConstant->m_StateMachineArray[stateMachineIndex].Get();
+ mecanim::statemachine::StateMachineMemory const* apStateMachineMem = m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_StateMachineMemory[stateMachineIndex].Get();
+
+ mecanim::uint32_t stateIndex = currentState ? apStateMachineMem->m_CurrentStateIndex : apStateMachineMem->m_InTransition ? apStateMachineMem->m_NextStateIndex : apStateMachineMem->m_StateMemoryCount;
+ if (stateIndex < apStateMachineMem->m_StateMemoryCount)
+ {
+ output.nameHash = apStateMachineConst->m_StateConstantArray[stateIndex]->m_NameID;
+ output.pathHash = apStateMachineConst->m_StateConstantArray[stateIndex]->m_PathID;
+ output.normalizedTime = apStateMachineMem->m_StateMemoryArray[stateIndex]->m_PreviousTime;
+ output.length = apStateMachineMem->m_StateMemoryArray[stateIndex]->m_Duration;
+ output.tagHash = apStateMachineConst->m_StateConstantArray[stateIndex]->m_TagID;
+ output.loop = apStateMachineConst->m_StateConstantArray[stateIndex]->m_Loop ? 1 : 0;
+
+ return true;
+
+ }
+ else
+ return false;
+}
+
+
+bool Animator::GetAnimatorTransitionInfo(int layerIndex, AnimatorTransitionInfo& info)
+{
+ if(!ValidateLayerIndex(layerIndex))
+ return false;
+
+ mecanim::uint32_t stateMachineIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_StateMachineIndex;
+ const mecanim::statemachine::StateMachineConstant* apStateMachineConst = m_EvaluationDataSet.m_ControllerConstant->m_StateMachineArray[stateMachineIndex].Get();
+
+ mecanim::statemachine::StateMachineMemory const* apStateMachineMem = m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_StateMachineMemory[stateMachineIndex].Get();
+
+ if (apStateMachineMem->m_InTransition)
+ {
+ mecanim::statemachine::TransitionConstant const* transition = mecanim::statemachine::GetTransitionConstant(apStateMachineConst, apStateMachineConst->m_StateConstantArray[apStateMachineMem->m_CurrentStateIndex].Get(), apStateMachineMem->m_TransitionId);
+ if(transition)
+ {
+ info.nameHash = transition->m_ID;
+ info.userNameHash = transition->m_UserID;
+ }
+ // Dynamic transition doesn't exist, they are created on demand by user.
+ else
+ {
+ info.nameHash = 0;
+ info.userNameHash = 0;
+ }
+ info.normalizedTime = apStateMachineMem->m_TransitionTime;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+string Animator::GetAnimatorStateName (int layerIndex, bool currentState)
+{
+ if (!ValidateLayerIndex(layerIndex))
+ return "";
+
+ mecanim::uint32_t stateMachineIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_StateMachineIndex;
+ const mecanim::statemachine::StateMachineConstant* apStateMachineConst = m_EvaluationDataSet.m_ControllerConstant->m_StateMachineArray[stateMachineIndex].Get();
+ mecanim::statemachine::StateMachineMemory const* apStateMachineMem = m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_StateMachineMemory[stateMachineIndex].Get();
+
+ mecanim::uint32_t stateIndex = currentState ? apStateMachineMem->m_CurrentStateIndex : apStateMachineMem->m_InTransition ? apStateMachineMem->m_NextStateIndex : apStateMachineMem->m_StateMemoryCount;
+ if (stateIndex < apStateMachineMem->m_StateMemoryCount)
+ {
+ return m_Controller->StringFromID(apStateMachineConst->m_StateConstantArray[stateIndex]->m_PathID);
+ }
+
+ return "";
+}
+
+void Animator::GetRootBlendTreeConstantAndWorkspace (int layerIndex, int stateHash, mecanim::animation::BlendTreeNodeConstant const* & constant, mecanim::animation::BlendTreeWorkspace*& workspace)
+{
+ if(!ValidateLayerIndex(layerIndex))
+ return;
+
+ mecanim::uint32_t stateMachineIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_StateMachineIndex;
+ mecanim::uint32_t motionSetIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[layerIndex]->m_StateMachineMotionSetIndex;
+
+ mecanim::statemachine::StateMachineConstant const* apStateMachineConst = m_EvaluationDataSet.m_ControllerConstant->m_StateMachineArray[stateMachineIndex].Get();
+ mecanim::statemachine::StateMachineWorkspace* apStateMachineWorkspace = m_EvaluationDataSet.m_AvatarWorkspace->m_ControllerWorkspace->m_StateMachineWorkspace[stateMachineIndex];
+
+ for (int i=0; i<apStateMachineConst->m_StateConstantCount; i++)
+ {
+ if (CompareStateID (apStateMachineConst->m_StateConstantArray[i].Get(), stateHash))
+ {
+ constant = apStateMachineConst->m_StateConstantArray[i]->m_BlendTreeConstantArray[motionSetIndex]->m_NodeArray[0].Get();
+ workspace = apStateMachineWorkspace->m_StateWorkspaceArray[i]->m_BlendTreeWorkspaceArray[motionSetIndex];
+ }
+ }
+}
+
+float Animator::GetFeetPivotActive()
+{
+ if(!IsAvatarInitialize())
+ return false;
+
+ return m_EvaluationDataSet.m_AvatarInput->m_FeetPivotActive;
+}
+void Animator::SetFeetPivotActive(float value)
+{
+ if(!IsAvatarInitialize())
+ return ;
+
+ m_EvaluationDataSet.m_AvatarInput->m_FeetPivotActive = value;
+}
+
+bool Animator::GetStabilizeFeet()
+{
+ if(!IsAvatarInitialize())
+ return false;
+
+ return m_EvaluationDataSet.m_AvatarInput->m_StabilizeFeet;
+
+}
+void Animator::SetStabilizeFeet(bool value)
+{
+ if(!IsAvatarInitialize())
+ return ;
+
+ m_EvaluationDataSet.m_AvatarInput->m_StabilizeFeet = value;
+}
+
+float Animator::GetLeftFeetBottomHeight()
+{
+ if(!m_Avatar.IsValid())
+ return 0.f;
+
+ return m_Avatar->GetLeftFeetBottomHeight();
+}
+
+float Animator::GetRightFeetBottomHeight()
+{
+ if(!m_Avatar.IsValid())
+ return 0.f;
+
+ return m_Avatar->GetRightFeetBottomHeight();
+}
+
+#if UNITY_EDITOR
+
+void Animator::WriteSkeletonPose(mecanim::skeleton::SkeletonPose& pose)
+{
+ PROFILER_AUTO(gAnimatorWriteSkeletonPose, this)
+
+ if (m_EvaluationDataSet.m_GenericBindingConstant != NULL)
+ {
+ SetHumanTransformPropertyValues (*m_EvaluationDataSet.m_AvatarBindingConstant, pose);
+ SetTransformPropertyApplyMainThread (GetComponent(Transform), *m_EvaluationDataSet.m_GenericBindingConstant, *m_EvaluationDataSet.m_AvatarBindingConstant,false); // we don't skip root for ui update pose stuff
+ }
+ else
+ {
+ ErrorString("Failed to write skeleton pose");
+ }
+}
+
+void Animator::WriteHumanPose(mecanim::human::HumanPose &pose)
+{
+ // this function is only called from the editor.
+ // always fetch the avatar constant from the Avatar asset in case it been updated.
+ // Looking if the pptr is valid
+ if(!m_Avatar.IsValid())
+ return;
+
+ // Looking if the mecanim avatar constant is valid
+ if(!m_Avatar->IsValid())
+ return;
+
+ if(!Prepare())
+ return;
+
+ mecanim::animation::AvatarConstant *avatar = m_Avatar->GetAsset();
+
+ if(avatar && avatar->isHuman())
+ {
+ mecanim::human::Human *human = avatar->m_Human.Get();
+ mecanim::human::RetargetTo(human,&pose,0,math::xformIdentity(),m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput,m_EvaluationDataSet.m_AvatarWorkspace->m_BodySkeletonPoseWs,m_EvaluationDataSet.m_AvatarWorkspace->m_BodySkeletonPoseWsA);
+ mecanim::animation::EvaluateAvatarEnd(avatar,m_EvaluationDataSet.m_AvatarInput,m_EvaluationDataSet.m_AvatarOutput,m_EvaluationDataSet.m_AvatarMemory,m_EvaluationDataSet.m_AvatarWorkspace,m_EvaluationDataSet.m_ControllerConstant);
+
+ WriteSkeletonPose(*m_EvaluationDataSet.m_AvatarOutput->m_SkeletonPoseOutput);
+ }
+ else
+ {
+ ErrorString("Failed to write human pose");
+ }
+}
+
+void Animator::WriteDefaultPose()
+{
+ // Looking if the pptr is valid
+ if(!m_Avatar.IsValid())
+ return;
+
+ // Looking if the mecanim avatar constant is valid
+ if(!m_Avatar->IsValid())
+ return;
+
+ if(!Prepare())
+ return;
+
+ mecanim::animation::AvatarConstant *avatar = m_Avatar->GetAsset();
+
+ WriteSkeletonPose(*avatar->m_AvatarSkeletonPose);
+}
+#endif
+
+bool Animator::IsAutoPlayingBack()
+{
+ return m_RecorderMode == eRecord && GetSpeed() < 0;
+}
+
+bool Animator::IsPlayingBack()
+{
+ return m_RecorderMode == ePlayback || IsAutoPlayingBack();
+}
+
+int Animator::GetLayerClipOffset(int layerIndex)
+{
+ int layerClipOffset = 0;
+ for(int i = 0; i < layerIndex ; i++)
+ {
+ mecanim::uint32_t stateMachineIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[i]->m_StateMachineIndex;
+ mecanim::uint32_t motionSetIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[i]->m_StateMachineMotionSetIndex;
+
+ if(stateMachineIndex != mecanim::DISABLED_SYNCED_LAYER_IN_NON_PRO)
+ {
+ const mecanim::statemachine::StateMachineConstant* apStateMachineConst = m_EvaluationDataSet.m_ControllerConstant->m_StateMachineArray[stateMachineIndex].Get();
+ int j;
+ for( j = 0 ; j < apStateMachineConst->m_StateConstantCount; j++)
+ {
+ layerClipOffset += apStateMachineConst->m_StateConstantArray[j]->m_LeafInfoArray[motionSetIndex].m_Count ;
+ }
+ }
+ }
+
+ return layerClipOffset;
+}
+
+bool Animator::ShouldInterruptMatchTarget()const
+{
+ if(IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1))
+ return false;
+
+ // case 516805: Match target must be interrupted if we begin a transition.
+ if(IsInTransitionInternal(0))
+ return true;
+
+ // case 542102: when the frame rate drop we miss the end of the match target and the last match target parameter stick.
+ // detect such case in TargetMatch by watching the initial state id
+ if(!IsMatchingTarget())
+ return true;
+
+ return false;
+}
+
+void Animator::TargetMatch(bool matchCurrentFrame)
+{
+ if(ShouldInterruptMatchTarget())
+ InterruptMatchTarget(false);
+
+ if(IsInitialize() && IsMatchingTarget())
+ {
+ math::xform avatarX = m_EvaluationDataSet.m_AvatarMemory->m_AvatarX;
+
+ math::float1 scale(m_EvaluationDataSet.m_AvatarConstant->m_Human->m_Scale);
+ math::xform dx = m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_DX;
+ dx.t *= scale;
+ avatarX = math::xformMul(avatarX, dx);
+
+ float stateTime = 0 ;
+ float stateDuration = 1 ;
+ if( m_EvaluationDataSet.m_ControllerConstant->m_LayerCount > 0)
+ {
+ mecanim::uint32_t stateMachineIndex = m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[0]->m_StateMachineIndex;
+ mecanim::statemachine::StateMachineMemory const* stateMachineMem = m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_StateMachineMemory[stateMachineIndex].Get();
+ if(stateMachineMem->m_StateMemoryCount > 0 )
+ {
+ stateTime = stateMachineMem->m_StateMemoryArray[stateMachineMem->m_CurrentStateIndex]->m_PreviousTime;
+ stateDuration = stateMachineMem->m_StateMemoryArray[stateMachineMem->m_CurrentStateIndex]->m_Duration;
+ }
+ }
+
+ if(stateTime >= m_MatchStartTime)
+ {
+ float endTime = m_EvaluationDataSet.m_AvatarInput->m_TargetTime;
+
+ float normalizeDeltaTime = m_EvaluationDataSet.m_AvatarInput->m_DeltaTime / stateDuration;
+ float remainingTime = max(0.0f, endTime - stateTime );
+ float w = remainingTime != 0 ? normalizeDeltaTime / remainingTime : 1;
+ w = math::saturate(w);
+
+ math::float4 targetT = m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_TargetX.t;
+ math::float4 targetQ = m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_TargetX.q;
+
+ mecanim::uint32_t targetIndex = m_EvaluationDataSet.m_AvatarInput->m_TargetIndex;
+
+ if(matchCurrentFrame)
+ {
+ w = 1;
+ if(targetIndex == mecanim::animation::kTargetReference)
+ {
+ targetT = math::float4::zero();
+ targetQ = math::quatIdentity();
+ }
+ else if(targetIndex == mecanim::animation::kTargetRoot)
+ {
+ targetT = m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_RootX.t;
+ targetQ = m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_RootX.q;
+ }
+ else if (targetIndex >= mecanim::animation::kTargetLeftFoot && targetIndex <= mecanim::animation::kTargetRightHand)
+ {
+ targetT = m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_GoalArray[targetIndex-2].m_X.t;
+ targetQ = m_EvaluationDataSet.m_AvatarOutput->m_HumanPoseOutput->m_GoalArray[targetIndex-2].m_X.q;
+ }
+ }
+
+
+ if (targetIndex >= mecanim::animation::kTargetLeftFoot && targetIndex <= mecanim::animation::kTargetRightHand)
+ targetQ = math::normalize(math::quatMul(targetQ,mecanim::human::HumanGetGoalOrientationOffset(mecanim::human::Goal(targetIndex - mecanim::animation::kTargetLeftFoot))));
+
+
+ // from muscleclips, local to avatarX
+ math::xform targetXLocal(targetT,targetQ, math::scaleIdentity());
+
+ // from user, where we need to be, in world space
+ math::xform matchX(Vector3fTofloat4(m_MatchPosition), QuaternionfTofloat4(m_MatchRotation), math::scaleIdentity());
+
+ // make match local to avatar X
+ math::xform matchXLocal = math::xformInvMul(avatarX, matchX); // @Sonny: any problem here for match target bug on IOS
+
+ // m_EvaluationDataSet.m_AvatarOutput->m_DX is in normalized space, so dx musy be computed in normalized space too.
+ math::xform dx;
+ dx.t = matchXLocal.t - targetXLocal.t; // dt not influenced by rotation
+ dx.t *= rcp(scale);
+ dx.q = quatMul(matchXLocal.q, quatConj(targetXLocal.q));
+ dx.t *= Vector3fTofloat4(w*m_MatchTargetMask.m_PositionXYZWeight);
+ dx.q = math::quatLerp( math::quatIdentity(), dx.q, math::float1(w*m_MatchTargetMask.m_RotationWeight));
+
+ m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_DX = math::xformMul(m_EvaluationDataSet.m_AvatarOutput->m_MotionOutput->m_DX,dx);
+
+ if(w >= 1)
+ {
+ InterruptMatchTarget(false);
+ }
+ }
+ }
+}
+
+bool Animator::ValidateGoalIndex(int index)
+{
+ if(index < mecanim::human::kLeftFootGoal || index > mecanim::human::kRightHandGoal )
+ {
+ ErrorString("Invalid Goal Index");
+ return false;
+ }
+
+ return true;
+}
+
+bool Animator::ValidateTargetIndex(int index)
+{
+ if(index < mecanim::animation::kTargetReference || index > mecanim::animation::kTargetRightHand )
+ {
+ ErrorString("Invalid Target Index");
+ return false;
+ }
+
+ return true;
+}
+
+bool Animator::ValidateLayerIndex(int index)const
+{
+ if(!IsInitialize() )
+ return false;
+
+ if(index < 0 || index >= GetLayerCount())
+ {
+ ErrorString("Invalid Layer Index");
+ return false;
+ }
+
+ if(m_EvaluationDataSet.m_ControllerConstant->m_LayerArray[index]->m_StateMachineIndex == mecanim::DISABLED_SYNCED_LAYER_IN_NON_PRO)
+ {
+ ErrorString("Sync Layer is only supported in Unity Pro");
+ return false;
+ }
+
+ return true;
+}
+
+void Animator::ValidateParameterID (GetSetValueResult result, int identifier)
+{
+ ValidateParameterString (result, Format("Hash %d", identifier));
+}
+
+void Animator::ValidateParameterString (GetSetValueResult result, const std::string& name)
+{
+
+ if (result == kParameterMismatchFailure)
+ {
+ WarningStringIfLoggingActive(Format ("Parameter type '%s' does not match.", name.c_str()));
+ }
+ else if (result == kParameterDoesNotExist)
+ {
+ WarningStringIfLoggingActive(Format ("Parameter '%s' does not exist.", name.c_str()));
+ }
+ else if (result == kAnimatorNotInitialized)
+ {
+ WarningStringIfLoggingActive("Animator has not been initialized.");
+ }
+ else if (result == kParameterIsControlledByCurve)
+ {
+ WarningStringIfLoggingActive(Format("Parameter '%s' is controlled by a curve.", name.c_str()));
+ }
+}
+
+template<typename T> inline bool IsTypeMatching(mecanim::ValueConstant const& valueConstant, T const& value)
+{
+ return valueConstant.m_Type == mecanim::traits<T>::type() ||
+ (valueConstant.m_Type == mecanim::kTriggerType && mecanim::traits<T>::type() == mecanim::kBoolType);
+}
+
+template<typename T> inline
+GetSetValueResult Animator::SetValue(mecanim::uint32_t id, T const& value)
+{
+ if(IsPlayingBack())
+ return kAnimatorInPlaybackMode;
+
+ if (!IsInitialize())
+ return kAnimatorNotInitialized;
+
+ mecanim::int32_t i = mecanim::FindValueIndex(m_EvaluationDataSet.m_ControllerConstant->m_Values.Get(), id);
+ if( i == -1)
+ return kParameterDoesNotExist;
+
+ bool isCurve = m_EvaluationDataSet.m_GenericBindingConstant->controllerBindingConstant->m_AnimationSet->m_AdditionalIndexArray[i] != -1;
+ if (isCurve)
+ return kParameterIsControlledByCurve;
+
+ if (!IsTypeMatching(m_EvaluationDataSet.m_ControllerConstant->m_Values->m_ValueArray[i], value))
+ return kParameterMismatchFailure;
+
+ m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_Values->WriteData(value, m_EvaluationDataSet.m_ControllerConstant->m_Values->m_ValueArray[i].m_Index);
+ return kGetSetSuccess;
+}
+
+template<typename T> inline
+GetSetValueResult Animator::GetValue(mecanim::uint32_t id, T& value)
+{
+ if (!IsInitialize())
+ {
+ value = T();
+ return kAnimatorNotInitialized;
+ }
+
+ mecanim::int32_t i = mecanim::FindValueIndex(m_EvaluationDataSet.m_ControllerConstant->m_Values.Get(), id);
+ if ( i == -1)
+ {
+ value = T();
+ return kParameterDoesNotExist;
+ }
+
+ if (! IsTypeMatching(m_EvaluationDataSet.m_ControllerConstant->m_Values->m_ValueArray[i], value) )
+ {
+ value = T();
+ return kParameterMismatchFailure;
+ }
+
+
+ m_EvaluationDataSet.m_AvatarMemory->m_ControllerMemory->m_Values->ReadData(value, m_EvaluationDataSet.m_ControllerConstant->m_Values->m_ValueArray[i].m_Index);
+
+ return kGetSetSuccess;
+}
+
+
+void Animator::WarningStringIfLoggingActive(const std::string& warning) const
+{
+ WarningStringIfLoggingActive(warning.c_str());
+}
+
+/// add or modify curve for a read only imported clip ... put Animation Window modification to curve in .meta
+
+void Animator::WarningStringIfLoggingActive(const char* warning) const
+{
+ if(m_LogWarnings)
+ {
+ WarningStringObject(warning ,this);
+ }
+}
+
+Transform* Animator::GetAvatarRoot()
+{
+ Transform *root = &GetComponent(Transform);
+ if(IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_3_a1))
+ {
+ if(m_Avatar.IsValid())
+ {
+ Transform* effectiveRoot = 0;
+ if(m_Avatar->GetAsset()&& m_Avatar->GetAsset()->m_AvatarSkeleton.Get())
+ {
+ effectiveRoot = FindAvatarRoot(m_Avatar->GetAsset()->m_AvatarSkeleton.Get(), m_Avatar->GetAsset()->m_SkeletonNameIDArray.Get(), *root, m_HasTransformHierarchy) ;
+ }
+ if(effectiveRoot) root = effectiveRoot;
+ }
+ }
+
+ return root;
+}
+
+
+///@TODO:
+
+/// * How does a user setup the animation range in the animation window?
+/// * Figure out how we want to move loop blend feel intuitive.
+
+/// * LivePreview pretends that non-looping animations loop. At least that looks like what it is visualizing...
+/// * Blending of dynamic values does not treat default values correctly
+/// muscle clip info for animation window created clips
+/// visible avatar for some objects like lights
+/// * Figure out how we will handle startTime / stopTime from AnimationWindow.
+
+///@TODO: Write Test
+/// Create cube with light attached
+/// * Create Animation clip with transform changes -> Automatically applies root motion
+/// * Animated child object is animated as normal position movement
+/// * Blending between one clip that has a property and one that doesn't