summaryrefslogtreecommitdiff
path: root/Runtime/mecanim/animation
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/mecanim/animation')
-rw-r--r--Runtime/mecanim/animation/avatar.cpp1799
-rw-r--r--Runtime/mecanim/animation/avatar.h706
-rw-r--r--Runtime/mecanim/animation/blendtree.cpp967
-rw-r--r--Runtime/mecanim/animation/blendtree.h285
-rw-r--r--Runtime/mecanim/animation/clipmuscle.cpp1366
-rw-r--r--Runtime/mecanim/animation/clipmuscle.h264
-rw-r--r--Runtime/mecanim/animation/constantclip.cpp34
-rw-r--r--Runtime/mecanim/animation/constantclip.h36
-rw-r--r--Runtime/mecanim/animation/curvedata.cpp118
-rw-r--r--Runtime/mecanim/animation/curvedata.h82
-rw-r--r--Runtime/mecanim/animation/damp.cpp24
-rw-r--r--Runtime/mecanim/animation/damp.h42
-rw-r--r--Runtime/mecanim/animation/denseclip.cpp63
-rw-r--r--Runtime/mecanim/animation/denseclip.h47
-rw-r--r--Runtime/mecanim/animation/poseblender.cpp228
-rw-r--r--Runtime/mecanim/animation/poseblender.h113
-rw-r--r--Runtime/mecanim/animation/streamedclip.cpp196
-rw-r--r--Runtime/mecanim/animation/streamedclip.h93
18 files changed, 6463 insertions, 0 deletions
diff --git a/Runtime/mecanim/animation/avatar.cpp b/Runtime/mecanim/animation/avatar.cpp
new file mode 100644
index 0000000..441353b
--- /dev/null
+++ b/Runtime/mecanim/animation/avatar.cpp
@@ -0,0 +1,1799 @@
+#include "UnityPrefix.h"
+
+#include "Runtime/mecanim/animation/avatar.h"
+
+#include "Runtime/mecanim/generic/valuearray.h"
+#include "Runtime/mecanim/generic/crc32.h"
+#include "Runtime/mecanim/generic/stringtable.h"
+#include "Runtime/mecanim/skeleton/skeleton.h"
+#include "Runtime/mecanim/human/human.h"
+#include "Runtime/mecanim/animation/clipmuscle.h"
+#include "Runtime/mecanim/animation/curvedata.h"
+#include "Runtime/mecanim/statemachine/statemachine.h"
+#include "Runtime/Serialize/TransferFunctions/TransferNameConversions.h"
+
+#include "Runtime/Misc/BuildSettings.h"
+
+namespace mecanim
+{
+
+namespace memory
+{
+ Profiler* Profiler::s_Profiler = NULL;
+}
+
+namespace animation
+{
+
+ AnimationSet* CreateAnimationSet(ControllerConstant const* controller, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(AnimationSet);
+
+ AnimationSet* animationSet = alloc.Construct<AnimationSet>();
+
+ animationSet->m_LayerCount = controller->m_LayerCount;
+
+ animationSet->m_ClipPerLayer = alloc.ConstructArray<uint32_t>(animationSet->m_LayerCount);
+ memset(animationSet->m_ClipPerLayer,0,sizeof(uint32_t)*animationSet->m_LayerCount);
+
+ animationSet->m_ClipConstant = alloc.ConstructArray<AnimationSet::Clip*>(animationSet->m_LayerCount);
+
+ animationSet->m_AdditionalCount = controller->m_Values->m_Count;
+ animationSet->m_AdditionalIndexArray = alloc.ConstructArray<int32_t>(animationSet->m_AdditionalCount);
+ for(int i = 0; i < animationSet->m_AdditionalCount; i++)
+ animationSet->m_AdditionalIndexArray[i] = -1;
+
+ animationSet->m_DynamicValuesMaskArray = alloc.ConstructArray<ValueArrayMask *>(animationSet->m_LayerCount);
+
+ for(int layerIter = 0; layerIter < animationSet->m_LayerCount; layerIter++)
+ {
+ int clipCount = 0;
+
+ mecanim::uint32_t stateMachineIndex = controller->m_LayerArray[layerIter]->m_StateMachineIndex;
+
+ if(stateMachineIndex != DISABLED_SYNCED_LAYER_IN_NON_PRO)
+ {
+ mecanim::uint32_t motionSetIndex = controller->m_LayerArray[layerIter]->m_StateMachineMotionSetIndex;
+
+ const statemachine::StateMachineConstant* stateMachineConstant = controller->m_StateMachineArray[stateMachineIndex].Get();
+
+ for(int stateIter = 0; stateIter < stateMachineConstant->m_StateConstantCount; stateIter++)
+ {
+ statemachine::StateConstant const& stateConstant = *controller->m_StateMachineArray[stateMachineIndex]->m_StateConstantArray[stateIter];
+
+ clipCount += stateConstant.m_LeafInfoArray[motionSetIndex].m_Count;
+
+ for(int blendTreeIter = 0; blendTreeIter < stateConstant.m_BlendTreeCount; blendTreeIter++)
+ {
+ uint32_t blendCount = GetMaxBlendCount(*stateConstant.m_BlendTreeConstantArray[blendTreeIter]);
+
+ if(blendCount > animationSet->m_MaxBlendState)
+ {
+ animationSet->m_MaxBlendState = blendCount;
+ }
+ }
+ }
+ }
+
+ animationSet->m_ClipPerLayer[layerIter] = clipCount;
+ animationSet->m_ClipConstant[layerIter] = alloc.ConstructArray<AnimationSet::Clip>(clipCount);
+
+ animationSet->m_DynamicValuesMaskArray[layerIter] = 0;
+ }
+
+ // if there is no blend tree the worst case is when we are doing a transition between two state
+ // in this case we need to evaluate two clip.
+ animationSet->m_MaxBlendState = math::maximum<uint32_t>(animationSet->m_MaxBlendState*2, 2);
+
+ return animationSet;
+ }
+
+ void DestroyAnimationSet(AnimationSet* animationSet, memory::Allocator& alloc)
+ {
+ if(animationSet)
+ {
+ alloc.Deallocate(animationSet->m_ClipPerLayer);
+
+ for(int layerIter = 0; layerIter < animationSet->m_LayerCount; layerIter++)
+ {
+ alloc.Deallocate(animationSet->m_ClipConstant[layerIter]);
+ DestroyValueArrayMask(animationSet->m_DynamicValuesMaskArray[layerIter],alloc);
+ }
+
+ alloc.Deallocate(animationSet->m_ClipConstant);
+ alloc.Deallocate(animationSet->m_AdditionalIndexArray);
+ alloc.Deallocate(animationSet->m_DynamicValuesMaskArray);
+ DestroyValueArrayConstant(animationSet->m_DynamicFullValuesConstant, alloc);
+ alloc.Deallocate(animationSet);
+ }
+ }
+
+ AnimationSetMemory* CreateAnimationSetMemory(AnimationSet const* animationSet, bool allowConstantCurveOptimization, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(AnimationSetMemory);
+
+ AnimationSetMemory* memory = alloc.Construct<AnimationSetMemory>();
+
+ memory->m_LayerCount = animationSet->m_LayerCount;
+ memory->m_ClipPerLayer = alloc.ConstructArray<uint32_t>(animationSet->m_LayerCount);
+ memory->m_ClipMemory = alloc.ConstructArray<ClipMemory**>(animationSet->m_LayerCount);
+
+ uint32_t maxCurvesCount = 0;
+ for(int layerIter = 0; layerIter < animationSet->m_LayerCount; layerIter++)
+ {
+ memory->m_ClipPerLayer[layerIter] = animationSet->m_ClipPerLayer[layerIter];
+ memory->m_ClipMemory[layerIter] = alloc.ConstructArray<ClipMemory*>(animationSet->m_ClipPerLayer[layerIter]);
+
+ for(int clipIter = 0; clipIter < memory->m_ClipPerLayer[layerIter]; clipIter++)
+ {
+ const ClipMuscleConstant* clip = animationSet->m_ClipConstant[layerIter][clipIter].m_Clip;
+
+ if(clip != 0)
+ {
+ uint32_t usedCurves;
+ if (allowConstantCurveOptimization)
+ usedCurves = animationSet->m_ClipConstant[layerIter][clipIter].m_TotalUsedOptimizedCurveCount;
+ else
+ usedCurves = GetClipCurveCount(*clip);
+
+ maxCurvesCount = math::maximum(maxCurvesCount, usedCurves);
+ memory->m_ClipMemory[layerIter][clipIter] = mecanim::animation::CreateClipMemory(clip->m_Clip.Get(), usedCurves, alloc);
+ }
+ else
+ {
+ memory->m_ClipMemory[layerIter][clipIter] = 0;
+ }
+ }
+ }
+
+ memory->m_ClipOutput = mecanim::animation::CreateClipOutput(maxCurvesCount, alloc);
+
+ return memory;
+ }
+
+ void DestroyAnimationSetMemory(AnimationSetMemory* animationSetMemory, memory::Allocator& alloc)
+ {
+ if (animationSetMemory)
+ {
+ for(int layerIter = 0; layerIter < animationSetMemory->m_LayerCount; layerIter++)
+ {
+ for(int clipIter = 0 ; clipIter < animationSetMemory->m_ClipPerLayer[layerIter]; clipIter++)
+ {
+ mecanim::animation::DestroyClipMemory(animationSetMemory->m_ClipMemory[layerIter][clipIter], alloc);
+ }
+
+ alloc.Deallocate(animationSetMemory->m_ClipMemory[layerIter]);
+ }
+
+ alloc.Deallocate(animationSetMemory->m_ClipPerLayer);
+ alloc.Deallocate(animationSetMemory->m_ClipMemory);
+ mecanim::animation::DestroyClipOutput(animationSetMemory->m_ClipOutput, alloc);
+ alloc.Deallocate(animationSetMemory);
+ }
+ }
+
+ void AvatarConstant::InitializeClass()
+ {
+ RegisterAllowNameConversion("AvatarConstant", "m_Skeleton", "m_AvatarSkeleton");
+ RegisterAllowNameConversion("AvatarConstant", "m_SkeletonPose", "m_AvatarSkeletonPose");
+ }
+
+ AvatarConstant* CreateAvatarConstant( skeleton::Skeleton* skeleton,
+ skeleton::SkeletonPose* skeletonPose,
+ skeleton::SkeletonPose* defaultPose,
+ human::Human* human,
+ skeleton::Skeleton* rootMotionSkeleton,
+ int rootMotionIndex,
+ math::xform const& rootMotionX,
+ memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(AvatarConstant);
+
+ AvatarConstant* cst = alloc.Construct<AvatarConstant>();
+
+ cst->m_AvatarSkeleton = skeleton;
+ cst->m_AvatarSkeletonPose = skeletonPose;
+ cst->m_DefaultPose = defaultPose;
+ cst->m_Human = human;
+ cst->m_RootMotionSkeleton = rootMotionSkeleton;
+ cst->m_RootMotionBoneIndex = rootMotionIndex;
+ cst->m_RootMotionBoneX = rootMotionX;
+
+ if(human != 0)
+ {
+ cst->m_HumanSkeletonIndexCount = human->m_Skeleton->m_Count;
+ cst->m_HumanSkeletonIndexArray = alloc.ConstructArray<mecanim::int32_t>(cst->m_HumanSkeletonIndexCount);
+ skeleton::SkeletonBuildIndexArray(cst->m_HumanSkeletonIndexArray.Get(),human->m_Skeleton.Get(),skeleton);
+ cst->m_HumanSkeletonReverseIndexCount = cst->m_AvatarSkeleton->m_Count;
+ cst->m_HumanSkeletonReverseIndexArray = alloc.ConstructArray<mecanim::int32_t>(cst->m_HumanSkeletonReverseIndexCount);
+ skeleton::SkeletonBuildReverseIndexArray(cst->m_HumanSkeletonReverseIndexArray.Get(),cst->m_HumanSkeletonIndexArray.Get(),human->m_Skeleton.Get(),skeleton);
+ }
+ else if(rootMotionIndex != -1)
+ {
+ cst->m_RootMotionSkeletonIndexCount = rootMotionSkeleton->m_Count;
+ cst->m_RootMotionSkeletonIndexArray = alloc.ConstructArray<mecanim::int32_t>(cst->m_RootMotionSkeletonIndexCount);
+ skeleton::SkeletonBuildIndexArray(cst->m_RootMotionSkeletonIndexArray.Get(),cst->m_RootMotionSkeleton.Get(),skeleton);
+ }
+
+ return cst;
+ }
+
+ void DestroyAvatarConstant(AvatarConstant* constant, memory::Allocator& alloc)
+ {
+ if(constant)
+ {
+ alloc.Deallocate(constant->m_HumanSkeletonIndexArray);
+ alloc.Deallocate(constant->m_HumanSkeletonReverseIndexArray);
+ alloc.Deallocate(constant->m_RootMotionSkeletonIndexArray);
+ alloc.Deallocate(constant);
+ }
+ }
+
+ void ClearAvatarConstant(AvatarConstant * constant, memory::Allocator& alloc)
+ {
+
+ }
+
+ AvatarInput* CreateAvatarInput(AvatarConstant const* constant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(AvatarInput);
+ AvatarInput* input = alloc.Construct<AvatarInput>();
+
+ return input;
+ }
+
+ void DestroyAvatarInput(AvatarInput* input, memory::Allocator& alloc)
+ {
+ if(input)
+ {
+ alloc.Deallocate(input->m_GotoStateInfos);
+ alloc.Deallocate(input);
+ }
+ }
+
+ AvatarMemory* CreateAvatarMemory(AvatarConstant const* constant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(AvatarMemory);
+ AvatarMemory* mem = alloc.Construct<AvatarMemory>();
+
+ return mem;
+ }
+
+ void DestroyAvatarMemory(AvatarMemory* memory, memory::Allocator& alloc)
+ {
+ if(memory)
+ {
+ alloc.Deallocate(memory);
+ }
+ }
+
+ AvatarWorkspace* CreateAvatarWorkspace(AvatarConstant const* constant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(AvatarWorkspace);
+ AvatarWorkspace* ws = alloc.Construct<AvatarWorkspace>();
+
+ if(constant->isHuman())
+ {
+ if(!constant->m_AvatarSkeleton.IsNull())
+ {
+ ws->m_BodySkeletonPoseWs = skeleton::CreateSkeletonPose(constant->m_Human->m_Skeleton.Get(), alloc);
+ ws->m_BodySkeletonPoseWsA = skeleton::CreateSkeletonPose(constant->m_Human->m_Skeleton.Get(), alloc);
+ ws->m_BodySkeletonPoseWsB = skeleton::CreateSkeletonPose(constant->m_Human->m_Skeleton.Get(), alloc);
+
+ ws->m_HumanPoseWs = alloc.Construct<human::HumanPose>();
+ }
+ }
+ else if(constant->m_RootMotionBoneIndex != -1)
+ {
+ if(!constant->m_RootMotionSkeleton.IsNull())
+ {
+ ws->m_RootMotionSkeletonPoseWsA = skeleton::CreateSkeletonPose(constant->m_RootMotionSkeleton.Get(), alloc);
+ ws->m_RootMotionSkeletonPoseWsB = skeleton::CreateSkeletonPose(constant->m_RootMotionSkeleton.Get(), alloc);
+ }
+ }
+
+ return ws;
+ }
+
+ void DestroyAvatarWorkspace(AvatarWorkspace* workspace, memory::Allocator& alloc)
+ {
+ if(workspace)
+ {
+ skeleton::DestroySkeletonPose(workspace->m_BodySkeletonPoseWsB, alloc);
+ skeleton::DestroySkeletonPose(workspace->m_BodySkeletonPoseWsA, alloc);
+ skeleton::DestroySkeletonPose(workspace->m_BodySkeletonPoseWs, alloc);
+ skeleton::DestroySkeletonPose(workspace->m_RootMotionSkeletonPoseWsB, alloc);
+ skeleton::DestroySkeletonPose(workspace->m_RootMotionSkeletonPoseWsA, alloc);
+ alloc.Deallocate(workspace->m_HumanPoseWs);
+
+ alloc.Deallocate(workspace);
+ }
+ }
+
+ AvatarOutput* CreateAvatarOutput(AvatarConstant const* constant, bool hasTransformHierarchy, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(AvatarOutput);
+ AvatarOutput* out = alloc.Construct<AvatarOutput>();
+
+ if(hasTransformHierarchy)
+ {
+ if(!constant->m_Human.IsNull())
+ {
+ if(!constant->m_AvatarSkeleton.IsNull() && constant->m_AvatarSkeleton->m_Count > 0)
+ {
+ out-> m_SkeletonPoseOutput = skeleton::CreateSkeletonPose(constant->m_AvatarSkeleton.Get(), alloc);
+ }
+ }
+ }
+ else
+ {
+ if(!constant->m_AvatarSkeleton.IsNull() && constant->m_AvatarSkeleton->m_Count > 0)
+ {
+ out->m_SkeletonPoseOutput = skeleton::CreateSkeletonPose(constant->m_AvatarSkeleton.Get(), alloc);
+ }
+ }
+
+ if(constant->m_RootMotionBoneIndex != -1 || !constant->m_Human.IsNull())
+ {
+ out->m_MotionOutput = alloc.Construct<MotionOutput>();
+
+ if(!constant->m_Human.IsNull())
+ {
+ out->m_HumanPoseBaseOutput = alloc.Construct<human::HumanPose>();
+ out->m_HumanPoseOutput = alloc.Construct<human::HumanPose>();
+ }
+ }
+
+ return out;
+ }
+
+ void DestroyAvatarOutput(AvatarOutput* output, memory::Allocator& alloc)
+ {
+ if(output)
+ {
+ if(output->m_DynamicValuesOutput) DestroyValueArray(output->m_DynamicValuesOutput,alloc);
+ skeleton::DestroySkeletonPose(output->m_SkeletonPoseOutput, alloc);
+ alloc.Deallocate(output->m_MotionOutput);
+ alloc.Deallocate(output->m_HumanPoseOutput);
+ alloc.Deallocate(output->m_HumanPoseBaseOutput);
+ alloc.Deallocate(output);
+ }
+ }
+
+ void ControllerConstant::InitializeClass()
+ {
+ RegisterAllowNameConversion("ControllerConstant", "m_HumanLayerCount", "m_LayerCount");
+ RegisterAllowNameConversion("ControllerConstant", "m_HumanLayerArray", "m_LayerArray");
+ }
+
+ ControllerConstant* CreateControllerConstant( uint32_t layerCount, LayerConstant** layerArray,
+ uint32_t stateMachineCount, statemachine::StateMachineConstant** stateMachineConstantArray,
+ ValueArrayConstant* values, ValueArray* defaultValues,
+ memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(ControllerConstant);
+
+ ControllerConstant* controller = alloc.Construct<ControllerConstant>();
+
+ controller->m_LayerCount = layerCount;
+ controller->m_LayerArray = alloc.ConstructArray< OffsetPtr<LayerConstant> >(controller->m_LayerCount);
+
+ uint32_t i;
+ for(i=0;i<controller->m_LayerCount;i++)
+ controller->m_LayerArray[i] = layerArray[i];
+
+ controller->m_StateMachineCount = stateMachineCount;
+ controller->m_StateMachineArray = alloc.ConstructArray< OffsetPtr<statemachine::StateMachineConstant> >(controller->m_StateMachineCount);
+
+ for(i=0;i<controller->m_StateMachineCount;i++)
+ controller->m_StateMachineArray[i] = stateMachineConstantArray[i];
+
+ controller->m_Values = values;
+ controller->m_DefaultValues = defaultValues;
+
+ return controller;
+ }
+
+ void DestroyControllerConstant(ControllerConstant* controller, memory::Allocator& alloc)
+ {
+ if(controller)
+ {
+ alloc.Deallocate(controller->m_LayerArray);
+ alloc.Deallocate(controller->m_StateMachineArray);
+ alloc.Deallocate(controller);
+ }
+ }
+
+
+ LayerConstant* CreateLayerConstant(mecanim::uint32_t stateMachineIndex, mecanim::uint32_t motionSetIndex, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(LayerConstant);
+
+ LayerConstant* cst = alloc.Construct<LayerConstant>();
+ cst->m_StateMachineIndex = stateMachineIndex;
+ cst->m_StateMachineMotionSetIndex = motionSetIndex;
+ return cst;
+ }
+
+ void DestroyLayerConstant(LayerConstant* constant, memory::Allocator& alloc)
+ {
+ if(constant)
+ {
+ alloc.Deallocate(constant);
+ }
+ }
+
+ ControllerMemory* CreateControllerMemory(ControllerConstant const* controller, AvatarConstant const *avatar, AnimationSet const *animationSet, const ValueArrayConstant* dynamicValueConstant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(ControllerMemory);
+
+ ControllerMemory* mem = alloc.Construct<ControllerMemory>();
+
+ mem->m_LayerCount = controller->m_LayerCount;
+ mem->m_StateMachineCount = controller->m_StateMachineCount;
+ mem->m_StateMachineMemory = alloc.ConstructArray< OffsetPtr<statemachine::StateMachineMemory> >(mem->m_StateMachineCount);
+ mem->m_InteruptedTransitionsBlendingStateArray = alloc.ConstructArray< BlendingState<false> > (mem->m_LayerCount);
+ mem->m_LayerWeights = alloc.ConstructArray<float>(mem->m_LayerCount);
+
+ mem->m_Values = CreateValueArray( controller->m_Values.Get(), alloc);
+ ValueArrayCopy(controller->m_DefaultValues.Get(), mem->m_Values.Get());
+
+ for(int layerIter = 0; layerIter < controller->m_LayerCount; layerIter++)
+ {
+ mem->m_LayerWeights[layerIter] = controller->m_LayerArray[layerIter]->m_DefaultWeight;
+
+ mem->m_InteruptedTransitionsBlendingStateArray[layerIter].m_DynamicValuesBlending = mecanim::CreateValueArray(dynamicValueConstant, alloc);
+ mem->m_InteruptedTransitionsBlendingStateArray[layerIter].m_DynamicValuesBlendingMask = mecanim::CreateValueArrayMask(dynamicValueConstant, alloc);
+
+ if(avatar->isHuman())
+ {
+ mem->m_InteruptedTransitionsBlendingStateArray[layerIter].m_MotionBlending = alloc.Construct<mecanim::animation::MotionOutput>();
+ mem->m_InteruptedTransitionsBlendingStateArray[layerIter].m_HumanPoseBlending = alloc.Construct<mecanim::human::HumanPose>();
+ }
+ else if(avatar->m_RootMotionBoneIndex != -1)
+ {
+ mem->m_InteruptedTransitionsBlendingStateArray[layerIter].m_MotionBlending = alloc.Construct<mecanim::animation::MotionOutput>();
+ mem->m_InteruptedTransitionsBlendingStateArray[layerIter].m_HumanPoseBlending = 0;
+ }
+ else
+ {
+ mem->m_InteruptedTransitionsBlendingStateArray[layerIter].m_MotionBlending = 0;
+ mem->m_InteruptedTransitionsBlendingStateArray[layerIter].m_HumanPoseBlending = 0;
+ }
+ }
+
+ for(int stateMachineIter = 0; stateMachineIter < mem->m_StateMachineCount; stateMachineIter++)
+ {
+ mem->m_StateMachineMemory[stateMachineIter] = statemachine::CreateStateMachineMemory(controller->m_StateMachineArray[stateMachineIter].Get(), alloc);
+ }
+
+ return mem;
+ }
+
+ void DestroyControllerMemory(ControllerMemory* controllerMemory, memory::Allocator& alloc)
+ {
+ if(controllerMemory)
+ {
+ for(int layerIter = 0; layerIter < controllerMemory->m_LayerCount; layerIter++)
+ {
+ if(!controllerMemory->m_InteruptedTransitionsBlendingStateArray[layerIter].m_DynamicValuesBlending.IsNull()) DestroyValueArray(controllerMemory->m_InteruptedTransitionsBlendingStateArray[layerIter].m_DynamicValuesBlending.Get(),alloc);
+ if(!controllerMemory->m_InteruptedTransitionsBlendingStateArray[layerIter].m_DynamicValuesBlendingMask.IsNull()) DestroyValueArrayMask(controllerMemory->m_InteruptedTransitionsBlendingStateArray[layerIter].m_DynamicValuesBlendingMask.Get(),alloc);
+ if(!controllerMemory->m_InteruptedTransitionsBlendingStateArray[layerIter].m_MotionBlending.IsNull()) alloc.Deallocate(controllerMemory->m_InteruptedTransitionsBlendingStateArray[layerIter].m_MotionBlending.Get());
+ if(!controllerMemory->m_InteruptedTransitionsBlendingStateArray[layerIter].m_HumanPoseBlending.IsNull()) alloc.Deallocate(controllerMemory->m_InteruptedTransitionsBlendingStateArray[layerIter].m_HumanPoseBlending.Get());
+ }
+
+ for(int smIter = 0; smIter < controllerMemory->m_StateMachineCount; smIter++)
+ {
+ statemachine::DestroyStateMachineMemory(controllerMemory->m_StateMachineMemory[smIter].Get(), alloc);
+ }
+
+ DestroyValueArray(controllerMemory->m_Values.Get(), alloc);
+ alloc.Deallocate(controllerMemory->m_LayerWeights);
+ alloc.Deallocate(controllerMemory->m_InteruptedTransitionsBlendingStateArray);
+ alloc.Deallocate(controllerMemory->m_StateMachineMemory);
+
+ alloc.Deallocate(controllerMemory);
+ }
+ }
+
+ BlendingState<true>* CreateBlendingState(uint32_t size, bool createMotionState, bool createHumanPose, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendingState_true);
+
+ BlendingState<true>* blendingState = alloc.Construct< BlendingState<true> >();
+
+ blendingState->m_DynamicValuesBlending = alloc.ConstructArray<ValueArray*>(size);
+ blendingState->m_BlendFactor = alloc.ConstructArray<float>(size);
+
+ memset(&blendingState->m_DynamicValuesBlending[0], 0, sizeof(ValueArray*) * size);
+ memset(&blendingState->m_BlendFactor[0], 0, sizeof(float) * size);
+
+ if(createMotionState || createHumanPose)
+ {
+ blendingState->m_MotionBlending = alloc.ConstructArray<MotionOutput*>(size);
+ memset(&blendingState->m_MotionBlending[0], 0, sizeof(MotionOutput*) * size);
+ }
+
+ if(createHumanPose)
+ {
+ blendingState->m_HumanPoseBlending = alloc.ConstructArray<human::HumanPose*>(size);
+ memset(&blendingState->m_HumanPoseBlending[0], 0, sizeof(human::HumanPose*) * size);
+ }
+
+ blendingState->m_Size = size;
+
+ return blendingState;
+ }
+
+ void DestroyBlendingState(BlendingState<true>* blendingState, memory::Allocator& alloc)
+ {
+ if(blendingState)
+ {
+ for(uint32_t i=0;i<blendingState->m_Size;++i)
+ {
+ DestroyValueArray(blendingState->m_DynamicValuesBlending[i],alloc);
+ if(blendingState->m_MotionBlending) alloc.Deallocate(blendingState->m_MotionBlending[i]);
+ if(blendingState->m_HumanPoseBlending) alloc.Deallocate(blendingState->m_HumanPoseBlending[i]);
+ }
+
+ alloc.Deallocate(blendingState->m_DynamicValuesBlending);
+ alloc.Deallocate(blendingState->m_MotionBlending);
+ alloc.Deallocate(blendingState->m_HumanPoseBlending);
+ alloc.Deallocate(blendingState->m_BlendFactor);
+
+ alloc.Deallocate(blendingState);
+ }
+ }
+
+ ControllerWorkspace *CreateControllerWorkspace(ControllerConstant const* controller, AvatarConstant const *avatar, AnimationSet const *animationSet, const ValueArrayConstant* dynamicValueConstant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(ControllerWorkspace);
+ bool forRootMotion = avatar->m_RootMotionBoneIndex != -1;
+ bool forHuman = avatar->isHuman();
+
+ ControllerWorkspace* ws = alloc.Construct<ControllerWorkspace>();
+
+ ws->m_StateMachineOutput = alloc.ConstructArray<statemachine::StateMachineOutput*>(controller->m_StateMachineCount);
+ ws->m_StateMachineWorkspace = alloc.ConstructArray<statemachine::StateMachineWorkspace*>(controller->m_StateMachineCount);
+ ws->m_StateMachineCount = controller->m_StateMachineCount;
+
+ uint32_t i;
+ uint32_t maxMotionSet = 0;
+ for(i = 0 ; i < controller->m_StateMachineCount; ++i)
+ {
+ maxMotionSet = math::maximum<uint32_t>(maxMotionSet, controller->m_StateMachineArray[i]->m_MotionSetCount);
+ }
+ ws->m_MotionSetTimingWeightArray = alloc.ConstructArray<float>(maxMotionSet);
+
+ for(int stateMachineIter = 0; stateMachineIter < ws->m_StateMachineCount; stateMachineIter++)
+ {
+ ws->m_StateMachineOutput[stateMachineIter] = statemachine::CreateStateMachineOutput(controller->m_StateMachineArray[stateMachineIter].Get(), animationSet->m_MaxBlendState, alloc);
+ ws->m_StateMachineWorkspace[stateMachineIter] = statemachine::CreateStateMachineWorkspace(controller->m_StateMachineArray[stateMachineIter].Get(), animationSet->m_MaxBlendState, alloc);
+ }
+
+ ws->m_BlendingState = CreateBlendingState(animationSet->m_MaxBlendState, forRootMotion, forHuman, alloc);
+
+ for(int blendStateIter = 0; blendStateIter < ws->m_BlendingState->m_Size; blendStateIter++)
+ {
+ ws->m_BlendingState->m_DynamicValuesBlending[blendStateIter] = mecanim::CreateValueArray(dynamicValueConstant, alloc);
+
+ if(forHuman)
+ {
+ {
+ SETPROFILERLABEL(MotionOutput);
+ ws->m_BlendingState->m_MotionBlending[blendStateIter] = alloc.Construct<mecanim::animation::MotionOutput>();
+ }
+ {
+ SETPROFILERLABEL(HumanPose);
+ ws->m_BlendingState->m_HumanPoseBlending[blendStateIter] = alloc.Construct<mecanim::human::HumanPose>();
+ }
+ }
+ else if(forRootMotion)
+ {
+ ws->m_BlendingState->m_MotionBlending[blendStateIter] = alloc.Construct<mecanim::animation::MotionOutput>();
+ }
+ }
+
+ ws->m_BlendingStateWs.m_DynamicValuesBlending = mecanim::CreateValueArray(dynamicValueConstant, alloc);
+
+ if (forHuman)
+ {
+ ws->m_BlendingStateWs.m_MotionBlending = alloc.Construct<animation::MotionOutput>();
+ ws->m_BlendingStateWs.m_HumanPoseBlending = alloc.Construct<human::HumanPose>();
+ }
+ else if (forRootMotion)
+ {
+ ws->m_BlendingStateWs.m_MotionBlending = alloc.Construct<mecanim::animation::MotionOutput>();
+ }
+
+ ws->m_ValueArrayStart = CreateValueArray(dynamicValueConstant, alloc);
+ ws->m_ValueArrayStop = CreateValueArray(dynamicValueConstant, alloc);
+ ws->m_BlendingClipArray = alloc.ConstructArray<mecanim::animation::BlendingClip>(controller->m_LayerCount*animationSet->m_MaxBlendState);
+
+ ws->m_ReadMask = CreateValueArrayMask(dynamicValueConstant, alloc);
+ ws->m_BlendMask = CreateValueArrayMask(dynamicValueConstant, alloc);
+ ws->m_DefaultMask = CreateValueArrayMask(dynamicValueConstant, alloc);
+
+ return ws;
+ }
+
+ void DestroyControllerWorkspace(ControllerWorkspace* controllerWorkspace, memory::Allocator& alloc)
+ {
+ if(controllerWorkspace)
+ {
+ uint32_t i;
+ for(i=0;i<controllerWorkspace->m_StateMachineCount;i++)
+ {
+ DestroyStateMachineOutput(controllerWorkspace->m_StateMachineOutput[i], alloc);
+ DestroyStateMachineWorkspace(controllerWorkspace->m_StateMachineWorkspace[i], alloc);
+ }
+
+ DestroyBlendingState(controllerWorkspace->m_BlendingState, alloc);
+
+ DestroyValueArray(controllerWorkspace->m_BlendingStateWs.m_DynamicValuesBlending.Get(), alloc);
+ alloc.Deallocate(controllerWorkspace->m_BlendingStateWs.m_MotionBlending);
+ alloc.Deallocate(controllerWorkspace->m_BlendingStateWs.m_HumanPoseBlending);
+
+ alloc.Deallocate(controllerWorkspace->m_BlendingClipArray);
+
+ alloc.Deallocate(controllerWorkspace->m_MotionSetTimingWeightArray);
+
+ alloc.Deallocate(controllerWorkspace->m_StateMachineWorkspace);
+ alloc.Deallocate(controllerWorkspace->m_StateMachineOutput);
+ DestroyValueArray(controllerWorkspace->m_ValueArrayStart,alloc);
+ DestroyValueArray(controllerWorkspace->m_ValueArrayStop,alloc);
+
+ DestroyValueArrayMask(controllerWorkspace->m_ReadMask,alloc);
+ DestroyValueArrayMask(controllerWorkspace->m_BlendMask,alloc);
+ DestroyValueArrayMask(controllerWorkspace->m_DefaultMask,alloc);
+
+ alloc.Deallocate(controllerWorkspace);
+ }
+ }
+
+ void UpdateLeafNodeDuration(const ControllerConstant &controllerConstant, const AnimationSet &animationSet, ControllerMemory &controllerMemory)
+ {
+ for(int layerIter = 0; layerIter < controllerConstant.m_LayerCount; layerIter++)
+ {
+ mecanim::uint32_t stateMachineIndex = controllerConstant.m_LayerArray[layerIter]->m_StateMachineIndex;
+ mecanim::uint32_t motionSetIndex = controllerConstant.m_LayerArray[layerIter]->m_StateMachineMotionSetIndex;
+
+ const statemachine::StateMachineConstant& stateMachineConstant = *controllerConstant.m_StateMachineArray[stateMachineIndex].Get();
+
+ for(int stateIter = 0; stateIter < stateMachineConstant.m_StateConstantCount; stateIter++)
+ {
+ statemachine::StateConstant const &stateConstant = *stateMachineConstant.m_StateConstantArray[stateIter].Get();
+
+ for(int leafIter = 0; leafIter < stateConstant.m_LeafInfoArray[motionSetIndex].m_Count ; leafIter++)
+ {
+ int clipIndex = leafIter + stateConstant.m_LeafInfoArray[motionSetIndex].m_IndexOffset;
+
+ AnimationSet::Clip& setClip = animationSet.m_ClipConstant[layerIter][clipIndex];
+
+ statemachine::GetBlendTreeMemory(stateConstant,*controllerMemory.m_StateMachineMemory[stateMachineIndex]->m_StateMemoryArray[stateIter],motionSetIndex)->m_NodeDurationArray[leafIter] = setClip.m_Clip != 0 ? (setClip.m_Clip->m_StopTime - setClip.m_Clip->m_StartTime) : 0.0f;
+ }
+ }
+ }
+ }
+
+ void SetIKOnFeet(bool left,AvatarConstant const &avatar, const AvatarInput &input, AvatarMemory &memory, AvatarWorkspace &workspace, AvatarOutput &output)
+ {
+ float deltaTime = input.m_DeltaTime;
+ bool stabilizeFeet = input.m_StabilizeFeet;
+
+ math::xform avatarX = memory.m_AvatarX;
+
+ math::float1 scale(avatar.m_Human->m_Scale);
+
+ math::xform ddx = math::xformInvMulNS(avatarX,workspace.m_AvatarX);
+ float dSpeedT = math::length(ddx.t).tofloat() / deltaTime;
+ float dSpeedQ = math::length(math::doubleAtan(math::quat2Qtan(ddx.q))).tofloat() /deltaTime;
+
+ int32_t footIndex = left ? human::kLeftFoot : human::kRightFoot;
+ int32_t goalIndex = left ? human::kLeftFootGoal : human::kRightFootGoal;
+
+ output.m_HumanPoseOutput->m_GoalArray[goalIndex].m_WeightT = 1;
+ output.m_HumanPoseOutput->m_GoalArray[goalIndex].m_WeightR = 1;
+
+ if(stabilizeFeet && footIndex != -1 && memory.m_FirstEval==0)
+ {
+ float speedT = left ? workspace.m_LeftFootSpeedT : workspace.m_RightFootSpeedT;
+ float speedQ = left ? workspace.m_LeftFootSpeedQ : workspace.m_RightFootSpeedQ;
+
+ speedT += dSpeedT;
+ speedQ += dSpeedQ;
+
+ math::xform &footX = left ? memory.m_LeftFootX : memory.m_RightFootX;
+
+ math::xform footGoalX0 = output.m_HumanPoseOutput->m_GoalArray[goalIndex].m_X;
+ math::xform footGoalX = footGoalX0;
+
+ math::xform goalDX = math::xformInvMulNS(footX,footGoalX);
+
+ float goalDTLen = math::length(goalDX.t).tofloat();
+
+ if(goalDTLen > 0)
+ {
+ float goalSpeedT = goalDTLen / deltaTime;
+ speedT = math::cond(speedT > 0.1f, speedT, 0.0f);
+ speedT = math::cond(speedT > 1.0f, 2.0f * speedT, speedT);
+ float goalSpeedTClamp = math::minimum(goalSpeedT,speedT);
+ goalDX.t = goalDX.t * math::float1(goalSpeedTClamp/goalSpeedT);
+ }
+
+ float goalDQLen = math::length(math::doubleAtan(math::quat2Qtan(goalDX.q))).tofloat();
+
+ if(goalDQLen > 0)
+ {
+ float goalSpeedQ = goalDQLen / deltaTime;
+ speedQ = math::cond(speedQ > math::radians(10.0f), speedQ, 0.0f);
+ speedQ = math::cond(speedQ > math::radians(100.0f), 2.0f * speedQ, speedQ);
+ float goalSpeedQClamp = math::minimum(goalSpeedQ,speedQ);
+ goalDX.q = math::qtan2Quat(math::halfTan(math::doubleAtan(math::quat2Qtan(goalDX.q))*math::float1(goalSpeedQClamp/goalSpeedQ)));
+ }
+
+ footX = math::xformMul(footX,goalDX);
+
+ output.m_HumanPoseOutput->m_GoalArray[goalIndex].m_X = footX;
+ }
+
+ math::float4 feetSpacing = math::quatMulVec(output.m_HumanPoseOutput->m_GoalArray[goalIndex].m_X.q, math::float4(0,0,(left?-1:1)*avatar.m_Human->m_Scale*avatar.m_Human->m_FeetSpacing,0));
+ feetSpacing.y() = math::float1::zero();
+
+ output.m_HumanPoseOutput->m_GoalArray[goalIndex].m_X.t += avatarX.s * feetSpacing;
+ }
+
+ void EvaluateAvatarSM( AvatarConstant const* constant,
+ AvatarInput const* input,
+ AvatarOutput * output,
+ AvatarMemory * memory,
+ AvatarWorkspace * workspace,
+ ControllerConstant const* controllerConstant)
+ {
+ if(controllerConstant)
+ {
+ workspace->m_IKOnFeet = false;
+
+ uint32_t i;
+
+ for(i = 0; i < controllerConstant->m_StateMachineCount; i++)
+ {
+ statemachine::StateMachineInput stateMachineInput;
+ stateMachineInput.m_MotionSetTimingWeightArray = workspace->m_ControllerWorkspace->m_MotionSetTimingWeightArray;
+
+ for(int layerIndex = 0; layerIndex < controllerConstant->m_LayerCount; layerIndex++)
+ {
+ const uint32_t stateMachineIndex = controllerConstant->m_LayerArray[layerIndex]->m_StateMachineIndex;
+ const uint32_t motionSetIndex = controllerConstant->m_LayerArray[layerIndex]->m_StateMachineMotionSetIndex;
+
+ if(stateMachineIndex != DISABLED_SYNCED_LAYER_IN_NON_PRO)
+ {
+ if(i == stateMachineIndex )
+ {
+ if(motionSetIndex == 0)
+ stateMachineInput.m_GotoStateInfo = &input->m_GotoStateInfos[layerIndex];
+
+ if( controllerConstant->m_LayerArray[layerIndex]->m_SyncedLayerAffectsTiming || motionSetIndex == 0 )
+ stateMachineInput.m_MotionSetTimingWeightArray[motionSetIndex] = motionSetIndex == 0 ? 1 : memory->m_ControllerMemory->m_LayerWeights[layerIndex];
+ else
+ stateMachineInput.m_MotionSetTimingWeightArray[motionSetIndex] = 0 ;
+ }
+ }
+ }
+
+
+ stateMachineInput.m_DeltaTime = input->m_DeltaTime;
+
+ stateMachineInput.m_Values = memory->m_ControllerMemory->m_Values.Get();
+ workspace->m_ControllerWorkspace->m_StateMachineWorkspace[i]->m_ValuesConstant = const_cast<mecanim::ValueArrayConstant *>(controllerConstant->m_Values.Get());
+
+ statemachine::EvaluateStateMachine( controllerConstant->m_StateMachineArray[i].Get(),
+ &stateMachineInput,
+ workspace->m_ControllerWorkspace->m_StateMachineOutput[i],
+ memory->m_ControllerMemory->m_StateMachineMemory[i].Get(),
+ workspace->m_ControllerWorkspace->m_StateMachineWorkspace[i]);
+
+ workspace->m_IKOnFeet |= workspace->m_ControllerWorkspace->m_StateMachineOutput[i]->m_Left.m_IKOnFeet;
+ workspace->m_IKOnFeet |= workspace->m_ControllerWorkspace->m_StateMachineOutput[i]->m_Right.m_IKOnFeet;
+ }
+ }
+ }
+
+ void AdjustPoseForMotion(ControllerBindingConstant const* controllerBindingConstant, math::xform const &motionX, ValueArray &values, skeleton::SkeletonPose &pose, skeleton::SkeletonPose &poseWs)
+ {
+ AvatarConstant const *constant = controllerBindingConstant->m_Avatar;
+
+ int lastIndex = (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1)) ? constant->m_RootMotionSkeleton->m_Count-1 : constant->m_RootMotionBoneIndex ;
+
+ SkeletonPoseFromValue(*constant->m_RootMotionSkeleton.Get(), *constant->m_AvatarSkeletonPose.Get(), values, controllerBindingConstant->m_SkeletonTQSMap, constant->m_RootMotionSkeletonIndexArray.Get(), pose,lastIndex, 0);
+
+ skeleton::SkeletonPoseComputeGlobal(constant->m_RootMotionSkeleton.Get(),&pose,&poseWs);
+ pose.m_X[0] = motionX;
+
+ if(constant->m_RootMotionBoneIndex > 0)
+ skeleton::SkeletonPoseComputeGlobal(constant->m_RootMotionSkeleton.Get(),&pose,&poseWs,lastIndex-1,0);
+
+ skeleton::SkeletonPoseComputeLocal(constant->m_RootMotionSkeleton.Get(),&poseWs,&pose,lastIndex,lastIndex);
+ pose.m_X[0] = math::xformIdentity();
+
+ ValueFromSkeletonPose(*constant->m_RootMotionSkeleton.Get(), pose, controllerBindingConstant->m_SkeletonTQSMap, constant->m_RootMotionSkeletonIndexArray.Get(), values, lastIndex, 0);
+ }
+
+ void ComputeRootMotion(ControllerBindingConstant const* controllerBindingConstant, MotionOutput const &motionOutput, ValueArray &values, AvatarWorkspace *workspace)
+ {
+ AdjustPoseForMotion(controllerBindingConstant, motionOutput.m_MotionStartX, *workspace->m_ControllerWorkspace->m_ValueArrayStart, *workspace->m_RootMotionSkeletonPoseWsA, *workspace->m_RootMotionSkeletonPoseWsB);
+ AdjustPoseForMotion(controllerBindingConstant, motionOutput.m_MotionStopX, *workspace->m_ControllerWorkspace->m_ValueArrayStop, *workspace->m_RootMotionSkeletonPoseWsA, *workspace->m_RootMotionSkeletonPoseWsB);
+ AdjustPoseForMotion(controllerBindingConstant, motionOutput.m_MotionX, values, *workspace->m_RootMotionSkeletonPoseWsA, *workspace->m_RootMotionSkeletonPoseWsB);
+ }
+
+ void EvaluateBlendNode( ControllerBindingConstant const* controllerBindingConstant,
+ AvatarInput const* input,
+ int32_t layerIndex,
+ bool additive,
+ bool left,
+ uint32_t &clipCount,
+ ControllerMemory *memory,
+ AvatarWorkspace * workspace,
+ AvatarOutput * avatarOutput,
+ AnimationSetMemory* animationSetMemory,
+ float *blendFactor)
+ {
+ AvatarConstant const *constant = controllerBindingConstant->m_Avatar;
+ ControllerConstant const *controllerConstant = controllerBindingConstant->m_Controller;
+ AnimationSet const *animationSet = controllerBindingConstant->m_AnimationSet;
+
+ bool hasRootMotion = constant->m_RootMotionBoneIndex != -1;
+ bool isHuman = constant->isHuman();
+
+ uint32_t stateMachineIndex = controllerConstant->m_LayerArray[layerIndex]->m_StateMachineIndex;
+ uint32_t motionSetIndex = controllerConstant->m_LayerArray[layerIndex]->m_StateMachineMotionSetIndex;
+
+ if(stateMachineIndex == DISABLED_SYNCED_LAYER_IN_NON_PRO)
+ return ;
+
+ statemachine::StateMachineOutput& stateMachineOutput = *workspace->m_ControllerWorkspace->m_StateMachineOutput[stateMachineIndex];
+ statemachine::BlendNode &blendNode = left ? stateMachineOutput.m_Left : stateMachineOutput.m_Right;
+
+ animation::ClipOutput* clipOutput = animationSetMemory->m_ClipOutput;
+
+ for(int32_t outputIter=0; outputIter < blendNode.m_BlendNodeLayer[motionSetIndex].m_OutputCount; outputIter++)
+ {
+ uint32_t index = blendNode.m_BlendNodeLayer[motionSetIndex].m_OutputIndexArray[outputIter];
+
+ AnimationSet::Clip& setClip = animationSet->m_ClipConstant[layerIndex][index];
+
+ if (setClip.m_Clip)
+ {
+ animation::ClipMuscleConstant const& muscleConstant = *setClip.m_Clip;
+
+ if(hasRootMotion || isHuman)
+ {
+ ClipMuscleInput muscleIn;
+ muscleIn.m_Time = blendNode.m_CurrentTime;
+ muscleIn.m_PreviousTime = blendNode.m_PreviousTime;
+ muscleIn.m_Mirror = blendNode.m_BlendNodeLayer[motionSetIndex].m_MirrorArray[outputIter];
+ muscleIn.m_CycleOffset = blendNode.m_BlendNodeLayer[motionSetIndex].m_CycleOffsetArray[outputIter];
+ muscleIn.m_Reverse = blendNode.m_BlendNodeLayer[motionSetIndex].m_ReverseArray[outputIter];
+
+ animation::MotionOutput* motionOutput = workspace->m_ControllerWorkspace->m_BlendingState->m_MotionBlending[clipCount];
+
+ if(isHuman) EvaluateClipMusclePrevTime(muscleConstant,muscleIn,*motionOutput,*animationSetMemory->m_ClipMemory[layerIndex][index]);
+ else if(hasRootMotion) EvaluateClipRootMotionDeltaX(muscleConstant,muscleIn,*motionOutput,*animationSetMemory->m_ClipMemory[layerIndex][index]);
+ }
+
+ ValueArray &values = avatarOutput != 0 && clipCount == 0 ? *avatarOutput->m_DynamicValuesOutput : *workspace->m_ControllerWorkspace->m_BlendingState->m_DynamicValuesBlending[clipCount];
+
+ ClipInput in;
+ float timeInt;
+ float normalizedTime = 0;
+ in.m_Time = ComputeClipTime(blendNode.m_CurrentTime,muscleConstant.m_StartTime,muscleConstant.m_StopTime,
+ muscleConstant.m_CycleOffset+blendNode.m_BlendNodeLayer[motionSetIndex].m_CycleOffsetArray[outputIter],
+ muscleConstant.m_LoopTime,
+ blendNode.m_BlendNodeLayer[motionSetIndex].m_ReverseArray[outputIter],
+ normalizedTime,timeInt);
+
+ ValueArrayMask& readMask = *workspace->m_ControllerWorkspace->m_ReadMask;
+ ValueArrayMask& blendMask = *workspace->m_ControllerWorkspace->m_BlendMask;
+
+ EvaluateClip(muscleConstant.m_Clip.Get(),&in,animationSetMemory->m_ClipMemory[layerIndex][index], clipOutput);
+ ValuesFromClip(*controllerBindingConstant->m_DynamicValuesDefault, muscleConstant, *clipOutput, setClip.m_Bindings, animationSet->m_IntegerRemapStride, values, readMask);
+
+ OrValueMask(&blendMask,&readMask);
+
+ if(muscleConstant.m_LoopTime && muscleConstant.m_LoopBlend)
+ {
+ DeltasFromClip(muscleConstant, setClip.m_Bindings, readMask, *workspace->m_ControllerWorkspace->m_ValueArrayStart, *workspace->m_ControllerWorkspace->m_ValueArrayStop);
+ }
+
+ if(hasRootMotion || isHuman)
+ {
+ ClipMuscleInput muscleIn;
+
+ muscleIn.m_Time = blendNode.m_CurrentTime;
+ muscleIn.m_PreviousTime = blendNode.m_PreviousTime;
+
+ animation::MotionOutput* motionOutput = workspace->m_ControllerWorkspace->m_BlendingState->m_MotionBlending[clipCount];
+
+ if(isHuman)
+ {
+ muscleIn.m_TargetIndex = input->m_TargetIndex;
+ muscleIn.m_TargetTime = input->m_TargetTime;
+ muscleIn.m_TargetIndex = muscleIn.m_TargetIndex > int32_t(animation::kTargetReference) ? muscleIn.m_TargetIndex : int32_t(animation::kTargetReference);
+ muscleIn.m_TargetIndex = muscleIn.m_TargetIndex < int32_t(animation::kTargetRightHand) ? muscleIn.m_TargetIndex : int32_t(animation::kTargetRightHand);
+
+ muscleIn.m_Mirror = blendNode.m_BlendNodeLayer[motionSetIndex].m_MirrorArray[outputIter];
+ muscleIn.m_CycleOffset = blendNode.m_BlendNodeLayer[motionSetIndex].m_CycleOffsetArray[outputIter];
+ muscleIn.m_Reverse = blendNode.m_BlendNodeLayer[motionSetIndex].m_ReverseArray[outputIter];
+
+ human::HumanPose *humanPose = workspace->m_ControllerWorkspace->m_BlendingState->m_HumanPoseBlending[clipCount];
+ EvaluateClipMuscle(muscleConstant,muscleIn,clipOutput->m_Values,*motionOutput,*humanPose,*animationSetMemory->m_ClipMemory[layerIndex][index]);
+
+ if(additive)
+ {
+ // put goals in same space as GetHumanPose
+ // @todo check with bob why referential spaces are different
+ for(int i = 0; i < human::kLastGoal; i++)
+ humanPose->m_GoalArray[i].m_X = math::xformInvMul(humanPose->m_RootX,humanPose->m_GoalArray[i].m_X);
+ humanPose->m_RootX = math::xformMul(motionOutput->m_MotionX, humanPose->m_RootX);
+
+ GetHumanPose(muscleConstant,muscleConstant.m_ValueArrayDelta.Get(),*workspace->m_HumanPoseWs);
+ human::HumanPoseSub(*humanPose,*humanPose,*workspace->m_HumanPoseWs);
+ }
+ }
+ else if(hasRootMotion)
+ {
+ ComputeRootMotion(controllerBindingConstant,*motionOutput,values,workspace);
+ }
+
+ if(animationSet->m_GravityWeightIndex != -1 && readMask.m_FloatValues[animationSet->m_GravityWeightIndex])
+ {
+ values.ReadData(motionOutput->m_GravityWeight, animationSet->m_GravityWeightIndex);
+ }
+ else
+ {
+ motionOutput->m_GravityWeight = muscleConstant.m_LoopBlendPositionY ? 1 : 0;
+ }
+ }
+
+ if(additive)
+ {
+ ValueArraySub(*workspace->m_ControllerWorkspace->m_ValueArrayStart,values, &readMask);
+ }
+
+ if(muscleConstant.m_LoopTime && muscleConstant.m_LoopBlend)
+ {
+ ValueArrayLoop(*workspace->m_ControllerWorkspace->m_ValueArrayStart, *workspace->m_ControllerWorkspace->m_ValueArrayStop,values,normalizedTime, readMask);
+ }
+
+ blendFactor[clipCount] = (left ? ( 1.f - stateMachineOutput.m_BlendFactor) : stateMachineOutput.m_BlendFactor) * blendNode.m_BlendNodeLayer[motionSetIndex].m_OutputBlendArray[outputIter];
+
+ BlendingClip &blendingClip = workspace->m_ControllerWorkspace->m_BlendingClipArray[workspace->m_ControllerWorkspace->m_BlendingClipCount];
+ blendingClip.m_ClipIndex = setClip.m_ClipIndex;
+ blendingClip.m_LayerIndex = layerIndex;
+ blendingClip.m_Weight = blendFactor[clipCount];
+ blendingClip.m_PrevTime = ComputeClipTime(blendNode.m_PreviousTime,muscleConstant.m_StartTime,muscleConstant.m_StopTime,muscleConstant.m_CycleOffset,muscleConstant.m_LoopTime,blendNode.m_BlendNodeLayer[motionSetIndex].m_ReverseArray[outputIter],normalizedTime,timeInt);
+ blendingClip.m_Time = in.m_Time;
+ blendingClip.m_Reverse = blendNode.m_BlendNodeLayer[motionSetIndex].m_ReverseArray[outputIter];
+
+ workspace->m_ControllerWorkspace->m_BlendingClipCount++;
+
+ clipCount++;
+ }
+ }
+ }
+
+ bool BlendDynamicStates(ValueArray const &valuesDefault, ValueArrayMask const &valueMask, ValueArray **valuesArray, float *blendFactor, int clipCount, ValueArray &valuesOutput, ValueArray *valuesInterupted, ValueArrayMask *valuesMaskInterupted, AvatarOutput *avatarOutput)
+ {
+ bool fastCopy = false;
+
+ if(clipCount > 0)
+ {
+ if(clipCount == 1 && CompareApproximately(blendFactor[0],1))
+ {
+ if(avatarOutput == 0)
+ {
+ ValueArrayCopy(valuesArray[0],&valuesOutput, &valueMask);
+ }
+ else
+ {
+ fastCopy = true;
+ }
+ }
+ else
+ {
+ ValueArray *values0 = valuesArray[0];
+ valuesArray[0] = avatarOutput ? avatarOutput->m_DynamicValuesOutput : valuesArray[0];
+ ValueArrayBlend(&valuesDefault,&valuesOutput, valuesArray, blendFactor, clipCount, &valueMask);
+ valuesArray[0] = values0;
+ }
+ }
+ else if(clipCount == 0)
+ {
+ ValueArrayCopy(&valuesDefault,&valuesOutput, &valueMask);
+ }
+
+ if(valuesInterupted != 0)
+ {
+ ValueArrayCopy(&valuesOutput,valuesInterupted, &valueMask);
+ CopyValueMask(valuesMaskInterupted,&valueMask);
+ }
+
+ return fastCopy;
+ }
+
+ void BlendMotionStates(MotionOutput **motionArray, float *blendFactor, int clipCount, MotionOutput *motionOutput, MotionOutput *motionInterupted, bool hasRootMotion, bool isHuman, human::HumanPoseMask const &mask)
+ {
+ if(clipCount > 0)
+ {
+ if(clipCount == 1 && CompareApproximately(blendFactor[0],1))
+ {
+ MotionOutputCopy(motionOutput,motionArray[0], hasRootMotion, isHuman, mask);
+ }
+ else
+ {
+ MotionOutputBlend(motionOutput,motionArray,blendFactor,clipCount, hasRootMotion, isHuman, mask);
+ }
+ }
+
+ if(motionInterupted)
+ {
+ MotionOutputCopy(motionInterupted,motionOutput, hasRootMotion, isHuman, mask);
+ }
+ }
+
+ void BlendHumanStates(human::HumanPose **poseArray, float *blendFactor, int clipCount, human::HumanPose *poseOut, human::HumanPose *poseInterupted)
+ {
+ if(clipCount > 0)
+ {
+ if(clipCount == 1 && CompareApproximately(blendFactor[0],1))
+ {
+ human::HumanPoseCopy(*poseOut,*poseArray[0]);
+ }
+ else
+ {
+ human::HumanPoseBlend(*poseOut,poseArray,blendFactor,clipCount);
+ }
+ }
+
+ if(poseInterupted)
+ {
+ human::HumanPoseCopy(*poseInterupted,*poseOut);
+ }
+ }
+
+ int EvaluateOneLayer( ControllerBindingConstant const* controllerBindingConstant,
+ AvatarInput const* input,
+ int32_t layerIndex,
+ BlendingState<false> * output,
+ AvatarMemory * memory,
+ AvatarWorkspace * workspace,
+ AvatarOutput * avatarOutput,
+ AnimationSetMemory* animationSetMemory,
+ bool &fastCopy)
+ {
+ AvatarConstant const *constant = controllerBindingConstant->m_Avatar;
+ ControllerConstant const *controllerConstant = controllerBindingConstant->m_Controller;
+
+ bool hasRootMotion = constant->m_RootMotionBoneIndex != -1;
+ bool isHuman = constant->isHuman();
+ bool additive = controllerConstant->m_LayerArray[layerIndex]->m_LayerBlendingMode == kLayerBlendingModeAdditive;
+
+ uint32_t stateMachineIndex = controllerConstant->m_LayerArray[layerIndex]->m_StateMachineIndex;
+
+ statemachine::StateMachineMemory* stateMachineMemory = memory->m_ControllerMemory->m_StateMachineMemory[stateMachineIndex].Get();
+ statemachine::StateMachineOutput& stateMachineOutput = *workspace->m_ControllerWorkspace->m_StateMachineOutput[stateMachineIndex];
+
+ BlendingState<true>& blendingStates = *workspace->m_ControllerWorkspace->m_BlendingState;
+ BlendingState<false>& interruptedBlendingState = memory->m_ControllerMemory->m_InteruptedTransitionsBlendingStateArray[layerIndex];
+
+ uint32_t clipCount = 0;
+
+ SetValueMask(workspace->m_ControllerWorkspace->m_BlendMask,false);
+
+ if(stateMachineMemory->m_InInterruptedTransition)
+ {
+ blendingStates.m_BlendFactor[clipCount] = ( 1.f - stateMachineOutput.m_BlendFactor);
+
+ ValueArrayCopy(interruptedBlendingState.m_DynamicValuesBlending.Get(),blendingStates.m_DynamicValuesBlending[clipCount]);
+ CopyValueMask(workspace->m_ControllerWorkspace->m_ReadMask,interruptedBlendingState.m_DynamicValuesBlendingMask.Get());
+ if(hasRootMotion || isHuman)
+ {
+ human::HumanPoseMask mask = controllerConstant->m_LayerArray[layerIndex]->m_BodyMask;
+ MotionOutputCopy(blendingStates.m_MotionBlending[clipCount], interruptedBlendingState.m_MotionBlending.Get(), hasRootMotion, isHuman, mask);
+ }
+ if(isHuman) human::HumanPoseCopy(*blendingStates.m_HumanPoseBlending[clipCount],*interruptedBlendingState.m_HumanPoseBlending.Get());
+
+ clipCount++;
+ }
+ else
+ {
+ EvaluateBlendNode(controllerBindingConstant,input,layerIndex,additive,true,clipCount, memory->m_ControllerMemory.Get(), workspace, avatarOutput, animationSetMemory, blendingStates.m_BlendFactor);
+ }
+
+ EvaluateBlendNode(controllerBindingConstant,input,layerIndex,additive,false,clipCount, memory->m_ControllerMemory.Get(), workspace, avatarOutput, animationSetMemory, blendingStates.m_BlendFactor);
+
+ bool isInterruptable = stateMachineMemory->m_InTransition;; // Now with dynamic transition, every transition can be interruptable
+
+ fastCopy = BlendDynamicStates( *controllerBindingConstant->m_DynamicValuesDefault,
+ *workspace->m_ControllerWorkspace->m_BlendMask,
+ blendingStates.m_DynamicValuesBlending,
+ blendingStates.m_BlendFactor,
+ clipCount,
+ *output->m_DynamicValuesBlending.Get(),
+ isInterruptable ? interruptedBlendingState.m_DynamicValuesBlending.Get() : 0,
+ isInterruptable ? interruptedBlendingState.m_DynamicValuesBlendingMask.Get() : 0,
+ avatarOutput);
+
+ if(hasRootMotion || isHuman)
+ {
+ human::HumanPoseMask mask = controllerConstant->m_LayerArray[layerIndex]->m_BodyMask;
+
+ BlendMotionStates(blendingStates.m_MotionBlending,blendingStates.m_BlendFactor,clipCount,output->m_MotionBlending.Get(),isInterruptable ? interruptedBlendingState.m_MotionBlending.Get() : 0,hasRootMotion,isHuman,mask);
+ }
+
+ assert(clipCount <= blendingStates.m_Size);
+
+ if(isHuman)
+ {
+ BlendHumanStates(blendingStates.m_HumanPoseBlending,blendingStates.m_BlendFactor,clipCount,output->m_HumanPoseBlending.Get(),isInterruptable ? interruptedBlendingState.m_HumanPoseBlending.Get() : 0);
+
+ if(input->m_DeltaTime != 0)
+ {
+ float deltaTime = input->m_DeltaTime;
+
+ for(int32_t clipIter = 0; clipIter < clipCount; clipIter++)
+ {
+ math::xform leftFootDX = math::xformInvMul(blendingStates.m_MotionBlending[clipIter]->m_PrevLeftFootX,math::xformMul(blendingStates.m_MotionBlending[clipIter]->m_DX,blendingStates.m_HumanPoseBlending[clipIter]->m_GoalArray[human::kLeftFootGoal].m_X));
+ math::xform rightFootDX = math::xformInvMul(blendingStates.m_MotionBlending[clipIter]->m_PrevRightFootX,math::xformMul(blendingStates.m_MotionBlending[clipIter]->m_DX,blendingStates.m_HumanPoseBlending[clipIter]->m_GoalArray[human::kRightFootGoal].m_X));
+
+ workspace->m_LeftFootSpeedT = math::maximum<float>(workspace->m_LeftFootSpeedT,math::length(leftFootDX.t).tofloat()/deltaTime);
+ workspace->m_LeftFootSpeedQ = math::maximum<float>(workspace->m_LeftFootSpeedQ,math::length(math::doubleAtan(math::quat2Qtan(leftFootDX.q))).tofloat()/deltaTime);
+ workspace->m_RightFootSpeedT = math::maximum<float>(workspace->m_RightFootSpeedT,math::length(rightFootDX.t).tofloat()/deltaTime);
+ workspace->m_RightFootSpeedQ = math::maximum<float>(workspace->m_RightFootSpeedQ,math::length(math::doubleAtan(math::quat2Qtan(rightFootDX.q))).tofloat()/deltaTime);
+ }
+ }
+ }
+
+ return clipCount;
+ }
+
+ void AddMotionLayer(MotionOutput const &motionIn, int index, float weight, bool additive, MotionOutput &motionOut, bool hasRootMotion, bool isHuman, human::HumanPoseMask const &poseMask)
+ {
+ if(index == 0)
+ {
+ motionOut = motionIn;
+ }
+ else if(weight > 0)
+ {
+ if(additive)
+ {
+ MotionAddAdditiveLayer(&motionOut,&motionIn,weight,hasRootMotion,isHuman,poseMask);
+ }
+ else
+ {
+ MotionAddOverrideLayer(&motionOut,&motionIn,weight,hasRootMotion,isHuman,poseMask);
+ }
+ }
+ }
+
+ void AddHumanLayer(human::Human const &human, human::HumanPose const &poseIn,human::HumanPoseMask const &poseMask,int index, float weight, bool additive,human::HumanPose &pose,human::HumanPose &poseBase)
+ {
+ if(index == 0)
+ {
+ human::HumanPoseCopy(poseBase,poseIn, poseMask);
+ human::HumanPoseCopy(pose,poseIn, poseMask);
+ }
+ else if(weight > 0)
+ {
+ if(additive)
+ {
+ mecanim::human::HumanPoseMask mask = poseMask;
+ mask.set(mecanim::human::kMaskLeftHand,mask.test(mecanim::human::kMaskLeftHand) && human.m_HasLeftHand);
+ mask.set(mecanim::human::kMaskRightHand,mask.test(mecanim::human::kMaskRightHand) && human.m_HasRightHand);
+
+ human::HumanPoseAddAdditiveLayer(pose, poseIn, weight, mask);
+
+ if(mask.test(human::kMaskRootIndex))
+ {
+ human::HumanPoseAddAdditiveLayer(poseBase,poseIn,weight, mask);
+ }
+ }
+ else
+ {
+ mecanim::human::HumanPoseMask mask = poseMask;
+ mask.set(mecanim::human::kMaskLeftHand,mask.test(mecanim::human::kMaskLeftHand) && human.m_HasLeftHand);
+ mask.set(mecanim::human::kMaskRightHand,mask.test(mecanim::human::kMaskRightHand) && human.m_HasRightHand);
+
+ human::HumanPoseAddOverrideLayer(pose,poseIn,weight,mask);
+
+ if(mask.test(human::kMaskRootIndex))
+ {
+ human::HumanPoseAddOverrideLayer(poseBase,poseIn,weight, mask);
+ }
+ }
+ }
+ }
+
+ void EvaluateAvatarLayers( ControllerBindingConstant const* controllerBindingConstant,
+ AvatarInput const* input,
+ AvatarOutput * output,
+ AvatarMemory * memory,
+ AvatarWorkspace * workspace,
+ AnimationSetMemory* animationSetMemory)
+ {
+ AvatarConstant const *constant = controllerBindingConstant->m_Avatar;
+ ControllerConstant const *controllerConstant = controllerBindingConstant->m_Controller;
+ AnimationSet const *animationSet = controllerBindingConstant->m_AnimationSet;
+
+ bool hasRootMotion = constant->m_RootMotionBoneIndex != -1;
+ bool isHuman = constant->isHuman();
+
+ SetValueMask(workspace->m_ControllerWorkspace->m_DefaultMask,true);
+ workspace->m_ControllerWorkspace->m_BlendingClipCount = 0;
+
+ if(hasRootMotion || isHuman) MotionOutputClear(output->m_MotionOutput);
+ if(isHuman)
+ {
+ workspace->m_LeftFootSpeedT = 0;
+ workspace->m_LeftFootSpeedQ = 0;
+ workspace->m_RightFootSpeedT = 0;
+ workspace->m_RightFootSpeedQ = 0;
+ }
+
+ if(controllerConstant && !memory->m_ControllerMemory->m_StateMachineMemory.IsNull())
+ {
+ for(int layerIter = 0; layerIter < controllerConstant->m_LayerCount; layerIter++)
+ {
+ BlendingState<false> &layerOutput = workspace->m_ControllerWorkspace->m_BlendingStateWs;
+
+ const uint32_t stateMachineIndex = controllerConstant->m_LayerArray[layerIter]->m_StateMachineIndex;
+ if(stateMachineIndex != DISABLED_SYNCED_LAYER_IN_NON_PRO)
+ {
+ const uint32_t motionSetIndex = controllerConstant->m_LayerArray[layerIter]->m_StateMachineMotionSetIndex;
+ float layerWeight = layerIter == 0 ? 1 : memory->m_ControllerMemory->m_LayerWeights[layerIter] * memory->m_ControllerMemory->m_StateMachineMemory[stateMachineIndex]->m_MotionSetAutoWeightArray[motionSetIndex];
+ bool additive = controllerConstant->m_LayerArray[layerIter]->m_LayerBlendingMode == kLayerBlendingModeAdditive;
+
+ bool fastCopy = false;
+ int clipCount = EvaluateOneLayer(controllerBindingConstant, input, layerIter, &layerOutput, memory, workspace, layerIter == 0 ? output : 0, animationSetMemory,fastCopy);
+
+ AndValueMask(workspace->m_ControllerWorkspace->m_BlendMask, animationSet->m_DynamicValuesMaskArray[layerIter]);
+
+ if (fastCopy)
+ {
+ CopyValueMask(workspace->m_ControllerWorkspace->m_DefaultMask,workspace->m_ControllerWorkspace->m_BlendMask);
+ InvertValueMask(workspace->m_ControllerWorkspace->m_DefaultMask);
+ }
+ else
+ {
+ ValueArrayAdd(controllerBindingConstant->m_DynamicValuesDefault, layerOutput.m_DynamicValuesBlending.Get(),workspace->m_ControllerWorkspace->m_BlendMask,layerWeight,additive,output->m_DynamicValuesOutput,workspace->m_ControllerWorkspace->m_DefaultMask);
+ }
+
+ if(hasRootMotion || isHuman)
+ {
+ if(layerIter == 0 && clipCount == 0)
+ {
+ MotionOutputClear(output->m_MotionOutput);
+ }
+ else
+ {
+ bool rootMotionMask = hasRootMotion && controllerBindingConstant->m_RootMotionLayerMask[layerIter];
+ AddMotionLayer(*layerOutput.m_MotionBlending.Get(),layerIter,layerWeight,additive,*output->m_MotionOutput,rootMotionMask,isHuman,controllerConstant->m_LayerArray[layerIter]->m_BodyMask);
+ }
+ }
+
+ if(isHuman)
+ {
+ if(layerIter == 0 && clipCount == 0)
+ {
+ HumanPoseClear(*output->m_HumanPoseOutput);
+ HumanPoseClear(*output->m_HumanPoseBaseOutput);
+ }
+ else
+ {
+ AddHumanLayer(*constant->m_Human.Get(),*layerOutput.m_HumanPoseBlending.Get(),controllerConstant->m_LayerArray[layerIter]->m_BodyMask,layerIter,layerWeight,additive,*output->m_HumanPoseOutput,*output->m_HumanPoseBaseOutput);
+ }
+ }
+ }
+ }
+
+ ValueArrayCopy(controllerBindingConstant->m_DynamicValuesDefault,output->m_DynamicValuesOutput,workspace->m_ControllerWorkspace->m_DefaultMask);
+ ValueArrayCopy(controllerBindingConstant->m_DynamicValuesConstant, output->m_DynamicValuesOutput,controllerConstant->m_Values.Get(),memory->m_ControllerMemory->m_Values.Get(),animationSet->m_AdditionalIndexArray);
+ }
+
+ if(isHuman)
+ {
+ /////////////////////////////////////////////////////////////////
+ // Pivot management
+ if(memory->m_FirstEval==0)
+ {
+ math::float1 pivotWeight(memory->m_PivotWeight);
+ math::float4 prevPivot = math::lerp(output->m_MotionOutput->m_PrevLeftFootX.t,output->m_MotionOutput->m_PrevRightFootX.t,pivotWeight);
+ math::float4 deltaPivot = memory->m_Pivot-prevPivot;
+ deltaPivot.y() = 0;
+ deltaPivot *= input->m_FeetPivotActive;
+ output->m_MotionOutput->m_DX.t = math::xformMulVec(output->m_MotionOutput->m_DX,deltaPivot);
+ }
+
+ memory->m_PivotWeight = math::saturate(0.5f * (0.5f + 2.0f * workspace->m_LeftFootSpeedT) / (0.5f + workspace->m_LeftFootSpeedT + workspace->m_RightFootSpeedT));
+
+ math::xform lLeftFootX = output->m_HumanPoseOutput->m_GoalArray[human::kLeftFootGoal].m_X;
+ math::xform lRightFootX = output->m_HumanPoseOutput->m_GoalArray[human::kRightFootGoal].m_X;
+ memory->m_Pivot = math::lerp(lLeftFootX.t,lRightFootX.t,math::float1(memory->m_PivotWeight));
+ /////////////////////////////////////////////////////////////////
+
+ math::float1 scale(constant->isHuman() ? constant->m_Human->m_Scale : 1);
+ workspace->m_AvatarX = memory->m_AvatarX;
+ math::xform dx = output->m_MotionOutput->m_DX;
+ dx.t *= scale;
+ workspace->m_AvatarX = math::xformMul(workspace->m_AvatarX,dx);
+ }
+ }
+
+ void EvaluateAvatarX( AvatarConstant const* constant,
+ AvatarInput const* input,
+ AvatarOutput * output,
+ AvatarMemory * memory,
+ AvatarWorkspace * workspace)
+ {
+ bool isHuman = constant->isHuman();
+ int rootMotionIndex = constant->m_RootMotionBoneIndex;
+
+ if(isHuman || rootMotionIndex != -1)
+ {
+ math::xform dx = output->m_MotionOutput->m_DX;
+ if(isHuman) dx.t *= math::float1(constant->m_Human->m_Scale);
+ memory->m_AvatarX = math::xformMul(memory->m_AvatarX, dx); // @Sonny: Can memory avatarX.s degenerate here even if dx.s == 1?
+ }
+ }
+
+ void EvaluateAvatarRetarget( AvatarConstant const* constant,
+ AvatarInput const* input,
+ AvatarOutput * output,
+ AvatarMemory * memory,
+ AvatarWorkspace * workspace,
+ ControllerConstant const* controllerConstant)
+ {
+ if(controllerConstant && constant->isHuman())
+ {
+ math::xform avatarX = memory->m_AvatarX;
+
+ human::HumanPose humanPose;
+ human::HumanPose *humanPosePtr = 0;
+
+ if(input->m_LayersAffectMassCenter)
+ {
+ human::HumanPoseCopy(*output->m_HumanPoseBaseOutput,*output->m_HumanPoseOutput);
+ }
+ else if (controllerConstant->m_LayerCount > 1)
+ {
+ human::HumanPoseCopy(humanPose,*output->m_HumanPoseOutput);
+ humanPosePtr = &humanPose;
+ }
+
+ human::RetargetTo( constant->m_Human.Get(),
+ output->m_HumanPoseBaseOutput,
+ humanPosePtr,
+ avatarX,
+ output->m_HumanPoseOutput,
+ workspace->m_BodySkeletonPoseWs,
+ workspace->m_BodySkeletonPoseWsA);
+
+ if(workspace->m_IKOnFeet)
+ {
+ SetIKOnFeet(true,*constant, *input, *memory, *workspace, *output);
+ SetIKOnFeet(false,*constant, *input, *memory, *workspace, *output);
+
+ /* Usefull to debug IK on arms in avatar preview
+ output->m_HumanPose.m_GoalArray[human::kLeftHandGoal].m_WeightT = 1;
+ output->m_HumanPose.m_GoalArray[human::kRightHandGoal].m_WeightT = 1;
+ output->m_HumanPose.m_GoalArray[human::kLeftHandGoal].m_WeightR = 1;
+ output->m_HumanPose.m_GoalArray[human::kRightHandGoal].m_WeightR = 1;
+ */
+ }
+ }
+ }
+
+ void EvaluateAvatarIK( AvatarConstant const* constant,
+ AvatarInput const* input,
+ AvatarOutput * output,
+ AvatarMemory * memory,
+ AvatarWorkspace * workspace,
+ ControllerConstant const* controllerConstant)
+ {
+ if(controllerConstant && constant->isHuman())
+ {
+ ////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+ bool needIK = any(output->m_HumanPoseOutput->m_LookAtWeight > math::float4::zero());
+ for(int i = 0; !needIK && i < mecanim::human::kLastGoal; i++) needIK |= (output->m_HumanPoseOutput->m_GoalArray[i].m_WeightT > 0 || output->m_HumanPoseOutput->m_GoalArray[i].m_WeightR > 0);
+
+ workspace->m_BodySkeletonPoseWs->m_X[0] = output->m_HumanPoseOutput->m_RootX;
+
+ if(needIK)
+ {
+ skeleton::SkeletonPoseComputeGlobal(constant->m_Human->m_Skeleton.Get(), workspace->m_BodySkeletonPoseWs, workspace->m_BodySkeletonPoseWsA);
+ FullBodySolve(constant->m_Human.Get(), output->m_HumanPoseOutput, workspace->m_BodySkeletonPoseWs, workspace->m_BodySkeletonPoseWsA, workspace->m_BodySkeletonPoseWsB);
+ }
+
+ memory->m_LeftFootX = output->m_HumanPoseOutput->m_GoalArray[human::kLeftFootGoal].m_X;
+ memory->m_RightFootX = output->m_HumanPoseOutput->m_GoalArray[human::kRightFootGoal].m_X;
+
+ output->m_HumanPoseOutput->m_LookAtWeight = math::float4::zero();
+ for(int i = 0; i < mecanim::human::kLastGoal; i++)
+ {
+ output->m_HumanPoseOutput->m_GoalArray[i].m_WeightT = 0;
+ output->m_HumanPoseOutput->m_GoalArray[i].m_WeightR = 0;
+ }
+ }
+ }
+
+ void EvaluateAvatarEnd( AvatarConstant const* constant,
+ AvatarInput const* input,
+ AvatarOutput * output,
+ AvatarMemory * memory,
+ AvatarWorkspace * workspace,
+ ControllerConstant const* controllerConstant)
+ {
+ if(constant->isHuman())
+ {
+ int32_t rootIndex = constant->m_HumanSkeletonIndexArray[0];
+
+ skeleton::SkeletonPoseCopy(workspace->m_BodySkeletonPoseWs,workspace->m_BodySkeletonPoseWsA);
+ TwistSolve(constant->m_Human.Get(), workspace->m_BodySkeletonPoseWsA,workspace->m_BodySkeletonPoseWsB);
+
+ skeleton::SkeletonPoseCopy(constant->m_AvatarSkeletonPose.Get(),output->m_SkeletonPoseOutput);
+ output->m_SkeletonPoseOutput->m_X[0] = memory->m_AvatarX;
+ skeleton::SkeletonPoseComputeGlobal(constant->m_AvatarSkeleton.Get(),output->m_SkeletonPoseOutput,output->m_SkeletonPoseOutput,rootIndex,0);
+ skeleton::SkeletonPoseComputeGlobal(constant->m_Human->m_Skeleton.Get(),workspace->m_BodySkeletonPoseWsA,workspace->m_BodySkeletonPoseWsA,1,1);
+ workspace->m_BodySkeletonPoseWsA->m_X[0] = output->m_SkeletonPoseOutput->m_X[rootIndex];
+ skeleton::SkeletonPoseComputeLocal(constant->m_AvatarSkeleton.Get(),output->m_SkeletonPoseOutput,output->m_SkeletonPoseOutput,rootIndex,0);
+ skeleton::SkeletonPoseComputeLocal(constant->m_Human->m_Skeleton.Get(),workspace->m_BodySkeletonPoseWsA,workspace->m_BodySkeletonPoseWsA,1,1);
+ workspace->m_BodySkeletonPoseWsA->m_X[0] = output->m_SkeletonPoseOutput->m_X[rootIndex];
+
+ skeleton::SkeletonPoseCopy( workspace->m_BodySkeletonPoseWsA,
+ output->m_SkeletonPoseOutput,
+ constant->m_HumanSkeletonIndexCount,
+ constant->m_HumanSkeletonIndexArray.Get());
+ }
+ }
+
+ void ValuesFromClip( mecanim::ValueArray const &defaultValues,
+ mecanim::animation::ClipMuscleConstant const &cst,
+ mecanim::animation::ClipOutput const &clip,
+ const ClipBindings& bindings,
+ int32_t integerRemapStride,
+ mecanim::ValueArray &values,
+ mecanim::ValueArrayMask& valueArrayMask)
+ {
+ float* RESTRICT output;
+ bool* RESTRICT mask;
+ const float* RESTRICT inputArray = clip.m_Values;
+
+ // Extract position values from curvedata (float[])
+ output = reinterpret_cast<float*> (values.m_PositionValues.Get());
+ mask = valueArrayMask.m_PositionValues.Get();
+ for (int valueIndex=0;valueIndex<values.m_PositionCount;valueIndex++)
+ {
+ int curveIndex = bindings.m_PositionIndex[valueIndex];
+
+ if (curveIndex == -1)
+ {
+ values.m_PositionValues[valueIndex] = defaultValues.m_PositionValues[valueIndex];
+ mask[valueIndex] = false;
+ }
+ else
+ {
+ float* outputValue = output + valueIndex * 4;
+ outputValue[0] = inputArray[curveIndex+0];
+ outputValue[1] = inputArray[curveIndex+1];
+ outputValue[2] = inputArray[curveIndex+2];
+ outputValue[3] = 0.0F;
+ mask[valueIndex] = true;
+ }
+ }
+
+ // Extract quaternion values from curvedata (float[])
+ output = reinterpret_cast<float*> (values.m_QuaternionValues.Get());
+ mask = valueArrayMask.m_QuaternionValues.Get();
+ for (int valueIndex=0;valueIndex<values.m_QuaternionCount;valueIndex++)
+ {
+ int curveIndex = bindings.m_QuaternionIndex[valueIndex];
+ if (curveIndex == -1)
+ {
+ values.m_QuaternionValues[valueIndex] = defaultValues.m_QuaternionValues[valueIndex];
+ mask[valueIndex] = false;
+ }
+ else
+ {
+ float* outputValue = output + valueIndex * 4;
+ outputValue[0] = inputArray[curveIndex+0];
+ outputValue[1] = inputArray[curveIndex+1];
+ outputValue[2] = inputArray[curveIndex+2];
+ outputValue[3] = inputArray[curveIndex+3];
+ mask[valueIndex] = true;
+ }
+ }
+
+ // Extract scale values from curvedata (float[])
+ output = reinterpret_cast<float*> (values.m_ScaleValues.Get());
+ mask = valueArrayMask.m_ScaleValues.Get();
+ for (int valueIndex=0;valueIndex<values.m_ScaleCount;valueIndex++)
+ {
+ int curveIndex = bindings.m_ScaleIndex[valueIndex];
+
+ if (curveIndex == -1)
+ {
+ values.m_ScaleValues[valueIndex] = defaultValues.m_ScaleValues[valueIndex];
+ mask[valueIndex] = false;
+ }
+ else
+ {
+ float* outputValue = output + valueIndex * 4;
+ outputValue[0] = inputArray[curveIndex+0];
+ outputValue[1] = inputArray[curveIndex+1];
+ outputValue[2] = inputArray[curveIndex+2];
+ outputValue[3] = 1.0F;
+ mask[valueIndex] = true;
+ }
+ }
+
+ // Extract float values from curvedata (float[])
+ output = reinterpret_cast<float*> (values.m_FloatValues.Get());
+ mask = valueArrayMask.m_FloatValues.Get();
+ for (int valueIndex=0;valueIndex<values.m_FloatCount;valueIndex++)
+ {
+ int curveIndex = bindings.m_FloatIndex[valueIndex];
+
+ if (curveIndex == -1)
+ {
+ values.m_FloatValues[valueIndex] = defaultValues.m_FloatValues[valueIndex];
+ mask[valueIndex] = false;
+ }
+ else
+ {
+ output[valueIndex] = inputArray[curveIndex];
+ mask[valueIndex] = true;
+ }
+ }
+
+ // Extract integer values from curvedata (float[])
+ // Used for PPtr animation. The integers actually represent an instanceID.
+ mask = valueArrayMask.m_IntValues.Get();
+ for (int valueIndex=0;valueIndex<values.m_IntCount;valueIndex++)
+ {
+ int curveIndex = bindings.m_IntIndex[valueIndex];
+
+ if (curveIndex == -1)
+ {
+ values.m_IntValues[valueIndex] = defaultValues.m_IntValues[valueIndex];
+ mask[valueIndex] = false;
+ }
+ else
+ {
+ uint32_t index = (uint32_t)inputArray[curveIndex];
+ const uint8_t* integerRemapBuffer = reinterpret_cast<const uint8_t*> (bindings.m_IntegerRemap);
+ int32_t valueInt32 = *reinterpret_cast<const int32_t*> (integerRemapBuffer + (index * integerRemapStride));
+
+ values.m_IntValues[valueIndex] = valueInt32;
+ mask[valueIndex] = true;
+ }
+ }
+ }
+
+ void DeltasFromClip( ClipMuscleConstant const &cst,
+ const ClipBindings& bindings,
+ const ValueArrayMask& mask,
+ mecanim::ValueArray &starts,
+ mecanim::ValueArray &stops)
+ {
+
+ ATTRIBUTE_ALIGN(ALIGN4F) float start[4];
+ ATTRIBUTE_ALIGN(ALIGN4F) float stop[4];
+
+ int curveIter;
+
+ // Positions
+ for (int valueIndex=0;valueIndex<starts.m_PositionCount;valueIndex++)
+ {
+ if (!mask.m_PositionValues[valueIndex])
+ continue;
+
+ curveIter = bindings.m_PositionIndex[valueIndex];
+
+ start[0] = cst.m_ValueArrayDelta[curveIter+0].m_Start;
+ start[1] = cst.m_ValueArrayDelta[curveIter+1].m_Start;
+ start[2] = cst.m_ValueArrayDelta[curveIter+2].m_Start;
+ start[3] = 0;
+
+ stop[0] = cst.m_ValueArrayDelta[curveIter+0].m_Stop;
+ stop[1] = cst.m_ValueArrayDelta[curveIter+1].m_Stop;
+ stop[2] = cst.m_ValueArrayDelta[curveIter+2].m_Stop;
+ stop[3] = 0;
+
+ math::float4 start4 = math::load(start);
+ math::float4 stop4 = math::load(stop);
+
+ starts.WritePosition(start4,valueIndex);
+ stops.WritePosition(stop4,valueIndex);
+ }
+
+ // Quaternions
+ for (int valueIndex=0;valueIndex<starts.m_QuaternionCount;valueIndex++)
+ {
+ if (!mask.m_QuaternionValues[valueIndex])
+ continue;
+
+ curveIter = bindings.m_QuaternionIndex[valueIndex];
+
+ start[0] = cst.m_ValueArrayDelta[curveIter+0].m_Start;
+ start[1] = cst.m_ValueArrayDelta[curveIter+1].m_Start;
+ start[2] = cst.m_ValueArrayDelta[curveIter+2].m_Start;
+ start[3] = cst.m_ValueArrayDelta[curveIter+3].m_Start;
+
+ stop[0] = cst.m_ValueArrayDelta[curveIter+0].m_Stop;
+ stop[1] = cst.m_ValueArrayDelta[curveIter+1].m_Stop;
+ stop[2] = cst.m_ValueArrayDelta[curveIter+2].m_Stop;
+ stop[3] = cst.m_ValueArrayDelta[curveIter+3].m_Stop;
+
+ math::float4 start4 = math::load(start);
+ math::float4 stop4 = math::load(stop);
+
+ starts.WriteQuaternion(start4,valueIndex);
+ stops.WriteQuaternion(stop4,valueIndex);
+ }
+
+ // Scales
+ for (int valueIndex=0;valueIndex<starts.m_ScaleCount;valueIndex++)
+ {
+ if (!mask.m_ScaleValues[valueIndex])
+ continue;
+
+ curveIter = bindings.m_ScaleIndex[valueIndex];
+
+ start[0] = cst.m_ValueArrayDelta[curveIter+0].m_Start;
+ start[1] = cst.m_ValueArrayDelta[curveIter+1].m_Start;
+ start[2] = cst.m_ValueArrayDelta[curveIter+2].m_Start;
+ start[3] = 0;
+
+ stop[0] = cst.m_ValueArrayDelta[curveIter+0].m_Stop;
+ stop[1] = cst.m_ValueArrayDelta[curveIter+1].m_Stop;
+ stop[2] = cst.m_ValueArrayDelta[curveIter+2].m_Stop;
+ stop[3] = 0;
+
+ math::float4 start4 = math::load(start);
+ math::float4 stop4 = math::load(stop);
+
+ starts.WriteScale(start4,valueIndex);
+ stops.WriteScale(stop4,valueIndex);
+ }
+
+ // Generic floats
+ for (int valueIndex=0;valueIndex<starts.m_FloatCount;valueIndex++)
+ {
+ if (!mask.m_FloatValues[valueIndex])
+ continue;
+
+ curveIter = bindings.m_FloatIndex[valueIndex];
+
+ start[0] = cst.m_ValueArrayDelta[curveIter+0].m_Start;
+ stop[0] = cst.m_ValueArrayDelta[curveIter+0].m_Stop;
+
+ starts.WriteData(start[0],valueIndex);
+ stops.WriteData(stop[0],valueIndex);
+ }
+ }
+
+ void SkeletonPoseFromValue(skeleton::Skeleton const &skeleton, skeleton::SkeletonPose const &defaultPose, ValueArray const &values, SkeletonTQSMap const *skeletonTQSMap, skeleton::SkeletonPose &pose, int32_t const*humanReverseIndex, bool skipRoot)
+ {
+ for (int index=skipRoot?1:0;index<skeleton.m_Count;index++)
+ {
+ if(!(humanReverseIndex != 0 && humanReverseIndex[index] != -1))
+ {
+ if (skeletonTQSMap[index].m_TIndex != -1)
+ pose.m_X[index].t = values.ReadPosition(skeletonTQSMap[index].m_TIndex);
+ else
+ pose.m_X[index].t = defaultPose.m_X[index].t;
+
+ if (skeletonTQSMap[index].m_QIndex != -1)
+ pose.m_X[index].q = values.ReadQuaternion(skeletonTQSMap[index].m_QIndex);
+ else
+ pose.m_X[index].q = defaultPose.m_X[index].q;
+
+ if (skeletonTQSMap[index].m_SIndex != -1)
+ pose.m_X[index].s = values.ReadScale(skeletonTQSMap[index].m_SIndex);
+ else
+ pose.m_X[index].s = defaultPose.m_X[index].s;
+ }
+ }
+ }
+
+ void SkeletonPoseFromValue(skeleton::Skeleton const &skeleton, skeleton::SkeletonPose const &defaultPose, ValueArray const &values, SkeletonTQSMap const *skeletonTQSMap, int32_t const *indexArray, skeleton::SkeletonPose &pose,int index, int stopIndex)
+ {
+ if(index != -1)
+ {
+ if(index != stopIndex)
+ {
+ SkeletonPoseFromValue(skeleton,defaultPose, values,skeletonTQSMap,indexArray,pose,skeleton.m_Node[index].m_ParentId,stopIndex);
+
+ int avatarIndex = indexArray[index];
+
+ if(skeletonTQSMap[avatarIndex].m_TIndex != -1)
+ {
+ pose.m_X[index].t = values.ReadPosition(skeletonTQSMap[avatarIndex].m_TIndex);
+ }
+ else
+ {
+ pose.m_X[index].t = defaultPose.m_X[avatarIndex].t;
+ }
+
+ if(skeletonTQSMap[avatarIndex].m_QIndex != -1)
+ {
+ pose.m_X[index].q = values.ReadQuaternion(skeletonTQSMap[avatarIndex].m_QIndex);
+ }
+ else
+ {
+ pose.m_X[index].q = defaultPose.m_X[avatarIndex].q;
+ }
+
+ if(skeletonTQSMap[avatarIndex].m_SIndex != -1)
+ {
+ pose.m_X[index].s = values.ReadScale(skeletonTQSMap[avatarIndex].m_SIndex);
+ }
+ else
+ {
+ pose.m_X[index].s = defaultPose.m_X[avatarIndex].s;
+ }
+ }
+ }
+ }
+
+ void ValueFromSkeletonPose(skeleton::Skeleton const &skeleton, skeleton::SkeletonPose const &pose, SkeletonTQSMap const *skeletonTQSMap, ValueArray &values)
+ {
+ for (int index=0;index<skeleton.m_Count;index++)
+ {
+ if(skeletonTQSMap[index].m_TIndex != -1)
+ {
+ values.WritePosition(pose.m_X[index].t, skeletonTQSMap[index].m_TIndex);
+ }
+
+ if(skeletonTQSMap[index].m_QIndex != -1)
+ {
+ values.WriteQuaternion(pose.m_X[index].q, skeletonTQSMap[index].m_QIndex);
+ }
+
+ if(skeletonTQSMap[index].m_SIndex != -1)
+ {
+ values.WriteScale(pose.m_X[index].s, skeletonTQSMap[index].m_SIndex);
+ }
+ }
+ }
+
+ void ValueFromSkeletonPose(skeleton::Skeleton const &skeleton, skeleton::SkeletonPose const &pose, SkeletonTQSMap const *skeletonTQSMap,int32_t const *indexArray, ValueArray &values, int index, int stopIndex)
+ {
+ if(index != -1)
+ {
+ if(index != stopIndex)
+ {
+ ValueFromSkeletonPose(skeleton,pose,skeletonTQSMap,indexArray, values,skeleton.m_Node[index].m_ParentId,stopIndex);
+
+ int avatarIndex = indexArray[index];
+
+ if(skeletonTQSMap[avatarIndex].m_TIndex != -1)
+ {
+ values.WritePosition(pose.m_X[index].t, skeletonTQSMap[avatarIndex].m_TIndex);
+ }
+
+ if(skeletonTQSMap[avatarIndex].m_QIndex != -1)
+ {
+ values.WriteQuaternion(pose.m_X[index].q, skeletonTQSMap[avatarIndex].m_QIndex);
+ }
+
+ if(skeletonTQSMap[avatarIndex].m_SIndex != -1)
+ {
+ values.WriteScale(pose.m_X[index].s, skeletonTQSMap[avatarIndex].m_SIndex);
+ }
+ }
+ }
+ }
+}
+}
diff --git a/Runtime/mecanim/animation/avatar.h b/Runtime/mecanim/animation/avatar.h
new file mode 100644
index 0000000..e90be7a
--- /dev/null
+++ b/Runtime/mecanim/animation/avatar.h
@@ -0,0 +1,706 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/mecanim/bind.h"
+#include "Runtime/Math/Simd/float4.h"
+
+#include "Runtime/mecanim/skeleton/skeleton.h"
+#include "Runtime/mecanim/human/human.h"
+#include "Runtime/mecanim/animation/clipmuscle.h"
+#include "Runtime/mecanim/animation/curvedata.h"
+#include "Runtime/mecanim/statemachine/statemachine.h"
+
+#include "Runtime/Serialize/Blobification/offsetptr.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Animation/MecanimArraySerialization.h"
+
+typedef UInt32 BindingHash;
+
+namespace mecanim
+{
+
+ static const uint32_t DISABLED_SYNCED_LAYER_IN_NON_PRO = 0xffffffff;
+ struct ValueArrayConstant;
+
+ typedef int ProcessString(mecanim::String const&);
+
+ namespace animation
+ {
+ struct ClipMuscleConstant;
+ struct AvatarConstant;
+ struct ControllerConstant;
+
+ enum LayerBlendingMode
+ {
+ kLayerBlendingModeOverride,
+ kLayerBlendingModeAdditive
+ };
+
+ struct LayerConstant
+ {
+ DEFINE_GET_TYPESTRING(LayerConstant)
+
+ LayerConstant():m_IKPass(true), m_SyncedLayerAffectsTiming(false), m_LayerBlendingMode(kLayerBlendingModeOverride){}
+
+ uint32_t m_StateMachineIndex;
+ uint32_t m_StateMachineMotionSetIndex;
+
+ human::HumanPoseMask m_BodyMask;
+ OffsetPtr<skeleton::SkeletonMask> m_SkeletonMask;
+
+ uint32_t m_Binding;
+ uint32_t m_LayerBlendingMode; //LayerBlendingMode
+ float m_DefaultWeight;
+ bool m_IKPass;
+ bool m_SyncedLayerAffectsTiming;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+
+ TRANSFER(m_StateMachineIndex);
+ TRANSFER(m_StateMachineMotionSetIndex);
+ TRANSFER(m_BodyMask);
+ TRANSFER(m_SkeletonMask);
+ TRANSFER(m_Binding);
+ TRANSFER((int&)m_LayerBlendingMode);
+ TRANSFER(m_DefaultWeight);
+ TRANSFER(m_IKPass);
+ TRANSFER(m_SyncedLayerAffectsTiming);
+ transfer.Align();
+
+
+ }
+ };
+
+ struct ControllerConstant
+ {
+ DEFINE_GET_TYPESTRING(ControllerConstant)
+
+ ControllerConstant(): m_LayerCount(0),
+ m_StateMachineCount(0) {}
+
+ uint32_t m_LayerCount;
+ OffsetPtr< OffsetPtr<LayerConstant> > m_LayerArray;
+
+ uint32_t m_StateMachineCount;
+ OffsetPtr< OffsetPtr<statemachine::StateMachineConstant> > m_StateMachineArray;
+
+ OffsetPtr<ValueArrayConstant> m_Values;
+ OffsetPtr<ValueArray> m_DefaultValues;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_LayerCount);
+ MANUAL_ARRAY_TRANSFER2( OffsetPtr<mecanim::animation::LayerConstant>, m_LayerArray, m_LayerCount);
+
+ TRANSFER_BLOB_ONLY(m_StateMachineCount);
+ MANUAL_ARRAY_TRANSFER2( OffsetPtr<mecanim::statemachine::StateMachineConstant>, m_StateMachineArray, m_StateMachineCount);
+
+ TRANSFER(m_Values);
+ TRANSFER(m_DefaultValues);
+
+ }
+
+ static void InitializeClass();
+ };
+
+ struct SkeletonTQSMap
+ {
+ SkeletonTQSMap() : m_TIndex(-1), m_QIndex(-1), m_SIndex(-1) {};
+
+ int32_t m_TIndex;
+ int32_t m_QIndex;
+ int32_t m_SIndex;
+ };
+
+ struct ClipBindings
+ {
+ ClipBindings () : m_PositionIndex(0), m_QuaternionIndex(0), m_ScaleIndex(0), m_FloatIndex(0), m_IntIndex(0), m_IntegerRemap(0) {}
+
+ // Maps from the curve float array of the clip to the ValueArrayConstant.
+ // This allows us to bring the curve float array into a ValueArray that has the same layout between all clips.
+ int16_t* m_PositionIndex;
+ int16_t* m_QuaternionIndex;
+ int16_t* m_ScaleIndex;
+ int16_t* m_FloatIndex;
+ int16_t* m_IntIndex;
+
+ // Points directly to AnimationClipBindingConstant pptrCurveMapping.
+ int32_t* m_IntegerRemap;
+ };
+
+ struct AnimationSet
+ {
+ struct Clip
+ {
+ Clip() : m_Clip(0), m_TotalUsedOptimizedCurveCount(0), m_ClipIndex(-1) {}
+
+ // The referenced constant clip
+ ClipMuscleConstant* m_Clip;
+ int32_t m_ClipIndex;
+ // The amount of ConstantClip curves that need to be sampled (Constant curves are often ignored during binding if they are determined to have no impact)
+ uint32_t m_TotalUsedOptimizedCurveCount;
+ // Binding indices to index from the curve float[] to the ValueArray
+ ClipBindings m_Bindings;
+ };
+
+ AnimationSet() : m_MaxBlendState(0),
+ m_LayerCount(0),
+ m_ClipPerLayer(0),
+ m_ClipConstant(0),
+ m_AdditionalCount(0),
+ m_AdditionalIndexArray(0),
+ m_DynamicFullValuesConstant(0),
+ m_DynamicValuesMaskArray(0),
+ m_GravityWeightIndex(-1),
+ m_IntegerRemapStride(-1)
+ {}
+
+ uint32_t m_MaxBlendState;
+ uint32_t m_LayerCount;
+ uint32_t* m_ClipPerLayer;
+
+ Clip** m_ClipConstant;
+
+ uint32_t m_AdditionalCount;
+ int32_t* m_AdditionalIndexArray;
+
+ mecanim::ValueArrayConstant* m_DynamicFullValuesConstant;
+ mecanim::ValueArrayMask** m_DynamicValuesMaskArray;
+
+ int32_t m_GravityWeightIndex;
+ int32_t m_IntegerRemapStride;
+ };
+
+ struct ControllerBindingConstant
+ {
+ ControllerBindingConstant(): m_DynamicValuesDefault(0),
+ m_SkeletonTQSMap(0),
+ m_RootMotionLayerMask(0),
+ m_Avatar(0),
+ m_Controller(0),
+ m_DynamicValuesConstant(0),
+ m_AnimationSet(0) {}
+
+ ValueArrayConstant* m_DynamicValuesConstant;
+ ValueArray* m_DynamicValuesDefault;
+
+ SkeletonTQSMap* m_SkeletonTQSMap;
+
+ bool* m_RootMotionLayerMask;
+
+ AvatarConstant const* m_Avatar;
+ ControllerConstant const* m_Controller;
+ AnimationSet const* m_AnimationSet;
+ };
+
+ struct AnimationSetMemory
+ {
+ AnimationSetMemory() : m_LayerCount(0), m_ClipPerLayer(0), m_ClipMemory(0), m_ClipOutput(0) {}
+
+ uint32_t m_LayerCount;
+ uint32_t* m_ClipPerLayer;
+ ClipMemory*** m_ClipMemory;
+ ClipOutput* m_ClipOutput;
+ };
+
+ template<bool dynamic>
+ struct BlendingState
+ {
+ BlendingState():
+ m_DynamicValuesBlending(0),
+ m_MotionBlending(0),
+ m_HumanPoseBlending(0),
+ m_BlendFactor(0)
+ {}
+
+ ValueArray** m_DynamicValuesBlending;
+ MotionOutput** m_MotionBlending;
+ human::HumanPose** m_HumanPoseBlending;
+ float* m_BlendFactor;
+
+ uint32_t m_Size;
+ };
+
+ template<>
+ struct BlendingState<false>
+ {
+ BlendingState(): m_BlendFactor(0) {}
+
+ OffsetPtr<ValueArray> m_DynamicValuesBlending;
+ OffsetPtr<ValueArrayMask> m_DynamicValuesBlendingMask;
+ OffsetPtr<MotionOutput> m_MotionBlending;
+ OffsetPtr<human::HumanPose> m_HumanPoseBlending;
+ float m_BlendFactor;
+ };
+
+ struct BlendingClip
+ {
+ BlendingClip() : m_ClipIndex(-1), m_LayerIndex(-1), m_Weight(0), m_PrevTime(0), m_Time(0), m_Reverse(false) {}
+
+ int m_ClipIndex;
+ int m_LayerIndex;
+ float m_Weight;
+ float m_PrevTime;
+ float m_Time;
+ bool m_Reverse;
+ };
+
+ struct ControllerMemory
+ {
+ DEFINE_GET_TYPESTRING(ControllerMemory)
+
+ ControllerMemory(): m_StateMachineCount(0),
+ m_LayerCount(0) {}
+
+ uint32_t m_StateMachineCount;
+ OffsetPtr< OffsetPtr<statemachine::StateMachineMemory> > m_StateMachineMemory;
+
+ uint32_t m_LayerCount;
+ OffsetPtr<BlendingState<false> > m_InteruptedTransitionsBlendingStateArray;
+ OffsetPtr<float> m_LayerWeights;
+
+ OffsetPtr<ValueArray> m_Values;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_StateMachineCount);
+ MANUAL_ARRAY_TRANSFER2( OffsetPtr<mecanim::statemachine::StateMachineMemory>, m_StateMachineMemory, m_StateMachineCount);
+
+ TRANSFER_BLOB_ONLY(m_LayerCount);
+ MANUAL_ARRAY_TRANSFER2( BlendingState<false>, m_InteruptedTransitionsBlendingStateArray, m_LayerCount);
+ MANUAL_ARRAY_TRANSFER2( float, m_LayerWeights, m_LayerCount);
+
+ TRANSFER(m_Values);
+ }
+ };
+
+ struct ControllerWorkspace
+ {
+ ControllerWorkspace() : m_StateMachineWorkspace(0),
+ m_StateMachineOutput(0),
+ m_BlendingState(0),
+ m_BlendingClipCount(0),
+ m_BlendingClipArray(0),
+ m_ValueArrayStart(0),
+ m_ValueArrayStop(0),
+ m_ReadMask(0),
+ m_BlendMask(0),
+ m_DefaultMask(0),
+ m_DoIK(false),
+ m_DoWrite(false) {}
+
+ statemachine::StateMachineWorkspace** m_StateMachineWorkspace;
+ statemachine::StateMachineOutput** m_StateMachineOutput;
+
+ uint32_t m_StateMachineCount;
+
+ float* m_MotionSetTimingWeightArray;
+
+ BlendingState<true>* m_BlendingState;
+ BlendingState<false> m_BlendingStateWs;
+
+ int m_BlendingClipCount;
+ BlendingClip* m_BlendingClipArray;
+
+ ValueArray *m_ValueArrayStart;
+ ValueArray *m_ValueArrayStop;
+
+ ValueArrayMask *m_ReadMask;
+ ValueArrayMask *m_BlendMask;
+ ValueArrayMask *m_DefaultMask;
+
+ bool m_DoIK;
+ bool m_DoWrite;
+ };
+
+ struct ExposedTransform
+ {
+ DEFINE_GET_TYPESTRING(ExposedTransform);
+
+ // For SkinnedMeshRenderer, the following two indices are different
+ // - 'skeletonIndex'
+ // corresponds to the SkinnedMeshRenderer itself
+ // - 'skeletonIndexForUpdateTransform'
+ // corresponds to the root bone of the SkinnedMeshRenderer
+ uint32_t skeletonIndex;
+ uint32_t skeletonIndexForUpdateTransform;
+
+ BindingHash transformPath; // flattened path
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(skeletonIndex);
+ TRANSFER(skeletonIndexForUpdateTransform);
+ TRANSFER(transformPath);
+ }
+ };
+
+ struct AvatarConstant
+ {
+ DEFINE_GET_TYPESTRING(AvatarConstant)
+
+ AvatarConstant() : m_SkeletonNameIDCount(0),
+ m_HumanSkeletonIndexCount(0),
+ m_HumanSkeletonReverseIndexCount(0),
+ m_RootMotionBoneIndex(-1),
+ m_RootMotionBoneX(math::xformIdentity()),
+ m_RootMotionSkeletonIndexCount(0) {}
+
+ OffsetPtr<skeleton::Skeleton> m_AvatarSkeleton;
+ OffsetPtr<skeleton::SkeletonPose> m_AvatarSkeletonPose;
+
+ OffsetPtr<skeleton::SkeletonPose> m_DefaultPose; // The default pose when model is imported.
+
+ uint32_t m_SkeletonNameIDCount;
+ OffsetPtr<uint32_t> m_SkeletonNameIDArray; // CRC(name)
+
+ OffsetPtr<human::Human> m_Human;
+
+ uint32_t m_HumanSkeletonIndexCount;
+ OffsetPtr<int32_t> m_HumanSkeletonIndexArray;
+
+ // needed to update human pose and additonal bones in optimize mode
+ // decided to put the info in constant for perf and memory reason vs doing masking at runtime
+ uint32_t m_HumanSkeletonReverseIndexCount;
+ OffsetPtr<int32_t> m_HumanSkeletonReverseIndexArray;
+
+ int32_t m_RootMotionBoneIndex;
+ math::xform m_RootMotionBoneX;
+ OffsetPtr<skeleton::Skeleton> m_RootMotionSkeleton;
+ OffsetPtr<skeleton::SkeletonPose> m_RootMotionSkeletonPose;
+ uint32_t m_RootMotionSkeletonIndexCount;
+ OffsetPtr<int32_t> m_RootMotionSkeletonIndexArray;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ transfer.SetVersion(3);
+
+ TRANSFER(m_AvatarSkeleton);
+
+ TRANSFER(m_AvatarSkeletonPose);
+ TRANSFER(m_DefaultPose);
+
+ TRANSFER_BLOB_ONLY(m_SkeletonNameIDCount);
+ MANUAL_ARRAY_TRANSFER2(uint32_t,m_SkeletonNameIDArray,m_SkeletonNameIDCount);
+
+ TRANSFER(m_Human);
+
+ TRANSFER_BLOB_ONLY(m_HumanSkeletonIndexCount);
+ MANUAL_ARRAY_TRANSFER2(int32_t,m_HumanSkeletonIndexArray,m_HumanSkeletonIndexCount);
+
+ TRANSFER_BLOB_ONLY(m_HumanSkeletonReverseIndexCount);
+ MANUAL_ARRAY_TRANSFER2(int32_t,m_HumanSkeletonReverseIndexArray,m_HumanSkeletonReverseIndexCount);
+
+ TRANSFER(m_RootMotionBoneIndex);
+ TRANSFER(m_RootMotionBoneX);
+ TRANSFER(m_RootMotionSkeleton);
+ TRANSFER(m_RootMotionSkeletonPose);
+
+ TRANSFER_BLOB_ONLY(m_RootMotionSkeletonIndexCount);
+ MANUAL_ARRAY_TRANSFER2(int32_t,m_RootMotionSkeletonIndexArray,m_RootMotionSkeletonIndexCount);
+
+ transfer.Align();
+
+ if (transfer.IsVersionSmallerOrEqual (1))
+ {
+ if(m_RootMotionBoneIndex != -1)
+ {
+ mecanim::memory::Allocator *alloc = reinterpret_cast<mecanim::memory::Allocator *>(transfer.GetUserData());
+
+ m_RootMotionSkeleton = skeleton::CreateSkeleton(m_AvatarSkeleton->m_Count,m_AvatarSkeleton->m_AxesCount,*alloc);
+ skeleton::SkeletonCopy(m_AvatarSkeleton.Get(),m_RootMotionSkeleton.Get());
+
+ m_RootMotionSkeletonPose = skeleton::CreateSkeletonPose(m_RootMotionSkeleton.Get(),*alloc);
+ skeleton::SkeletonPoseCopy(m_AvatarSkeletonPose.Get(),m_RootMotionSkeletonPose.Get());
+
+ m_RootMotionSkeletonIndexCount = m_AvatarSkeleton->m_Count;
+ m_RootMotionSkeletonIndexArray = alloc->ConstructArray<mecanim::int32_t>(m_RootMotionSkeletonIndexCount);
+
+ for(int i = 0; i < m_RootMotionSkeletonIndexCount; i++)
+ {
+ m_RootMotionSkeletonIndexArray[i] = i;
+ }
+ }
+ }
+
+ if (transfer.IsVersionSmallerOrEqual (2))
+ {
+ if(isHuman())
+ {
+ mecanim::memory::Allocator *alloc = reinterpret_cast<mecanim::memory::Allocator *>(transfer.GetUserData());
+
+ m_HumanSkeletonReverseIndexCount = m_AvatarSkeleton->m_Count;
+ m_HumanSkeletonReverseIndexArray = alloc->ConstructArray<mecanim::int32_t>(m_HumanSkeletonReverseIndexCount);
+ skeleton::SkeletonBuildReverseIndexArray(m_HumanSkeletonReverseIndexArray.Get(),m_HumanSkeletonIndexArray.Get(),m_Human->m_Skeleton.Get(),m_AvatarSkeleton.Get());
+ }
+ }
+ }
+
+ bool isHuman() const { return !m_Human.IsNull() && m_Human->GetTypeString() != 0 && m_Human->m_Skeleton->m_Count > 0; };
+
+ static void InitializeClass();
+ };
+
+ struct AvatarInput
+ {
+ AvatarInput() : m_GotoStateInfos(0), m_DeltaTime(0), m_TargetIndex(-1), m_TargetTime(1), m_FeetPivotActive(1), m_StabilizeFeet(false), m_ForceStateTime(false), m_StateTime(0), m_LayersAffectMassCenter(false) {}
+
+ statemachine::GotoStateInfo* m_GotoStateInfos;
+ float m_DeltaTime;
+ int m_TargetIndex;
+ float m_TargetTime;
+
+ float m_FeetPivotActive;
+ bool m_StabilizeFeet;
+
+ bool m_ForceStateTime;
+ float m_StateTime;
+ bool m_LayersAffectMassCenter;
+ };
+
+ struct AvatarMemory
+ {
+ DEFINE_GET_TYPESTRING(AvatarMemory)
+
+ AvatarMemory() : m_AvatarX(math::xformIdentity()),
+ m_LeftFootX(math::xformIdentity()),
+ m_RightFootX(math::xformIdentity()),
+ m_Pivot(math::float4::zero()),
+ m_PivotWeight(0.5),
+ m_FirstEval(1),
+ m_SkeletonPoseOutputReady(0) {}
+
+ OffsetPtr<ControllerMemory> m_ControllerMemory;
+
+ math::xform m_AvatarX;
+
+ math::xform m_LeftFootX;
+ math::xform m_RightFootX;
+ math::float4 m_Pivot;
+
+ float m_PivotWeight;
+ UInt8 m_FirstEval;
+ UInt8 m_SkeletonPoseOutputReady;
+
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_ControllerMemory);
+ TRANSFER(m_AvatarX);
+ TRANSFER(m_LeftFootX);
+ TRANSFER(m_RightFootX);
+ TRANSFER(m_Pivot);
+ TRANSFER(m_PivotWeight);
+ TRANSFER(m_FirstEval);
+ TRANSFER(m_SkeletonPoseOutputReady);
+ transfer.Align();
+ }
+ };
+
+ struct AvatarWorkspace
+ {
+ AvatarWorkspace() : m_BodySkeletonPoseWs(0),
+ m_BodySkeletonPoseWsA(0),
+ m_BodySkeletonPoseWsB(0),
+ m_RootMotionSkeletonPoseWsA(0),
+ m_RootMotionSkeletonPoseWsB(0),
+ m_HumanPoseWs(0),
+ m_ControllerWorkspace(0),
+ m_LeftFootSpeedT(0),
+ m_LeftFootSpeedQ(0),
+ m_RightFootSpeedT(0),
+ m_RightFootSpeedQ(0),
+ m_IKOnFeet(false)
+ {}
+
+ skeleton::SkeletonPose* m_BodySkeletonPoseWs;
+ skeleton::SkeletonPose* m_BodySkeletonPoseWsA;
+ skeleton::SkeletonPose* m_BodySkeletonPoseWsB;
+
+ skeleton::SkeletonPose* m_RootMotionSkeletonPoseWsA;
+ skeleton::SkeletonPose* m_RootMotionSkeletonPoseWsB;
+
+ human::HumanPose* m_HumanPoseWs;
+
+ ControllerWorkspace* m_ControllerWorkspace;
+
+ math::xform m_AvatarX;
+
+ float m_LeftFootSpeedT;
+ float m_LeftFootSpeedQ;
+ float m_RightFootSpeedT;
+ float m_RightFootSpeedQ;
+ bool m_IKOnFeet;
+ };
+
+ struct AvatarOutput
+ {
+ AvatarOutput() : m_DynamicValuesOutput(0),
+ m_SkeletonPoseOutput(0),
+ m_MotionOutput(0),
+ m_HumanPoseBaseOutput(0),
+ m_HumanPoseOutput(0) {}
+
+ ValueArray* m_DynamicValuesOutput;
+
+ skeleton::SkeletonPose* m_SkeletonPoseOutput;
+
+ MotionOutput* m_MotionOutput;
+ human::HumanPose* m_HumanPoseBaseOutput;
+ human::HumanPose* m_HumanPoseOutput;
+ };
+
+ AvatarConstant* CreateAvatarConstant( skeleton::Skeleton* skeleton,
+ skeleton::SkeletonPose* skeletonPose,
+ skeleton::SkeletonPose* defaultPose,
+ human::Human* human,
+ skeleton::Skeleton* rootMotionSkeleton,
+ int rootMotionIndex,
+ math::xform const& rootMotionX,
+ memory::Allocator& alloc);
+
+ void DestroyAvatarConstant(AvatarConstant* constant, memory::Allocator& alloc);
+
+ void InitializeAvatarConstant(AvatarConstant * constant, memory::Allocator& alloc);
+ void ClearAvatarConstant(AvatarConstant * constant, memory::Allocator& alloc);
+
+ ControllerConstant* CreateControllerConstant( uint32_t LayerCount, LayerConstant** layerArray,
+ uint32_t stateMachineCount, statemachine::StateMachineConstant** stateMachineConstant,
+ ValueArrayConstant* values, ValueArray* defaultValues,
+ memory::Allocator& alloc);
+
+ void DestroyControllerConstant(ControllerConstant* controller, memory::Allocator& alloc);
+
+ void InitializeControllerConstant(ControllerConstant * controller, memory::Allocator& alloc);
+ void ClearControllerConstant(ControllerConstant * controller, memory::Allocator& alloc);
+
+ LayerConstant* CreateLayerConstant(mecanim::uint32_t stateMachineIndex, mecanim::uint32_t motionSetIndex, memory::Allocator& alloc);
+ void DestroyLayerConstant(LayerConstant* constant, memory::Allocator& alloc);
+
+ ControllerMemory* CreateControllerMemory(ControllerConstant const* controller, AvatarConstant const *avatar, AnimationSet const *animationSet, const ValueArrayConstant* dynamicValueConstant, memory::Allocator& alloc);
+ void DestroyControllerMemory(ControllerMemory* memory, memory::Allocator& alloc);
+
+ ControllerWorkspace* CreateControllerWorkspace(ControllerConstant const* controller, AvatarConstant const *avatar, AnimationSet const *animationSet, const ValueArrayConstant* dynamicValueConstant, memory::Allocator& alloc);
+ void DestroyControllerWorkspace(ControllerWorkspace* workspace, memory::Allocator& alloc);
+
+ AvatarInput* CreateAvatarInput(AvatarConstant const* constant, memory::Allocator& alloc);
+ void DestroyAvatarInput(AvatarInput* input, memory::Allocator& alloc);
+
+ AvatarMemory* CreateAvatarMemory(AvatarConstant const* constant, memory::Allocator& alloc);
+ void DestroyAvatarMemory(AvatarMemory* memory, memory::Allocator& alloc);
+
+ AvatarWorkspace* CreateAvatarWorkspace(AvatarConstant const* constant, memory::Allocator& alloc);
+ void DestroyAvatarWorkspace(AvatarWorkspace* workspace, memory::Allocator& alloc);
+
+ AvatarOutput* CreateAvatarOutput(AvatarConstant const* constant, bool hasTransformHierarchy, memory::Allocator& alloc);
+ void DestroyAvatarOutput(AvatarOutput* output, memory::Allocator& alloc);
+
+ AnimationSet* CreateAnimationSet(ControllerConstant const* controller, memory::Allocator& alloc);
+ void DestroyAnimationSet(AnimationSet* animationSet, memory::Allocator& alloc);
+
+ AnimationSetMemory* CreateAnimationSetMemory(AnimationSet const* animationSet, bool allowConstantCurveOptimization, memory::Allocator& alloc);
+ void DestroyAnimationSetMemory(AnimationSetMemory* animationSetMemory, memory::Allocator& alloc);
+
+ void UpdateLeafNodeDuration(const ControllerConstant &controllerConstant, const AnimationSet &animationSet, ControllerMemory &controllerMemory);
+
+ void SetIKOnFeet( bool left,
+ AvatarConstant const &avatar,
+ const AvatarInput &input,
+ AvatarMemory &memory,
+ AvatarWorkspace &workspace,
+ AvatarOutput &output);
+
+ void EvaluateAvatarSM( AvatarConstant const* constant,
+ AvatarInput const* input,
+ AvatarOutput * output,
+ AvatarMemory * memory,
+ AvatarWorkspace * workspace,
+ ControllerConstant const* controllerConstant);
+
+ void EvaluateAvatarLayers( ControllerBindingConstant const* controllerBindingConstant,
+ AvatarInput const* input,
+ AvatarOutput *output,
+ AvatarMemory *memory,
+ AvatarWorkspace *workspace,
+ AnimationSetMemory* animationSetMemory);
+
+ void EvaluateAvatarX( AvatarConstant const* constant,
+ AvatarInput const* input,
+ AvatarOutput *output,
+ AvatarMemory *memory,
+ AvatarWorkspace *workspace);
+
+ void EvaluateAvatarRetarget( AvatarConstant const* constant,
+ AvatarInput const* input,
+ AvatarOutput *output,
+ AvatarMemory *memory,
+ AvatarWorkspace *workspace,
+ ControllerConstant const* controllerConstant);
+
+ void EvaluateAvatarIK( AvatarConstant const* constant,
+ AvatarInput const* input,
+ AvatarOutput *output,
+ AvatarMemory *memory,
+ AvatarWorkspace *workspace,
+ ControllerConstant const* controllerConstant);
+
+ void EvaluateAvatarEnd( AvatarConstant const* constant,
+ AvatarInput const* input,
+ AvatarOutput *output,
+ AvatarMemory *memory,
+ AvatarWorkspace *workspace,
+ ControllerConstant const* controllerConstant);
+
+ void ValuesFromClip( mecanim::ValueArray const &valuesDefault,
+ mecanim::animation::ClipMuscleConstant const &cst,
+ mecanim::animation::ClipOutput const &out,
+ const ClipBindings& bindings,
+ int32_t integerRemapStride,
+ mecanim::ValueArray &values,
+ mecanim::ValueArrayMask &mask);
+
+ void DeltasFromClip( mecanim::animation::ClipMuscleConstant const &cst,
+ const ClipBindings& bindings,
+ const ValueArrayMask& mask,
+ mecanim::ValueArray &starts,
+ mecanim::ValueArray &stops);
+
+ void SkeletonPoseFromValue(skeleton::Skeleton const &skeleton, skeleton::SkeletonPose const &defaultPose, ValueArray const &values, SkeletonTQSMap const *skeletonTQSMap, skeleton::SkeletonPose &pose,int32_t const *humanReverseIndex,bool skipRoot);
+ void SkeletonPoseFromValue(skeleton::Skeleton const &skeleton, skeleton::SkeletonPose const &defaultPose, ValueArray const &values, SkeletonTQSMap const *skeletonTQSMap, int32_t const *indexArray, skeleton::SkeletonPose &pose,int index, int stopIndex);
+ void ValueFromSkeletonPose(skeleton::Skeleton const &skeleton, skeleton::SkeletonPose const &pose, SkeletonTQSMap const *skeletonTQSMap, ValueArray &values);
+ void ValueFromSkeletonPose(skeleton::Skeleton const &skeleton, skeleton::SkeletonPose const &pose, SkeletonTQSMap const *skeletonTQSMap, int32_t const *indexArray, ValueArray &values, int index, int stopIndex);
+ }
+}
+
+template<>
+class SerializeTraits< mecanim::animation::BlendingState<false> > : public SerializeTraitsBase< mecanim::animation::BlendingState<false> >
+{
+ public:
+
+ typedef mecanim::animation::BlendingState<false> value_type;
+ inline static const char* GetTypeString (void*) { return "BlendingState<1>"; }
+ inline static bool IsAnimationChannel () { return false; }
+ inline static bool MightContainPPtr () { return true; }
+ inline static bool AllowTransferOptimization () { return false; }
+
+ template<class TransferFunction> inline
+ static void Transfer (value_type& data, TransferFunction& transfer)
+ {
+ transfer.Transfer(data.m_DynamicValuesBlending, "m_DynamicValuesBlending");
+ transfer.Transfer(data.m_DynamicValuesBlendingMask, "m_DynamicValuesBlendingMask");
+ transfer.Transfer(data.m_MotionBlending, "m_MotionBlending");
+ transfer.Transfer(data.m_HumanPoseBlending, "m_HumanPoseBlending");
+ transfer.Transfer(data.m_BlendFactor, "m_BlendFactor");
+ }
+};
+
+
diff --git a/Runtime/mecanim/animation/blendtree.cpp b/Runtime/mecanim/animation/blendtree.cpp
new file mode 100644
index 0000000..e326b77
--- /dev/null
+++ b/Runtime/mecanim/animation/blendtree.cpp
@@ -0,0 +1,967 @@
+#include "UnityPrefix.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/mecanim/animation/blendtree.h"
+#include "Runtime/mecanim/generic/stringtable.h"
+#include "Runtime/Utilities/dynamic_array.h"
+
+namespace mecanim
+{
+
+namespace animation
+{
+ void GetWeightsFreeformDirectional (const Blend2dDataConstant& blendConstant,
+ float* weightArray, int* cropArray, Vector2f* workspaceBlendVectors,
+ float blendValueX, float blendValueY, bool preCompute);
+ void GetWeightsFreeformCartesian (const Blend2dDataConstant& blendConstant,
+ float* weightArray, int* cropArray, Vector2f* workspaceBlendVectors,
+ float blendValueX, float blendValueY, bool preCompute);
+
+ void PrecomputeFreeform (int type, Blend2dDataConstant& out, memory::Allocator& alloc)
+ {
+ const Vector2f* positionArray = out.m_ChildPositionArray.Get();
+ mecanim::uint32_t count = out.m_ChildCount;
+ float* constantMagnitudes = out.m_ChildMagnitudeArray.Get();
+ Vector2f* constantChildPairVectors = out.m_ChildPairVectorArray.Get();
+ float* constantChildPairAvgMagInv = out.m_ChildPairAvgMagInvArray.Get();
+ MotionNeighborList* constantChildNeighborLists = out.m_ChildNeighborListArray.Get();
+
+ if (type == 2)
+ {
+ for (int i=0; i<count; i++)
+ constantMagnitudes[i] = Magnitude (positionArray[i]);
+
+ for (int i=0; i<count; i++)
+ {
+ for (int j=0; j<count; j++)
+ {
+ int pairIndex = i + j*count;
+
+ // Calc avg magnitude for pair
+ float magSum = constantMagnitudes[j] + constantMagnitudes[i];
+ if (magSum > 0)
+ constantChildPairAvgMagInv[pairIndex] = 2.0f / magSum;
+ else
+ constantChildPairAvgMagInv[pairIndex] = 2.0f / magSum;
+
+ // Calc mag of vector and divide by avg magnitude
+ float mag = (constantMagnitudes[j] - constantMagnitudes[i]) * constantChildPairAvgMagInv[pairIndex];
+
+ if (constantMagnitudes[j] == 0 || constantMagnitudes[i] == 0)
+ constantChildPairVectors[pairIndex] = Vector2f (0, mag);
+ else
+ {
+ float angle = Angle (positionArray[i], positionArray[j]);
+ if (positionArray[i].x * positionArray[j].y - positionArray[i].y * positionArray[j].x < 0)
+ angle = -angle;
+ constantChildPairVectors[pairIndex] = Vector2f (angle, mag);
+ }
+ }
+ }
+ }
+ else if (type == 3)
+ {
+ for (int i=0; i<count; i++)
+ {
+ for (int j=0; j<count; j++)
+ {
+ int pairIndex = i + j*count;
+ constantChildPairAvgMagInv[pairIndex] = 1 / SqrMagnitude (positionArray[j] - positionArray[i]);
+ constantChildPairVectors[pairIndex] = positionArray[j] - positionArray[i];
+ }
+ }
+ }
+
+ float* weightArray;
+ ALLOC_TEMP (weightArray, float, count);
+
+ int* cropArray;
+ ALLOC_TEMP (cropArray, int, count);
+
+ Vector2f* workspaceBlendVectors;
+ ALLOC_TEMP (workspaceBlendVectors, Vector2f, count);
+
+ bool* neighborArray;
+ ALLOC_TEMP (neighborArray, bool, count*count);
+ for (int c=0; c<count*count; c++)
+ neighborArray[c] = false;
+
+ float minX = 10000.0f;
+ float maxX = -10000.0f;
+ float minY = 10000.0f;
+ float maxY = -10000.0f;
+ for (int c=0; c<count; c++)
+ {
+ minX = min (minX, positionArray[c].x);
+ maxX = max (maxX, positionArray[c].x);
+ minY = min (minY, positionArray[c].y);
+ maxY = max (maxY, positionArray[c].y);
+ }
+ float xRange = (maxX - minX) * 0.5f;
+ float yRange = (maxY - minY) * 0.5f;
+ minX -= xRange;
+ maxX += xRange;
+ minY -= yRange;
+ maxY += yRange;
+
+ for (int i=0; i<=100; i++)
+ {
+ for (int j=0; j<=100; j++)
+ {
+ float x = i*0.01f;
+ float y = j*0.01f;
+ if (type == 2)
+ GetWeightsFreeformDirectional (out, weightArray, cropArray, workspaceBlendVectors, minX * (1-x) + maxX * x, minY * (1-y) + maxY * y, true);
+ else if (type == 3)
+ GetWeightsFreeformCartesian (out, weightArray, cropArray, workspaceBlendVectors, minX * (1-x) + maxX * x, minY * (1-y) + maxY * y, true);
+ for (int c=0; c<count; c++)
+ if (cropArray[c] >= 0)
+ neighborArray[c * count + cropArray[c]] = true;
+ }
+ }
+ for (int c=0; c<count; c++)
+ {
+ dynamic_array<int> nList;
+ for (int d=0; d<count; d++)
+ if (neighborArray[c * count + d])
+ nList.push_back (d);
+
+ constantChildNeighborLists[c].m_Count = nList.size ();
+ constantChildNeighborLists[c].m_NeighborArray = alloc.ConstructArray<mecanim::uint32_t>(nList.size ());
+
+ for (int d=0; d<nList.size (); d++)
+ constantChildNeighborLists[c].m_NeighborArray[d] = nList[d];
+ }
+ }
+
+ void GetAllBlendValue(uint32_t nodeIndex, OffsetPtr<BlendTreeNodeConstant>* const allTreeNodes, dynamic_array<int> &arBlendValueIds)
+ {
+ BlendTreeNodeConstant* const currentNode = allTreeNodes[nodeIndex].Get();
+
+ if(currentNode->m_BlendEventID != -1)
+ {
+ dynamic_array<int>::const_iterator it = std::find(arBlendValueIds.begin(), arBlendValueIds.end(), currentNode->m_BlendEventID);
+ if(it == arBlendValueIds.end())
+ arBlendValueIds.push_back(currentNode->m_BlendEventID);
+
+ if(currentNode->m_BlendType >= 1)
+ {
+ dynamic_array<int>::const_iterator itY = std::find(arBlendValueIds.begin(), arBlendValueIds.end(), currentNode->m_BlendEventYID);
+ if(itY == arBlendValueIds.end())
+ arBlendValueIds.push_back(currentNode->m_BlendEventYID);
+ }
+
+ for(mecanim::uint32_t i = 0 ; i < currentNode->m_ChildCount; i++)
+ {
+ GetAllBlendValue(currentNode->m_ChildIndices[i], allTreeNodes, arBlendValueIds);
+ }
+ }
+
+ }
+ void GetAllBlendValue(BlendTreeConstant* const constant, dynamic_array<int> &arBlendValueIds)
+ {
+ if(constant->m_NodeCount > 0)
+ GetAllBlendValue(0, constant->m_NodeArray.Get(), arBlendValueIds);
+ }
+
+
+ BlendTreeNodeConstant* CreateBlendTreeNodeConstant(uint32_t blendValueID, uint32_t childCount, uint32_t* childIndices, float* blendTreeThresholdArray, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeNodeConstant);
+
+ BlendTreeNodeConstant *blendTreeNodeConstant = alloc.Construct<BlendTreeNodeConstant>();
+
+ blendTreeNodeConstant->m_BlendEventID = blendValueID;
+ blendTreeNodeConstant->m_ChildCount = childCount;
+
+ blendTreeNodeConstant->m_ChildIndices = alloc.ConstructArray<uint32_t>(childCount);
+ memcpy(&blendTreeNodeConstant->m_ChildIndices[0], &childIndices[0], sizeof(uint32_t)*childCount);
+
+ // Setup blend 1d data constant
+ blendTreeNodeConstant->m_BlendType = 0;
+ blendTreeNodeConstant->m_Blend1dData = alloc.Construct<Blend1dDataConstant>();
+ Blend1dDataConstant& data = *blendTreeNodeConstant->m_Blend1dData;
+
+ // Populate blend 1d data constant
+ data.m_ChildCount = childCount;
+ data.m_ChildThresholdArray = alloc.ConstructArray<float>(data.m_ChildCount);
+ memcpy(&data.m_ChildThresholdArray[0], &blendTreeThresholdArray[0], sizeof(float)*childCount);
+
+ return blendTreeNodeConstant ;
+ }
+
+ BlendTreeNodeConstant* CreateBlendTreeNodeConstant(uint32_t blendValueID, uint32_t blendValueYID, int blendType, uint32_t childCount, uint32_t* childIndices, Vector2f* blendTreePositionArray, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeNodeConstant);
+
+ BlendTreeNodeConstant *blendTreeNodeConstant = alloc.Construct<BlendTreeNodeConstant>();
+
+ blendTreeNodeConstant->m_BlendEventID = blendValueID;
+ blendTreeNodeConstant->m_BlendEventYID = blendValueYID;
+ blendTreeNodeConstant->m_ChildCount = childCount;
+
+ blendTreeNodeConstant->m_ChildIndices = alloc.ConstructArray<uint32_t>(childCount);
+ memcpy(&blendTreeNodeConstant->m_ChildIndices[0], &childIndices[0], sizeof(uint32_t)*childCount);
+
+ // Setup blend 2d data constant
+ blendTreeNodeConstant->m_BlendType = blendType;
+ blendTreeNodeConstant->m_Blend2dData = alloc.Construct<Blend2dDataConstant>();
+ Blend2dDataConstant& data = *blendTreeNodeConstant->m_Blend2dData;
+
+ // Populate blend 2d data constant
+ data.m_ChildCount = childCount;
+ data.m_ChildPositionArray = alloc.ConstructArray<Vector2f>(data.m_ChildCount);
+ memcpy(&data.m_ChildPositionArray[0], &blendTreePositionArray[0], sizeof(Vector2f)*childCount);
+
+ if (blendType == 2 || blendType == 3)
+ {
+ // Populate blend 2d precomputed data for type 2 or 3
+ if (blendType == 2)
+ {
+ data.m_ChildMagnitudeCount = childCount;
+ data.m_ChildMagnitudeArray = alloc.ConstructArray<float>(data.m_ChildMagnitudeCount);
+ }
+ data.m_ChildPairAvgMagInvCount = childCount * childCount;
+ data.m_ChildPairVectorCount = childCount * childCount;
+ data.m_ChildNeighborListCount = childCount;
+ data.m_ChildPairAvgMagInvArray = alloc.ConstructArray<float>(data.m_ChildPairAvgMagInvCount);
+ data.m_ChildPairVectorArray = alloc.ConstructArray<Vector2f>(data.m_ChildPairVectorCount);
+ data.m_ChildNeighborListArray = alloc.ConstructArray<MotionNeighborList>(data.m_ChildNeighborListCount);
+ PrecomputeFreeform (blendType, data, alloc);
+ }
+
+ return blendTreeNodeConstant;
+ }
+
+ BlendTreeNodeConstant* CreateBlendTreeNodeConstant(uint32_t clipID, float duration, bool mirror, float cycle, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeNodeConstant);
+ BlendTreeNodeConstant *blendTreeNodeConstant = alloc.Construct<BlendTreeNodeConstant>();
+
+ blendTreeNodeConstant->m_ChildCount = 0;
+ blendTreeNodeConstant->m_ClipID = clipID;
+ blendTreeNodeConstant->m_Duration = duration;
+ blendTreeNodeConstant->m_Mirror = mirror;
+ blendTreeNodeConstant->m_CycleOffset = cycle;
+
+ return blendTreeNodeConstant ;
+ }
+
+ BlendTreeConstant* CreateBlendTreeConstant(BlendTreeNodeConstant** nodeArray, uint32_t nodeCount, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeConstant);
+
+ BlendTreeConstant *blendTreeConstant = alloc.Construct<BlendTreeConstant>();
+
+ blendTreeConstant->m_NodeCount = nodeCount;
+ blendTreeConstant->m_NodeArray = alloc.ConstructArray< OffsetPtr<BlendTreeNodeConstant> >(nodeCount);
+
+ uint32_t i;
+ for(i = 0; i < nodeCount; i++)
+ blendTreeConstant->m_NodeArray[i] = nodeArray[i];
+
+ dynamic_array<int> blendValueIds;
+ GetAllBlendValue(blendTreeConstant, blendValueIds);
+
+ blendTreeConstant->m_BlendEventArrayConstant = CreateValueArrayConstant(kFloatType, blendValueIds.size(), alloc);
+
+ for(i = 0; i < blendValueIds.size(); i++)
+ {
+ blendTreeConstant->m_BlendEventArrayConstant->m_ValueArray[i].m_ID = blendValueIds[i];
+ }
+
+ return blendTreeConstant ;
+ }
+
+ BlendTreeConstant* CreateBlendTreeConstant(uint32_t clipID, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeConstant);
+
+ BlendTreeConstant *blendTreeConstant = alloc.Construct<BlendTreeConstant>();
+ blendTreeConstant->m_NodeCount = 1;
+ blendTreeConstant->m_NodeArray = alloc.ConstructArray< OffsetPtr<BlendTreeNodeConstant> >(1);
+
+ blendTreeConstant->m_NodeArray[0] = CreateBlendTreeNodeConstant(clipID,1.0f,false,0,alloc);
+
+ return blendTreeConstant;
+
+ }
+
+ void DestroyBlendTreeNodeConstant(BlendTreeNodeConstant * constant, memory::Allocator& alloc)
+ {
+ alloc.Deallocate(constant->m_ChildIndices);
+
+ if (!constant->m_Blend1dData.IsNull ())
+ {
+ alloc.Deallocate(constant->m_Blend1dData->m_ChildThresholdArray);
+ }
+
+ if (!constant->m_Blend2dData.IsNull ())
+ {
+ alloc.Deallocate(constant->m_Blend2dData->m_ChildPositionArray);
+ alloc.Deallocate(constant->m_Blend2dData->m_ChildMagnitudeArray);
+ alloc.Deallocate(constant->m_Blend2dData->m_ChildPairVectorArray);
+ alloc.Deallocate(constant->m_Blend2dData->m_ChildPairAvgMagInvArray);
+
+ if (!constant->m_Blend2dData->m_ChildNeighborListArray.IsNull ())
+ {
+ for (int i=0; i<constant->m_Blend2dData->m_ChildNeighborListCount; i++)
+ alloc.Deallocate(constant->m_Blend2dData->m_ChildNeighborListArray[i].m_NeighborArray);
+ alloc.Deallocate(constant->m_Blend2dData->m_ChildNeighborListArray);
+ }
+ }
+
+ alloc.Deallocate(constant);
+ }
+
+ void DestroyBlendTreeConstant(BlendTreeConstant * constant, memory::Allocator& alloc)
+ {
+ if(constant)
+ {
+ if(!constant->m_BlendEventArrayConstant.IsNull())
+ {
+ DestroyValueArrayConstant(constant->m_BlendEventArrayConstant.Get(), alloc);
+ }
+
+ for(uint32_t i = 0; i < constant->m_NodeCount; i++)
+ {
+ DestroyBlendTreeNodeConstant(constant->m_NodeArray[i].Get(), alloc);
+ }
+
+ alloc.Deallocate(constant->m_NodeArray);
+ alloc.Deallocate(constant);
+ }
+
+ }
+
+ BlendTreeMemory* CreateBlendTreeMemory(BlendTreeConstant const* constant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeMemory);
+ BlendTreeMemory *blendTreeMemory = alloc.Construct<BlendTreeMemory>();
+
+ blendTreeMemory->m_NodeCount = GetLeafCount(*constant);
+ blendTreeMemory->m_NodeDurationArray = alloc.ConstructArray<float>(blendTreeMemory->m_NodeCount);
+
+ return blendTreeMemory ;
+ }
+
+ void DestroyBlendTreeMemory(BlendTreeMemory *memory, memory::Allocator& alloc)
+ {
+ if(memory)
+ {
+ alloc.Deallocate(memory->m_NodeDurationArray);
+ alloc.Deallocate(memory);
+ }
+ }
+
+ BlendTreeInput* CreateBlendTreeInput(BlendTreeConstant const* constant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeInput);
+
+ BlendTreeInput *blendTreeInput = alloc.Construct<BlendTreeInput>();
+
+ if(!constant->m_BlendEventArrayConstant.IsNull())
+ blendTreeInput->m_BlendValueArray = CreateValueArray(constant->m_BlendEventArrayConstant.Get(), alloc);
+
+ return blendTreeInput ;
+ }
+
+ void DestroyBlendTreeInput(BlendTreeInput * input, memory::Allocator& alloc)
+ {
+ if(input)
+ {
+ if(input->m_BlendValueArray)
+ {
+ DestroyValueArray(input->m_BlendValueArray, alloc);
+ }
+
+ alloc.Deallocate(input);
+ }
+ }
+
+ BlendTreeOutput* CreateBlendTreeOutput(BlendTreeConstant const* constant, uint32_t maxBlendedClip, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeOutput);
+ BlendTreeOutput *blendTreeOutput = alloc.Construct<BlendTreeOutput>();
+
+ blendTreeOutput->m_MaxBlendedClip = maxBlendedClip;
+ blendTreeOutput->m_OutputBlendArray = alloc.ConstructArray< BlendTreeNodeOutput >(maxBlendedClip);
+
+ return blendTreeOutput ;
+ }
+
+ void DestroyBlendTreeOutput(BlendTreeOutput *output, memory::Allocator& alloc)
+ {
+ if(output)
+ {
+ alloc.Deallocate(output->m_OutputBlendArray);
+ alloc.Deallocate(output);
+ }
+ }
+
+ BlendTreeWorkspace* CreateBlendTreeWorkspace(BlendTreeConstant const* constant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(BlendTreeWorkspace);
+
+ BlendTreeWorkspace *blendTreeWorkspace = alloc.Construct<BlendTreeWorkspace>();
+ blendTreeWorkspace->m_BlendArray = alloc.ConstructArray<float>(constant->m_NodeCount);
+ // Optimize later to only have room for worst case number of immediate children instead of m_NodeCount:
+ blendTreeWorkspace->m_TempWeightArray = alloc.ConstructArray<float>(constant->m_NodeCount);
+ blendTreeWorkspace->m_TempCropArray = alloc.ConstructArray<int>(constant->m_NodeCount);
+ blendTreeWorkspace->m_ChildInputVectorArray = alloc.ConstructArray<Vector2f>(constant->m_NodeCount);
+
+ return blendTreeWorkspace ;
+ }
+
+ void DestroyBlendTreeWorkspace(BlendTreeWorkspace * workspace, memory::Allocator& alloc)
+ {
+ if(workspace != 0)
+ {
+ alloc.Deallocate(workspace->m_BlendArray);
+ alloc.Deallocate(workspace->m_TempWeightArray);
+ alloc.Deallocate(workspace->m_TempCropArray);
+ alloc.Deallocate(workspace->m_ChildInputVectorArray);
+ alloc.Deallocate(workspace);
+ }
+ }
+
+ float WeightForIndex( const float* thresholdArray, mecanim::uint32_t count, mecanim::uint32_t index, float blend)
+ {
+ if( blend >= thresholdArray[index])
+ {
+ if(index+1 == count)
+ {
+ return 1.0f;
+ }
+ else if(thresholdArray[index+1] < blend)
+ {
+ return 0.0f;
+ }
+ else
+ {
+ if(thresholdArray[index]-thresholdArray[index+1] != 0)
+ {
+ return (blend - thresholdArray[index+1]) / (thresholdArray[index]-thresholdArray[index+1]);
+ }
+ else
+ {
+ return thresholdArray[index];
+ }
+
+ }
+ }
+ else
+ {
+ if(index == 0)
+ {
+ return 1.0f;
+ }
+ else if(thresholdArray[index-1] > blend)
+ {
+ return 0.0f;
+ }
+ else
+ {
+ if(( thresholdArray[index]-thresholdArray[index-1]) != 0)
+ {
+ return (blend - thresholdArray[index-1]) / (thresholdArray[index]-thresholdArray[index-1]);
+ }
+ else
+ {
+ return thresholdArray[index];
+ }
+ }
+ }
+
+ }
+
+ void GetWeightsSimpleDirectional (const Blend2dDataConstant& blendConstant,
+ float* weightArray, int* cropArray, Vector2f* workspaceBlendVectors,
+ float blendValueX, float blendValueY, bool preCompute = false)
+ {
+ // Get constants
+ const Vector2f* positionArray = blendConstant.m_ChildPositionArray.Get();
+ mecanim::uint32_t count = blendConstant.m_ChildCount;
+
+ if (weightArray == NULL || positionArray == NULL)
+ return;
+
+ // Initialize all weights to 0
+ for (int i=0; i<count; i++)
+ weightArray[i] = 0;
+
+ // Handle fallback
+ if (count < 2)
+ {
+ if (count == 1)
+ weightArray[0] = 1;
+ return;
+ }
+
+ Vector2f blendPosition = Vector2f (blendValueX, blendValueY);
+
+ // Handle special case when sampled ecactly in the middle
+ if (blendPosition == Vector2f::zero)
+ {
+ // If we have a center motion, give that one all the weight
+ for (int i=0; i<count; i++)
+ {
+ if (positionArray[i] == Vector2f::zero)
+ {
+ weightArray[i] = 1;
+ return;
+ }
+ }
+
+ // Otherwise divide weight evenly
+ float sharedWeight = 1.0f / count;
+ for (int i=0; i<count; i++)
+ weightArray[i] = sharedWeight;
+ return;
+ }
+
+ int indexA = -1;
+ int indexB = -1;
+ int indexCenter = -1;
+ float maxDotForNegCross = -100000.0f;
+ float maxDotForPosCross = -100000.0f;
+ for (int i=0; i<count; i++)
+ {
+ if (positionArray[i] == Vector2f::zero)
+ {
+ if (indexCenter >= 0)
+ return;
+ indexCenter = i;
+ continue;
+ }
+ Vector2f posNormalized = Normalize (positionArray[i]);
+ float dot = Dot (posNormalized, blendPosition);
+ float cross = posNormalized.x * blendPosition.y - posNormalized.y * blendPosition.x;
+ if (cross > 0)
+ {
+ if (dot > maxDotForPosCross)
+ {
+ maxDotForPosCross = dot;
+ indexA = i;
+ }
+ }
+ else
+ {
+ if (dot > maxDotForNegCross)
+ {
+ maxDotForNegCross = dot;
+ indexB = i;
+ }
+ }
+ }
+
+ float centerWeight = 0;
+
+ if (indexA < 0 || indexB < 0)
+ {
+ // Fallback if sampling point is not inside a triangle
+ centerWeight = 1;
+ }
+ else
+ {
+ Vector2f a = positionArray[indexA];
+ Vector2f b = positionArray[indexB];
+
+ // Calculate weights using barycentric coordinates
+ // (formulas from http://en.wikipedia.org/wiki/Barycentric_coordinate_system_%28mathematics%29 )
+ float det = b.y*a.x - b.x*a.y; // Simplified from: (b.y-0)*(a.x-0) + (0-b.x)*(a.y-0);
+ float wA = (b.y*blendValueX - b.x*blendValueY) / det; // Simplified from: ((b.y-0)*(l.x-0) + (0-b.x)*(l.y-0)) / det;
+ float wB = (a.x*blendValueY - a.y*blendValueX) / det; // Simplified from: ((0-a.y)*(l.x-0) + (a.x-0)*(l.y-0)) / det;
+ centerWeight = 1 - wA - wB;
+
+ // Clamp to be inside triangle
+ if (centerWeight < 0)
+ {
+ centerWeight = 0;
+ float sum = wA + wB;
+ wA /= sum;
+ wB /= sum;
+ }
+ else if (centerWeight > 1)
+ {
+ centerWeight = 1;
+ wA = 0;
+ wB = 0;
+ }
+
+ // Give weight to the two vertices on the periphery that are closest
+ weightArray[indexA] = wA;
+ weightArray[indexB] = wB;
+ }
+
+ if (indexCenter >= 0)
+ {
+ weightArray[indexCenter] = centerWeight;
+ }
+ else
+ {
+ // Give weight to all children when input is in the center
+ float sharedWeight = 1.0f / count;
+ for (int i=0; i<count; i++)
+ weightArray[i] += sharedWeight * centerWeight;
+ }
+ }
+
+ const float kInversePI = 1 / kPI;
+ float GetWeightFreeformDirectional (const Blend2dDataConstant& blendConstant, Vector2f* workspaceBlendVectors, int i, int j, Vector2f blendPosition)
+ {
+ int pairIndex = i + j*blendConstant.m_ChildCount;
+ Vector2f vecIJ = blendConstant.m_ChildPairVectorArray[pairIndex];
+ Vector2f vecIO = workspaceBlendVectors[i];
+ vecIO.y *= blendConstant.m_ChildPairAvgMagInvArray[pairIndex];
+
+ if (blendConstant.m_ChildPositionArray[i] == Vector2f::zero)
+ vecIJ.x = workspaceBlendVectors[j].x;
+ else if (blendConstant.m_ChildPositionArray[j] == Vector2f::zero)
+ vecIJ.x = workspaceBlendVectors[i].x;
+ else if (vecIJ.x == 0 || blendPosition == Vector2f::zero)
+ vecIO.x = vecIJ.x;
+
+ return 1 - Dot (vecIJ, vecIO) / SqrMagnitude (vecIJ);
+ }
+
+ void GetWeightsFreeformDirectional (const Blend2dDataConstant& blendConstant,
+ float* weightArray, int* cropArray, Vector2f* workspaceBlendVectors,
+ float blendValueX, float blendValueY, bool preCompute = false)
+ {
+ // Get constants
+ const Vector2f* positionArray = blendConstant.m_ChildPositionArray.Get();
+ mecanim::uint32_t count = blendConstant.m_ChildCount;
+ const float* constantMagnitudes = blendConstant.m_ChildMagnitudeArray.Get();
+ const MotionNeighborList* constantChildNeighborLists = blendConstant.m_ChildNeighborListArray.Get();
+
+ Vector2f blendPosition = Vector2f (blendValueX, blendValueY);
+ float magO = Magnitude (blendPosition);
+
+ if (blendPosition == Vector2f::zero)
+ {
+ for (int i=0; i<count; i++)
+ workspaceBlendVectors[i] = Vector2f (0, magO - constantMagnitudes[i]);
+ }
+ else
+ {
+ for (int i=0; i<count; i++)
+ {
+ if (positionArray[i] == Vector2f::zero)
+ workspaceBlendVectors[i] = Vector2f (0, magO - constantMagnitudes[i]);
+ else
+ {
+ float angle = Angle (positionArray[i], blendPosition);
+ if (positionArray[i].x * blendPosition.y - positionArray[i].y * blendPosition.x < 0)
+ angle = -angle;
+ workspaceBlendVectors[i] = Vector2f (angle, magO - constantMagnitudes[i]);
+ }
+ }
+ }
+
+ if (preCompute)
+ {
+ for (int i=0; i<count; i++)
+ {
+ // Fade out over 180 degrees away from example
+ float value = 1 - abs (workspaceBlendVectors[i].x) * kInversePI;
+ cropArray[i] = -1;
+ for (int j=0; j<count; j++)
+ {
+ if (i==j)
+ continue;
+
+ float newValue = GetWeightFreeformDirectional (blendConstant, workspaceBlendVectors, i, j, blendPosition);
+
+ if (newValue <= 0)
+ {
+ value = 0;
+ cropArray[i] = -1;
+ break;
+ }
+ // Used for determining neighbors
+ if (newValue < value)
+ cropArray[i] = j;
+ value = min (value, newValue);
+ }
+ }
+ return;
+ }
+
+ for (int i=0; i<count; i++)
+ {
+ // Fade out over 180 degrees away from example
+ float value = 1 - abs (workspaceBlendVectors[i].x) * kInversePI;
+ for (int jIndex=0; jIndex<constantChildNeighborLists[i].m_Count; jIndex++)
+ {
+ int j = constantChildNeighborLists[i].m_NeighborArray[jIndex];
+ float newValue = GetWeightFreeformDirectional (blendConstant, workspaceBlendVectors, i, j, blendPosition);
+ if (newValue <= 0)
+ {
+ value = 0;
+ break;
+ }
+ value = min (value, newValue);
+ }
+ weightArray[i] = value;
+ }
+
+ // Normalize weights
+ float summedWeight = 0;
+ for (int i=0; i<count; i++)
+ summedWeight += weightArray[i];
+
+ if (summedWeight > 0)
+ {
+ summedWeight = 1.0f / summedWeight; // Do division once instead of for every sample
+ for (int i=0; i<count; i++)
+ weightArray[i] *= summedWeight;
+ }
+ else
+ {
+ // Give weight to all children as fallback when no children have any weight.
+ // This happens when sampling in the center if no center motion is provided.
+ float evenWeight = 1.0f / count;
+ for (int i=0; i<count; i++)
+ weightArray[i] = evenWeight;
+ }
+ }
+
+ void GetWeightsFreeformCartesian (const Blend2dDataConstant& blendConstant,
+ float* weightArray, int* cropArray, Vector2f* workspaceBlendVectors,
+ float blendValueX, float blendValueY, bool preCompute = false)
+ {
+ // Get constants
+ const Vector2f* positionArray = blendConstant.m_ChildPositionArray.Get();
+ mecanim::uint32_t count = blendConstant.m_ChildCount;
+ const MotionNeighborList* constantChildNeighborLists = blendConstant.m_ChildNeighborListArray.Get();
+
+ Vector2f blendPosition = Vector2f (blendValueX, blendValueY);
+ for (int i=0; i<count; i++)
+ workspaceBlendVectors[i] = blendPosition - positionArray[i];
+
+ if (preCompute)
+ {
+ for (int i=0; i<count; i++)
+ {
+ cropArray[i] = -1;
+ Vector2f vecIO = workspaceBlendVectors[i];
+ float value = 1;
+ for (int j=0; j<count; j++)
+ {
+ if (i==j)
+ continue;
+
+ int pairIndex = i + j*blendConstant.m_ChildCount;
+ Vector2f vecIJ = blendConstant.m_ChildPairVectorArray[pairIndex];
+ float newValue = 1 - Dot (vecIJ, vecIO) * blendConstant.m_ChildPairAvgMagInvArray[pairIndex];
+ if (newValue <= 0)
+ {
+ value = 0;
+ cropArray[i] = -1;
+ break;
+ }
+ // Used for determining neighbors
+ if (newValue < value)
+ cropArray[i] = j;
+ value = min (value, newValue);
+ }
+ }
+ return;
+ }
+
+ for (int i=0; i<count; i++)
+ {
+ Vector2f vecIO = workspaceBlendVectors[i];
+ float value = 1;
+ for (int jIndex=0; jIndex<constantChildNeighborLists[i].m_Count; jIndex++)
+ {
+ int j = constantChildNeighborLists[i].m_NeighborArray[jIndex];
+ if (i==j)
+ continue;
+
+ int pairIndex = i + j*blendConstant.m_ChildCount;
+ Vector2f vecIJ = blendConstant.m_ChildPairVectorArray[pairIndex];
+ float newValue = 1 - Dot (vecIJ, vecIO) * blendConstant.m_ChildPairAvgMagInvArray[pairIndex];
+ if (newValue < 0)
+ {
+ value = 0;
+ break;
+ }
+ value = min (value, newValue);
+ }
+ weightArray[i] = value;
+ }
+
+ // Normalize weights
+ float summedWeight = 0;
+ for (int i=0; i<count; i++)
+ summedWeight += weightArray[i];
+ summedWeight = 1.0f / summedWeight; // Do division once instead of for every sample
+ for (int i=0; i<count; i++)
+ weightArray[i] *= summedWeight;
+ }
+
+ void GetWeights1d (const Blend1dDataConstant& blendConstant, float* weightArray, float blendValue)
+ {
+ blendValue = math::clamp (blendValue, blendConstant.m_ChildThresholdArray[0], blendConstant.m_ChildThresholdArray[blendConstant.m_ChildCount-1]);
+ for (mecanim::uint32_t j = 0 ; j < blendConstant.m_ChildCount; j++)
+ weightArray[j] = WeightForIndex (blendConstant.m_ChildThresholdArray.Get (), blendConstant.m_ChildCount, j, blendValue);
+ }
+
+ void GetWeights (const BlendTreeNodeConstant& nodeConstant, BlendTreeWorkspace &workspace, float* weightArray, float blendValueX, float blendValueY)
+ {
+ if (nodeConstant.m_BlendType == 0)
+ GetWeights1d (*nodeConstant.m_Blend1dData.Get(), weightArray, blendValueX);
+ else if (nodeConstant.m_BlendType == 1)
+ GetWeightsSimpleDirectional (*nodeConstant.m_Blend2dData.Get(), weightArray, workspace.m_TempCropArray, workspace.m_ChildInputVectorArray, blendValueX, blendValueY);
+ else if (nodeConstant.m_BlendType == 2)
+ GetWeightsFreeformDirectional (*nodeConstant.m_Blend2dData.Get(), weightArray, workspace.m_TempCropArray, workspace.m_ChildInputVectorArray, blendValueX, blendValueY);
+ else if (nodeConstant.m_BlendType == 3)
+ GetWeightsFreeformCartesian (*nodeConstant.m_Blend2dData.Get(), weightArray, workspace.m_TempCropArray, workspace.m_ChildInputVectorArray, blendValueX, blendValueY);
+ }
+
+ uint32_t ComputeBlends(const BlendTreeConstant& constant, const BlendTreeInput &input, const BlendTreeMemory &memory, BlendTreeOutput &output, BlendTreeWorkspace &workspace)
+ {
+ uint32_t leafIndex = 0;
+ uint32_t currentOutputIndex = 0 ;
+ workspace.m_BlendArray[0] = 1;
+
+ uint32_t i = 0;
+ for(i = 0 ; i < constant.m_NodeCount; i ++)
+ {
+ const BlendTreeNodeConstant* nodeConstant = constant.m_NodeArray[i].Get();
+
+ if(nodeConstant->m_ClipID != -1)
+ {
+ if(workspace.m_BlendArray[i] > 0)
+ {
+ float duration = IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_3_a1) ? memory.m_NodeDurationArray[leafIndex] * nodeConstant->m_Duration : nodeConstant->m_Duration;
+
+ output.m_OutputBlendArray[currentOutputIndex].m_ID = nodeConstant->m_ClipID;
+ output.m_OutputBlendArray[currentOutputIndex].m_BlendValue = workspace.m_BlendArray[i];
+ output.m_OutputBlendArray[currentOutputIndex].m_Reverse = duration < 0;
+ output.m_OutputBlendArray[currentOutputIndex].m_CycleOffset = nodeConstant->m_CycleOffset;
+ output.m_OutputBlendArray[currentOutputIndex].m_Mirror = nodeConstant->m_Mirror;
+
+ output.m_Duration += math::abs(duration) * workspace.m_BlendArray[i];
+ currentOutputIndex++;
+ }
+
+ leafIndex++;
+ }
+ else if(nodeConstant->m_ChildCount> 0)
+ {
+ if (nodeConstant->m_BlendType == 0)
+ {
+ // 1D blending
+ int32_t index = FindValueIndex(constant.m_BlendEventArrayConstant.Get(), nodeConstant->m_BlendEventID);
+ float blendValue;
+ input.m_BlendValueArray->ReadData(blendValue, constant.m_BlendEventArrayConstant->m_ValueArray[index].m_Index);
+
+ GetWeights (*nodeConstant, workspace, workspace.m_TempWeightArray, blendValue, 0);
+ }
+ else if (nodeConstant->m_BlendType >= 1)
+ {
+ // 2D blending
+ int32_t indexX = FindValueIndex(constant.m_BlendEventArrayConstant.Get(), nodeConstant->m_BlendEventID);
+ int32_t indexY = FindValueIndex(constant.m_BlendEventArrayConstant.Get(), nodeConstant->m_BlendEventYID);
+ float blendValueX, blendValueY;
+ input.m_BlendValueArray->ReadData(blendValueX, constant.m_BlendEventArrayConstant->m_ValueArray[indexX].m_Index);
+ input.m_BlendValueArray->ReadData(blendValueY, constant.m_BlendEventArrayConstant->m_ValueArray[indexY].m_Index);
+
+ GetWeights (*nodeConstant, workspace, workspace.m_TempWeightArray, blendValueX, blendValueY);
+ }
+
+ for(mecanim::uint32_t j = 0 ; j < nodeConstant->m_ChildCount; j++)
+ {
+ float w = workspace.m_TempWeightArray[j];
+ workspace.m_BlendArray[nodeConstant->m_ChildIndices[j]] = w * workspace.m_BlendArray[i];
+ }
+ }
+ }
+
+ return currentOutputIndex;
+ }
+
+ void EvaluateBlendTree(const BlendTreeConstant& constant, const BlendTreeInput &input, const BlendTreeMemory &memory, BlendTreeOutput &output, BlendTreeWorkspace &workspace)
+ {
+ for(uint32_t i = 0 ; i < output.m_MaxBlendedClip ; i++) output.m_OutputBlendArray[i].m_ID = -1;
+
+ output.m_Duration = 0;
+ uint32_t currentOutputIndex = 0;
+
+ if(constant.m_NodeCount >0)
+ currentOutputIndex = ComputeBlends(constant, input, memory, output, workspace);
+
+ if(currentOutputIndex == 0)
+ output.m_Duration = 1;
+ }
+
+
+ mecanim::uint32_t GetLeafCount(const BlendTreeConstant& constant)
+ {
+ mecanim::uint32_t leafCount = 0 ;
+
+ for(int i = 0 ; i < constant.m_NodeCount ; i++)
+ {
+ if(constant.m_NodeArray[i]->m_ClipID != -1)
+ leafCount++;
+ }
+
+ return leafCount;
+ }
+
+ void FillLeafIDArray(const BlendTreeConstant& constant, uint32_t* leafIDArray)
+ {
+ uint32_t baseIndex = 0 ;
+ uint32_t i;
+ for(i = 0 ; i < constant.m_NodeCount ; i++)
+ {
+ if(constant.m_NodeArray[i]->m_ClipID != -1)
+ {
+ leafIDArray[baseIndex] = constant.m_NodeArray[i]->m_ClipID;
+ baseIndex++;
+ }
+ }
+ }
+
+ mecanim::uint32_t GetMaxBlendCount(const BlendTreeConstant& constant, const BlendTreeNodeConstant& node)
+ {
+ uint32_t maxBlendCount = node.m_ClipID != -1 ? 1 : 0;
+
+ // Blending occur between closest sibbling only
+ uint32_t current = 0;
+ uint32_t previous = 0;
+
+ if(node.m_BlendType == 0 )
+ {
+ for(int i = 0 ; i < node.m_ChildCount ; i++)
+ {
+ current = GetMaxBlendCount(constant, *constant.m_NodeArray[node.m_ChildIndices[i]]);
+ maxBlendCount = math::maximum( maxBlendCount, previous + current);
+ previous = current;
+ }
+ }
+ else
+ {
+ for(int i = 0 ; i < node.m_ChildCount ; i++)
+ {
+ maxBlendCount += GetMaxBlendCount(constant, *constant.m_NodeArray[node.m_ChildIndices[i]]);
+ }
+ }
+
+ return maxBlendCount;
+ }
+
+ mecanim::uint32_t GetMaxBlendCount(const BlendTreeConstant& constant)
+ {
+ uint32_t maxBlendCount = 0;
+ if(constant.m_NodeCount)
+ maxBlendCount = GetMaxBlendCount(constant, *constant.m_NodeArray[0]);
+ return maxBlendCount;
+ }
+
+
+}// namespace animation
+
+}//namespace mecanim
diff --git a/Runtime/mecanim/animation/blendtree.h b/Runtime/mecanim/animation/blendtree.h
new file mode 100644
index 0000000..ee3f7a5
--- /dev/null
+++ b/Runtime/mecanim/animation/blendtree.h
@@ -0,0 +1,285 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/mecanim/animation/curvedata.h"
+#include "Runtime/mecanim/human/human.h"
+#include "Runtime/mecanim/human/hand.h"
+#include "Runtime/Math/Vector2.h"
+
+#include "Runtime/Serialize/Blobification/offsetptr.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Animation/MecanimArraySerialization.h"
+
+namespace mecanim
+{
+
+namespace animation
+{
+ struct MotionNeighborList
+ {
+ DEFINE_GET_TYPESTRING(MotionNeighborList)
+
+ MotionNeighborList() : m_Count(0)
+ {
+ }
+
+ uint32_t m_Count;
+ OffsetPtr<uint32_t> m_NeighborArray;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_Count);
+ MANUAL_ARRAY_TRANSFER2(uint32_t, m_NeighborArray, m_Count);
+ }
+ };
+
+ // Constant data for 1D blend node types - thresholds
+ struct Blend1dDataConstant
+ {
+ DEFINE_GET_TYPESTRING(Blend2dDataConstant)
+
+ Blend1dDataConstant() : m_ChildCount(0)
+ {
+ }
+
+ uint32_t m_ChildCount;
+ OffsetPtr<float> m_ChildThresholdArray;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_ChildCount);
+ MANUAL_ARRAY_TRANSFER2(float, m_ChildThresholdArray, m_ChildCount);
+ }
+ };
+
+ // Constant data for 2D blend node types - positions plus precomputed data to speed up blending
+ struct Blend2dDataConstant
+ {
+ DEFINE_GET_TYPESTRING(Blend2dDataConstant)
+
+ Blend2dDataConstant() : m_ChildCount(0), m_ChildMagnitudeCount(0), m_ChildPairVectorCount(0), m_ChildPairAvgMagInvCount(0), m_ChildNeighborListCount(0)
+ {
+ }
+
+ uint32_t m_ChildCount;
+ OffsetPtr<Vector2f> m_ChildPositionArray;
+
+ uint32_t m_ChildMagnitudeCount;
+ OffsetPtr<float> m_ChildMagnitudeArray; // Used by type 2
+ uint32_t m_ChildPairVectorCount;
+ OffsetPtr<Vector2f> m_ChildPairVectorArray; // Used by type 2, (3 TODO)
+ uint32_t m_ChildPairAvgMagInvCount;
+ OffsetPtr<float> m_ChildPairAvgMagInvArray; // Used by type 2
+ uint32_t m_ChildNeighborListCount;
+ OffsetPtr<MotionNeighborList> m_ChildNeighborListArray; // Used by type 2, (3 TODO)
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_ChildCount);
+ MANUAL_ARRAY_TRANSFER2(Vector2f, m_ChildPositionArray, m_ChildCount);
+
+ TRANSFER_BLOB_ONLY(m_ChildMagnitudeCount);
+ MANUAL_ARRAY_TRANSFER2(float, m_ChildMagnitudeArray, m_ChildMagnitudeCount);
+ TRANSFER_BLOB_ONLY(m_ChildPairVectorCount);
+ MANUAL_ARRAY_TRANSFER2(Vector2f, m_ChildPairVectorArray, m_ChildPairVectorCount);
+ TRANSFER_BLOB_ONLY(m_ChildPairAvgMagInvCount);
+ MANUAL_ARRAY_TRANSFER2(float, m_ChildPairAvgMagInvArray, m_ChildPairAvgMagInvCount);
+ TRANSFER_BLOB_ONLY(m_ChildNeighborListCount);
+ MANUAL_ARRAY_TRANSFER2(MotionNeighborList, m_ChildNeighborListArray, m_ChildNeighborListCount);
+ }
+ };
+
+ struct BlendTreeNodeConstant
+ {
+ DEFINE_GET_TYPESTRING(BlendTreeNodeConstant)
+
+ BlendTreeNodeConstant(): m_BlendType(0), m_BlendEventID(-1), m_BlendEventYID(-1), m_ChildCount(0), m_ClipID(-1), m_Duration(0), m_CycleOffset(0), m_Mirror(false)
+ {
+
+ }
+
+ uint32_t m_BlendType;
+
+ uint32_t m_BlendEventID;
+ uint32_t m_BlendEventYID;
+ uint32_t m_ChildCount;
+ OffsetPtr<uint32_t> m_ChildIndices;
+
+ OffsetPtr<Blend1dDataConstant> m_Blend1dData;
+ OffsetPtr<Blend2dDataConstant> m_Blend2dData;
+
+ uint32_t m_ClipID; // assert( m_ClipID != -1 && mClipBlendCount == 0)
+ float m_Duration;
+ float m_CycleOffset;
+ bool m_Mirror;
+
+ // Unity 4.1 introduced 2D blendtrees. The data layout has been changed there.
+ template<class TransferFunction>
+ inline void Transfer_4_0_BackwardsCompatibility (TransferFunction& transfer)
+ {
+ if (transfer.IsOldVersion(1))
+ {
+ if (m_Blend1dData.IsNull())
+ {
+ mecanim::memory::ChainedAllocator* allocator = static_cast<mecanim::memory::ChainedAllocator*> (transfer.GetUserData());
+ m_Blend1dData = allocator->Construct<Blend1dDataConstant>();
+ }
+
+ OffsetPtr<float>& m_ChildThresholdArray = m_Blend1dData->m_ChildThresholdArray;
+ MANUAL_ARRAY_TRANSFER2(float, m_ChildThresholdArray, m_Blend1dData->m_ChildCount);
+ }
+ }
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ transfer.SetVersion(2);
+
+ TRANSFER(m_BlendType);
+ TRANSFER(m_BlendEventID);
+ TRANSFER(m_BlendEventYID);
+
+ TRANSFER_BLOB_ONLY(m_ChildCount);
+ MANUAL_ARRAY_TRANSFER2(uint32_t, m_ChildIndices, m_ChildCount);
+
+ TRANSFER(m_Blend1dData);
+ TRANSFER(m_Blend2dData);
+
+ TRANSFER(m_ClipID);
+ TRANSFER(m_Duration);
+
+ TRANSFER(m_CycleOffset);
+ TRANSFER(m_Mirror);
+ transfer.Align();
+
+ Transfer_4_0_BackwardsCompatibility(transfer);
+
+ }
+ };
+
+ struct BlendTreeConstant
+ {
+ DEFINE_GET_TYPESTRING(BlendTreeConstant)
+
+ BlendTreeConstant () :m_NodeCount(0)
+ {
+ }
+
+ uint32_t m_NodeCount;
+ OffsetPtr< OffsetPtr<BlendTreeNodeConstant> > m_NodeArray;
+
+ OffsetPtr<ValueArrayConstant> m_BlendEventArrayConstant;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_NodeCount);
+ MANUAL_ARRAY_TRANSFER2( OffsetPtr<mecanim::animation::BlendTreeNodeConstant>, m_NodeArray, m_NodeCount);
+
+ TRANSFER(m_BlendEventArrayConstant);
+ }
+ };
+
+ struct BlendTreeMemory
+ {
+ DEFINE_GET_TYPESTRING(BlendTreeMemory)
+
+ BlendTreeMemory() : m_NodeCount(0) {}
+
+ uint32_t m_NodeCount;
+ OffsetPtr<float> m_NodeDurationArray;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(m_NodeCount);
+ MANUAL_ARRAY_TRANSFER2(float, m_NodeDurationArray, m_NodeCount);
+ }
+ };
+
+ struct BlendTreeInput
+ {
+ BlendTreeInput() : m_BlendValueArray(0)
+ {
+ }
+
+ ValueArray* m_BlendValueArray;
+ };
+
+ struct BlendTreeNodeOutput
+ {
+ BlendTreeNodeOutput() : m_BlendValue(0), m_ID(0), m_Reverse(false), m_Mirror(false), m_CycleOffset(0)
+ {
+
+ }
+
+ float m_BlendValue;
+ uint32_t m_ID;
+ bool m_Reverse;
+ bool m_Mirror;
+ float m_CycleOffset;
+ };
+
+ struct BlendTreeOutput
+ {
+ BlendTreeOutput() : m_Duration(0),
+ m_MaxBlendedClip(0)
+ {}
+
+ BlendTreeNodeOutput* m_OutputBlendArray;
+ uint32_t m_MaxBlendedClip;
+ float m_Duration;
+ };
+
+ struct BlendTreeWorkspace
+ {
+ BlendTreeWorkspace() : m_BlendArray(0), m_TempWeightArray(0), m_TempCropArray(0), m_ChildInputVectorArray(0)
+ {
+
+ }
+
+
+ float* m_BlendArray;
+ float* m_TempWeightArray;
+ int* m_TempCropArray;
+ Vector2f* m_ChildInputVectorArray;
+ };
+
+ void GetWeights (const BlendTreeNodeConstant& nodeConstant, BlendTreeWorkspace &workspace, float* weightArray, float blendValueX, float blendValueY);
+
+ // Overload for creating 1D blend node
+ BlendTreeNodeConstant* CreateBlendTreeNodeConstant(uint32_t blendValueID, uint32_t childCount, uint32_t* childIndices, float* blendTreeThresholdArray, memory::Allocator& alloc);
+ // Overload for creating 2D blend node
+ BlendTreeNodeConstant* CreateBlendTreeNodeConstant(uint32_t blendValueID, uint32_t blendValueYID, int blendType, uint32_t childCount, uint32_t* childIndices, Vector2f* blendTreePositionArray, memory::Allocator& alloc);
+ // Overload for creating leaf blend node
+ BlendTreeNodeConstant* CreateBlendTreeNodeConstant(uint32_t clipID, float duration, bool mirror, float cycle, memory::Allocator& alloc);
+
+ BlendTreeConstant* CreateBlendTreeConstant(BlendTreeNodeConstant** nodeArray, uint32_t nodeCount, memory::Allocator& alloc);
+ BlendTreeConstant* CreateBlendTreeConstant(uint32_t clipID, memory::Allocator& alloc);
+ void DestroyBlendTreeConstant(BlendTreeConstant * constant, memory::Allocator& alloc);
+
+ BlendTreeMemory* CreateBlendTreeMemory(BlendTreeConstant const* constant, memory::Allocator& alloc);
+ void DestroyBlendTreeMemory(BlendTreeMemory *memory, memory::Allocator& alloc);
+
+ BlendTreeInput* CreateBlendTreeInput(BlendTreeConstant const* constant, memory::Allocator& alloc);
+ void DestroyBlendTreeInput(BlendTreeInput * input, memory::Allocator& alloc);
+
+ BlendTreeOutput* CreateBlendTreeOutput(BlendTreeConstant const* constant, uint32_t maxBlendedClip, memory::Allocator& alloc);
+ void DestroyBlendTreeOutput(BlendTreeOutput * output, memory::Allocator& alloc);
+
+ BlendTreeWorkspace* CreateBlendTreeWorkspace(BlendTreeConstant const* constant, memory::Allocator& alloc);
+ void DestroyBlendTreeWorkspace(BlendTreeWorkspace * workspace, memory::Allocator& alloc);
+
+
+ void EvaluateBlendTree(const BlendTreeConstant& constant, const BlendTreeInput &input, const BlendTreeMemory &memory, BlendTreeOutput &output, BlendTreeWorkspace &workspace);
+
+ mecanim::uint32_t GetLeafCount(const BlendTreeConstant& constant);
+ void FillLeafIDArray(const BlendTreeConstant& constant, uint32_t* leafIDArray);
+ mecanim::uint32_t GetMaxBlendCount(const BlendTreeConstant& constant);
+}
+}
diff --git a/Runtime/mecanim/animation/clipmuscle.cpp b/Runtime/mecanim/animation/clipmuscle.cpp
new file mode 100644
index 0000000..f6f53d2
--- /dev/null
+++ b/Runtime/mecanim/animation/clipmuscle.cpp
@@ -0,0 +1,1366 @@
+#include "UnityPrefix.h"
+#include "Runtime/mecanim/animation/clipmuscle.h"
+#include "Runtime/mecanim/skeleton/skeleton.h"
+#include "Runtime/mecanim/generic/stringtable.h"
+
+namespace mecanim
+{
+
+namespace animation
+{
+ struct MuscleIndexId
+ {
+ mecanim::uint32_t index;
+ mecanim::uint32_t id;
+ };
+
+ static mecanim::String s_ClipMuscleNameArray[s_ClipMuscleCurveCount];
+ static MuscleIndexId s_ClipMuscleIndexIDArray[s_ClipMuscleCurveCount];
+
+ static void ClipMuscleNameArrayInit()
+ {
+ int i,j;
+
+ i = 0;
+ s_ClipMuscleNameArray[i++] = "MotionT.x";
+ s_ClipMuscleNameArray[i++] = "MotionT.y";
+ s_ClipMuscleNameArray[i++] = "MotionT.z";
+
+ s_ClipMuscleNameArray[i++] = "MotionQ.x";
+ s_ClipMuscleNameArray[i++] = "MotionQ.y";
+ s_ClipMuscleNameArray[i++] = "MotionQ.z";
+ s_ClipMuscleNameArray[i++] = "MotionQ.w";
+
+ s_ClipMuscleNameArray[i++] = "RootT.x";
+ s_ClipMuscleNameArray[i++] = "RootT.y";
+ s_ClipMuscleNameArray[i++] = "RootT.z";
+
+ s_ClipMuscleNameArray[i++] = "RootQ.x";
+ s_ClipMuscleNameArray[i++] = "RootQ.y";
+ s_ClipMuscleNameArray[i++] = "RootQ.z";
+ s_ClipMuscleNameArray[i++] = "RootQ.w";
+
+ for(j=0;j<mecanim::human::kLastGoal;j++)
+ {
+ mecanim::String nameT(mecanim::human::BoneName(mecanim::human::s_HumanGoalInfo[j].m_Index));
+ nameT += "T";
+ mecanim::String nameTx = nameT;
+ nameTx += ".x";
+ mecanim::String nameTy = nameT;
+ nameTy += ".y";
+ mecanim::String nameTz = nameT;
+ nameTz += ".z";
+
+ mecanim::String nameQ(mecanim::human::BoneName(mecanim::human::s_HumanGoalInfo[j].m_Index));
+ nameQ += "Q";
+ mecanim::String nameQx = nameQ;
+ nameQx += ".x";
+ mecanim::String nameQy = nameQ;
+ nameQy += ".y";
+ mecanim::String nameQz = nameQ;
+ nameQz += ".z";
+ mecanim::String nameQw = nameQ;
+ nameQw += ".w";
+
+ s_ClipMuscleNameArray[i++] = nameTx;
+ s_ClipMuscleNameArray[i++] = nameTy;
+ s_ClipMuscleNameArray[i++] = nameTz;
+
+ s_ClipMuscleNameArray[i++] = nameQx;
+ s_ClipMuscleNameArray[i++] = nameQy;
+ s_ClipMuscleNameArray[i++] = nameQz;
+ s_ClipMuscleNameArray[i++] = nameQw;
+ }
+
+ for(j=0;j<mecanim::human::kLastDoF;j++)
+ {
+ s_ClipMuscleNameArray[i++] = mecanim::human::MuscleName(j);
+ }
+
+ for(int f=0;f<mecanim::hand::kLastFinger;++f)
+ {
+ for(int d=0;d<mecanim::hand::kLastFingerDoF;++d)
+ {
+ mecanim::String name("LeftHand.");
+ name += mecanim::hand::FingerName(f);
+ name += ".";
+ name += mecanim::hand::FingerDoFName(d);
+
+ s_ClipMuscleNameArray[i++] = name;
+ }
+ }
+
+ for(int f=0;f<mecanim::hand::kLastFinger;++f)
+ {
+ for(int d=0;d<mecanim::hand::kLastFingerDoF;++d)
+ {
+ mecanim::String name("RightHand.");
+ name += mecanim::hand::FingerName(f);
+ name += ".";
+ name += mecanim::hand::FingerDoFName(d);
+
+ s_ClipMuscleNameArray[i++] = name;
+ }
+ }
+ }
+
+ static bool MuscleIndexIdSortFunction(MuscleIndexId i, MuscleIndexId j)
+ {
+ return i.id < j.id;
+ }
+
+ static void ClipMuscleIDArrayInit()
+ {
+ for(int i = 0; i < s_ClipMuscleCurveCount; i++)
+ {
+ s_ClipMuscleIndexIDArray[i].index = i;
+ s_ClipMuscleIndexIDArray[i].id = mecanim::processCRC32(GetMuscleCurveName(i));
+ }
+
+ std::sort(s_ClipMuscleIndexIDArray, s_ClipMuscleIndexIDArray + s_ClipMuscleCurveCount, MuscleIndexIdSortFunction);
+ }
+
+ void InitializeMuscleClipTables ()
+ {
+ ClipMuscleNameArrayInit();
+ ClipMuscleIDArrayInit();
+ }
+
+
+ mecanim::String const &GetMuscleCurveName(int32_t curveIndex)
+ {
+ Assert(s_ClipMuscleNameArray != NULL);
+ return s_ClipMuscleNameArray[curveIndex];
+ }
+
+ class MuscleIndexIdFindfunction
+ {
+ public:
+
+ uint32_t mId;
+
+ MuscleIndexIdFindfunction(uint32_t id) : mId(id) {}
+
+ bool operator()(const MuscleIndexId &indexId)
+ {
+ return indexId.id == mId;
+ }
+ };
+
+ int32_t FindMuscleIndex(uint32_t id)
+ {
+ Assert(s_ClipMuscleIndexIDArray[0].id != 0);
+
+ MuscleIndexId* end = s_ClipMuscleIndexIDArray + s_ClipMuscleCurveCount;
+
+ const MuscleIndexId* found = std::find_if(s_ClipMuscleIndexIDArray, end, MuscleIndexIdFindfunction(id));
+ if (found != end)
+ {
+ Assert(found->id == id);
+ return found->index;
+ }
+ else
+ return -1;
+ }
+
+ int32_t GetMuscleCurveTQIndex(int32_t curveIndex)
+ {
+ return (curveIndex < (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount) ? curveIndex%s_ClipMuscleCurveTQCount : -1;
+ };
+
+ float GetXformCurveValue(math::xform const& x, int32_t index)
+ {
+ float ret = 0;
+
+ if(index == 0)
+ {
+ ret = x.t.x().tofloat();
+ }
+ else if(index == 1)
+ {
+ ret = x.t.y().tofloat();
+ }
+ else if(index == 2)
+ {
+ ret = x.t.z().tofloat();
+ }
+ else if(index == 3)
+ {
+ ret = x.q.x().tofloat();
+ }
+ else if(index == 4)
+ {
+ ret = x.q.y().tofloat();
+ }
+ else if(index == 5)
+ {
+ ret = x.q.z().tofloat();
+ }
+ else if(index == 6)
+ {
+ ret = x.q.w().tofloat();
+ }
+
+ return ret;
+ }
+
+ float GetMuscleCurveValue(human::HumanPose const& pose, math::xform const &motionX, int32_t curveIndex)
+ {
+ float ret = 0;
+
+ if(curveIndex < s_ClipMuscleCurveTQCount)
+ {
+ ret = GetXformCurveValue(motionX,curveIndex);
+ }
+ else if(curveIndex < 2*s_ClipMuscleCurveTQCount)
+ {
+ ret = GetXformCurveValue(pose.m_RootX,curveIndex-s_ClipMuscleCurveTQCount);
+ }
+ else if(curveIndex < (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount)
+ {
+ int index = curveIndex - (2*s_ClipMuscleCurveTQCount);
+ int goalIndex = index / s_ClipMuscleCurveTQCount;
+ int xformIndex = index % s_ClipMuscleCurveTQCount;
+
+ ret = GetXformCurveValue(pose.m_GoalArray[goalIndex].m_X,xformIndex);
+ }
+ else if(curveIndex < (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount + human::kLastDoF)
+ {
+ int dofIndex = curveIndex - (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount;
+ ret = pose.m_DoFArray[dofIndex];
+ }
+ else if(curveIndex < (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount + human::kLastDoF + hand::s_DoFCount)
+ {
+ int dofIndex = curveIndex - ((2 + human::kLastGoal) * s_ClipMuscleCurveTQCount + human::kLastDoF);
+ ret = pose.m_LeftHandPose.m_DoFArray[dofIndex];
+ }
+ else if(curveIndex < (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount + human::kLastDoF + 2 * hand::s_DoFCount)
+ {
+ int dofIndex = curveIndex - ((2 + human::kLastGoal) * s_ClipMuscleCurveTQCount + human::kLastDoF + hand::s_DoFCount);
+ ret = pose.m_RightHandPose.m_DoFArray[dofIndex];
+ }
+
+ return ret;
+ }
+
+ bool GetMuscleCurveInMask(human::HumanPoseMask const& mask, int32_t curveIndex)
+ {
+ bool ret = false;
+
+ if(curveIndex < s_ClipMuscleCurveTQCount) // root motion
+ {
+ ret = true;
+ }
+ else if(curveIndex < 2*s_ClipMuscleCurveTQCount) // root xform
+ {
+ ret = mask.test(human::kMaskRootIndex);
+ }
+ else if(curveIndex < (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount)
+ {
+ int index = curveIndex - (2*s_ClipMuscleCurveTQCount);
+ int goalIndex = index / s_ClipMuscleCurveTQCount;
+
+ ret = mask.test(human::kMaskGoalStartIndex+goalIndex);
+ }
+ else if(curveIndex < (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount+ human::kLastDoF)
+ {
+ int dofIndex = curveIndex - (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount;
+ ret = mask.test(human::kMaskDoFStartIndex+dofIndex);
+ }
+ else if(curveIndex < (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount + human::kLastDoF + hand::s_DoFCount)
+ {
+ ret = mask.test(human::kMaskLeftHand);
+ }
+ else if(curveIndex < (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount + human::kLastDoF + 2 * hand::s_DoFCount)
+ {
+ ret = mask.test(human::kMaskRightHand);
+ }
+
+ return ret;
+ }
+
+ ClipMuscleConstant* CreateClipMuscleConstant(Clip * clip, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(ClipMuscleConstant);
+
+ ClipMuscleConstant *clipMuscle = alloc.Construct<ClipMuscleConstant>();
+
+ clipMuscle->m_Clip = clip;
+ clipMuscle->m_Mirror = false;
+
+ clipMuscle->m_StartTime = 0;
+ clipMuscle->m_StopTime = 1;
+
+ clipMuscle->m_Level = 0;
+
+ clipMuscle->m_CycleOffset = 0;
+ clipMuscle->m_LoopTime = false;
+ clipMuscle->m_LoopBlend = false;
+ clipMuscle->m_LoopBlendOrientation = false;
+ clipMuscle->m_LoopBlendPositionY = false;
+ clipMuscle->m_LoopBlendPositionXZ = false;
+ clipMuscle->m_KeepOriginalOrientation = false;
+ clipMuscle->m_KeepOriginalPositionY = true;
+ clipMuscle->m_KeepOriginalPositionXZ = false;
+ clipMuscle->m_HeightFromFeet = false;
+
+ clipMuscle->m_ValueArrayCount = GetClipCurveCount(*clip);
+ clipMuscle->m_ValueArrayDelta = alloc.ConstructArray<ValueDelta>(clipMuscle->m_ValueArrayCount);
+
+ return clipMuscle;
+ }
+
+ void DestroyClipMuscleConstant(ClipMuscleConstant * constant, memory::Allocator& alloc)
+ {
+ if(constant)
+ {
+ alloc.Deallocate(constant->m_ValueArrayDelta);
+ alloc.Deallocate(constant);
+ }
+ }
+
+ void MotionOutputClear(MotionOutput *output)
+ {
+ output->m_DX.t = math::float4::zero();
+ output->m_DX.q = math::quatIdentity();
+ output->m_DX.s = math::float4::one();
+
+ output->m_MotionX.t = math::float4::zero();
+ output->m_MotionX.q = math::quatIdentity();
+ output->m_MotionX.s = math::float4::one();
+
+ output->m_MotionStartX.t = math::float4::zero();
+ output->m_MotionStartX.q = math::quatIdentity();
+ output->m_MotionStartX.s = math::float4::one();
+
+ output->m_MotionStopX.t = math::float4::zero();
+ output->m_MotionStopX.q = math::quatIdentity();
+ output->m_MotionStopX.s = math::float4::one();
+
+ output->m_PrevRootX.t = math::float4::zero();
+ output->m_PrevRootX.q = math::quatIdentity();
+ output->m_PrevRootX.s = math::float4::one();
+
+ output->m_PrevLeftFootX.t = math::float4::zero();
+ output->m_PrevLeftFootX.q = math::quatIdentity();
+ output->m_PrevLeftFootX.s = math::float4::one();
+
+ output->m_PrevRightFootX.t = math::float4::zero();
+ output->m_PrevRightFootX.q = math::quatIdentity();
+ output->m_PrevRightFootX.s = math::float4::one();
+
+ output->m_TargetX.t = math::float4::zero();
+ output->m_TargetX.q = math::quatIdentity();
+ output->m_TargetX.s = math::float4::one();
+
+ output->m_GravityWeight = 0;
+ }
+
+ void MotionOutputCopy(MotionOutput *output, MotionOutput const *motion, bool hasRootMotion, bool isHuman, human::HumanPoseMask const &poseMask)
+ {
+ if(hasRootMotion || (isHuman && poseMask.test(human::kMaskRootIndex)))
+ {
+ output->m_DX = motion->m_DX;
+ output->m_GravityWeight = motion->m_GravityWeight;
+ }
+
+ if(hasRootMotion)
+ {
+ output->m_MotionX = motion->m_MotionX;
+ output->m_MotionStartX = motion->m_MotionStartX;
+ output->m_MotionStopX = motion->m_MotionStopX;
+ }
+
+ if(isHuman)
+ {
+ if(human::MaskHasLegs(poseMask))
+ {
+ output->m_PrevRootX = motion->m_PrevRootX;
+ output->m_PrevLeftFootX = motion->m_PrevLeftFootX;
+ output->m_PrevRightFootX = motion->m_PrevRightFootX;
+ }
+
+ if(poseMask.test(human::kMaskRootIndex))
+ {
+ output->m_TargetX = motion->m_TargetX;
+ }
+ }
+ }
+
+ void MotionOutputBlend(MotionOutput *output, MotionOutput **outputArray, float *weight, uint32_t count, bool hasRootMotion, bool isHuman, human::HumanPoseMask const &poseMask)
+ {
+ output->m_DX.t = math::float4::zero();
+ output->m_DX.q = math::float4::zero();
+ output->m_DX.s = math::float4::one();
+
+ output->m_MotionX.t = math::float4::zero();
+ output->m_MotionX.q = math::float4::zero();
+ output->m_MotionX.s = math::float4::one();
+
+ output->m_MotionStartX.t = math::float4::zero();
+ output->m_MotionStartX.q = math::float4::zero();
+ output->m_MotionStartX.s = math::float4::one();
+
+ output->m_MotionStopX.t = math::float4::zero();
+ output->m_MotionStopX.q = math::float4::zero();
+ output->m_MotionStopX.s = math::float4::one();
+
+ output->m_PrevRootX.t = math::float4::zero();
+ output->m_PrevRootX.q = math::float4::zero();
+ output->m_PrevRootX.s = math::float4::one();
+
+ output->m_PrevLeftFootX.t = math::float4::zero();
+ output->m_PrevLeftFootX.q = math::float4::zero();
+ output->m_PrevLeftFootX.s = math::float4::one();
+
+ output->m_PrevRightFootX.t = math::float4::zero();
+ output->m_PrevRightFootX.q = math::float4::zero();
+ output->m_PrevRightFootX.s = math::float4::one();
+
+ output->m_TargetX.t = math::float4::zero();
+ output->m_TargetX.q = math::float4::zero();
+ output->m_TargetX.s = math::float4::one();
+
+ output->m_GravityWeight = 0;
+
+ float sumW = 0;
+
+ for(int iter = 0; iter < count; iter++)
+ {
+ float w = weight[iter];
+
+ math::float1 w1(w);
+
+ sumW += w;
+
+ if(hasRootMotion || (isHuman && poseMask.test(human::kMaskRootIndex)))
+ {
+ output->m_DX.t += outputArray[iter]->m_DX.t*w1;
+ output->m_DX.q += math::cond(math::dot(output->m_DX.q,outputArray[iter]->m_DX.q) < math::float1::zero(),outputArray[iter]->m_DX.q * -w1,outputArray[iter]->m_DX.q * w1);
+
+ output->m_GravityWeight += outputArray[iter]->m_GravityWeight*w;
+ }
+
+ if(hasRootMotion)
+ {
+ output->m_MotionX.t += outputArray[iter]->m_MotionX.t*w1;
+ output->m_MotionX.q += math::cond(math::dot(output->m_MotionX.q,outputArray[iter]->m_MotionX.q) < math::float1::zero(),outputArray[iter]->m_MotionX.q * -w1,outputArray[iter]->m_MotionX.q * w1);
+
+ output->m_MotionStartX.t += outputArray[iter]->m_MotionStartX.t*w1;
+ output->m_MotionStartX.q += math::cond(math::dot(output->m_MotionStartX.q,outputArray[iter]->m_MotionStartX.q) < math::float1::zero(),outputArray[iter]->m_MotionStartX.q * -w1,outputArray[iter]->m_MotionStartX.q * w1);
+
+ output->m_MotionStopX.t += outputArray[iter]->m_MotionStopX.t*w1;
+ output->m_MotionStopX.q += math::cond(math::dot(output->m_MotionStopX.q,outputArray[iter]->m_MotionStopX.q) < math::float1::zero(),outputArray[iter]->m_MotionStopX.q * -w1,outputArray[iter]->m_MotionStopX.q * w1);
+ }
+
+ if(isHuman)
+ {
+ if(human::MaskHasLegs(poseMask))
+ {
+ output->m_PrevRootX.t += outputArray[iter]->m_PrevRootX.t*w1;
+ output->m_PrevRootX.q += math::cond(math::dot(output->m_PrevRootX.q,outputArray[iter]->m_PrevRootX.q) < math::float1::zero(),outputArray[iter]->m_PrevRootX.q * -w1,outputArray[iter]->m_PrevRootX.q * w1);
+
+ output->m_PrevLeftFootX.t += outputArray[iter]->m_PrevLeftFootX.t*w1;
+ output->m_PrevLeftFootX.q += math::cond(math::dot(output->m_PrevLeftFootX.q,outputArray[iter]->m_PrevLeftFootX.q) < math::float1::zero(),outputArray[iter]->m_PrevLeftFootX.q * -w1,outputArray[iter]->m_PrevLeftFootX.q * w1);
+
+ output->m_PrevRightFootX.t += outputArray[iter]->m_PrevRightFootX.t*w1;
+ output->m_PrevRightFootX.q += math::cond(math::dot(output->m_PrevRightFootX.q,outputArray[iter]->m_PrevRightFootX.q) < math::float1::zero(),outputArray[iter]->m_PrevRightFootX.q * -w1,outputArray[iter]->m_PrevRightFootX.q * w1);
+ }
+
+ if(poseMask.test(human::kMaskRootIndex))
+ {
+ output->m_TargetX.t += outputArray[iter]->m_TargetX.t*w1;
+ output->m_TargetX.q += math::cond(math::dot(output->m_TargetX.q,outputArray[iter]->m_TargetX.q) < math::float1::zero(),outputArray[iter]->m_TargetX.q * -w1,outputArray[iter]->m_TargetX.q * w1);
+ }
+ }
+ }
+
+ math::float4 q(0,0,0,math::saturate(1.0f-sumW));
+
+ if(hasRootMotion || (isHuman && poseMask.test(human::kMaskRootIndex)))
+ {
+ output->m_DX.q = math::normalize(output->m_DX.q+q);
+ if(sumW > 0) output->m_GravityWeight /= sumW;
+ }
+
+ if(hasRootMotion)
+ {
+ output->m_MotionX.q = math::normalize(output->m_MotionX.q+q);
+ output->m_MotionStartX.q = math::normalize(output->m_MotionStartX.q+q);
+ output->m_MotionStopX.q = math::normalize(output->m_MotionStopX.q+q);
+ }
+
+ if(isHuman)
+ {
+ if(human::MaskHasLegs(poseMask))
+ {
+ output->m_PrevRootX.q = math::normalize(output->m_PrevRootX.q+q);
+ output->m_PrevLeftFootX.q = math::normalize(output->m_PrevLeftFootX.q+q);
+ output->m_PrevRightFootX.q = math::normalize(output->m_PrevRightFootX.q+q);
+ }
+
+ if(poseMask.test(human::kMaskRootIndex))
+ {
+ output->m_TargetX.q = math::normalize(output->m_TargetX.q+q);
+ }
+ }
+ }
+
+ void MotionAddAdditiveLayer(MotionOutput *output, MotionOutput const *motion, float weight, bool hasRootMotion, bool isHuman, human::HumanPoseMask const &poseMask)
+ {
+ math::float1 w(weight);
+
+ if(hasRootMotion || (isHuman && poseMask.test(human::kMaskRootIndex)))
+ {
+ output->m_DX = math::xformMul(output->m_DX, math::xformWeight(motion->m_DX,w));
+ output->m_GravityWeight = output->m_GravityWeight + motion->m_GravityWeight * weight;
+ }
+
+ if(hasRootMotion)
+ {
+ output->m_MotionX = math::xformMul(output->m_MotionX, math::xformWeight(motion->m_MotionX,w));
+ output->m_MotionStartX = math::xformMul(output->m_MotionStartX, math::xformWeight(motion->m_MotionStartX,w));
+ output->m_MotionStopX = math::xformMul(output->m_MotionStopX, math::xformWeight(motion->m_MotionStopX,w));
+ }
+
+ if(isHuman)
+ {
+ if(human::MaskHasLegs(poseMask))
+ {
+ output->m_PrevRootX = math::xformMul(output->m_PrevRootX, math::xformWeight(motion->m_PrevRootX,w));
+ output->m_PrevLeftFootX = math::xformMul(output->m_PrevLeftFootX, math::xformWeight(motion->m_PrevLeftFootX,w));
+ output->m_PrevRightFootX = math::xformMul(output->m_PrevRightFootX, math::xformWeight(motion->m_PrevRightFootX,w));
+ }
+
+ if(poseMask.test(human::kMaskRootIndex))
+ {
+ output->m_TargetX = math::xformMul(output->m_TargetX, math::xformWeight(motion->m_TargetX,w));
+ }
+ }
+ }
+
+ void MotionAddOverrideLayer(MotionOutput *output, MotionOutput const *motion, float weight, bool hasRootMotion, bool isHuman, human::HumanPoseMask const &poseMask)
+ {
+ if(weight < 1.0f)
+ {
+ math::float1 w(weight);
+
+ if(hasRootMotion || (isHuman && poseMask.test(human::kMaskRootIndex)))
+ {
+ output->m_DX = math::xformBlend(output->m_DX,motion->m_DX,w);
+ output->m_GravityWeight = math::lerp(output->m_GravityWeight,motion->m_GravityWeight,weight);
+ }
+
+ if(hasRootMotion)
+ {
+ output->m_MotionX = math::xformBlend(output->m_MotionX,motion->m_MotionX,w);
+ output->m_MotionStartX = math::xformBlend(output->m_MotionStartX,motion->m_MotionStartX,w);
+ output->m_MotionStopX = math::xformBlend(output->m_MotionStopX,motion->m_MotionStopX,w);
+ }
+
+ if(isHuman)
+ {
+ if(human::MaskHasLegs(poseMask))
+ {
+ output->m_PrevRootX = math::xformBlend(output->m_PrevRootX,motion->m_PrevRootX,w);
+ output->m_PrevLeftFootX = math::xformBlend(output->m_PrevLeftFootX,motion->m_PrevLeftFootX,w);
+ output->m_PrevRightFootX = math::xformBlend(output->m_PrevRightFootX,motion->m_PrevRightFootX,w);
+ }
+
+ if(poseMask.test(human::kMaskRootIndex))
+ {
+ output->m_TargetX = math::xformBlend(output->m_TargetX,motion->m_TargetX,w);
+ }
+ }
+ }
+ else
+ {
+ MotionOutputCopy(output,motion,hasRootMotion,isHuman,poseMask);
+ }
+ }
+
+ ClipMuscleInput* CreateClipMuscleInput(ClipMuscleConstant const* constant, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(ClipMuscleInput);
+
+ ClipMuscleInput *in = alloc.Construct<ClipMuscleInput>();
+ return in;
+ }
+
+ void DestroyClipMuscleInput(ClipMuscleInput * input, memory::Allocator& alloc)
+ {
+ if(input)
+ {
+ alloc.Deallocate(input);
+ }
+ }
+
+ float ComputeClipTime(float normalizedTimeIn, float startTime, float stopTime, float cycleOffset, bool loop, bool reverse, float &normalizedTimeOut, float &timeInt)
+ {
+ float timeFrac = math::cond(loop,math::modf(normalizedTimeIn+cycleOffset,timeInt),math::saturate(normalizedTimeIn));
+ normalizedTimeOut = math::cond(!reverse, timeFrac, 1.f - timeFrac);
+ return startTime+normalizedTimeOut*(stopTime-startTime);
+ }
+
+ math::xform EvaluateMotion(ClipMuscleConstant const& constant, ClipMemory &memory, float time)
+ {
+ math::xform ret;
+
+ float ATTRIBUTE_ALIGN(ALIGN4F) value[4];
+
+ ClipInput clipIn;
+ clipIn.m_Time = time;
+
+ value[0] = constant.m_IndexArray[0] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[0]) : 0;
+ value[1] = constant.m_IndexArray[1] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[1]) : 0;
+ value[2] = constant.m_IndexArray[2] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[2]) : 0;
+ value[3] = 0.f;
+
+ ret.t = math::load(value);
+
+ value[0] = constant.m_IndexArray[3] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[3]) : 0;
+ value[1] = constant.m_IndexArray[4] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[4]) : 0;
+ value[2] = constant.m_IndexArray[5] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[5]) : 0;
+ value[3] = constant.m_IndexArray[6] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[6]) : 1;
+
+ ret.q = math::normalize(math::load(value));
+
+ return ret;
+ }
+
+ math::xform EvaluateRoot(ClipMuscleConstant const& constant, ClipMemory &memory, float time)
+ {
+ math::xform ret;
+
+ float ATTRIBUTE_ALIGN(ALIGN4F) value[4];
+
+ ClipInput clipIn;
+ clipIn.m_Time = time;
+
+ value[0] = constant.m_IndexArray[7] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[7]) : 0;
+ value[1] = constant.m_IndexArray[8] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[8]) : 0;
+ value[2] = constant.m_IndexArray[9] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[9]) : 0;
+ value[3] = 0.f;
+
+ ret.t = math::load(value);
+
+ value[0] = constant.m_IndexArray[10] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[10]) : 0;
+ value[1] = constant.m_IndexArray[11] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[11]) : 0;
+ value[2] = constant.m_IndexArray[12] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[12]) : 0;
+ value[3] = constant.m_IndexArray[13] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory, constant.m_IndexArray[13]) : 1;
+
+ ret.q = math::normalize(math::load(value));
+
+ return ret;
+ }
+
+ math::xform EvaluateGoal(ClipMuscleConstant const& constant, ClipMemory &memory, float time, int32_t goalIndex)
+ {
+ math::xform ret;
+
+ float ATTRIBUTE_ALIGN(ALIGN4F) value[4];
+
+ const int32_t index = (2 + goalIndex) * s_ClipMuscleCurveTQCount;
+
+ ClipInput clipIn;
+ clipIn.m_Time = time;
+
+ value[0] = constant.m_IndexArray[index+0] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory,constant.m_IndexArray[index+0]) : 0;
+ value[1] = constant.m_IndexArray[index+1] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory,constant.m_IndexArray[index+1]) : 0;
+ value[2] = constant.m_IndexArray[index+2] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory,constant.m_IndexArray[index+2]) : 0;
+ value[3] = 0.f;
+
+ ret.t = math::load(value);
+
+ value[0] = constant.m_IndexArray[index+3] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory,constant.m_IndexArray[index+3]) : 0;
+ value[1] = constant.m_IndexArray[index+4] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory,constant.m_IndexArray[index+4]) : 0;
+ value[2] = constant.m_IndexArray[index+5] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory,constant.m_IndexArray[index+5]) : 0;
+ value[3] = constant.m_IndexArray[index+6] != -1 ? EvaluateClipAtIndex(constant.m_Clip.Get(), &clipIn, &memory,constant.m_IndexArray[index+6]) : 1;
+
+ ret.q = math::normalize(math::load(value));
+
+ return ret;
+ }
+
+ math::xform GetMotionX(const ClipMuscleConstant& constant, const ClipOutput &output)
+ {
+ math::xform ret;
+
+ float ATTRIBUTE_ALIGN(ALIGN4F) value[4];
+
+ uint32_t currentCurveIndex = 0;
+
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[0] = output.m_Values[constant.m_IndexArray[currentCurveIndex]]; else value[0] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[1] = output.m_Values[constant.m_IndexArray[currentCurveIndex]]; else value[1] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[2] = output.m_Values[constant.m_IndexArray[currentCurveIndex]]; else value[2] = 0; currentCurveIndex++;
+ value[3] = 0;
+
+ ret.t = math::load(value);
+
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[0] = output.m_Values[constant.m_IndexArray[currentCurveIndex]]; else value[0] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[1] = output.m_Values[constant.m_IndexArray[currentCurveIndex]]; else value[1] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[2] = output.m_Values[constant.m_IndexArray[currentCurveIndex]]; else value[2] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[3] = output.m_Values[constant.m_IndexArray[currentCurveIndex]]; else value[3] = 1.0f; currentCurveIndex++;
+
+ ret.q = math::normalize(math::load(value));
+
+ ret.s = math::float4::one();
+
+ return ret;
+ }
+
+ template<typename TYPE>
+ struct ValueAccessor
+ {
+ ValueAccessor(const TYPE* t) : m_Values(t)
+ {}
+
+ const TYPE* m_Values;
+
+ float operator[](int index)
+ {
+ return m_Values[index];
+ }
+ };
+
+ template<>
+ struct ValueAccessor<ValueDelta>
+ {
+ ValueAccessor(const ValueDelta* t) : m_Values(t)
+ {}
+
+ const ValueDelta* m_Values;
+
+ float operator[](int index)
+ {
+ return m_Values[index].m_Start;
+ }
+ };
+
+ template<typename TYPE>
+ void GetHumanPose(const ClipMuscleConstant& constant, const TYPE* values, human::HumanPose &humanPose)
+ {
+ float ATTRIBUTE_ALIGN(ALIGN4F) value[4];
+
+ ValueAccessor<TYPE> accessor(values);
+
+ uint32_t i,currentCurveIndex = s_ClipMotionCurveCount;
+
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[0] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[0] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[1] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[1] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[2] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[2] = 0; currentCurveIndex++;
+ value[3] = 0;
+
+ humanPose.m_RootX.t = math::load(value);
+
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[0] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[0] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[1] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[1] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[2] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[2] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[3] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[3] = 1.0f; currentCurveIndex++;
+
+ humanPose.m_RootX.q = math::normalize(math::load(value));
+ humanPose.m_RootX.s = math::float4::one();
+
+ for(i = 0; i < human::kLastGoal; i++)
+ {
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[0] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[0] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[1] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[1] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[2] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[2] = 0; currentCurveIndex++;
+ value[3] = 0;
+
+ humanPose.m_GoalArray[i].m_X.t = math::load(value);
+
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[0] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[0] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[1] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[1] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[2] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[2] = 0; currentCurveIndex++;
+ if(constant.m_IndexArray[currentCurveIndex] != -1) value[3] = accessor[constant.m_IndexArray[currentCurveIndex]]; else value[3] = 1.0f; currentCurveIndex++;
+
+
+ humanPose.m_GoalArray[i].m_X.q = math::load(value);
+ humanPose.m_GoalArray[i].m_X.q = math::normalize(humanPose.m_GoalArray[i].m_X.q);
+ }
+
+ for(i = 0 ; i < human::kLastDoF; i++)
+ {
+ if(constant.m_IndexArray[currentCurveIndex] != -1) humanPose.m_DoFArray[i] = accessor[constant.m_IndexArray[currentCurveIndex]];
+ else humanPose.m_DoFArray[i] = 0;
+ currentCurveIndex++;
+ }
+
+ for(i = 0 ; i < hand::s_DoFCount; i++)
+ {
+ if(constant.m_IndexArray[currentCurveIndex] != -1) humanPose.m_LeftHandPose.m_DoFArray[i] = accessor[constant.m_IndexArray[currentCurveIndex]];
+ else humanPose.m_LeftHandPose.m_DoFArray[i] = 0;
+ currentCurveIndex++;
+ }
+
+ for(i = 0 ; i < hand::s_DoFCount; i++)
+ {
+ if(constant.m_IndexArray[currentCurveIndex] != -1) humanPose.m_RightHandPose.m_DoFArray[i] = accessor[constant.m_IndexArray[currentCurveIndex]];
+ else humanPose.m_RightHandPose.m_DoFArray[i] = 0;
+ currentCurveIndex++;
+ }
+ }
+
+ void GetHumanPose(const ClipMuscleConstant& constant, const float* values, human::HumanPose &humanPose)
+ {
+ GetHumanPose<float>(constant, values, humanPose);
+ }
+
+ void GetHumanPose(const ClipMuscleConstant& constant, const ValueDelta* values, human::HumanPose &humanPose)
+ {
+ GetHumanPose<ValueDelta>(constant, values, humanPose);
+ }
+
+
+ // root motion extraction WIP
+ void EvaluateClipRootMotionDeltaX(const ClipMuscleConstant& constant, const ClipMuscleInput &input, MotionOutput &output, ClipMemory &memory)
+ {
+ output.m_DX = math::xformIdentity();
+ output.m_MotionX = math::xformIdentity();
+
+ if(constant.m_IndexArray[0] != -1)
+ {
+ float cycleOffset = constant.m_CycleOffset + input.m_CycleOffset;
+
+ float prevTime;
+ float prevTimeFrac = math::cond(constant.m_LoopTime,math::modf(input.m_PreviousTime+cycleOffset,prevTime),math::saturate(input.m_PreviousTime));
+ prevTimeFrac = math::cond(!input.m_Reverse, prevTimeFrac, 1.f - prevTimeFrac);
+ prevTime = constant.m_StartTime+prevTimeFrac*(constant.m_StopTime-constant.m_StartTime);
+
+ float currentTime;
+ float lTimeFrac = math::cond(constant.m_LoopTime,math::modf(input.m_Time+cycleOffset,currentTime),math::saturate(input.m_Time));
+ lTimeFrac = math::cond(!input.m_Reverse, lTimeFrac, 1.f - lTimeFrac);
+ currentTime = constant.m_StartTime+lTimeFrac*(constant.m_StopTime-constant.m_StartTime);
+
+ math::xform refStartX(constant.m_MotionStartX);
+ math::xform refStopX(constant.m_MotionStopX);
+ math::xform prevRefX(EvaluateMotion(constant,memory,prevTime));
+ math::xform refX(EvaluateMotion(constant,memory,currentTime));
+
+ refStartX.q = math::quatProjOnYPlane(refStartX.q);
+ refStopX.q = math::quatProjOnYPlane(refStopX.q);
+ prevRefX.q = math::quatProjOnYPlane(prevRefX.q);
+ refX.q = math::quatProjOnYPlane(refX.q);
+
+ math::float4 refOffsetT = math::float4(0,constant.m_Level,0,0);
+ math::float4 refOffsetQ = math::qtan2Quat(math::float4(0,math::halfTan(math::radians(constant.m_OrientationOffsetY)),0,1));
+
+ if(constant.m_KeepOriginalPositionY)
+ {
+ refOffsetT.y() -= refStartX.t.y();
+ }
+
+ if(constant.m_KeepOriginalPositionXZ)
+ {
+ refOffsetT.x() -= refStartX.t.x();
+ refOffsetT.z() -= refStartX.t.z();
+ }
+
+ if(constant.m_KeepOriginalOrientation)
+ {
+ refOffsetQ = math::normalize(math::quatMul(refOffsetQ,math::quatConj(refStartX.q)));
+ }
+
+ refStartX.t = refStartX.t + refOffsetT;
+ refStopX.t = refStopX.t + refOffsetT;
+ prevRefX.t = prevRefX.t + refOffsetT;
+ refX.t = refX.t + refOffsetT;
+
+ refStartX.q = math::normalize(math::quatMul(refOffsetQ,refStartX.q));
+ refStopX.q = math::normalize(math::quatMul(refOffsetQ,refStopX.q));
+ prevRefX.q = math::normalize(math::quatMul(refOffsetQ,prevRefX.q));
+ refX.q = math::normalize(math::quatMul(refOffsetQ,refX.q));
+
+ if(constant.m_LoopBlendOrientation)
+ {
+ refStopX.q = refStartX.q;
+ prevRefX.q = refStartX.q;
+ refX.q = refStartX.q;
+ }
+
+ if(constant.m_LoopBlendPositionY)
+ {
+ refStopX.t.y() = refStartX.t.y();
+ prevRefX.t.y() = refStartX.t.y();
+ refX.t.y() = refStartX.t.y();
+ }
+
+ if(constant.m_LoopBlendPositionXZ)
+ {
+ refStartX.t.x() = refStartX.t.x();
+ refStopX.t.x() = refStartX.t.x();;
+ prevRefX.t.x() = refStartX.t.x();;
+ refX.t.x() = refStartX.t.x();;
+
+ refStartX.t.z() = refStartX.t.z();
+ refStopX.t.z() = refStartX.t.z();;
+ prevRefX.t.z() = refStartX.t.z();;
+ refX.t.z() = refStartX.t.z();;
+ }
+
+ output.m_MotionX = refX;
+ output.m_MotionStartX = refStartX;
+ output.m_MotionStopX = refStopX;
+
+ if(constant.m_LoopTime)
+ {
+ float deltaTime = math::abs(lTimeFrac - prevTimeFrac);
+
+ if(deltaTime > 0.5f)
+ {
+ if(lTimeFrac < prevTimeFrac)
+ {
+ output.m_DX = math::xformInvMulNS(prevRefX,math::xformMul(refStopX,math::xformInvMulNS(refStartX,refX)));
+ }
+ else if(lTimeFrac > prevTimeFrac)
+ {
+ output.m_DX = math::xformInvMulNS(prevRefX,math::xformMul(refStartX,math::xformInvMulNS(refStopX,refX)));
+ }
+ }
+ else if(lTimeFrac != prevTimeFrac)
+ {
+ output.m_DX = math::xformInvMulNS(prevRefX,refX);
+ }
+ }
+ else
+ {
+ if(input.m_PreviousTime != input.m_Time)
+ {
+ output.m_DX = math::xformInvMulNS(prevRefX,refX);
+ }
+ }
+
+ output.m_DX.q = math::quat2Qtan(output.m_DX.q);
+
+ output.m_DX.q.x() = 0;
+ output.m_DX.q.z() = 0;
+
+ if(constant.m_LoopBlendPositionY)
+ {
+ output.m_DX.t.y() = 0;
+ }
+
+ if(constant.m_LoopBlendPositionXZ)
+ {
+ output.m_DX.t.x() = 0;
+ output.m_DX.t.z() = 0;
+ }
+
+ if(constant.m_LoopBlendOrientation)
+ {
+ output.m_DX.q.y() = 0;
+ }
+
+ output.m_DX.q = math::qtan2Quat(output.m_DX.q);
+ }
+ }
+
+ void EvaluateClipMusclePrevTime(const ClipMuscleConstant& constant, const ClipMuscleInput &input, MotionOutput &output, ClipMemory &memory)
+ {
+ float prevTimeInt;
+ float prevTimeFrac;
+ float prevTime = ComputeClipTime(input.m_PreviousTime,constant.m_StartTime,constant.m_StopTime,constant.m_CycleOffset+input.m_CycleOffset,constant.m_LoopTime,input.m_Reverse,prevTimeFrac,prevTimeInt);
+
+ output.m_PrevRootX = EvaluateRoot(constant,memory,prevTime);
+ output.m_PrevLeftFootX = EvaluateGoal(constant,memory,prevTime,human::kLeftFootGoal);
+ output.m_PrevRightFootX = EvaluateGoal(constant,memory,prevTime,human::kRightFootGoal);
+ }
+
+ void EvaluateClipMuscle(const ClipMuscleConstant& constant, const ClipMuscleInput &input, const float *valuesOutput, MotionOutput &motionOutput, human::HumanPose &humanPose, ClipMemory &memory)
+ {
+ float cycleOffset = constant.m_CycleOffset+input.m_CycleOffset;
+ float prevTimeInt;
+ float prevTimeFrac;
+ ComputeClipTime(input.m_PreviousTime,constant.m_StartTime,constant.m_StopTime,cycleOffset,constant.m_LoopTime,input.m_Reverse,prevTimeFrac,prevTimeInt);
+
+ ClipInput in;
+ float timeFrac;
+ float currentTimeInt;
+ in.m_Time = ComputeClipTime(input.m_Time,constant.m_StartTime,constant.m_StopTime,cycleOffset,constant.m_LoopTime,input.m_Reverse,timeFrac,currentTimeInt);
+
+ bool mirror = (constant.m_Mirror || input.m_Mirror) && !(constant.m_Mirror && input.m_Mirror);
+
+ math::xform stopX = math::xformMulInv(constant.m_StartX,constant.m_DeltaPose.m_RootX);
+
+ math::xform prevRootX = motionOutput.m_PrevRootX ;
+ math::xform prevLeftFootX = motionOutput.m_PrevLeftFootX;
+ math::xform prevRightFootX = motionOutput.m_PrevRightFootX;
+
+ GetHumanPose(constant,valuesOutput,humanPose);
+ math::xform rootX = humanPose.m_RootX;
+
+ math::xform refStartX(constant.m_StartX.t,math::quatProjOnYPlane(constant.m_StartX.q),constant.m_StartX.s);
+ math::xform refStopX(stopX.t,math::quatProjOnYPlane(stopX.q),stopX.s);
+ math::xform prevRefX(prevRootX.t,math::quatProjOnYPlane(prevRootX.q),prevRootX.s);
+ math::xform refX(rootX.t,math::quatProjOnYPlane(rootX.q),rootX.s);
+
+ // todo: target stuff most likely breaks curve cache
+ float targetTime = constant.m_StopTime;
+ float targetTimeFrac = 1;
+ math::xform targetRootX = stopX;
+ math::xform targetRefX = refStopX;
+ math::xform targetGoalX = math::xformIdentity();
+
+ float targetTimeInt = 0;
+ if(input.m_TargetTime != 1)
+ {
+ targetTime = ComputeClipTime(input.m_TargetTime,constant.m_StartTime,constant.m_StopTime,cycleOffset,constant.m_LoopTime,input.m_Reverse,targetTimeFrac,targetTimeInt);
+
+ targetRootX = EvaluateRoot(constant,memory,targetTime);
+ targetRefX = math::xform(targetRootX.t,math::quatProjOnYPlane(targetRootX.q),targetRootX.s);
+ }
+
+ int32_t targetGoalIndex = input.m_TargetIndex - kTargetLeftFoot;
+
+ if(targetGoalIndex >= 0)
+ {
+ targetGoalIndex = mirror ? ((targetGoalIndex % 2) ? targetGoalIndex-1 : targetGoalIndex+1) : targetGoalIndex;
+ targetGoalX = EvaluateGoal(constant,memory,targetTime,targetGoalIndex);
+ }
+ //////////////////////////////////////////
+
+ if(constant.m_HeightFromFeet)
+ {
+ math::xform refLeftFootStartX = math::xformMul(constant.m_StartX,constant.m_LeftFootStartX);
+ math::xform refRightFootStartX = math::xformMul(constant.m_StartX,constant.m_RightFootStartX);
+
+ math::xform refLeftFootStopX = math::xformMul(stopX,math::xformMulInv(constant.m_LeftFootStartX,constant.m_DeltaPose.m_GoalArray[human::kLeftFootGoal].m_X));
+ math::xform refRightFootStopX = math::xformMul(stopX,math::xformMulInv(constant.m_RightFootStartX,constant.m_DeltaPose.m_GoalArray[human::kRightFootGoal].m_X));
+
+ math::xform refLeftFootPrevX = math::xformMul(prevRootX,prevLeftFootX);
+ math::xform refRightFootPrevX = math::xformMul(prevRootX,prevRightFootX);
+
+ math::xform refLeftFootX = math::xformMul(rootX,humanPose.m_GoalArray[human::kLeftFootGoal].m_X);
+ math::xform refRightFootX = math::xformMul(rootX,humanPose.m_GoalArray[human::kRightFootGoal].m_X);
+
+ math::xform refTargetLeftFootX = math::xformMul(targetRootX,EvaluateGoal(constant,memory,targetTime,human::kLeftFootGoal));
+ math::xform refTargetRightFootX = math::xformMul(targetRootX,EvaluateGoal(constant,memory,targetTime,human::kRightFootGoal));
+
+ refStartX.t.y() = math::minimum(refStartX.t,math::minimum(refLeftFootStartX.t,refRightFootStartX.t)).y();
+ refStopX.t.y() = math::minimum(refStopX.t,math::minimum(refLeftFootStopX.t,refRightFootStopX.t)).y();
+ prevRefX.t.y() = math::minimum(prevRefX.t,math::minimum(refLeftFootPrevX.t,refRightFootPrevX.t)).y();
+ refX.t.y() = math::minimum(refX.t,math::minimum(refLeftFootX.t,refRightFootX.t)).y();
+ targetRefX.t.y() = math::minimum(targetRefX.t,math::minimum(refTargetLeftFootX.t,refTargetRightFootX.t)).y();
+ }
+
+ math::float4 refOffsetT = math::float4(0,constant.m_Level,0,0);
+ math::float4 refOffsetQ = math::qtan2Quat(math::float4(0,math::halfTan(math::radians(constant.m_OrientationOffsetY)),0,1));
+
+ if(constant.m_KeepOriginalPositionY)
+ {
+ refOffsetT.y() -= refStartX.t.y();
+ }
+
+ if(constant.m_KeepOriginalPositionXZ)
+ {
+ refOffsetT.x() -= refStartX.t.x();
+ refOffsetT.z() -= refStartX.t.z();
+ }
+
+ if(constant.m_KeepOriginalOrientation)
+ {
+ refOffsetQ = math::normalize(math::quatMul(refOffsetQ,math::quatConj(refStartX.q)));
+ }
+
+ refStartX.t = refStartX.t + refOffsetT;
+ refStopX.t = refStopX.t + refOffsetT;
+ prevRefX.t = prevRefX.t + refOffsetT;
+ refX.t = refX.t + refOffsetT;
+ targetRefX.t = targetRefX.t + refOffsetT;
+
+ refStartX.q = math::normalize(math::quatMul(refOffsetQ,refStartX.q));
+ refStopX.q = math::normalize(math::quatMul(refOffsetQ,refStopX.q));
+ prevRefX.q = math::normalize(math::quatMul(refOffsetQ,prevRefX.q));
+ refX.q = math::normalize(math::quatMul(refOffsetQ,refX.q));
+ targetRefX.q = math::normalize(math::quatMul(refOffsetQ,targetRefX.q));
+
+ if(constant.m_LoopBlendOrientation)
+ {
+ refStopX.q = refStartX.q;
+ prevRefX.q = refStartX.q;
+ refX.q = refStartX.q;
+ targetRefX.q = refStartX.q;
+ }
+
+ if(constant.m_LoopBlendPositionY)
+ {
+ refStopX.t.y() = refStartX.t.y();
+ prevRefX.t.y() = refStartX.t.y();
+ refX.t.y() = refStartX.t.y();
+ targetRefX.t.y() = refStartX.t.y();
+ }
+
+ if(constant.m_LoopBlendPositionXZ)
+ {
+ refStartX.t.x() = refStartX.t.x();
+ refStopX.t.x() = refStartX.t.x();;
+ prevRefX.t.x() = refStartX.t.x();;
+ refX.t.x() = refStartX.t.x();;
+ targetRefX.t.x() = refStartX.t.x();;
+
+ refStartX.t.z() = refStartX.t.z();
+ refStopX.t.z() = refStartX.t.z();;
+ prevRefX.t.z() = refStartX.t.z();;
+ refX.t.z() = refStartX.t.z();;
+ targetRefX.t.z() = refStartX.t.z();;
+ }
+
+ prevRootX = math::xformInvMulNS(prevRefX,prevRootX);
+ rootX = math::xformInvMulNS(refX,rootX);
+ targetRootX = math::xformInvMulNS(targetRefX,targetRootX);
+
+ if(constant.m_LoopTime && constant.m_LoopBlend)
+ {
+ human::HumanPose deltaPose;
+ human::HumanPoseWeight(deltaPose,constant.m_DeltaPose,timeFrac);
+ human::HumanPoseAdd(humanPose,humanPose,deltaPose);
+
+ prevLeftFootX = math::xformMul(prevLeftFootX,math::xformWeight(constant.m_DeltaPose.m_GoalArray[human::kLeftFootGoal].m_X,math::float1(prevTimeFrac)));
+ prevRightFootX = math::xformMul(prevRightFootX,math::xformWeight(constant.m_DeltaPose.m_GoalArray[human::kRightFootGoal].m_X,math::float1(prevTimeFrac)));
+
+ math::xform rootDeltaX = math::xformInvMulNS(math::xformInvMulNS(refStopX,stopX),math::xformInvMulNS(refStartX,constant.m_StartX));
+ math::xform prevRootDeltaX = math::xformWeight(rootDeltaX,math::float1(prevTimeFrac));
+ math::xform targetRootDeltaX = math::xformWeight(rootDeltaX,math::float1(targetTimeFrac));
+ rootDeltaX = math::xformWeight(rootDeltaX,math::float1(timeFrac));
+ prevRootX = math::xformMul(prevRootX,prevRootDeltaX);
+ rootX = math::xformMul(rootX,rootDeltaX);
+ targetRootX = math::xformMul(targetRootX,targetRootDeltaX);
+
+ math::xform cycleDelta;
+ for(int i = 0 ; i < targetTimeInt - currentTimeInt; i++)
+ {
+ if(i == 0) cycleDelta = math::xformInvMulNS(refStartX,refStopX);
+ targetRefX = math::xformMul(targetRefX,cycleDelta);
+ }
+ if(targetGoalIndex >= 0) targetGoalX = math::xformMul(targetGoalX,math::xformWeight(constant.m_DeltaPose.m_GoalArray[targetGoalIndex].m_X,math::float1(targetTimeFrac)));
+ }
+
+ humanPose.m_RootX = rootX;
+
+ for(int i = 0; i < human::kLastGoal; i++)
+ {
+ humanPose.m_GoalArray[i].m_X = math::xformMul(rootX,humanPose.m_GoalArray[i].m_X);
+ }
+
+ motionOutput.m_PrevLeftFootX = math::xformMul(prevRootX,prevLeftFootX);
+ motionOutput.m_PrevRightFootX = math::xformMul(prevRootX,prevRightFootX);
+
+ motionOutput.m_DX = math::xformIdentity();
+ motionOutput.m_MotionX = math::xformIdentity();
+
+ if(constant.m_LoopTime)
+ {
+ float deltaTime = math::abs(timeFrac - prevTimeFrac);
+
+ if(deltaTime > 0.5f)
+ {
+ if(timeFrac < prevTimeFrac)
+ {
+ motionOutput.m_DX = math::xformInvMulNS(prevRefX,math::xformMul(refStopX,math::xformInvMulNS(refStartX,refX)));
+ }
+ else if(timeFrac > prevTimeFrac)
+ {
+ motionOutput.m_DX = math::xformInvMulNS(prevRefX,math::xformMul(refStartX,math::xformInvMulNS(refStopX,refX)));
+ }
+ }
+ else if(timeFrac != prevTimeFrac)
+ {
+ motionOutput.m_DX = math::xformInvMulNS(prevRefX,refX);
+ }
+ }
+ else
+ {
+ if(input.m_PreviousTime != input.m_Time)
+ {
+ motionOutput.m_DX = math::xformInvMulNS(prevRefX,refX);
+ }
+ }
+
+ motionOutput.m_DX.q = math::quat2Qtan(motionOutput.m_DX.q);
+
+ motionOutput.m_DX.q.x() = 0;
+ motionOutput.m_DX.q.z() = 0;
+
+ if(constant.m_LoopBlendPositionY)
+ {
+ motionOutput.m_DX.t.y() = 0;
+ }
+
+ if(constant.m_LoopBlendPositionXZ)
+ {
+ motionOutput.m_DX.t.x() = 0;
+ motionOutput.m_DX.t.z() = 0;
+ }
+
+ if(constant.m_LoopBlendOrientation)
+ {
+ motionOutput.m_DX.q.y() = 0;
+ }
+
+ motionOutput.m_DX.q = math::qtan2Quat(motionOutput.m_DX.q);
+ motionOutput.m_DX.s = math::float4::one();
+ motionOutput.m_TargetX = math::xformInvMulNS(refX,targetRefX);
+
+ if(mirror)
+ {
+ human::HumanPoseMirror(humanPose,humanPose);
+
+ motionOutput.m_DX = math::mirror(motionOutput.m_DX);
+
+ math::xform x = math::mirror(motionOutput.m_PrevLeftFootX);
+ motionOutput.m_PrevLeftFootX = math::mirror(motionOutput.m_PrevRightFootX);
+ motionOutput.m_PrevRightFootX = x;
+
+ constant_float4(offsetQY,0,1,0,0);
+ constant_float4(offsetQZ,0,0,1,0);
+
+ motionOutput.m_PrevLeftFootX.q = math::normalize(math::quatMul(motionOutput.m_PrevLeftFootX.q,offsetQY));
+ motionOutput.m_PrevRightFootX.q = math::normalize(math::quatMul(motionOutput.m_PrevRightFootX.q,offsetQY));
+
+ motionOutput.m_TargetX = math::mirror(motionOutput.m_TargetX);
+
+ if(input.m_TargetIndex > kTargetReference)
+ {
+ targetRootX = math::mirror(targetRootX);
+
+ if(input.m_TargetIndex > kTargetRoot)
+ {
+ targetGoalX = math::mirror(targetGoalX);
+ targetGoalX.q = math::normalize(math::quatMul(targetGoalX.q,math::cond(math::bool4(input.m_TargetIndex > kTargetRightFoot),offsetQZ,offsetQY)));
+ }
+ }
+ }
+
+ if(input.m_TargetIndex > kTargetReference)
+ {
+ motionOutput.m_TargetX = math::xformMul(motionOutput.m_TargetX,targetRootX);
+
+ if(input.m_TargetIndex > kTargetRoot)
+ {
+ motionOutput.m_TargetX = math::xformMul(motionOutput.m_TargetX,targetGoalX);
+ }
+ }
+
+ motionOutput.m_MotionX = refX;
+ }
+
+ void ComputeClipMuscleDeltaPose(ClipMuscleConstant const& constant, float startTime, float stopTime, human::HumanPose &deltaPose, math::xform &startX, math::xform &leftFootStartX, math::xform &rightFootStartX, memory::Allocator& alloc)
+ {
+ ClipInput in;
+ human::HumanPose poseStart;
+ human::HumanPose poseStop;
+
+ ClipOutput *clipOutStart = CreateClipOutput(constant.m_Clip.Get(),alloc);
+ ClipOutput *clipOutStop = CreateClipOutput(constant.m_Clip.Get(),alloc);
+ ClipMemory *mem = CreateClipMemory(constant.m_Clip.Get(), alloc);
+
+ in.m_Time = startTime;
+ EvaluateClip(constant.m_Clip.Get(),&in,mem,clipOutStart);
+ GetHumanPose(constant,clipOutStart->m_Values,poseStart);
+
+ in.m_Time = stopTime;
+ EvaluateClip(constant.m_Clip.Get(),&in,mem,clipOutStop);
+ GetHumanPose(constant,clipOutStop->m_Values,poseStop);
+
+ human::HumanPoseSub(deltaPose,poseStart,poseStop);
+
+ startX = poseStart.m_RootX;
+ leftFootStartX = poseStart.m_GoalArray[human::kLeftFootGoal].m_X;
+ rightFootStartX = poseStart.m_GoalArray[human::kRightFootGoal].m_X;
+
+ DestroyClipOutput(clipOutStart,alloc);
+ DestroyClipOutput(clipOutStop,alloc);
+ DestroyClipMemory(mem,alloc);
+ }
+
+ void InitClipMuscleDeltaValues(ClipMuscleConstant& constant)
+ {
+ mecanim::memory::MecanimAllocator alloc(kMemTempAlloc);
+
+ ClipInput in;
+ ClipOutput *outStart = CreateClipOutput(constant.m_Clip.Get(),alloc);
+ ClipOutput *outStop = CreateClipOutput(constant.m_Clip.Get(),alloc);
+ ClipMemory *mem = CreateClipMemory(constant.m_Clip.Get(), alloc);
+
+ in.m_Time = constant.m_StartTime;
+ EvaluateClip(constant.m_Clip.Get(),&in,mem,outStart);
+
+ in.m_Time = constant.m_StopTime;
+ EvaluateClip(constant.m_Clip.Get(),&in,mem,outStop);
+
+ constant.m_MotionStartX = GetMotionX(constant,*outStart);
+ constant.m_MotionStopX = GetMotionX(constant,*outStop);
+
+ for(int valueIter = 0; valueIter < constant.m_ValueArrayCount; valueIter++)
+ {
+ constant.m_ValueArrayDelta[valueIter].m_Start = outStart->m_Values[valueIter];
+ constant.m_ValueArrayDelta[valueIter].m_Stop = outStop->m_Values[valueIter];
+ }
+
+ DestroyClipOutput(outStart,alloc);
+ DestroyClipOutput(outStop,alloc);
+ DestroyClipMemory(mem,alloc);
+ }
+
+ void InitClipMuscleDeltaPose(ClipMuscleConstant& constant)
+ {
+ mecanim::memory::MecanimAllocator alloc(kMemTempAlloc);
+ ComputeClipMuscleDeltaPose(constant,constant.m_StartTime,constant.m_StopTime,constant.m_DeltaPose,constant.m_StartX,constant.m_LeftFootStartX,constant.m_RightFootStartX,alloc);
+ }
+
+ void InitClipMuscleAverageSpeed(ClipMuscleConstant& constant, int steps)
+ {
+ mecanim::memory::MecanimAllocator alloc(kMemTempAlloc);
+
+ ClipMemory *mem = CreateClipMemory(constant.m_Clip.Get(), alloc);
+
+ float period = (constant.m_StopTime - constant.m_StartTime) / float(steps);
+ float time = constant.m_StartTime;
+
+ math::xform prevRootX;
+
+ math::float4 speed = math::float4::zero();
+ float angularSpeed = 0;
+
+ for(int i = 0; i <= steps; i++)
+ {
+ math::xform rootX = EvaluateRoot(constant,*mem,time);
+
+ math::float4 qYOffset = math::qtan2Quat(math::float4(0,math::halfTan(math::radians(constant.m_OrientationOffsetY)),0,1));
+
+ if(constant.m_KeepOriginalOrientation)
+ {
+ qYOffset = math::normalize(math::quatMul(qYOffset,math::quatConj(constant.m_StartX.q)));
+ }
+
+ rootX.q = math::normalize(math::quatMul(qYOffset,math::quatProjOnYPlane(math::cond(math::bool4(constant.m_LoopBlendOrientation),constant.m_StartX.q,rootX.q))));
+
+ if(i > 0)
+ {
+ math::xform dx = math::xformInvMul(prevRootX,rootX);
+ if(constant.m_Mirror) dx = math::mirror(dx);
+ math::float4 dxdof = math::doubleAtan(math::quat2Qtan(dx.q));
+ angularSpeed += dxdof.y().tofloat() / period;
+ speed = speed + dx.t / math::float1(period);
+ }
+
+ prevRootX = rootX;
+ time += period;
+ }
+
+ constant.m_AverageAngularSpeed = angularSpeed / float(steps);
+
+ constant.m_AverageSpeed = speed / math::float1(steps);
+ constant.m_AverageSpeed = math::cond(math::bool4(constant.m_LoopBlendPositionXZ,constant.m_LoopBlendPositionY,constant.m_LoopBlendPositionXZ,true),math::float4::zero(),constant.m_AverageSpeed);
+
+ DestroyClipMemory(mem,alloc);
+ }
+
+ size_t GetClipCurveCount(const ClipMuscleConstant& constant)
+ {
+ return GetClipCurveCount(*constant.m_Clip);
+ }
+}
+
+}
diff --git a/Runtime/mecanim/animation/clipmuscle.h b/Runtime/mecanim/animation/clipmuscle.h
new file mode 100644
index 0000000..e2bb685
--- /dev/null
+++ b/Runtime/mecanim/animation/clipmuscle.h
@@ -0,0 +1,264 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/Math/Simd/float4.h"
+#include "Runtime/mecanim/animation/curvedata.h"
+#include "Runtime/mecanim/human/human.h"
+#include "Runtime/mecanim/human/hand.h"
+
+#include "Runtime/Serialize/Blobification/offsetptr.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Animation/MecanimArraySerialization.h"
+
+namespace mecanim
+{
+
+namespace animation
+{
+ const int32_t s_ClipMuscleCurveTQCount = 7;
+ const int32_t s_ClipMuscleDoFBegin = (2 + human::kLastGoal) * s_ClipMuscleCurveTQCount;
+ const int32_t s_ClipMotionCurveCount = 7;
+ const int32_t s_ClipMuscleCurveCount = s_ClipMotionCurveCount + (1 + human::kLastGoal) * 7 + human::kLastDoF + 2 * hand::s_DoFCount;
+
+ int32_t FindMuscleIndex(uint32_t id);
+ mecanim::String const& GetMuscleCurveName(int32_t curveIndex);
+ float GetXformCurveValue(math::xform const& x, int32_t index);
+ float GetMuscleCurveValue(human::HumanPose const& pose, math::xform const &motionX, int32_t curveIndex);
+ bool GetMuscleCurveInMask(human::HumanPoseMask const& mask, int32_t curveIndex);
+ int32_t GetMuscleCurveTQIndex(int32_t curveIndex);
+
+ void InitializeMuscleClipTables ();
+
+ enum { kTargetReference, kTargetRoot, kTargetLeftFoot, kTargetRightFoot, kTargetLeftHand, kTargetRightHand };
+
+ struct ValueDelta
+ {
+ DEFINE_GET_TYPESTRING(ValueDelta)
+
+ float m_Start;
+ float m_Stop;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_Start);
+ TRANSFER(m_Stop);
+ }
+ };
+
+ // Constant
+ // @TODO: create a smaller constant to use with non humanoid clips. There is too much overhead here
+ struct ClipMuscleConstant
+ {
+ DEFINE_GET_TYPESTRING(ClipMuscleConstant)
+
+ ClipMuscleConstant() :
+ m_StartX(math::xformIdentity()),
+ m_LeftFootStartX(math::xformIdentity()),
+ m_RightFootStartX(math::xformIdentity()),
+ m_MotionStartX(math::xformIdentity()),
+ m_MotionStopX(math::xformIdentity()),
+ m_ValueArrayCount(0),
+ m_AverageSpeed(math::float4::zero()),
+ m_Mirror(false),
+ m_StartTime(0),
+ m_StopTime(1),
+ m_LoopTime(false),
+ m_LoopBlend(false),
+ m_LoopBlendOrientation(false),
+ m_LoopBlendPositionXZ(false),
+ m_LoopBlendPositionY(false),
+ m_KeepOriginalOrientation(false),
+ m_KeepOriginalPositionY(true),
+ m_KeepOriginalPositionXZ(false),
+ m_HeightFromFeet(false),
+ m_OrientationOffsetY(0),
+ m_Level(0),
+ m_CycleOffset(0),
+ m_AverageAngularSpeed(0)
+ {
+ int32_t i;
+ for(i = 0; i < s_ClipMuscleCurveCount; i++)
+ {
+ m_IndexArray[i] = -1;
+ }
+ }
+
+ human::HumanPose m_DeltaPose;
+
+ math::xform m_StartX;
+ math::xform m_LeftFootStartX;
+ math::xform m_RightFootStartX;
+
+ math::xform m_MotionStartX;
+ math::xform m_MotionStopX;
+
+ math::float4 m_AverageSpeed;
+
+ OffsetPtr<Clip> m_Clip;
+
+ float m_StartTime;
+ float m_StopTime;
+ float m_OrientationOffsetY;
+ float m_Level;
+ float m_CycleOffset;
+ float m_AverageAngularSpeed;
+
+ int32_t m_IndexArray[s_ClipMuscleCurveCount];
+
+ uint32_t m_ValueArrayCount;
+ OffsetPtr<ValueDelta> m_ValueArrayDelta;
+
+ bool m_Mirror;
+ bool m_LoopTime;
+ bool m_LoopBlend;
+ bool m_LoopBlendOrientation;
+ bool m_LoopBlendPositionY;
+ bool m_LoopBlendPositionXZ;
+
+ bool m_KeepOriginalOrientation;
+ bool m_KeepOriginalPositionY;
+ bool m_KeepOriginalPositionXZ;
+ bool m_HeightFromFeet;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ transfer.SetVersion(2);
+
+ TRANSFER(m_DeltaPose);
+ TRANSFER(m_StartX);
+ TRANSFER(m_LeftFootStartX);
+ TRANSFER(m_RightFootStartX);
+ TRANSFER(m_MotionStartX);
+ TRANSFER(m_MotionStopX);
+ TRANSFER(m_AverageSpeed);
+ TRANSFER(m_Clip);
+ TRANSFER(m_StartTime);
+ TRANSFER(m_StopTime);
+ TRANSFER(m_OrientationOffsetY);
+ TRANSFER(m_Level);
+ TRANSFER(m_CycleOffset);
+
+ TRANSFER(m_AverageAngularSpeed);
+
+ STATIC_ARRAY_TRANSFER(mecanim::int32_t, m_IndexArray, s_ClipMuscleCurveCount);
+
+ TRANSFER_BLOB_ONLY(m_ValueArrayCount);
+ MANUAL_ARRAY_TRANSFER2(ValueDelta, m_ValueArrayDelta, m_ValueArrayCount);
+
+ TRANSFER(m_Mirror);
+
+ TRANSFER(m_LoopTime);
+ TRANSFER(m_LoopBlend);
+
+ m_LoopTime = transfer.IsVersionSmallerOrEqual(1) ? m_LoopBlend : m_LoopTime;
+
+ TRANSFER(m_LoopBlendOrientation);
+ TRANSFER(m_LoopBlendPositionY);
+ TRANSFER(m_LoopBlendPositionXZ);
+
+ TRANSFER(m_KeepOriginalOrientation);
+ TRANSFER(m_KeepOriginalPositionY);
+ TRANSFER(m_KeepOriginalPositionXZ);
+ TRANSFER(m_HeightFromFeet);
+
+ transfer.Align();
+ }
+ };
+
+ // Input
+ struct ClipMuscleInput
+ {
+ ClipMuscleInput() : m_Time(0),
+ m_PreviousTime(0),
+ m_TargetIndex(kTargetReference),
+ m_TargetTime(1), m_Reverse(false), m_Mirror(false), m_CycleOffset(0){}
+
+ float m_Time;
+ float m_PreviousTime;
+
+ int32_t m_TargetIndex;
+ float m_TargetTime;
+ bool m_Reverse;
+ bool m_Mirror;
+ float m_CycleOffset;
+ };
+
+ // Output
+ struct MotionOutput
+ {
+ DEFINE_GET_TYPESTRING(MotionOutput)
+
+ MotionOutput() : m_DX(math::xformIdentity()),
+ m_MotionX(math::xformIdentity()),
+ m_MotionStartX(math::xformIdentity()),
+ m_MotionStopX(math::xformIdentity()),
+ m_PrevRootX(math::xformIdentity()),
+ m_PrevLeftFootX(math::xformIdentity()),
+ m_PrevRightFootX(math::xformIdentity()),
+ m_TargetX(math::xformIdentity()),
+ m_GravityWeight(0) {}
+
+ math::xform m_DX;
+ math::xform m_MotionX;
+ math::xform m_MotionStartX;
+ math::xform m_MotionStopX;
+
+ math::xform m_PrevRootX;
+ math::xform m_PrevLeftFootX;
+ math::xform m_PrevRightFootX;
+
+ math::xform m_TargetX;
+
+ float m_GravityWeight;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_DX);
+ TRANSFER(m_MotionX);
+ TRANSFER(m_MotionStartX);
+ TRANSFER(m_MotionStopX);
+ TRANSFER(m_PrevRootX);
+ TRANSFER(m_PrevLeftFootX);
+ TRANSFER(m_PrevRightFootX);
+ TRANSFER(m_TargetX);
+ TRANSFER(m_GravityWeight);
+ };
+ };
+
+ void MotionOutputClear(MotionOutput *output);
+ void MotionOutputCopy(MotionOutput *output, MotionOutput const *motion, bool hasRootMotion, bool isHuman, human::HumanPoseMask const &poseMask);
+ void MotionOutputBlend(MotionOutput *output, MotionOutput **outputArray, float *weigh, uint32_t count, bool hasRootMotion, bool isHuman, human::HumanPoseMask const &poseMask);
+ void MotionAddAdditiveLayer(MotionOutput *output, MotionOutput const *motion, float weight, bool hasRootMotion, bool isHuman, human::HumanPoseMask const &poseMask);
+ void MotionAddOverrideLayer(MotionOutput *output, MotionOutput const *motion, float weight, bool hasRootMotion, bool isHuman, human::HumanPoseMask const &poseMask);
+
+ ClipMuscleConstant* CreateClipMuscleConstant(Clip * clip, memory::Allocator& alloc);
+ void DestroyClipMuscleConstant(ClipMuscleConstant * constant, memory::Allocator& alloc);
+
+ ClipMuscleInput* CreateClipMuscleInput(ClipMuscleConstant const* constant, memory::Allocator& alloc);
+ void DestroyClipMuscleInput(ClipMuscleInput * input, memory::Allocator& alloc);
+
+ float ComputeClipTime(float normalizedTimeIn, float startTime, float stopTime, float cycleOffset, bool loop, bool reverse, float &nomralizedTimeOut, float &timeInt);
+
+ void EvaluateClipRootMotionDeltaX(const ClipMuscleConstant& constant, const ClipMuscleInput &input, MotionOutput &output, ClipMemory &memory);
+
+ void EvaluateClipMusclePrevTime(const ClipMuscleConstant& constant, const ClipMuscleInput &input, MotionOutput &output, ClipMemory &memory);
+ void EvaluateClipMuscle(const ClipMuscleConstant& constant, const ClipMuscleInput &input, const float *valuesOutput, MotionOutput &motionOutput, human::HumanPose &humanPose, ClipMemory &memory);
+
+ void ComputeClipMuscleDeltaPose(ClipMuscleConstant const &constant, float startTime, float stopTime, human::HumanPose &deltaPose, math::xform &startX, math::xform &leftFootStartX, math::xform &rightFootStartX, memory::Allocator& alloc);
+
+ void GetHumanPose(const ClipMuscleConstant& constant, const float* values, human::HumanPose &humanPose);
+ void GetHumanPose(const ClipMuscleConstant& constant, const ValueDelta* values, human::HumanPose &humanPose);
+
+ void InitClipMuscleDeltaValues(ClipMuscleConstant& constant);
+ void InitClipMuscleDeltaPose(ClipMuscleConstant& constant);
+ void InitClipMuscleAverageSpeed(ClipMuscleConstant& constant, int steps = 20);
+
+ void InitMuscleClipIndexArray(ClipMuscleConstant& constant, memory::Allocator& alloc);
+ size_t GetClipCurveCount(const ClipMuscleConstant& constant);
+}
+}
diff --git a/Runtime/mecanim/animation/constantclip.cpp b/Runtime/mecanim/animation/constantclip.cpp
new file mode 100644
index 0000000..1fd07cf
--- /dev/null
+++ b/Runtime/mecanim/animation/constantclip.cpp
@@ -0,0 +1,34 @@
+#include "UnityPrefix.h"
+#include "constantclip.h"
+#include "Runtime/mecanim/memory.h"
+
+namespace mecanim
+{
+ namespace animation
+ {
+ void SampleClip (const ConstantClip& curveData, uint32_t outputCount, float* output)
+ {
+ DebugAssert(outputCount <= curveData.curveCount);
+
+ memcpy(output, curveData.data.Get(), outputCount * sizeof(float));
+ }
+
+ float SampleClipAtIndex (const ConstantClip& curveData, int index)
+ {
+ Assert(index < curveData.curveCount);
+
+ return curveData.data[index];
+ }
+
+ void DestroyConstantClip (ConstantClip& clip, memory::Allocator& alloc)
+ {
+ alloc.Deallocate(clip.data);
+ }
+
+ void CreateConstantClip (ConstantClip& clip, size_t curveCount, memory::Allocator& alloc)
+ {
+ clip.curveCount = curveCount;
+ clip.data = alloc.ConstructArray<float> (curveCount);
+ }
+ }
+}
diff --git a/Runtime/mecanim/animation/constantclip.h b/Runtime/mecanim/animation/constantclip.h
new file mode 100644
index 0000000..a7de648
--- /dev/null
+++ b/Runtime/mecanim/animation/constantclip.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/Serialize/Blobification/offsetptr.h"
+
+
+namespace mecanim
+{
+namespace animation
+{
+struct ConstantClip
+{
+ UInt32 curveCount;
+ OffsetPtr<float> data;
+
+ ConstantClip () : curveCount (0) { }
+
+ DEFINE_GET_TYPESTRING(ConstantClip)
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(curveCount);
+ MANUAL_ARRAY_TRANSFER2(float, data, curveCount);
+ }
+};
+
+// Sample functions
+void SampleClip (const ConstantClip& curveData, uint32_t outputCount, float* output);
+float SampleClipAtIndex (const ConstantClip& curveData, int index);
+
+void DestroyConstantClip (ConstantClip& clip, memory::Allocator& alloc);
+void CreateConstantClip (ConstantClip& clip, size_t curveCount, memory::Allocator& alloc);
+
+}
+} \ No newline at end of file
diff --git a/Runtime/mecanim/animation/curvedata.cpp b/Runtime/mecanim/animation/curvedata.cpp
new file mode 100644
index 0000000..210bbe8
--- /dev/null
+++ b/Runtime/mecanim/animation/curvedata.cpp
@@ -0,0 +1,118 @@
+#include "UnityPrefix.h"
+#include "Runtime/mecanim/animation/curvedata.h"
+
+namespace mecanim
+{
+
+namespace animation
+{
+ Clip* CreateClipSimple(uint32_t count, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(Clip);
+
+ // Allocate data.
+ Clip* clip = alloc.Construct<Clip>();
+ return clip;
+ }
+
+
+ void DestroyClip(Clip* clip, memory::Allocator& alloc)
+ {
+ if(clip)
+ {
+ DestroyStreamedClip(clip->m_StreamedClip, alloc);
+ DestroyDenseClip(clip->m_DenseClip, alloc);
+
+ alloc.Deallocate(clip);
+ }
+ }
+
+ ClipMemory* CreateClipMemory(Clip const* clip, memory::Allocator& alloc)
+ {
+ return CreateClipMemory(clip, GetClipCurveCount(*clip), alloc);
+ }
+
+ ClipMemory* CreateClipMemory(Clip const* clip, int32_t totalUsedCurves, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(ClipMemory);
+
+ ClipMemory* mem = alloc.Construct<ClipMemory>();
+ mem->m_ConstantClipValueCount = totalUsedCurves - (GetClipCurveCount(*clip) - clip->m_ConstantClip.curveCount);
+ Assert(mem->m_ConstantClipValueCount >= 0 && mem->m_ConstantClipValueCount <= clip->m_ConstantClip.curveCount);
+
+ CreateStreamedClipMemory(clip->m_StreamedClip, mem->m_StreamedClipCache, alloc);
+
+ return mem;
+ }
+
+ void DestroyClipMemory(ClipMemory* memory, memory::Allocator& alloc)
+ {
+ if(memory)
+ {
+ DestroyStreamedClipMemory(memory->m_StreamedClipCache, alloc);
+ alloc.Deallocate(memory);
+ }
+ }
+
+ ClipOutput* CreateClipOutput(Clip const* clip, memory::Allocator& alloc)
+ {
+ return CreateClipOutput(GetClipCurveCount(*clip), alloc);
+ }
+
+ ClipOutput* CreateClipOutput(int32_t totalUsedCurves, memory::Allocator& alloc)
+ {
+ SETPROFILERLABEL(ClipOutput);
+
+ ClipOutput* out = alloc.Construct<ClipOutput>();
+ out->m_Values = alloc.ConstructArray<float> (totalUsedCurves);
+
+ return out;
+ }
+
+ void DestroyClipOutput(ClipOutput* output, memory::Allocator& alloc)
+ {
+ if(output)
+ {
+ alloc.Deallocate(output->m_Values);
+ alloc.Deallocate(output);
+ }
+ }
+
+ float EvaluateClipAtIndex(Clip const* clip, ClipInput const* input, ClipMemory* memory, uint32_t index)
+ {
+ if (index < clip->m_StreamedClip.curveCount)
+ return SampleClipAtIndex(clip->m_StreamedClip, memory->m_StreamedClipCache, index, input->m_Time);
+ index -= clip->m_StreamedClip.curveCount;
+
+ if (index < clip->m_DenseClip.m_CurveCount)
+ return SampleClipAtIndex(clip->m_DenseClip, index, input->m_Time);
+ index -= clip->m_DenseClip.m_CurveCount;
+
+ return SampleClipAtIndex(clip->m_ConstantClip, index);
+ }
+
+ void EvaluateClip(Clip const* clip, ClipInput const* input, ClipMemory* memory, ClipOutput* output )
+ {
+ float* outputData = output->m_Values;
+
+ if (clip->m_StreamedClip.curveCount != 0)
+ {
+ SampleClip(clip->m_StreamedClip, memory->m_StreamedClipCache, input->m_Time, outputData);
+ outputData += clip->m_StreamedClip.curveCount;
+ }
+
+ if (clip->m_DenseClip.m_CurveCount != 0)
+ {
+ SampleClip(clip->m_DenseClip, input->m_Time, outputData);
+ outputData += clip->m_DenseClip.m_CurveCount;
+ }
+
+ if (memory->m_ConstantClipValueCount != 0)
+ {
+ // Constant clips are not sampling based on the total curve count
+ SampleClip(clip->m_ConstantClip, memory->m_ConstantClipValueCount, outputData);
+ }
+ }
+}
+
+}
diff --git a/Runtime/mecanim/animation/curvedata.h b/Runtime/mecanim/animation/curvedata.h
new file mode 100644
index 0000000..0244684
--- /dev/null
+++ b/Runtime/mecanim/animation/curvedata.h
@@ -0,0 +1,82 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/mecanim/generic/valuearray.h"
+
+#include "Runtime/Serialize/Blobification/offsetptr.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Animation/MecanimArraySerialization.h"
+#include "streamedclip.h"
+#include "denseclip.h"
+#include "constantclip.h"
+
+namespace mecanim
+{
+
+namespace animation
+{
+ struct Clip
+ {
+ DEFINE_GET_TYPESTRING(Clip)
+
+ Clip() {}
+
+ StreamedClip m_StreamedClip;
+ DenseClip m_DenseClip;
+ ConstantClip m_ConstantClip;
+
+ OffsetPtr<ValueArrayConstant> m_DeprecatedBinding;
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER (m_StreamedClip);
+ TRANSFER (m_DenseClip);
+ TRANSFER (m_ConstantClip);
+
+ transfer.Transfer(m_DeprecatedBinding, "m_Binding");
+ }
+ };
+
+ struct ClipInput
+ {
+ float m_Time;
+ };
+
+ struct ClipMemory
+ {
+ StreamedClipMemory m_StreamedClipCache;
+ int m_ConstantClipValueCount;
+ };
+
+ struct ClipOutput
+ {
+ public:
+ float* m_Values;
+
+ };
+
+ Clip* CreateClipSimple(uint32_t count, memory::Allocator& alloc);
+ void DestroyClip(Clip* clip, memory::Allocator& alloc);
+
+ ClipMemory* CreateClipMemory(Clip const* clip, memory::Allocator& alloc);
+ ClipMemory* CreateClipMemory(Clip const* clip, int32_t totalUsedCurves, memory::Allocator& alloc);
+ void DestroyClipMemory(ClipMemory* memory, memory::Allocator& alloc);
+
+ ClipOutput* CreateClipOutput(int32_t usedConstantCurves, memory::Allocator& alloc);
+ ClipOutput* CreateClipOutput(Clip const* clip, memory::Allocator& alloc);
+
+ void DestroyClipOutput(ClipOutput* output, memory::Allocator& alloc);
+
+ float EvaluateClipAtIndex(Clip const* clip, ClipInput const* input, ClipMemory* memory, uint32_t index);
+ void EvaluateClip(Clip const* clip, ClipInput const* input, ClipMemory* memory, ClipOutput* output);
+
+ inline size_t GetClipCurveCount (const Clip& clip) { return clip.m_StreamedClip.curveCount + clip.m_DenseClip.m_CurveCount + clip.m_ConstantClip.curveCount; }
+ inline bool IsValidClip (const Clip& clip) { return GetClipCurveCount (clip) != 0; }
+
+
+} // namespace animation
+
+}
diff --git a/Runtime/mecanim/animation/damp.cpp b/Runtime/mecanim/animation/damp.cpp
new file mode 100644
index 0000000..c769108
--- /dev/null
+++ b/Runtime/mecanim/animation/damp.cpp
@@ -0,0 +1,24 @@
+#include "UnityPrefix.h"
+#include "Runtime/Math/Simd/math.h"
+#include "Runtime/mecanim/animation/damp.h"
+
+namespace mecanim
+{
+
+namespace dynamics
+{
+ void ScalDamp::Evaluate(float value, float deltaTime)
+ {
+ m_Value = math::cond(m_DampTime > 0, m_Value + (value - m_Value) * math::abs(deltaTime) / (m_DampTime + math::abs(deltaTime)), value);
+ }
+
+ void VectorDamp::Evaluate(math::float4 const& value, float deltaTime)
+ {
+ math::float1 dt(deltaTime);
+ math::float1 dampTime(m_DampTime);
+
+ m_Value = math::cond( math::bool4(m_DampTime > 0), m_Value + (value - m_Value) * math::abs(dt) / (dampTime + math::abs(dt)), value);
+ }
+}
+
+}
diff --git a/Runtime/mecanim/animation/damp.h b/Runtime/mecanim/animation/damp.h
new file mode 100644
index 0000000..319f6e6
--- /dev/null
+++ b/Runtime/mecanim/animation/damp.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/Math/Simd/float4.h"
+
+namespace mecanim
+{
+
+namespace dynamics
+{
+ class ScalDamp
+ {
+ public:
+
+ float m_DampTime;
+ float m_Value;
+
+ ScalDamp() { Reset(); }
+
+ void Reset() { m_DampTime = 0; m_Value = 0; }
+ void Evaluate(float value, float deltaTime);
+ };
+
+
+ class VectorDamp
+ {
+ public:
+
+ float m_DampTime;
+ math::float4 m_Value;
+
+ VectorDamp() { Reset(); }
+
+ void Reset() { m_DampTime = 0; m_Value = math::float4::zero(); }
+ void Evaluate(math::float4 const& value, float deltaTime);
+ };
+
+} // namespace dynamics
+
+}
diff --git a/Runtime/mecanim/animation/denseclip.cpp b/Runtime/mecanim/animation/denseclip.cpp
new file mode 100644
index 0000000..3c19e47
--- /dev/null
+++ b/Runtime/mecanim/animation/denseclip.cpp
@@ -0,0 +1,63 @@
+#include "UnityPrefix.h"
+#include "denseclip.h"
+#include "Runtime/Math/Simd/math.h"
+namespace mecanim
+{
+namespace animation
+{
+
+static void BlendArray(const float* lhs, const float* rhs, size_t size, float t, float* output)
+{
+ for (int i=0;i<size;i++)
+ output[i] = math::lerp(lhs[i], rhs[i], t);
+}
+
+
+static void PrepareBlendValues (const DenseClip& clip, float time, float*& lhs, float*& rhs, float& u)
+{
+ time -= clip.m_BeginTime;
+
+ float index;
+ u = math::modf(time * clip.m_SampleRate, index);
+
+ int lhsIndex = (int)index;
+ int rhsIndex = lhsIndex + 1;
+ lhsIndex = math::maximum(0, lhsIndex);
+ lhsIndex = math::minimum(clip.m_FrameCount-1, lhsIndex);
+
+ rhsIndex = math::maximum(0, rhsIndex);
+ rhsIndex = math::minimum(clip.m_FrameCount-1, rhsIndex);
+
+ lhs = const_cast<float*> (&clip.m_SampleArray[lhsIndex * clip.m_CurveCount]);
+ rhs = const_cast<float*> (&clip.m_SampleArray[rhsIndex * clip.m_CurveCount]);
+}
+
+void SampleClip (const DenseClip& clip, float time, float* output)
+{
+ float u;
+ float* lhsPtr;
+ float* rhsPtr;
+ PrepareBlendValues(clip, time, lhsPtr, rhsPtr, u);
+
+ BlendArray(lhsPtr, rhsPtr, clip.m_CurveCount, u, output);
+}
+
+float SampleClipAtIndex (const DenseClip& clip, int curveIndex, float time)
+{
+ float u;
+ float* lhsPtr;
+ float* rhsPtr;
+ PrepareBlendValues(clip, time, lhsPtr, rhsPtr, u);
+
+ return math::lerp(lhsPtr[curveIndex], rhsPtr[curveIndex], u);
+}
+
+
+// Creation & Destruction
+void DestroyDenseClip (DenseClip& clip, memory::Allocator& alloc)
+{
+ alloc.Deallocate(clip.m_SampleArray);
+}
+
+}
+}
diff --git a/Runtime/mecanim/animation/denseclip.h b/Runtime/mecanim/animation/denseclip.h
new file mode 100644
index 0000000..e3175f1
--- /dev/null
+++ b/Runtime/mecanim/animation/denseclip.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/Serialize/Blobification/offsetptr.h"
+
+
+namespace mecanim
+{
+ namespace animation
+ {
+ struct DenseClip
+ {
+ int m_FrameCount;
+ uint32_t m_CurveCount;
+ float m_SampleRate;
+ float m_BeginTime;
+
+ uint32_t m_SampleArraySize;
+ OffsetPtr<float> m_SampleArray;
+
+
+ DenseClip () : m_FrameCount (0),m_CurveCount(0),m_SampleRate(0.0F),m_SampleArraySize(0),m_BeginTime(0.0F) { }
+
+ DEFINE_GET_TYPESTRING(DenseClip)
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER(m_FrameCount);
+ TRANSFER(m_CurveCount);
+ TRANSFER(m_SampleRate);
+ TRANSFER(m_BeginTime);
+
+ TRANSFER_BLOB_ONLY(m_SampleArraySize);
+ MANUAL_ARRAY_TRANSFER2(float, m_SampleArray, m_SampleArraySize);
+ }
+ };
+
+ // Sample functions
+ void SampleClip (const DenseClip& curveData, float time, float* output);
+ float SampleClipAtIndex (const DenseClip& curveData, int index, float time);
+
+
+ // Creation & Destruction
+ void DestroyDenseClip (DenseClip& clip, memory::Allocator& alloc);
+ }
+} \ No newline at end of file
diff --git a/Runtime/mecanim/animation/poseblender.cpp b/Runtime/mecanim/animation/poseblender.cpp
new file mode 100644
index 0000000..ccbdf8f
--- /dev/null
+++ b/Runtime/mecanim/animation/poseblender.cpp
@@ -0,0 +1,228 @@
+#include "UnityPrefix.h"
+#include "Runtime/mecanim/skeleton/skeleton.h"
+
+#include "Runtime/mecanim/graph/plugbinder.h"
+#include "Runtime/mecanim/animation/common.h"
+#include "Runtime/mecanim/animation/poseblender.h"
+
+namespace mecanim
+{
+
+namespace animation
+{
+ namespace
+ {
+ void SetPoseBlenderValue(graph::GraphPlug const* plug, uint32_t index, PoseBlenderInput const* input, graph::EvaluationGraphWorkspace* graphMemory)
+ {
+ float value;
+ plug->ReadData(&value, graphMemory->m_EvaluationInfo);
+ input->m_WeightArray[index] += value;
+ }
+ }
+
+ PoseBlenderConstant* CreatePoseBlenderConstant(skeleton::Skeleton * skeleton, skeleton::SkeletonPose* defaultPose, skeleton::SkeletonPose** arrayPose, uint32_t count, graph::Graph* graph, memory::Allocator& alloc)
+ {
+ PoseBlenderConstant *cst = alloc.Construct<PoseBlenderConstant>();
+
+ cst->m_Skeleton = skeleton;
+ cst->m_SkeletonPoseCount = count;
+
+ cst->m_DefaultSkeletonPose = defaultPose;
+ cst->m_SkeletonPoseArray = alloc.ConstructArray<skeleton::SkeletonPose*>(count);
+ cst->m_PosesID = alloc.ConstructArray<uint32_t>(count);
+ cst->m_HasGraph = graph != 0;
+ cst->m_Graph = graph;
+
+ uint32_t i;
+ for(i=0;i<count;i++)
+ {
+ cst->m_SkeletonPoseArray[i] = arrayPose[i];
+ }
+
+ if(cst->m_HasGraph)
+ {
+ cst->m_ValuesConstant = CreateValueArrayConstant(kFloatType, cst->m_Graph->m_InEdgesCount, alloc);
+ for(i=0;i<cst->m_Graph->m_InEdgesCount;++i)
+ {
+ cst->m_ValuesConstant->m_ValueArray[i].m_ID = cst->m_Graph->m_InEdges[i].m_Binding;
+ }
+ }
+
+ return cst;
+ }
+
+ void DestroyPoseBlenderConstant(PoseBlenderConstant * constant, memory::Allocator& alloc)
+ {
+ if(constant)
+ {
+ alloc.Deallocate(constant->m_PosesID);
+ alloc.Deallocate(constant->m_SkeletonPoseArray);
+ alloc.Deallocate(constant);
+ }
+ }
+
+ void InitializePoseBlenderConstant(PoseBlenderConstant * constant, graph::GraphFactory const& factory, memory::Allocator& alloc)
+ {
+ if(constant->m_Graph)
+ {
+ constant->m_EvaluationGraph = graph::CreateEvaluationGraph(constant->m_Graph, factory, alloc);
+ }
+ }
+
+ void ClearPoseBlenderConstant(PoseBlenderConstant * constant, memory::Allocator& alloc)
+ {
+ if(constant->m_EvaluationGraph)
+ {
+ graph::DestroyEvaluationGraph(constant->m_EvaluationGraph, alloc);
+ constant->m_EvaluationGraph = 0;
+ }
+ }
+
+ PoseBlenderInput* CreatePoseBlenderInput(PoseBlenderConstant const* constant, memory::Allocator& alloc)
+ {
+ PoseBlenderInput *in = alloc.Construct<PoseBlenderInput>();
+
+ if(constant->m_ValuesConstant)
+ {
+ in->m_Values = CreateValueArray(constant->m_ValuesConstant, alloc);
+ }
+ in->m_WeightPoseCount = constant->m_SkeletonPoseCount;
+ in->m_SkeletonPose = skeleton::CreateSkeletonPose(constant->m_Skeleton, alloc);
+ in->m_WeightArray = alloc.ConstructArray<float>(constant->m_SkeletonPoseCount);
+
+ uint32_t i;
+ for(i=0;i<in->m_WeightPoseCount;i++)
+ {
+ in->m_WeightArray[i] = 0.f;
+ }
+
+ return in;
+ }
+
+ void DestroyPoseBlenderInput(PoseBlenderInput * input, memory::Allocator& alloc)
+ {
+ if(input)
+ {
+ skeleton::DestroySkeletonPose(input->m_SkeletonPose, alloc);
+ alloc.Deallocate(input->m_WeightArray);
+ alloc.Deallocate(input);
+ }
+ }
+
+ PoseBlenderWorkspace* CreatePoseBlenderWorkspace(PoseBlenderConstant const* constant, memory::Allocator& alloc)
+ {
+ PoseBlenderWorkspace *ws = alloc.Construct<PoseBlenderWorkspace>();
+ ws->m_SkeletonPose = skeleton::CreateSkeletonPose(constant->m_Skeleton, alloc);
+
+ return ws;
+ }
+ void DestroyPoseBlenderWorkspace(PoseBlenderWorkspace * workspace, memory::Allocator& alloc)
+ {
+ if(workspace)
+ {
+ skeleton::DestroySkeletonPose(workspace->m_SkeletonPose, alloc);
+ alloc.Deallocate(workspace);
+ }
+ }
+
+ PoseBlenderMemory* CreatePoseBlenderMemory(PoseBlenderConstant const* constant, memory::Allocator& alloc)
+ {
+ PoseBlenderMemory* mem = alloc.Construct<PoseBlenderMemory>();
+
+ if(constant->m_EvaluationGraph)
+ {
+ mem->m_GraphWS = graph::CreateEvaluationGraphWorkspace(constant->m_EvaluationGraph, alloc);
+ mem->m_InputPoseBlenderBindingCount = 0;
+ uint32_t i,j;
+ for(i=0;i<constant->m_EvaluationGraph->m_Output->mPlugCount;i++)
+ {
+ graph::GraphPlug const* plug = constant->m_EvaluationGraph->m_Output->mPlugArray[i];
+ for(j=0;j<constant->m_SkeletonPoseCount;j++)
+ {
+ if( plug->m_ID == constant->m_PosesID[j])
+ {
+ mem->m_InputPoseBlenderBindingCount++;
+ }
+ }
+ }
+ if(mem->m_InputPoseBlenderBindingCount)
+ {
+ mem->m_InputPoseBlenderBinding = alloc.ConstructArray<SetPoseBlenderInput2>(mem->m_InputPoseBlenderBindingCount);
+ uint32_t k = 0;
+ for(i=0;i<constant->m_EvaluationGraph->m_Output->mPlugCount;i++)
+ {
+ graph::GraphPlug const* plug = constant->m_EvaluationGraph->m_Output->mPlugArray[i];
+ for(j=0;j<constant->m_SkeletonPoseCount;j++)
+ {
+ if( plug->m_ID == constant->m_PosesID[j])
+ {
+ mem->m_InputPoseBlenderBinding[k++] = bind( SetPoseBlenderValue, plug, j);
+ }
+ }
+ }
+ }
+ }
+
+ return mem;
+ }
+
+ void DestroyPoseBlenderMemory(PoseBlenderMemory* memory, memory::Allocator& alloc)
+ {
+ if(memory)
+ {
+ graph::DestroyEvaluationGraphWorkspace(memory->m_GraphWS, alloc);
+ alloc.Deallocate(memory->m_InputPoseBlenderBinding);
+ alloc.Deallocate(memory);
+ }
+ }
+
+
+ PoseBlenderOutput* CreatePoseBlenderOutput(PoseBlenderConstant const* constant, memory::Allocator& alloc)
+ {
+ PoseBlenderOutput *out = alloc.Construct<PoseBlenderOutput>();
+ out->m_SkeletonPose = skeleton::CreateSkeletonPose(constant->m_Skeleton, alloc);
+
+ return out;
+ }
+
+ void DestroyPoseBlenderOutput(PoseBlenderOutput * output, memory::Allocator& alloc)
+ {
+ if(output)
+ {
+ skeleton::DestroySkeletonPose(output->m_SkeletonPose, alloc);
+ alloc.Deallocate(output);
+ }
+ }
+
+ void EvaluatePoseBlender(PoseBlenderConstant const * constant, PoseBlenderInput const * input, PoseBlenderOutput * output, PoseBlenderMemory* memory, PoseBlenderWorkspace * workspace)
+ {
+ if(constant->m_EvaluationGraph)
+ {
+ memory->m_GraphWS->m_EvaluationInfo.m_EvaluationId++;
+
+ // Value Array is constructed based on graph so they should be sync
+ uint32_t i, count = constant->m_ValuesConstant->m_Count;
+ for(i=0;i<count;i++)
+ {
+ SetPlugValue(&constant->m_EvaluationGraph->m_Input->GetPlug(i), i, input->m_Values, memory->m_GraphWS);
+ }
+
+ count = memory->m_InputPoseBlenderBindingCount;
+ for(i=0;i<count;i++)
+ {
+ memory->m_InputPoseBlenderBinding[i](input, memory->m_GraphWS);
+ }
+ }
+
+ skeleton::SkeletonPoseCopy(input->m_SkeletonPose, output->m_SkeletonPose);
+ uint32_t i;
+ for(i = 0; i < constant->m_SkeletonPoseCount; i++)
+ {
+ skeleton::SkeletonPoseSub(constant->m_SkeletonPoseArray[i], input->m_SkeletonPose, workspace->m_SkeletonPose);
+ skeleton::SkeletonPoseWeight(workspace->m_SkeletonPose, math::float1(input->m_WeightArray[i]), workspace->m_SkeletonPose);
+ skeleton::SkeletonPoseAdd(output->m_SkeletonPose, workspace->m_SkeletonPose, output->m_SkeletonPose);
+ }
+ }
+}
+
+}
diff --git a/Runtime/mecanim/animation/poseblender.h b/Runtime/mecanim/animation/poseblender.h
new file mode 100644
index 0000000..4bd683e
--- /dev/null
+++ b/Runtime/mecanim/animation/poseblender.h
@@ -0,0 +1,113 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/mecanim/object.h"
+#include "Runtime/mecanim/types.h"
+#include "Runtime/mecanim/bind.h"
+
+#include "Runtime/mecanim/generic/valuearray.h"
+#include "Runtime/mecanim/graph/plug.h"
+#include "Runtime/mecanim/graph/graph.h"
+
+// forwards
+
+namespace mecanim
+{
+
+namespace skeleton {struct Skeleton; struct SkeletonPose;}
+
+namespace animation
+{
+ // Constant
+ struct PoseBlenderConstant
+ {
+ PoseBlenderConstant()
+ :m_Skeleton(0),
+ m_DefaultSkeletonPose(0),
+ m_SkeletonPoseArray(0),
+ m_PosesID(0),
+ m_SkeletonPoseCount(0),
+ m_Graph(0),
+ m_ValuesConstant(0),
+ m_HasGraph(false),
+ m_EvaluationGraph(0)
+ {
+ }
+
+ skeleton::Skeleton* m_Skeleton;
+ skeleton::SkeletonPose* m_DefaultSkeletonPose;
+ skeleton::SkeletonPose** m_SkeletonPoseArray;
+ uint32_t* m_PosesID;
+ graph::Graph* m_Graph;
+ ValueArrayConstant const* m_ValuesConstant;
+ bool m_HasGraph;
+
+ uint32_t m_SkeletonPoseCount;
+
+ graph::EvaluationGraph* m_EvaluationGraph;
+ };
+
+ struct PoseBlenderInput
+ {
+ PoseBlenderInput(): m_Values(0), m_WeightArray(0), m_SkeletonPose(0), m_WeightPoseCount(0){}
+
+ ValueArray* m_Values;
+ float* m_WeightArray;
+ skeleton::SkeletonPose* m_SkeletonPose;
+ uint32_t m_WeightPoseCount;
+ };
+
+ struct PoseBlenderWorkspace
+ {
+ PoseBlenderWorkspace(): m_SkeletonPose(0){}
+ skeleton::SkeletonPose* m_SkeletonPose;
+ };
+
+ typedef binder2<function<void (graph::GraphPlug const*, uint32_t, PoseBlenderInput const*, graph::EvaluationGraphWorkspace*)>::ptr,
+ graph::GraphPlug const*,
+ uint32_t>
+ SetPoseBlenderInput2;
+
+ struct PoseBlenderMemory
+ {
+ PoseBlenderMemory() : m_InputPoseBlenderBindingCount(0), m_InputPoseBlenderBinding(0), m_GraphWS(0)
+ {
+ }
+
+ uint32_t m_InputPoseBlenderBindingCount;
+ SetPoseBlenderInput2* m_InputPoseBlenderBinding;
+
+ graph::EvaluationGraphWorkspace* m_GraphWS;
+ };
+
+
+ struct PoseBlenderOutput : public Object
+ {
+ PoseBlenderOutput():m_SkeletonPose(0){}
+
+ skeleton::SkeletonPose* m_SkeletonPose;
+ };
+
+ PoseBlenderConstant* CreatePoseBlenderConstant(skeleton::Skeleton * skeleton, skeleton::SkeletonPose* defaultPose, skeleton::SkeletonPose** arrayPose, uint32_t count, graph::Graph* graph, memory::Allocator& alloc);
+ void DestroyPoseBlenderConstant(PoseBlenderConstant * constant, memory::Allocator& alloc);
+
+ void InitializePoseBlenderConstant(PoseBlenderConstant * constant, graph::GraphFactory const& factory, memory::Allocator& alloc);
+ void ClearPoseBlenderConstant(PoseBlenderConstant * constant, memory::Allocator& alloc);
+
+ PoseBlenderInput* CreatePoseBlenderInput(PoseBlenderConstant const* constant, memory::Allocator& alloc);
+ void DestroyPoseBlenderInput(PoseBlenderInput * input, memory::Allocator& alloc);
+
+ PoseBlenderWorkspace* CreatePoseBlenderWorkspace(PoseBlenderConstant const* constant, memory::Allocator& alloc);
+ void DestroyPoseBlenderWorkspace(PoseBlenderWorkspace * workspace, memory::Allocator& alloc);
+
+ PoseBlenderMemory* CreatePoseBlenderMemory(PoseBlenderConstant const* constant, memory::Allocator& alloc);
+ void DestroyPoseBlenderMemory(PoseBlenderMemory* amemory, memory::Allocator& alloc);
+
+ PoseBlenderOutput* CreatePoseBlenderOutput(PoseBlenderConstant const* constant, memory::Allocator& alloc);
+ void DestroyPoseBlenderOutput(PoseBlenderOutput * output, memory::Allocator& alloc);
+
+ void EvaluatePoseBlender(PoseBlenderConstant const * constant, PoseBlenderInput const * input, PoseBlenderOutput * output, PoseBlenderMemory* memory, PoseBlenderWorkspace * workspace);
+}
+
+}
diff --git a/Runtime/mecanim/animation/streamedclip.cpp b/Runtime/mecanim/animation/streamedclip.cpp
new file mode 100644
index 0000000..8b27785
--- /dev/null
+++ b/Runtime/mecanim/animation/streamedclip.cpp
@@ -0,0 +1,196 @@
+#include "UnityPrefix.h"
+#include "streamedclip.h"
+#include "Runtime/mecanim/memory.h"
+#include "Runtime/Utilities/Prefetch.h"
+#include "Runtime/Math/Simd/math.h"
+
+namespace mecanim
+{
+namespace animation
+{
+
+
+inline static float EvaluateCache (const StreamedCacheItem& cache, float sampleTime)
+{
+ float t = sampleTime - cache.time;
+ return (t * (t * (t * cache.coeff[0] + cache.coeff[1]) + cache.coeff[2])) + cache.coeff[3];
+}
+
+inline static void EvaluateMultipleCaches ( const StreamedCacheItem& cache0,
+ const StreamedCacheItem& cache1,
+ const StreamedCacheItem& cache2,
+ const StreamedCacheItem& cache3,
+ float sampleTime, float* output)
+{
+ const math::float4 time(sampleTime);
+
+ const math::float4 cachetime(cache0.time, cache1.time, cache2.time, cache3.time);
+ const math::float4 dt = time - cachetime;
+
+ const math::float4 coeffs0(cache0.coeff[0], cache1.coeff[0], cache2.coeff[0], cache3.coeff[0]);
+ const math::float4 coeffs1(cache0.coeff[1], cache1.coeff[1], cache2.coeff[1], cache3.coeff[1]);
+ const math::float4 coeffs2(cache0.coeff[2], cache1.coeff[2], cache2.coeff[2], cache3.coeff[2]);
+ const math::float4 coeffs3(cache0.coeff[3], cache1.coeff[3], cache2.coeff[3], cache3.coeff[3]);
+
+ ATTRIBUTE_ALIGN(ALIGN4F) float v[4];
+ math::store(dt * (dt * (dt * coeffs0 + coeffs1) + coeffs2) + coeffs3, v);
+
+ output[0] = v[0];
+ output[1] = v[1];
+ output[2] = v[2];
+ output[3] = v[3];
+}
+
+static void EvaluateCaches (const StreamedClipMemory& cache, float sampleTime, float* output)
+{
+ const StreamedCacheItem* caches = &cache.caches[0];
+ Prefetch(caches);
+ Prefetch(caches+2);
+ Prefetch(caches+4);
+ Prefetch(caches+6);
+ Prefetch(caches+8);
+
+ int i = 0;
+ for ( ; i+4 <= cache.cacheCount; i+=4, caches+=4 )
+ {
+ Prefetch(caches+10);
+ Prefetch(caches+12);
+ const StreamedCacheItem& item0 = *(caches);
+ const StreamedCacheItem& item1 = *(caches+1);
+ const StreamedCacheItem& item2 = *(caches+2);
+ const StreamedCacheItem& item3 = *(caches+3);
+ EvaluateMultipleCaches(item0, item1, item2, item3, sampleTime, &output[i]);
+ }
+
+ for ( ; i<cache.cacheCount; ++i, ++caches)
+ {
+ const StreamedCacheItem& item = *caches;
+ output[i] = EvaluateCache(item, sampleTime);
+ }
+
+}
+
+static void RewindCache (StreamedClipMemory& cache)
+{
+ cache.time = -std::numeric_limits<float>::infinity();
+ cache.readByteOffset = 0;
+}
+
+static inline void ConsumeCurveTimeData (const CurveTimeData* __restrict curveData, StreamedCacheItem* __restrict caches)
+{
+ float time = curveData->time;
+ const CurveKey* __restrict keys = reinterpret_cast<const CurveKey*> (curveData + 1);
+ int count = curveData->count;
+
+ Prefetch(keys,0);
+ Prefetch(keys+3);
+
+ int curveIndex = keys[0].curveIndex;
+ float coeff0 = keys[0].coeff[0];
+ float coeff1 = keys[0].coeff[1];
+ float coeff2 = keys[0].coeff[2];
+ float coeff3 = keys[0].coeff[3];
+
+ for (int i=1;i<count;i++)
+ {
+ Prefetch(keys+3+i);
+
+ StreamedCacheItem& activeCache = caches[curveIndex];
+ activeCache.time = time;
+ activeCache.coeff[0] = coeff0;
+ activeCache.coeff[1] = coeff1;
+ activeCache.coeff[2] = coeff2;
+ activeCache.coeff[3] = coeff3;
+ curveIndex = keys[i].curveIndex;
+ coeff0 = keys[i].coeff[0];
+ coeff1 = keys[i].coeff[1];
+ coeff2 = keys[i].coeff[2];
+ coeff3 = keys[i].coeff[3];
+ }
+ StreamedCacheItem& activeCache = caches[curveIndex];
+ activeCache.time = time;
+ activeCache.coeff[0] = coeff0;
+ activeCache.coeff[1] = coeff1;
+ activeCache.coeff[2] = coeff2;
+ activeCache.coeff[3] = coeff3;
+}
+
+static void SeekClipForward (const UInt8* curveData, float time, StreamedClipMemory& cache)
+{
+ int readByteOffset = cache.readByteOffset;
+ const CurveTimeData* data = reinterpret_cast<const CurveTimeData*> (curveData + readByteOffset);
+
+ while (time >= data->time)
+ {
+ // Consume the data and apply it to the cache
+ ConsumeCurveTimeData(data, cache.caches);
+
+ // Seek forward by the consumed data
+ readByteOffset += sizeof(CurveTimeData) + data->count * sizeof(CurveKey);
+
+ data = reinterpret_cast<const CurveTimeData*> (curveData + readByteOffset);
+ }
+
+ // Synchronize cached time & offset
+ cache.time = time;
+ cache.readByteOffset = readByteOffset;
+}
+
+void SeekClip (const StreamedClip& curveData, StreamedClipMemory& cache, float time)
+{
+ Assert(cache.cacheCount == curveData.curveCount);
+
+ // No seeking is necessary, we are exactly at the same cached time
+ // (Happens due to SampleClipAtIndex)
+ // @TODO: it would be best to remove that and instead seperate root motion data from other data
+ if (time == cache.time)
+ return;
+
+ // Seeking backwards is not supported. Jump the beginning of curve.
+ if (time < cache.time)
+ RewindCache(cache);
+
+ // Seek and make sure the curve cache is up to date
+ const UInt8* stream = reinterpret_cast<const UInt8*> (curveData.data.Get());
+ SeekClipForward(stream, time, cache);
+}
+
+void SampleClip (const StreamedClip& curveData, StreamedClipMemory& cache, float time, float* output)
+{
+ SeekClip(curveData, cache, time);
+
+ // Evaluate the cache and write sampled values to output
+ EvaluateCaches(cache, time, output);
+}
+
+float SampleClipAtIndex (const StreamedClip& curveData, StreamedClipMemory& cache, int index, float time)
+{
+ Assert(index < curveData.curveCount);
+
+ SeekClip(curveData, cache, time);
+
+ return EvaluateCache(cache.caches[index], time);
+}
+
+void CreateStreamedClipMemory(const StreamedClip& clip, StreamedClipMemory& mem, memory::Allocator& alloc)
+{
+ SETPROFILERLABEL(StreamedClipMemory);
+
+ mem.caches = alloc.ConstructArray<StreamedCacheItem>(clip.curveCount);
+ mem.cacheCount = clip.curveCount;
+
+ RewindCache(mem);
+}
+
+void DestroyStreamedClipMemory (StreamedClipMemory& memory, memory::Allocator& alloc)
+{
+ alloc.Deallocate(memory.caches);
+}
+
+void DestroyStreamedClip (StreamedClip& clip, memory::Allocator& alloc)
+{
+ alloc.Deallocate(clip.data);
+}
+
+}
+}
diff --git a/Runtime/mecanim/animation/streamedclip.h b/Runtime/mecanim/animation/streamedclip.h
new file mode 100644
index 0000000..93e1fc7
--- /dev/null
+++ b/Runtime/mecanim/animation/streamedclip.h
@@ -0,0 +1,93 @@
+#pragma once
+
+#include "Runtime/mecanim/defs.h"
+#include "Runtime/Serialize/Blobification/offsetptr.h"
+
+
+namespace mecanim
+{
+namespace animation
+{
+
+// StreamedClip is a stream oriented clip format.
+// It stores hermite coefficients directly. Multiple curves are stored in the same stream. Keys for all curves are sorted by time.
+// This drastically reduces cache misses. In the best case we can sample a clip with a single cache miss. Clip data is read completely linearly.
+
+// Instead of laying out the animationclip by curves containing any array of keys.
+// We sort all keys of all curves by time and each key has an index.
+// This is a streamed format basically CurveTimeData defines the time value and the number of keys at this time.
+// Then CurveTimeData.count CurveKey elements will follow.
+
+// time = 0
+// CurveTimeData(4) ... CurveKey . CurveKey . CurveKey . CurveKey
+
+// time = 0.2
+// CurveTimeData(1) ... CurveKey
+
+// Sampling is separated into two functions.
+// 1. Seeking, it is responsible for updating the cached in the StreamedClipMemory to ensure each curve Index has an up to date time and hermite cofficients.
+// 2. Evaluating the caches. This simply evaluates the hermite caches
+
+// See StreamedClipBuilder.cpp and AnimationClip.cpp on how to build the streamed clip data.
+
+struct StreamedCacheItem
+{
+ float time;
+ float coeff[4];
+};
+
+struct StreamedClipMemory
+{
+ StreamedCacheItem* caches;
+ int cacheCount;
+ float time;
+ UInt32 readByteOffset;
+};
+
+struct StreamedClip
+{
+ uint32_t dataSize;
+ OffsetPtr<uint32_t> data;
+ UInt32 curveCount;
+
+
+ StreamedClip () : curveCount (0),dataSize(0) { }
+
+ DEFINE_GET_TYPESTRING(StreamedClip)
+
+ template<class TransferFunction>
+ inline void Transfer (TransferFunction& transfer)
+ {
+ TRANSFER_BLOB_ONLY(dataSize);
+ MANUAL_ARRAY_TRANSFER2(uint32_t, data, dataSize);
+ TRANSFER(curveCount);
+ }
+};
+
+struct CurveTimeData
+{
+ float time;
+ UInt32 count;
+ // This is implicitly followed by CurveKey in the stream
+};
+
+struct CurveKey
+{
+ int curveIndex;
+ float coeff[4];
+};
+
+
+// Sample functions
+void SampleClip (const StreamedClip& curveData, StreamedClipMemory& cache, float time, float* output);
+float SampleClipAtIndex (const StreamedClip& curveData, StreamedClipMemory& cache, int index, float time);
+
+
+// Creation & Destruction
+// StreamedClipBuilder.h actually creates the streamed clip from an array of AnimationCurve.
+void CreateStreamedClipMemory (const StreamedClip& clip, StreamedClipMemory& memory, memory::Allocator& alloc);
+void DestroyStreamedClipMemory (StreamedClipMemory& memory, memory::Allocator& alloc);
+void DestroyStreamedClip (StreamedClip& clip, memory::Allocator& alloc);
+
+}
+} \ No newline at end of file