Files
OldThink/Content.Client/_White/Trail/Line/TrailSpline.cs

184 lines
5.6 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using Content.Client._White.Trail.SplineRenderer;
using Content.Shared._White.Spline;
using Content.Shared._White.Spline.Linear;
using Content.Shared._White.Trail;
using Robust.Client.Graphics;
using Robust.Shared.Map;
using Robust.Shared.Random;
using Vector4 = Robust.Shared.Maths.Vector4;
namespace Content.Client._White.Trail.Line;
public sealed class TrailSpline : ITrailLine
{
private static readonly IRobustRandom Random = IoCManager.Resolve<IRobustRandom>();
[ViewVariables]
private readonly LinkedList<TrailSplineSegment> _segments = new();
[ViewVariables]
private Vector2 _lastCreationPos;
[ViewVariables]
private float _curLifetime;
[ViewVariables]
private Vector2? _virtualSegmentPos;
[ViewVariables]
public MapId MapId { get; set; }
[ViewVariables]
public bool Attached { get; set; }
[ViewVariables]
public ITrailSettings Settings { get; set; } = TrailSettings.Default;
[ViewVariables]
public ISpline<Vector2> SplineIterator { get; set; } = new SplineLinear2D();
[ViewVariables]
public ISpline<Vector4> GradientIterator { get; set; } = new SplineLinear4D();
[ViewVariables]
public ITrailSplineRenderer Renderer { get; set; } = new TrailSplineRendererDebug();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasSegments()
{
return _segments.Count > 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddLifetime(float time)
{
_curLifetime += time;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void ResetLifetime()
{
_curLifetime = 0f;
}
public void TryCreateSegment((Vector2 WorldPosition, Angle WorldRotation) worldPosRot, MapId mapId)
{
if (!Attached)
return;
if (mapId != MapId)
return;
if (worldPosRot.WorldPosition == Vector2.Zero)
return;
var pos = worldPosRot.WorldPosition + worldPosRot.WorldRotation.RotateVec(Settings.CreationOffset);
_lastCreationPos = pos;
if (_virtualSegmentPos.HasValue)
{
var vPos = _virtualSegmentPos.Value;
if ((vPos - pos).LengthSquared() > Settings.СreationDistanceThresholdSquared)
{
_segments.AddLast(new TrailSplineSegment()
{ Position = vPos, ExistTil = _curLifetime + Settings.Lifetime });
_virtualSegmentPos = null;
}
return;
}
var lastPos = _segments.Last?.Value.Position;
if (!lastPos.HasValue || (lastPos.Value - pos).LengthSquared() > Settings.СreationDistanceThresholdSquared)
_virtualSegmentPos = pos;
}
public void UpdateSegments(float dt)
{
var gravity = Settings.Gravity;
var maxRandomWalk = Settings.MaxRandomWalk;
var lifetime = Settings.Lifetime;
if (_segments.Last != null)
{
var i = 0;
var positions = new Vector2[_segments.Count + 1];
positions[_segments.Count] = _lastCreationPos;
var curNode = _segments.First;
while (curNode != null)
{
var offset = gravity;
var curValue = curNode.Value;
if (maxRandomWalk != Vector2.Zero)
{
positions[i] = curValue.Position;
if (curNode.Next != null)
{
positions[i + 1] = curNode.Next.Value.Position;
if (curNode.Next.Next != null)
positions[i + 2] = curNode.Next.Next.Value.Position;
}
var effectiveRandomWalk = maxRandomWalk * (curValue.ExistTil - _curLifetime) / lifetime;
var gradientNorm = -SplineIterator.SampleVelocity(positions, i).Normalized();
offset += gradientNorm * effectiveRandomWalk.Y * Random.NextFloat(-1.0f, 1.0f);
var rotated90Degrees = new Vector2(-gradientNorm.Y, gradientNorm.X);
offset += rotated90Degrees * effectiveRandomWalk.X * Random.NextFloat(-1.0f, 1.0f);
}
curValue.Position += offset;
i++;
curNode = curNode.Next;
}
}
if (_virtualSegmentPos.HasValue)
_virtualSegmentPos = _virtualSegmentPos.Value + gravity;
if (!Attached)
_lastCreationPos += gravity;
}
public void RemoveExpiredSegments()
{
while (_segments.First?.Value.ExistTil < _curLifetime)
{
_segments.RemoveFirst();
}
}
public void Render(DrawingHandleWorld handle, Texture? texture)
{
if (_segments.Last == null)
return;
var arrSize = _segments.Count + 1;
var paPositions = new Vector2[arrSize];
var paLifetimes = new float[arrSize];
paPositions[0] = _lastCreationPos;
paLifetimes[0] = 1f;
var reversedIndexedSegments = _segments.Reverse().Select((x, i) => (x, i + 1));
foreach (var (x, i) in reversedIndexedSegments)
{
paPositions[i] = x.Position;
paLifetimes[i] = (x.ExistTil - _curLifetime) / Settings.Lifetime;
}
Renderer.Render(handle, texture, SplineIterator, GradientIterator, Settings, paPositions, paLifetimes);
}
private sealed class TrailSplineSegment
{
public Vector2 Position { get; set; }
public float ExistTil { get; init; }
}
}