ShrimpleRagdoll.Lerp.cs
using Sandbox.Utility;
namespace ShrimpleRagdolls;
public enum LerpMode
{
Mesh,
Objects,
Bodies
}
public partial class ShrimpleRagdoll
{
private TimeUntil? _lerpToAnimation;
private Dictionary<int, Transform> _lerpStartTransforms = new();
private RagdollMode? _lerpTargetMode;
private LerpMode _lerpMode;
private HashSet<int> _lerpTargetBones;
public bool IsLerping => _lerpToAnimation != null;
public void UpdateLerp()
{
if ( !IsLerping )
return;
switch ( _lerpMode )
{
case LerpMode.Mesh:
UpdateLerpMesh();
break;
case LerpMode.Objects:
UpdateLerpObjects();
break;
case LerpMode.Bodies:
UpdateLerpBodies();
break;
}
if ( _lerpToAnimation.Value )
FinishLerp();
}
private void UpdateLerpMesh()
{
if ( !Renderer.IsValid() || !Renderer.SceneModel.IsValid() )
return;
var fraction = Easing.SineEaseInOut( _lerpToAnimation.Value.Fraction );
foreach ( var body in Bodies )
{
if ( _lerpTargetBones != null && !_lerpTargetBones.Contains( body.Bone ) )
continue;
if ( !_lerpStartTransforms.TryGetValue( body.Bone, out var startTransform ) )
continue;
var bone = Renderer.Model.Bones.AllBones[body.Bone];
if ( !Renderer.TryGetBoneTransformAnimation( bone, out var animTransform ) )
continue;
startTransform = Renderer.WorldTransform.ToWorld( startTransform );
var currentTransform = startTransform.LerpTo( animTransform, fraction, false );
currentTransform = Renderer.WorldTransform.ToLocal( currentTransform );
Renderer.SceneModel.SetBoneOverride( body.Bone, in currentTransform );
}
}
private void UpdateLerpObjects()
{
if ( !Renderer.IsValid() || !Renderer.SceneModel.IsValid() )
return;
var fraction = Easing.SineEaseInOut( _lerpToAnimation.Value.Fraction );
foreach ( var body in Bodies )
{
if ( !body.Component.IsValid() )
continue;
if ( _lerpTargetBones != null && !_lerpTargetBones.Contains( body.Bone ) )
continue;
if ( !_lerpStartTransforms.TryGetValue( body.Bone, out var startTransform ) )
continue;
var bone = Renderer.Model.Bones.AllBones[body.Bone];
if ( !Renderer.TryGetBoneTransformAnimation( bone, out var animTransform ) )
continue;
startTransform = Renderer.WorldTransform.ToWorld( startTransform );
var currentTransform = startTransform.LerpTo( animTransform, fraction, false );
body.Component.WorldTransform = currentTransform;
}
}
private void UpdateLerpBodies()
{
if ( !Renderer.IsValid() || !Renderer.SceneModel.IsValid() )
return;
var fraction = Easing.SineEaseInOut( _lerpToAnimation.Value.Fraction );
foreach ( var body in Bodies )
{
if ( !body.Component.IsValid() || !body.Component.PhysicsBody.IsValid() )
continue;
if ( _lerpTargetBones != null && !_lerpTargetBones.Contains( body.Bone ) )
continue;
if ( !_lerpStartTransforms.TryGetValue( body.Bone, out var startTransform ) )
continue;
var bone = Renderer.Model.Bones.AllBones[body.Bone];
if ( !Renderer.TryGetBoneTransformAnimation( bone, out var animTransform ) )
continue;
startTransform = Renderer.WorldTransform.ToWorld( startTransform );
var currentTransform = startTransform.LerpTo( animTransform, fraction, false );
body.Component.PhysicsBody.SmoothMove( in currentTransform, MathF.Max( Time.Delta, Time.Delta ), Time.Delta );
}
}
private void FinishLerp()
{
Renderer?.ClearPhysicsBones();
if ( _lerpTargetMode.HasValue )
Mode = _lerpTargetMode.Value;
_lerpToAnimation = null;
_lerpStartTransforms.Clear();
_lerpTargetBones = null;
_lerpTargetMode = null;
}
public void StopLerp()
{
if ( !IsLerping )
return;
Renderer?.ClearPhysicsBones();
_lerpToAnimation = null;
_lerpStartTransforms.Clear();
_lerpTargetBones = null;
_lerpTargetMode = null;
}
[Rpc.Broadcast( NetFlags.OwnerOnly )]
private void StartLerpInternal( Dictionary<int, Transform> startTransforms, float duration, LerpMode mode, RagdollMode? targetMode = null, bool allBodies = false )
{
if ( startTransforms == null || startTransforms.Count == 0 )
return;
_lerpStartTransforms = startTransforms;
_lerpTargetBones = allBodies ? null : new HashSet<int>( startTransforms.Keys );
_lerpMode = mode;
_lerpTargetMode = targetMode;
_lerpToAnimation = MathF.Max( duration, Time.Delta );
if ( Renderer.IsValid() && Renderer.SceneModel.IsValid() )
{
foreach ( var (boneIndex, transform) in startTransforms )
Renderer.SceneModel.SetBoneOverride( boneIndex, transform );
}
}
private Dictionary<int, Transform> GetStartTransformsFromMesh()
{
var startTransforms = new Dictionary<int, Transform>();
if ( !Renderer.IsValid() || !Renderer.SceneModel.IsValid() )
return startTransforms;
foreach ( var body in Bodies )
{
var renderBonePosition = Renderer.SceneModel.GetBoneWorldTransform( body.Bone );
startTransforms[body.Bone] = Renderer.WorldTransform.ToLocal( renderBonePosition );
}
return startTransforms;
}
private Dictionary<int, Transform> GetStartTransformsFromMesh( IEnumerable<ModelPhysics.Body> bodies )
{
var startTransforms = new Dictionary<int, Transform>();
if ( !Renderer.IsValid() || !Renderer.SceneModel.IsValid() )
return startTransforms;
foreach ( var body in bodies )
{
var renderBonePosition = Renderer.SceneModel.GetBoneWorldTransform( body.Bone );
startTransforms[body.Bone] = Renderer.WorldTransform.ToLocal( renderBonePosition );
}
return startTransforms;
}
public void StartLerpMeshToAnimation( float duration, RagdollMode targetMode = RagdollMode.None ) => StartLerpInternal( GetStartTransformsFromMesh(), duration, LerpMode.Mesh, targetMode, allBodies: true );
public void StartLerpMeshToAnimation( IEnumerable<ModelPhysics.Body> bodies, float duration ) => StartLerpInternal( GetStartTransformsFromMesh( bodies ), duration, LerpMode.Mesh );
public void StartLerpObjectsToAnimation( float duration, RagdollMode targetMode = RagdollMode.None ) => StartLerpInternal( GetStartTransformsFromMesh(), duration, LerpMode.Objects, targetMode, allBodies: true );
public void StartLerpObjectsToAnimation( IEnumerable<ModelPhysics.Body> bodies, float duration ) => StartLerpInternal( GetStartTransformsFromMesh( bodies ), duration, LerpMode.Objects );
public void StartLerpBodiesToAnimation( float duration, RagdollMode targetMode = RagdollMode.None ) => StartLerpInternal( GetStartTransformsFromMesh(), duration, LerpMode.Bodies, targetMode, allBodies: true );
public void StartLerpBodiesToAnimation( IEnumerable<ModelPhysics.Body> bodies, float duration ) => StartLerpInternal( GetStartTransformsFromMesh( bodies ), duration, LerpMode.Bodies );
public void StartLerpFromDisplacedTransforms( Dictionary<int, Transform> displacedTransforms, float duration, LerpMode mode = LerpMode.Mesh ) => StartLerpInternal( displacedTransforms, duration, mode );
public void StartLerpInRadiusToAnimation( Vector3 worldPosition, float radius, float duration, LerpMode mode = LerpMode.Mesh )
{
if ( !PhysicsWereCreated || Bodies == null || Bodies.Count == 0 )
return;
var affectedBodies = Bodies
.Where( b => b.Component.IsValid() && Vector3.DistanceBetween( worldPosition, b.Component.WorldPosition ) <= radius )
.ToList();
if ( affectedBodies.Count > 0 )
StartLerpInternal( GetStartTransformsFromMesh( affectedBodies ), duration, mode );
}
}