Add SourceState and DestinationState modes to State Machine interruption modes in Anim Graph
#1735
This commit is contained in:
@@ -1583,14 +1583,24 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Transition rule will be rechecked during active transition with option to interrupt transition.
|
||||
/// Transition rule will be rechecked during active transition with option to interrupt transition (to go back to the source state).
|
||||
/// </summary>
|
||||
RuleRechecking = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Interrupted transition is immediately stopped without blending out.
|
||||
/// Interrupted transition is immediately stopped without blending out (back to the source/destination state).
|
||||
/// </summary>
|
||||
Instant = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Enables checking other transitions in the source state that might interrupt this one.
|
||||
/// </summary>
|
||||
SourceState = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Enables checking transitions in the destination state that might interrupt this one.
|
||||
/// </summary>
|
||||
DestinationState = 8,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1613,6 +1623,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
UseDefaultRule = 4,
|
||||
InterruptionRuleRechecking = 8,
|
||||
InterruptionInstant = 16,
|
||||
InterruptionSourceState = 32,
|
||||
InterruptionDestinationState = 64,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1773,7 +1785,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transition interruption options.
|
||||
/// Transition interruption options (flags, can select multiple values).
|
||||
/// </summary>
|
||||
[EditorOrder(70), DefaultValue(InterruptionFlags.None)]
|
||||
public InterruptionFlags Interruption
|
||||
@@ -1785,12 +1797,18 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
flags |= InterruptionFlags.RuleRechecking;
|
||||
if (_data.HasFlag(Data.FlagTypes.InterruptionInstant))
|
||||
flags |= InterruptionFlags.Instant;
|
||||
if (_data.HasFlag(Data.FlagTypes.InterruptionSourceState))
|
||||
flags |= InterruptionFlags.SourceState;
|
||||
if (_data.HasFlag(Data.FlagTypes.InterruptionDestinationState))
|
||||
flags |= InterruptionFlags.DestinationState;
|
||||
return flags;
|
||||
}
|
||||
set
|
||||
{
|
||||
_data.SetFlag(Data.FlagTypes.InterruptionRuleRechecking, value.HasFlag(InterruptionFlags.RuleRechecking));
|
||||
_data.SetFlag(Data.FlagTypes.InterruptionInstant, value.HasFlag(InterruptionFlags.Instant));
|
||||
_data.SetFlag(Data.FlagTypes.InterruptionSourceState, value.HasFlag(InterruptionFlags.SourceState));
|
||||
_data.SetFlag(Data.FlagTypes.InterruptionDestinationState, value.HasFlag(InterruptionFlags.DestinationState));
|
||||
SourceState.SaveTransitions(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ namespace FlaxEditor.Surface.Undo
|
||||
|
||||
public ConnectBoxesAction(InputBox iB, OutputBox oB, bool connect)
|
||||
{
|
||||
if (iB == null || oB == null || iB.ParentNode == null || oB.ParentNode == null)
|
||||
throw new System.ArgumentNullException();
|
||||
_surface = iB.Surface;
|
||||
_context = new ContextHandle(iB.ParentNode.Context);
|
||||
_connect = connect;
|
||||
|
||||
@@ -99,10 +99,7 @@ void BlendPoseBucketInit(AnimGraphInstanceData::Bucket& bucket)
|
||||
|
||||
void StateMachineBucketInit(AnimGraphInstanceData::Bucket& bucket)
|
||||
{
|
||||
bucket.StateMachine.LastUpdateFrame = 0;
|
||||
bucket.StateMachine.CurrentState = nullptr;
|
||||
bucket.StateMachine.ActiveTransition = nullptr;
|
||||
bucket.StateMachine.TransitionPosition = 0.0f;
|
||||
Platform::MemoryClear(&bucket.StateMachine, sizeof(bucket.StateMachine));
|
||||
}
|
||||
|
||||
void SlotBucketInit(AnimGraphInstanceData::Bucket& bucket)
|
||||
|
||||
@@ -129,6 +129,8 @@ public:
|
||||
UseDefaultRule = 4,
|
||||
InterruptionRuleRechecking = 8,
|
||||
InterruptionInstant = 16,
|
||||
InterruptionSourceState = 32,
|
||||
InterruptionDestinationState = 64,
|
||||
};
|
||||
|
||||
public:
|
||||
@@ -256,7 +258,10 @@ public:
|
||||
uint64 LastUpdateFrame;
|
||||
AnimGraphNode* CurrentState;
|
||||
AnimGraphStateTransition* ActiveTransition;
|
||||
AnimGraphStateTransition* BaseTransition;
|
||||
AnimGraphNode* BaseTransitionState;
|
||||
float TransitionPosition;
|
||||
float BaseTransitionPosition;
|
||||
};
|
||||
|
||||
struct SlotBucket
|
||||
@@ -858,7 +863,7 @@ public:
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets all the state bucket used by the given graph including sub-graphs (total). Can eb used to reset the animation state of the nested graph (including children).
|
||||
/// Resets all the state bucket used by the given graph including sub-graphs (total). Can be used to reset the animation state of the nested graph (including children).
|
||||
/// </summary>
|
||||
void ResetBuckets(AnimGraphContext& context, AnimGraphBase* graph);
|
||||
|
||||
@@ -887,5 +892,7 @@ private:
|
||||
Variant SampleAnimationsWithBlend(AnimGraphNode* node, bool loop, float length, float startTimePos, float prevTimePos, float& newTimePos, Animation* animA, Animation* animB, Animation* animC, float speedA, float speedB, float speedC, float alphaA, float alphaB, float alphaC);
|
||||
Variant Blend(AnimGraphNode* node, const Value& poseA, const Value& poseB, float alpha, AlphaBlendMode alphaMode);
|
||||
Variant SampleState(AnimGraphNode* state);
|
||||
void InitStateTransition(AnimGraphContext& context, AnimGraphInstanceData::StateMachineBucket& stateMachineBucket, AnimGraphStateTransition* transition = nullptr);
|
||||
AnimGraphStateTransition* UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, AnimGraphNode* state, AnimGraphNode* ignoreState = nullptr);
|
||||
void UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, AnimGraphInstanceData::StateMachineBucket& stateMachineBucket, const AnimGraphNode::StateBaseData& stateData);
|
||||
};
|
||||
|
||||
@@ -470,10 +470,12 @@ Variant AnimGraphExecutor::Blend(AnimGraphNode* node, const Value& poseA, const
|
||||
{
|
||||
ANIM_GRAPH_PROFILE_EVENT("Blend Pose");
|
||||
|
||||
if (isnan(alpha) || isinf(alpha))
|
||||
alpha = 0;
|
||||
alpha = Math::Saturate(alpha);
|
||||
alpha = AlphaBlend::Process(alpha, alphaMode);
|
||||
|
||||
const auto nodes = node->GetNodes(this);
|
||||
|
||||
auto nodesA = static_cast<AnimGraphImpulse*>(poseA.AsPointer);
|
||||
auto nodesB = static_cast<AnimGraphImpulse*>(poseB.AsPointer);
|
||||
if (!ANIM_GRAPH_IS_VALID_PTR(poseA))
|
||||
@@ -494,32 +496,40 @@ Variant AnimGraphExecutor::Blend(AnimGraphNode* node, const Value& poseA, const
|
||||
|
||||
Variant AnimGraphExecutor::SampleState(AnimGraphNode* state)
|
||||
{
|
||||
// Prepare
|
||||
auto& data = state->Data.State;
|
||||
if (data.Graph == nullptr || data.Graph->GetRootNode() == nullptr)
|
||||
{
|
||||
// Invalid state graph
|
||||
return Value::Null;
|
||||
}
|
||||
|
||||
ANIM_GRAPH_PROFILE_EVENT("Evaluate State");
|
||||
|
||||
// Evaluate state
|
||||
auto rootNode = data.Graph->GetRootNode();
|
||||
auto result = eatBox((Node*)rootNode, &rootNode->Boxes[0]);
|
||||
|
||||
return result;
|
||||
return eatBox((Node*)rootNode, &rootNode->Boxes[0]);
|
||||
}
|
||||
|
||||
void AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, AnimGraphInstanceData::StateMachineBucket& stateMachineBucket, const AnimGraphNode::StateBaseData& stateData)
|
||||
void AnimGraphExecutor::InitStateTransition(AnimGraphContext& context, AnimGraphInstanceData::StateMachineBucket& stateMachineBucket, AnimGraphStateTransition* transition)
|
||||
{
|
||||
// Reset transiton
|
||||
stateMachineBucket.ActiveTransition = transition;
|
||||
stateMachineBucket.TransitionPosition = 0.0f;
|
||||
|
||||
// End base transition
|
||||
if (stateMachineBucket.BaseTransition)
|
||||
{
|
||||
ResetBuckets(context, stateMachineBucket.BaseTransitionState->Data.State.Graph);
|
||||
stateMachineBucket.BaseTransition = nullptr;
|
||||
stateMachineBucket.BaseTransitionState = nullptr;
|
||||
stateMachineBucket.BaseTransitionPosition = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
AnimGraphStateTransition* AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, AnimGraphNode* state, AnimGraphNode* ignoreState)
|
||||
{
|
||||
int32 transitionIndex = 0;
|
||||
const AnimGraphNode::StateBaseData& stateData = state->Data.State;
|
||||
while (transitionIndex < ANIM_GRAPH_MAX_STATE_TRANSITIONS && stateData.Transitions[transitionIndex] != AnimGraphNode::StateData::InvalidTransitionIndex)
|
||||
{
|
||||
const uint16 idx = stateData.Transitions[transitionIndex];
|
||||
ASSERT(idx < stateMachineData.Graph->StateTransitions.Count());
|
||||
auto& transition = stateMachineData.Graph->StateTransitions[idx];
|
||||
if (transition.Destination == stateMachineBucket.CurrentState)
|
||||
if (transition.Destination == state || transition.Destination == ignoreState)
|
||||
{
|
||||
// Ignore transition to the current state
|
||||
transitionIndex++;
|
||||
@@ -527,7 +537,7 @@ void AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const
|
||||
}
|
||||
|
||||
// Evaluate source state transition data (position, length, etc.)
|
||||
const Value sourceStatePtr = SampleState(stateMachineBucket.CurrentState);
|
||||
const Value sourceStatePtr = SampleState(state);
|
||||
auto& transitionData = context.TransitionData; // Note: this could support nested transitions but who uses state machine inside transition rule?
|
||||
if (ANIM_GRAPH_IS_VALID_PTR(sourceStatePtr))
|
||||
{
|
||||
@@ -548,6 +558,7 @@ void AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const
|
||||
if (transition.RuleGraph && !useDefaultRule)
|
||||
{
|
||||
// Execute transition rule
|
||||
ANIM_GRAPH_PROFILE_EVENT("Rule");
|
||||
auto rootNode = transition.RuleGraph->GetRootNode();
|
||||
ASSERT(rootNode);
|
||||
if (!(bool)eatBox((Node*)rootNode, &rootNode->Boxes[0]))
|
||||
@@ -570,10 +581,7 @@ void AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const
|
||||
canEnter = true;
|
||||
if (canEnter)
|
||||
{
|
||||
// Start transition
|
||||
stateMachineBucket.ActiveTransition = &transition;
|
||||
stateMachineBucket.TransitionPosition = 0.0f;
|
||||
break;
|
||||
return &transition;
|
||||
}
|
||||
|
||||
// Skip after Solo transition
|
||||
@@ -583,6 +591,18 @@ void AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const
|
||||
|
||||
transitionIndex++;
|
||||
}
|
||||
|
||||
// No transition
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AnimGraphExecutor::UpdateStateTransitions(AnimGraphContext& context, const AnimGraphNode::StateMachineData& stateMachineData, AnimGraphInstanceData::StateMachineBucket& stateMachineBucket, const AnimGraphNode::StateBaseData& stateData)
|
||||
{
|
||||
AnimGraphStateTransition* transition = UpdateStateTransitions(context, stateMachineData, stateMachineBucket.CurrentState);
|
||||
if (transition)
|
||||
{
|
||||
InitStateTransition(context, stateMachineBucket, transition);
|
||||
}
|
||||
}
|
||||
|
||||
void ComputeMultiBlendLength(float& length, AnimGraphNode* node)
|
||||
@@ -1511,10 +1531,9 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
|
||||
// Blend two animations
|
||||
{
|
||||
const float alpha = Math::Saturate(bucket.TransitionPosition / blendDuration);
|
||||
const float alpha = bucket.TransitionPosition / blendDuration;
|
||||
const auto valueA = tryGetValue(node->GetBox(FirstBlendPoseBoxIndex + bucket.PreviousBlendPoseIndex), Value::Null);
|
||||
const auto valueB = tryGetValue(node->GetBox(FirstBlendPoseBoxIndex + poseIndex), Value::Null);
|
||||
|
||||
value = Blend(node, valueA, valueB, alpha, mode);
|
||||
}
|
||||
|
||||
@@ -1620,22 +1639,21 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
|
||||
// Enter to the first state pointed by the Entry node (without transitions)
|
||||
bucket.CurrentState = data.Graph->GetRootNode();
|
||||
bucket.ActiveTransition = nullptr;
|
||||
bucket.TransitionPosition = 0.0f;
|
||||
InitStateTransition(context, bucket);
|
||||
|
||||
// Reset all state buckets pof the graphs and nodes included inside the state machine
|
||||
// Reset all state buckets of the graphs and nodes included inside the state machine
|
||||
ResetBuckets(context, data.Graph);
|
||||
}
|
||||
#define END_TRANSITION() \
|
||||
ResetBuckets(context, bucket.CurrentState->Data.State.Graph); \
|
||||
bucket.CurrentState = bucket.ActiveTransition->Destination; \
|
||||
bucket.ActiveTransition = nullptr; \
|
||||
bucket.TransitionPosition = 0.0f
|
||||
InitStateTransition(context, bucket)
|
||||
|
||||
// Update the active transition
|
||||
if (bucket.ActiveTransition)
|
||||
{
|
||||
bucket.TransitionPosition += context.DeltaTime;
|
||||
ASSERT(bucket.CurrentState);
|
||||
|
||||
// Check for transition end
|
||||
if (bucket.TransitionPosition >= bucket.ActiveTransition->BlendDuration)
|
||||
@@ -1643,38 +1661,70 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
END_TRANSITION();
|
||||
}
|
||||
// Check for transition interruption
|
||||
else if (EnumHasAnyFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::InterruptionRuleRechecking))
|
||||
else if (EnumHasAnyFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::InterruptionRuleRechecking) &&
|
||||
EnumHasNoneFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::UseDefaultRule) &&
|
||||
bucket.ActiveTransition->RuleGraph)
|
||||
{
|
||||
const bool useDefaultRule = EnumHasAnyFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::UseDefaultRule);
|
||||
if (bucket.ActiveTransition->RuleGraph && !useDefaultRule)
|
||||
// Execute transition rule
|
||||
auto rootNode = bucket.ActiveTransition->RuleGraph->GetRootNode();
|
||||
if (!(bool)eatBox((Node*)rootNode, &rootNode->Boxes[0]))
|
||||
{
|
||||
// Execute transition rule
|
||||
auto rootNode = bucket.ActiveTransition->RuleGraph->GetRootNode();
|
||||
if (!(bool)eatBox((Node*)rootNode, &rootNode->Boxes[0]))
|
||||
bool cancelTransition = false;
|
||||
if (EnumHasAnyFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::InterruptionInstant))
|
||||
{
|
||||
bool cancelTransition = false;
|
||||
if (EnumHasAnyFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::InterruptionInstant))
|
||||
cancelTransition = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Blend back to the source state (remove currently applied delta and rewind transition)
|
||||
bucket.TransitionPosition -= context.DeltaTime;
|
||||
bucket.TransitionPosition -= context.DeltaTime;
|
||||
if (bucket.TransitionPosition <= ZeroTolerance)
|
||||
{
|
||||
cancelTransition = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Blend back to the source state (remove currently applied delta and rewind transition)
|
||||
bucket.TransitionPosition -= context.DeltaTime;
|
||||
bucket.TransitionPosition -= context.DeltaTime;
|
||||
if (bucket.TransitionPosition <= ZeroTolerance)
|
||||
{
|
||||
cancelTransition = true;
|
||||
}
|
||||
}
|
||||
if (cancelTransition)
|
||||
{
|
||||
// Go back to the source state
|
||||
ResetBuckets(context, bucket.CurrentState->Data.State.Graph);
|
||||
bucket.ActiveTransition = nullptr;
|
||||
bucket.TransitionPosition = 0.0f;
|
||||
}
|
||||
}
|
||||
if (cancelTransition)
|
||||
{
|
||||
// Go back to the source state
|
||||
ResetBuckets(context, bucket.CurrentState->Data.State.Graph);
|
||||
InitStateTransition(context, bucket);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bucket.ActiveTransition && !bucket.BaseTransition && EnumHasAnyFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::InterruptionSourceState))
|
||||
{
|
||||
// Try to interrupt with any other transition in the source state (except the current transition)
|
||||
if (AnimGraphStateTransition* transition = UpdateStateTransitions(context, data, bucket.CurrentState, bucket.ActiveTransition->Destination))
|
||||
{
|
||||
// Change active transition to the interrupted one
|
||||
if (EnumHasNoneFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::InterruptionInstant))
|
||||
{
|
||||
// Cache the current blending state to be used as a base when blending towards new destination state (seamless blending after interruption)
|
||||
bucket.BaseTransition = bucket.ActiveTransition;
|
||||
bucket.BaseTransitionState = bucket.CurrentState;
|
||||
bucket.BaseTransitionPosition = bucket.TransitionPosition;
|
||||
}
|
||||
bucket.ActiveTransition = transition;
|
||||
bucket.TransitionPosition = 0.0f;
|
||||
}
|
||||
}
|
||||
if (bucket.ActiveTransition && !bucket.BaseTransition && EnumHasAnyFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::InterruptionDestinationState))
|
||||
{
|
||||
// Try to interrupt with any other transition in the destination state (except the transition back to the current state if exists)
|
||||
if (AnimGraphStateTransition* transition = UpdateStateTransitions(context, data, bucket.ActiveTransition->Destination, bucket.CurrentState))
|
||||
{
|
||||
// Change active transition to the interrupted one
|
||||
if (EnumHasNoneFlags(bucket.ActiveTransition->Flags, AnimGraphStateTransition::FlagTypes::InterruptionInstant))
|
||||
{
|
||||
// Cache the current blending state to be used as a base when blending towards new destination state (seamless blending after interruption)
|
||||
bucket.BaseTransition = bucket.ActiveTransition;
|
||||
bucket.BaseTransitionState = bucket.CurrentState;
|
||||
bucket.BaseTransitionPosition = bucket.TransitionPosition;
|
||||
}
|
||||
bucket.CurrentState = bucket.ActiveTransition->Destination;
|
||||
bucket.ActiveTransition = transition;
|
||||
bucket.TransitionPosition = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1703,9 +1753,23 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
}
|
||||
}
|
||||
|
||||
// Sample the current state
|
||||
const auto currentState = SampleState(bucket.CurrentState);
|
||||
value = currentState;
|
||||
if (bucket.BaseTransitionState)
|
||||
{
|
||||
// Sample the other state (eg. when blending from interrupted state to the another state from the old destination)
|
||||
value = SampleState(bucket.BaseTransitionState);
|
||||
if (bucket.BaseTransition)
|
||||
{
|
||||
// Evaluate the base pose from the time when transition was interrupted
|
||||
const auto destinationState = SampleState(bucket.BaseTransition->Destination);
|
||||
const float alpha = bucket.BaseTransitionPosition / bucket.BaseTransition->BlendDuration;
|
||||
value = Blend(node, value, destinationState, alpha, bucket.BaseTransition->BlendMode);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Sample the current state
|
||||
value = SampleState(bucket.CurrentState);
|
||||
}
|
||||
|
||||
// Handle active transition blending
|
||||
if (bucket.ActiveTransition)
|
||||
@@ -1714,14 +1778,12 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
const auto destinationState = SampleState(bucket.ActiveTransition->Destination);
|
||||
|
||||
// Perform blending
|
||||
const float alpha = Math::Saturate(bucket.TransitionPosition / bucket.ActiveTransition->BlendDuration);
|
||||
value = Blend(node, currentState, destinationState, alpha, bucket.ActiveTransition->BlendMode);
|
||||
const float alpha = bucket.TransitionPosition / bucket.ActiveTransition->BlendDuration;
|
||||
value = Blend(node, value, destinationState, alpha, bucket.ActiveTransition->BlendMode);
|
||||
}
|
||||
|
||||
// Update bucket
|
||||
bucket.LastUpdateFrame = context.CurrentFrameIndex;
|
||||
#undef END_TRANSITION
|
||||
|
||||
break;
|
||||
}
|
||||
// Entry
|
||||
@@ -2142,7 +2204,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
// Blend out
|
||||
auto input = tryGetValue(node->GetBox(1), Value::Null);
|
||||
bucket.BlendOutPosition += deltaTime;
|
||||
const float alpha = Math::Saturate(bucket.BlendOutPosition / slot.BlendOutTime);
|
||||
const float alpha = bucket.BlendOutPosition / slot.BlendOutTime;
|
||||
value = Blend(node, value, input, alpha, AlphaBlendMode::HermiteCubic);
|
||||
}
|
||||
else if (bucket.LoopsDone == 0 && slot.BlendInTime > 0.0f && bucket.BlendInPosition < slot.BlendInTime)
|
||||
@@ -2150,7 +2212,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
// Blend in
|
||||
auto input = tryGetValue(node->GetBox(1), Value::Null);
|
||||
bucket.BlendInPosition += deltaTime;
|
||||
const float alpha = Math::Saturate(bucket.BlendInPosition / slot.BlendInTime);
|
||||
const float alpha = bucket.BlendInPosition / slot.BlendInTime;
|
||||
value = Blend(node, input, value, alpha, AlphaBlendMode::HermiteCubic);
|
||||
}
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user