using System; using System.Collections.Generic; using GameCult.Aetheria.State.Verse; using GameCult.Eve.PluginFields; using UnityEngine; public sealed class AetheriaSceneRenderSplatSource : MonoBehaviour { [SerializeField] private string documentName = "Scene Mock"; [SerializeField] private AetheriaSceneRenderSplat[] splats = { AetheriaSceneRenderSplat.Animated( AetheriaRuntimeRenderSplatLayerKeys.FogSurfaceHeight, "scene.fog_surface", Vector2.zero, new Vector2(512, 512), Color.white, 4, 4, 0.015f), AetheriaSceneRenderSplat.Animated( AetheriaRuntimeRenderSplatLayerKeys.FogPatchHeight, "scene.fog_patch_height", Vector2.zero, new Vector2(512, 512), Color.white, 9, 9, 0.02f), AetheriaSceneRenderSplat.Animated( AetheriaRuntimeRenderSplatLayerKeys.FogPatch, "scene.fog_patch", Vector2.zero, new Vector2(512, 512), Color.white, 6, 6, 0.01f) }; [SerializeField] private AetheriaSceneRenderSplatFixture[] fixtures = Array.Empty(); public EveFieldsSplatsDocument BuildDocument(AetheriaRuntimeViewportBounds viewport) { viewport ??= new AetheriaRuntimeViewportBounds(); var normalizedViewport = Normalize(viewport); var layers = BuildLayers(); var layerIndices = new Dictionary(StringComparer.Ordinal); for (var index = 0; index < layers.Length; index++) layerIndices[layers[index].LayerKey] = index; var builder = new Builder(); foreach (var splat in splats ?? Array.Empty()) splat.AddTo(builder, layerIndices); foreach (var fixture in fixtures ?? Array.Empty()) fixture.AddTo(builder, layerIndices); return new EveFieldsSplatsDocument { PublishedAtUtc = DateTimeOffset.UtcNow.ToString("O"), SimulationTimeSeconds = Application.isPlaying ? Time.timeAsDouble : 0, RunId = "scene", ZoneName = string.IsNullOrWhiteSpace(documentName) ? gameObject.scene.name : documentName, Viewport = new EveFieldsViewport { MinX = normalizedViewport.MinX, MinY = normalizedViewport.MinY, MaxX = normalizedViewport.MaxX, MaxY = normalizedViewport.MaxY }, Layers = layers, Splats = builder.Build() }; } private static AetheriaRuntimeViewportBounds Normalize(AetheriaRuntimeViewportBounds viewport) { return new AetheriaRuntimeViewportBounds { MinX = Math.Min(viewport.MinX, viewport.MaxX), MinY = Math.Min(viewport.MinY, viewport.MaxY), MaxX = Math.Max(viewport.MinX, viewport.MaxX), MaxY = Math.Max(viewport.MinY, viewport.MaxY) }; } private static EveFieldsSplatLayer[] BuildLayers() { return new[] { Layer(AetheriaRuntimeRenderSplatLayerKeys.GravityHeight, "Gravity Height", AetheriaRuntimeRenderSplatChannels.Gravity), Layer(AetheriaRuntimeRenderSplatLayerKeys.GravityWave, "Gravity Wave", AetheriaRuntimeRenderSplatChannels.GravityWave), Layer(AetheriaRuntimeRenderSplatLayerKeys.Visibility, "Visibility Mask", AetheriaRuntimeRenderSplatChannels.Visibility, AetheriaRuntimeRenderSplatBlendModes.Max), Layer(AetheriaRuntimeRenderSplatLayerKeys.FogSurfaceHeight, "Fog Surface Height", AetheriaRuntimeRenderSplatChannels.Tint), Layer(AetheriaRuntimeRenderSplatLayerKeys.FogPatchHeight, "Fog Patch Height", AetheriaRuntimeRenderSplatChannels.Tint), Layer(AetheriaRuntimeRenderSplatLayerKeys.FogPatch, "Fog Patch", AetheriaRuntimeRenderSplatChannels.Tint, AetheriaRuntimeRenderSplatBlendModes.Max), Layer(AetheriaRuntimeRenderSplatLayerKeys.FogTint, "Fog Tint", AetheriaRuntimeRenderSplatChannels.Tint, graphicsFormat: "B10G11R11_UFloatPack32"), Layer(AetheriaRuntimeRenderSplatLayerKeys.Influence, "Influence", AetheriaRuntimeRenderSplatChannels.Influence) }; } private static EveFieldsSplatLayer Layer( string key, string displayName, int channel, string blendMode = AetheriaRuntimeRenderSplatBlendModes.Add, string graphicsFormat = "R16_SFloat") { return new EveFieldsSplatLayer { LayerKey = key, DisplayName = displayName, Channel = channel, BlendMode = blendMode, GraphicsFormat = graphicsFormat }; } public sealed class Builder { private readonly List _centerX = new List(); private readonly List _centerY = new List(); private readonly List _halfExtentX = new List(); private readonly List _halfExtentY = new List(); private readonly List _rotationCos = new List(); private readonly List _rotationSin = new List(); private readonly List _channel = new List(); private readonly List _falloff = new List(); private readonly List _valueR = new List(); private readonly List _valueG = new List(); private readonly List _valueB = new List(); private readonly List _valueA = new List(); private readonly List _sourceKey = new List(); private readonly List _layerIndex = new List(); private readonly List _sourceKind = new List(); private readonly List _frequencyX = new List(); private readonly List _frequencyY = new List(); private readonly List _phaseX = new List(); private readonly List _phaseY = new List(); private readonly List _animationSpeed = new List(); private readonly List _sourceFlags = new List(); public void Add(AetheriaSceneRenderSplat splat, int layerIndex) { var rotationRadians = splat.RotationDegrees * Mathf.Deg2Rad; _layerIndex.Add(layerIndex); _centerX.Add(splat.Center.x); _centerY.Add(splat.Center.y); _halfExtentX.Add(Math.Max(0, splat.HalfExtent.x)); _halfExtentY.Add(Math.Max(0, splat.HalfExtent.y)); _rotationCos.Add(Math.Cos(rotationRadians)); _rotationSin.Add(Math.Sin(rotationRadians)); _channel.Add(splat.Channel); _falloff.Add(splat.Falloff); _valueR.Add(splat.Value.r); _valueG.Add(splat.Value.g); _valueB.Add(splat.Value.b); _valueA.Add(splat.Value.a); _sourceKey.Add(splat.SourceKey ?? ""); _sourceKind.Add(splat.SourceKind); _frequencyX.Add(splat.Frequency.x); _frequencyY.Add(splat.Frequency.y); _phaseX.Add(splat.Phase.x); _phaseY.Add(splat.Phase.y); _animationSpeed.Add(splat.AnimationSpeed); _sourceFlags.Add(splat.SourceFlags); } public EveFieldsSplatSoa Build() { return new EveFieldsSplatSoa { Count = _centerX.Count, CenterX = _centerX.ToArray(), CenterY = _centerY.ToArray(), HalfExtentX = _halfExtentX.ToArray(), HalfExtentY = _halfExtentY.ToArray(), RotationCos = _rotationCos.ToArray(), RotationSin = _rotationSin.ToArray(), Channel = _channel.ToArray(), Falloff = _falloff.ToArray(), ValueR = _valueR.ToArray(), ValueG = _valueG.ToArray(), ValueB = _valueB.ToArray(), ValueA = _valueA.ToArray(), SourceKey = _sourceKey.ToArray(), LayerIndex = _layerIndex.ToArray(), SourceKind = _sourceKind.ToArray(), FrequencyX = _frequencyX.ToArray(), FrequencyY = _frequencyY.ToArray(), PhaseX = _phaseX.ToArray(), PhaseY = _phaseY.ToArray(), AnimationSpeed = _animationSpeed.ToArray(), SourceFlags = _sourceFlags.ToArray() }; } } } [Serializable] public sealed class AetheriaSceneRenderSplat { public string LayerKey = AetheriaRuntimeRenderSplatLayerKeys.FogTint; public string SourceKey = ""; public Vector2 Center; public Vector2 HalfExtent = Vector2.one * 32; public float RotationDegrees; public int Channel = AetheriaRuntimeRenderSplatChannels.Tint; public int Falloff = AetheriaRuntimeRenderSplatFalloffs.Smooth; public Color Value = Color.white; public int SourceKind = AetheriaRuntimeRenderSplatSourceKinds.Constant; public Vector2 Frequency = Vector2.one; public Vector2 Phase; public float AnimationSpeed; public float SourceFlags; public static AetheriaSceneRenderSplat Animated( string layerKey, string sourceKey, Vector2 center, Vector2 halfExtent, Color value, float frequencyX, float frequencyY, float animationSpeed) { return new AetheriaSceneRenderSplat { LayerKey = layerKey, SourceKey = sourceKey, Center = center, HalfExtent = halfExtent, Channel = AetheriaRuntimeRenderSplatChannels.Tint, Falloff = AetheriaRuntimeRenderSplatFalloffs.Solid, Value = value, SourceKind = AetheriaRuntimeRenderSplatSourceKinds.AnimatedSimplexNoise, Frequency = new Vector2(frequencyX, frequencyY), AnimationSpeed = animationSpeed, SourceFlags = 1 }; } public void AddTo(AetheriaSceneRenderSplatSource.Builder builder, IReadOnlyDictionary layerIndices) { if (builder == null || !layerIndices.TryGetValue(LayerKey ?? "", out var layerIndex)) return; builder.Add(this, layerIndex); } } [Serializable] public sealed class AetheriaSceneRenderSplatFixture { public Transform Transform; public AetheriaSceneRenderSplat Splat = new AetheriaSceneRenderSplat(); public void AddTo(AetheriaSceneRenderSplatSource.Builder builder, IReadOnlyDictionary layerIndices) { if (Transform == null || Splat == null) return; var previousCenter = Splat.Center; Splat.Center = new Vector2(Transform.position.x, Transform.position.z); try { Splat.AddTo(builder, layerIndices); } finally { Splat.Center = previousCenter; } } }