diff options
Diffstat (limited to 'Runtime/mecanim/animation')
-rw-r--r-- | Runtime/mecanim/animation/avatar.cpp | 1799 | ||||
-rw-r--r-- | Runtime/mecanim/animation/avatar.h | 706 | ||||
-rw-r--r-- | Runtime/mecanim/animation/blendtree.cpp | 967 | ||||
-rw-r--r-- | Runtime/mecanim/animation/blendtree.h | 285 | ||||
-rw-r--r-- | Runtime/mecanim/animation/clipmuscle.cpp | 1366 | ||||
-rw-r--r-- | Runtime/mecanim/animation/clipmuscle.h | 264 | ||||
-rw-r--r-- | Runtime/mecanim/animation/constantclip.cpp | 34 | ||||
-rw-r--r-- | Runtime/mecanim/animation/constantclip.h | 36 | ||||
-rw-r--r-- | Runtime/mecanim/animation/curvedata.cpp | 118 | ||||
-rw-r--r-- | Runtime/mecanim/animation/curvedata.h | 82 | ||||
-rw-r--r-- | Runtime/mecanim/animation/damp.cpp | 24 | ||||
-rw-r--r-- | Runtime/mecanim/animation/damp.h | 42 | ||||
-rw-r--r-- | Runtime/mecanim/animation/denseclip.cpp | 63 | ||||
-rw-r--r-- | Runtime/mecanim/animation/denseclip.h | 47 | ||||
-rw-r--r-- | Runtime/mecanim/animation/poseblender.cpp | 228 | ||||
-rw-r--r-- | Runtime/mecanim/animation/poseblender.h | 113 | ||||
-rw-r--r-- | Runtime/mecanim/animation/streamedclip.cpp | 196 | ||||
-rw-r--r-- | Runtime/mecanim/animation/streamedclip.h | 93 |
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 |