Aetheria/Assets/Scripts/Gameplay/AetheriaRenderSplatBuffer.cs
2026-06-28 01:18:21 +01:00

137 lines
4.7 KiB
C#

using GameCult.Aetheria.State.Verse;
using Unity.Mathematics;
using UnityEngine;
public sealed class AetheriaRenderSplatBuffer : System.IDisposable
{
public const string SplatBufferPropertyName = "_AetheriaSplats";
public const string SplatCountPropertyName = "_AetheriaSplatCount";
public const string ViewportToClipPropertyName = "_AetheriaViewportToClip";
public const string ChannelFilterPropertyName = "_AetheriaChannelFilter";
private const int SplatStrideBytes = 80;
private AetheriaRenderSplat[] _splats = System.Array.Empty<AetheriaRenderSplat>();
private GraphicsBuffer _buffer;
private int _capacity;
public int Count { get; private set; }
public GraphicsBuffer Buffer => _buffer;
public bool HasGpuBuffer => _buffer != null && _buffer.IsValid() && Count > 0;
public bool Upload(AetheriaRuntimeRenderSplatSoa splats, int channelFilter = -1, int layerFilter = -1)
{
Count = Pack(splats, channelFilter, layerFilter);
EnsureCapacity(Count);
if (Count <= 0)
return false;
_buffer.SetData(_splats, 0, 0, Count);
return true;
}
private int Pack(AetheriaRuntimeRenderSplatSoa splats, int channelFilter, int layerFilter)
{
if (splats == null || splats.Count <= 0)
return 0;
EnsureCpuCapacity(splats.Count);
var count = 0;
for (var i = 0; i < splats.Count; i++)
{
var channel = Read(splats.Channel, i, 0);
if (channelFilter >= 0 && channel != channelFilter)
continue;
var layerIndex = Read(splats.LayerIndex, i, -1);
if (layerFilter >= 0 && layerIndex != layerFilter)
continue;
var halfExtentX = (float)Read(splats.HalfExtentX, i, 0);
var halfExtentY = (float)Read(splats.HalfExtentY, i, 0);
if (halfExtentX <= 0 || halfExtentY <= 0)
continue;
_splats[count++] = new AetheriaRenderSplat
{
CenterHalfExtent = new float4(
(float)Read(splats.CenterX, i, 0),
(float)Read(splats.CenterY, i, 0),
halfExtentX,
halfExtentY),
RotationChannelFalloff = new float4(
(float)Read(splats.RotationCos, i, 1),
(float)Read(splats.RotationSin, i, 0),
channel,
Read(splats.Falloff, i, AetheriaRuntimeRenderSplatFalloffs.Smooth)),
LayerSource = new float4(
layerIndex,
Read(splats.SourceKind, i, AetheriaRuntimeRenderSplatSourceKinds.Constant),
(float)Read(splats.AnimationSpeed, i, 0),
(float)Read(splats.SourceFlags, i, 0)),
SourceFrequencyPhase = new float4(
(float)Read(splats.FrequencyX, i, 1),
(float)Read(splats.FrequencyY, i, 1),
(float)Read(splats.PhaseX, i, 0),
(float)Read(splats.PhaseY, i, 0)),
Value = new float4(
(float)Read(splats.ValueR, i, 0),
(float)Read(splats.ValueG, i, 0),
(float)Read(splats.ValueB, i, 0),
(float)Read(splats.ValueA, i, 1))
};
}
return count;
}
private void EnsureCpuCapacity(int count)
{
if (_splats.Length >= count)
return;
_splats = new AetheriaRenderSplat[math.ceilpow2(math.max(1, count))];
}
private void EnsureCapacity(int count)
{
if (_buffer != null && _buffer.IsValid() && _capacity >= count)
return;
_buffer?.Release();
_buffer = null;
_capacity = 0;
if (count <= 0)
return;
_capacity = math.ceilpow2(math.max(1, count));
_buffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, _capacity, SplatStrideBytes);
}
public void Dispose()
{
_buffer?.Release();
_buffer = null;
_capacity = 0;
Count = 0;
}
private static double Read(System.Collections.Generic.IReadOnlyList<double> values, int index, double fallback)
{
return values != null && index >= 0 && index < values.Count ? values[index] : fallback;
}
private static int Read(System.Collections.Generic.IReadOnlyList<int> values, int index, int fallback)
{
return values != null && index >= 0 && index < values.Count ? values[index] : fallback;
}
private struct AetheriaRenderSplat
{
public float4 CenterHalfExtent;
public float4 RotationChannelFalloff;
public float4 LayerSource;
public float4 SourceFrequencyPhase;
public float4 Value;
}
}