265 lines
8.9 KiB
C#
265 lines
8.9 KiB
C#
using T3.Core.Animation;
|
|
using T3.Core.Utils;
|
|
|
|
namespace Lib.numbers.anim.animators;
|
|
|
|
[Guid("95d586a2-ee14-4ff5-a5bb-40c497efde95")]
|
|
public sealed class TriggerAnim : Instance<TriggerAnim>
|
|
{
|
|
[Output(Guid = "aac4ecbf-436a-4414-94c1-53d517a8e587", DirtyFlagTrigger = DirtyFlagTrigger.Animated)]
|
|
public readonly Slot<float> Result = new();
|
|
|
|
[Output(Guid = "863C0A57-E893-4536-9EFE-6D001CB9D999", DirtyFlagTrigger = DirtyFlagTrigger.Animated)]
|
|
public readonly Slot<bool> HasCompleted = new();
|
|
|
|
public TriggerAnim()
|
|
{
|
|
Result.UpdateAction += Update;
|
|
HasCompleted.UpdateAction += Update;
|
|
}
|
|
|
|
private enum Directions
|
|
{
|
|
Forward,
|
|
Backwards,
|
|
None
|
|
}
|
|
|
|
private void Update(EvaluationContext context)
|
|
{
|
|
// Prevent double evaluation to so all connected outputs to WasHit receive event.
|
|
if (Math.Abs(context.LocalFxTime - _lastUpdateTime) < double.Epsilon)
|
|
return;
|
|
|
|
_lastUpdateTime = context.LocalFxTime;
|
|
|
|
_baseValue = Base.GetValue(context);
|
|
_amplitudeValue = Amplitude.GetValue(context);
|
|
_bias = Bias.GetValue(context);
|
|
_shape = Shape.GetEnumValue<Shapes>(context);
|
|
_duration = Duration.GetValue(context);
|
|
_delay = Delay.GetValue(context);
|
|
|
|
var timeMode = TimeMode.GetEnumValue<Times>(context);
|
|
var currentTime = timeMode switch
|
|
{
|
|
Times.PlayTime => context.Playback.TimeInBars,
|
|
Times.AppRunTime => Playback.RunTimeInSecs,
|
|
_ => context.LocalFxTime
|
|
};
|
|
|
|
var animMode = AnimMode.GetEnumValue<AnimModes>(context);
|
|
var triggerVariableName = UseTriggerVar.GetValue(context);
|
|
|
|
var isTriggeredByVar = !Trigger.HasInputConnections
|
|
&& context.IntVariables.GetValueOrDefault(triggerVariableName, 0 ) == 1;
|
|
|
|
var triggered = Trigger.GetValue(context) || isTriggeredByVar;
|
|
if (triggered != _trigger)
|
|
{
|
|
HasCompleted.Value = false;
|
|
_trigger = triggered;
|
|
|
|
if (animMode == AnimModes.ForwardAndBackwards)
|
|
{
|
|
_triggerTime = currentTime;
|
|
_currentDirection = triggered ? Directions.Forward : Directions.Backwards;
|
|
_startProgress = LastFraction;
|
|
}
|
|
else
|
|
{
|
|
if (triggered)
|
|
{
|
|
if (animMode == AnimModes.OnlyOnTrue)
|
|
{
|
|
_triggerTime = currentTime;
|
|
_currentDirection = Directions.Forward;
|
|
LastFraction = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (animMode == AnimModes.OnlyOnFalse)
|
|
{
|
|
_triggerTime = currentTime;
|
|
_currentDirection = Directions.Backwards;
|
|
LastFraction = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (animMode == AnimModes.ForwardAndBackwards)
|
|
{
|
|
var dp = (float)((currentTime - _triggerTime) / _duration);
|
|
switch (_currentDirection)
|
|
{
|
|
case Directions.Forward:
|
|
{
|
|
var delayedDp = (currentTime - _triggerTime < _delay) ? 0 : (float)((currentTime - _triggerTime - _delay) / _duration);
|
|
LastFraction = _startProgress + delayedDp;
|
|
if (LastFraction >= 1)
|
|
{
|
|
HasCompleted.Value = true;
|
|
LastFraction = 1;
|
|
_currentDirection = Directions.None;
|
|
}
|
|
break;
|
|
}
|
|
case Directions.Backwards:
|
|
{
|
|
LastFraction = _startProgress - dp;
|
|
if (LastFraction <= 0)
|
|
{
|
|
LastFraction = 0;
|
|
_currentDirection = Directions.None;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (_currentDirection)
|
|
{
|
|
case Directions.Forward:
|
|
{
|
|
// Apply delay to the progress calculation
|
|
var timeSinceTrigger = currentTime - _triggerTime;
|
|
if (timeSinceTrigger < _delay)
|
|
{
|
|
LastFraction = 0;
|
|
}
|
|
else
|
|
{
|
|
LastFraction = (timeSinceTrigger - _delay) / _duration;
|
|
if (LastFraction >= 1)
|
|
{
|
|
LastFraction = 1;
|
|
HasCompleted.Value = true;
|
|
_currentDirection = Directions.None;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case Directions.Backwards:
|
|
{
|
|
LastFraction = 1+( _triggerTime- currentTime )/_duration;
|
|
if (LastFraction < 0)
|
|
{
|
|
LastFraction = 0;
|
|
_currentDirection = Directions.None;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
var normalizedValue = CalcNormalizedValueForFraction(LastFraction, (int)_shape);
|
|
if (double.IsNaN(LastFraction) || double.IsInfinity(LastFraction))
|
|
{
|
|
LastFraction = 0;
|
|
}
|
|
|
|
//Result.Value = MathUtils.Lerp(_baseValue, _amplitudeValue, normalizedValue);
|
|
Result.Value = _baseValue + _amplitudeValue * normalizedValue;
|
|
}
|
|
|
|
public float CalcNormalizedValueForFraction(double t, int shapeIndex)
|
|
{
|
|
//var fraction = CalcFraction(t);
|
|
var value = MapShapes[shapeIndex]((float)t);
|
|
var biased = SchlickBias(value, _bias);
|
|
return biased;
|
|
}
|
|
|
|
private float SchlickBias(float x, float bias)
|
|
{
|
|
return x / ((1 / bias - 2) * (1 - x) + 1);
|
|
}
|
|
|
|
|
|
private delegate float MappingFunction(float fraction);
|
|
private static readonly MappingFunction[] MapShapes =
|
|
{
|
|
f => f.Clamp(0,1), // 0: Linear
|
|
f => MathUtils.SmootherStep(0,1,f.Clamp(0,1)), //1: Smooth Step
|
|
f => MathUtils.SmootherStep(0,1,f.Clamp(0,1)/2) *2 , //2: Easy In
|
|
f => MathUtils.SmootherStep(0,1,f.Clamp(0,1)/2 + 0.5f) *2 -1, //3: Easy Out
|
|
f => MathF.Sin(f.Clamp(0,1) * 40) * MathF.Pow(1-f.Clamp(0.0001f, 1),4) , //4: Shake
|
|
f => f<=0 ? 0 : (1-f.Clamp(0,1)), // 5: Kick
|
|
};
|
|
|
|
private bool _trigger;
|
|
private Shapes _shape;
|
|
private float _bias;
|
|
private float _baseValue;
|
|
private float _amplitudeValue;
|
|
private float _duration = 1;
|
|
private float _delay;
|
|
private double _lastUpdateTime = -1;
|
|
|
|
private double _startProgress;
|
|
public double LastFraction; // For Op Ui
|
|
|
|
public enum Shapes
|
|
{
|
|
Linear = 0,
|
|
SmoothStep = 1,
|
|
EaseIn = 2,
|
|
EaseOut = 3,
|
|
Shake = 4,
|
|
Kick = 5,
|
|
}
|
|
|
|
|
|
public enum AnimModes
|
|
{
|
|
OnlyOnTrue,
|
|
OnlyOnFalse,
|
|
ForwardAndBackwards,
|
|
}
|
|
|
|
private Directions _currentDirection = Directions.None;
|
|
private double _triggerTime;
|
|
|
|
[Input(Guid = "62949257-ADB3-4C67-AC0A-D37EE28DA81B")]
|
|
public readonly InputSlot<bool> Trigger = new();
|
|
|
|
[Input(Guid = "c0fa79d5-2c49-4d40-998f-4eb0101ae050", MappedType = typeof(Shapes))]
|
|
public readonly InputSlot<int> Shape = new();
|
|
|
|
[Input(Guid = "9913FCEA-3994-40D6-84A1-D56525B31C43", MappedType = typeof(AnimModes))]
|
|
public readonly InputSlot<int> AnimMode = new();
|
|
|
|
[Input(Guid = "0d56fc27-fa15-4f1e-aa09-f97af93d42c7")]
|
|
public readonly InputSlot<float> Duration = new();
|
|
|
|
[Input(Guid = "3AD8E756-7720-4F43-85DA-EFE1AF364CFE")]
|
|
public readonly InputSlot<float> Base = new();
|
|
|
|
[Input(Guid = "287fa06c-3e18-43f2-a4e1-0780c946dd84")]
|
|
public readonly InputSlot<float> Amplitude = new();
|
|
|
|
[Input(Guid = "214e244a-9e95-4292-81f5-cd0199f05c66")]
|
|
public readonly InputSlot<float> Delay = new();
|
|
|
|
[Input(Guid = "9bfd5ae3-9ca6-4f7b-b24b-f554ad4d0255")]
|
|
public readonly InputSlot<float> Bias = new();
|
|
|
|
[Input(Guid = "06E511D2-891A-4F81-B49E-327410A2CB95", MappedType = typeof(Times))]
|
|
public readonly InputSlot<int> TimeMode = new();
|
|
|
|
private enum Times
|
|
{
|
|
LocalFxTime,
|
|
PlayTime,
|
|
AppRunTime,
|
|
}
|
|
|
|
[Input(Guid = "FFECB0C3-4D62-40F6-8F46-B982AE0A1800")]
|
|
public readonly InputSlot<string> UseTriggerVar = new();
|
|
} |