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

241 lines
7.8 KiB
C#

using System;
using System.Collections.Generic;
using GameCult.Aetheria.State.Verse;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.UI;
public sealed class AetheriaRenderSplatLayerRenderer : MonoBehaviour
{
[SerializeField]
private AetheriaRenderSplatRasterizer rasterizer;
[SerializeField]
private AetheriaRenderSplatLayerBinding[] bindings =
{
new AetheriaRenderSplatLayerBinding
{
LayerKey = AetheriaRuntimeRenderSplatLayerKeys.GravityHeight,
MaterialTextureProperty = "_DetailTex",
GlobalTextureName = "_AetheriaGravityHeight"
},
new AetheriaRenderSplatLayerBinding
{
LayerKey = AetheriaRuntimeRenderSplatLayerKeys.FogTint,
MaterialTextureProperty = "_DetailTex",
GlobalTextureName = "_NebulaTint"
},
new AetheriaRenderSplatLayerBinding
{
LayerKey = AetheriaRuntimeRenderSplatLayerKeys.FogSurfaceHeight,
GlobalTextureName = "_NebulaSurfaceHeight",
WidthScale = 0.5f,
HeightScale = 0.5f
},
new AetheriaRenderSplatLayerBinding
{
LayerKey = AetheriaRuntimeRenderSplatLayerKeys.FogPatchHeight,
GlobalTextureName = "_NebulaPatchHeight",
WidthScale = 0.5f,
HeightScale = 0.5f
},
new AetheriaRenderSplatLayerBinding
{
LayerKey = AetheriaRuntimeRenderSplatLayerKeys.FogPatch,
GlobalTextureName = "_NebulaPatch",
WidthScale = 0.5f,
HeightScale = 0.5f
},
new AetheriaRenderSplatLayerBinding
{
LayerKey = AetheriaRuntimeRenderSplatLayerKeys.Influence,
MaterialTextureProperty = "_DetailTex",
GlobalTextureName = "_AetheriaInfluence"
}
};
private readonly Dictionary<string, RenderTexture> _texturesByLayerKey =
new Dictionary<string, RenderTexture>(StringComparer.Ordinal);
private void Reset()
{
rasterizer = GetComponent<AetheriaRenderSplatRasterizer>();
}
public bool TryGetTexture(string layerKey, out RenderTexture texture)
{
return _texturesByLayerKey.TryGetValue(layerKey ?? "", out texture);
}
public void Render(
AetheriaRuntimeRenderSplatsViewportDocument document,
int width,
int height)
{
if (document == null)
return;
ResolveRasterizer();
if (rasterizer == null)
return;
var layers = document.Layers ?? Array.Empty<AetheriaRuntimeRenderSplatLayerDefinition>();
for (var layerIndex = 0; layerIndex < layers.Count; layerIndex++)
{
var layer = layers[layerIndex];
if (layer == null || string.IsNullOrWhiteSpace(layer.LayerKey))
continue;
foreach (var binding in bindings ?? Array.Empty<AetheriaRenderSplatLayerBinding>())
{
if (binding == null ||
!binding.Enabled ||
!string.Equals(binding.LayerKey, layer.LayerKey, StringComparison.Ordinal))
{
continue;
}
var texture = EnsureTexture(binding, layer, width, height);
rasterizer.RenderLayerToTarget(
document,
texture,
layerIndex,
ResolveMaterialPass(layer.BlendMode),
ResolveClearColor(layer));
ApplyBinding(binding, texture);
_texturesByLayerKey[layer.LayerKey] = texture;
}
}
}
private void ResolveRasterizer()
{
if (rasterizer != null)
return;
rasterizer = GetComponent<AetheriaRenderSplatRasterizer>();
if (rasterizer == null)
rasterizer = gameObject.AddComponent<AetheriaRenderSplatRasterizer>();
}
private RenderTexture EnsureTexture(
AetheriaRenderSplatLayerBinding binding,
AetheriaRuntimeRenderSplatLayerDefinition layer,
int width,
int height)
{
var targetWidth = math.max(1, Mathf.RoundToInt(width * Mathf.Max(0.01f, binding.WidthScale)));
var targetHeight = math.max(1, Mathf.RoundToInt(height * Mathf.Max(0.01f, binding.HeightScale)));
var format = ResolveGraphicsFormat(binding.GraphicsFormatOverride, layer.GraphicsFormat);
var existing = binding.TargetTexture;
if (existing != null &&
existing.width == targetWidth &&
existing.height == targetHeight &&
existing.graphicsFormat == format)
{
return existing;
}
if (existing != null)
{
existing.Release();
Destroy(existing);
}
var descriptor = new RenderTextureDescriptor(targetWidth, targetHeight)
{
depthBufferBits = 0,
graphicsFormat = format,
msaaSamples = 1,
sRGB = false,
useMipMap = binding.UseMipMaps,
autoGenerateMips = binding.UseMipMaps
};
binding.TargetTexture = new RenderTexture(descriptor)
{
name = $"Aetheria {layer.LayerKey}",
filterMode = binding.FilterMode,
wrapMode = TextureWrapMode.Clamp
};
binding.TargetTexture.Create();
return binding.TargetTexture;
}
private static GraphicsFormat ResolveGraphicsFormat(string bindingOverride, string layerFormat)
{
var value = string.IsNullOrWhiteSpace(bindingOverride) ? layerFormat : bindingOverride;
return Enum.TryParse(value, out GraphicsFormat format)
? format
: GraphicsFormat.R16_SFloat;
}
private static int ResolveMaterialPass(string blendMode)
{
switch (blendMode)
{
case AetheriaRuntimeRenderSplatBlendModes.Max:
return 1;
case AetheriaRuntimeRenderSplatBlendModes.Alpha:
return 2;
default:
return 0;
}
}
private static Color ResolveClearColor(AetheriaRuntimeRenderSplatLayerDefinition layer)
{
return new Color(
(float)layer.ClearR,
(float)layer.ClearG,
(float)layer.ClearB,
(float)layer.ClearA);
}
private static void ApplyBinding(AetheriaRenderSplatLayerBinding binding, RenderTexture texture)
{
if (binding.Display != null)
{
var material = binding.Display.material;
if (material != null && !string.IsNullOrWhiteSpace(binding.MaterialTextureProperty))
material.SetTexture(binding.MaterialTextureProperty, texture);
else
binding.Display.material.mainTexture = texture;
}
if (!string.IsNullOrWhiteSpace(binding.GlobalTextureName))
Shader.SetGlobalTexture(binding.GlobalTextureName, texture);
}
private void OnDisable()
{
foreach (var binding in bindings ?? Array.Empty<AetheriaRenderSplatLayerBinding>())
{
if (binding?.TargetTexture == null)
continue;
binding.TargetTexture.Release();
Destroy(binding.TargetTexture);
binding.TargetTexture = null;
}
_texturesByLayerKey.Clear();
}
}
[Serializable]
public sealed class AetheriaRenderSplatLayerBinding
{
public bool Enabled = true;
public string LayerKey = "";
public Image Display;
public string MaterialTextureProperty = "_DetailTex";
public string GlobalTextureName = "";
public string GraphicsFormatOverride = "";
public float WidthScale = 1.0f;
public float HeightScale = 1.0f;
public bool UseMipMaps;
public FilterMode FilterMode = FilterMode.Bilinear;
[NonSerialized]
public RenderTexture TargetTexture;
}