2026-07-05 19:56:18 +01:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
2020-12-22 03:13:43 +00:00
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
|
|
using System;
|
2026-06-28 01:18:21 +01:00
|
|
|
using GameCult.Aetheria.State.Verse;
|
2026-07-15 20:17:05 +01:00
|
|
|
using GameCult.Eve.PluginFields;
|
2020-11-27 22:47:33 +00:00
|
|
|
using UnityEngine;
|
2021-02-26 19:01:47 +00:00
|
|
|
using Unity.Mathematics;
|
|
|
|
|
using UnityEngine.Experimental.Rendering;
|
2022-07-16 23:14:25 +01:00
|
|
|
using UnityEngine.Rendering;
|
2021-06-18 12:32:03 +01:00
|
|
|
using UnityEngine.Serialization;
|
2021-02-26 19:01:47 +00:00
|
|
|
using static Unity.Mathematics.math;
|
|
|
|
|
using static Unity.Mathematics.noise;
|
2026-06-30 19:14:49 +01:00
|
|
|
using System.Collections.Generic;
|
2020-11-27 22:47:33 +00:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Drives the volume render.
|
|
|
|
|
/// </summary>
|
2022-07-10 02:53:09 +01:00
|
|
|
[ExecuteInEditMode]
|
|
|
|
|
[RequireComponent( typeof( Camera ) )]
|
2020-11-27 22:47:33 +00:00
|
|
|
public class VolumeSampling : MonoBehaviour
|
|
|
|
|
{
|
2026-06-30 19:14:49 +01:00
|
|
|
private static readonly List<VolumeSampling> ActiveSamplers = new List<VolumeSampling>();
|
2025-01-30 01:58:05 +00:00
|
|
|
public Transform GridMesh;
|
2022-07-18 08:44:54 +01:00
|
|
|
public Camera GridCamera;
|
2021-06-18 12:32:03 +01:00
|
|
|
public GameSettings Settings;
|
|
|
|
|
|
|
|
|
|
public RenderTexture NebulaSurfaceHeight;
|
|
|
|
|
public RenderTexture NebulaPatchHeight;
|
|
|
|
|
public RenderTexture NebulaPatch;
|
|
|
|
|
public RenderTexture NebulaTint;
|
2026-06-28 01:18:21 +01:00
|
|
|
public AetheriaUnityRenderSplatViewportSource SplatViewportSource;
|
|
|
|
|
public AetheriaRenderSplatLayerRenderer SplatLayerRenderer;
|
2026-06-30 19:14:49 +01:00
|
|
|
|
|
|
|
|
[SerializeField]
|
|
|
|
|
private bool useRuntimeRenderSplats;
|
|
|
|
|
|
|
|
|
|
[SerializeField]
|
|
|
|
|
private bool renderSplatsEveryFrame;
|
|
|
|
|
|
|
|
|
|
[SerializeField]
|
|
|
|
|
private float splatRenderIntervalSeconds = 0.25f;
|
2025-07-10 21:05:54 +01:00
|
|
|
|
|
|
|
|
private MeshRenderer _gridMeshRenderer;
|
2022-07-18 08:44:54 +01:00
|
|
|
private float _flowScroll;
|
|
|
|
|
private Transform _gridTransform;
|
2026-06-30 19:14:49 +01:00
|
|
|
private Camera _ownerCamera;
|
|
|
|
|
private Vector2 _lastSplatCenter;
|
|
|
|
|
private Vector2 _lastSplatSize;
|
|
|
|
|
private Vector2 _publishedSplatCenter;
|
|
|
|
|
private Vector2 _publishedSplatSize;
|
|
|
|
|
private float _nextSplatRenderTime;
|
|
|
|
|
private bool _hasRenderedSplats;
|
2022-07-21 21:40:44 +01:00
|
|
|
private GlobalKeyword _keywordFlowGlobal;
|
|
|
|
|
private GlobalKeyword _keywordFlowSlope;
|
|
|
|
|
private GlobalKeyword _keywordNoiseSlope;
|
|
|
|
|
|
2026-06-30 19:14:49 +01:00
|
|
|
public static void RenderForCamera(Camera camera)
|
|
|
|
|
{
|
|
|
|
|
if (camera == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < ActiveSamplers.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
var sampler = ActiveSamplers[i];
|
|
|
|
|
if (sampler != null && sampler.TargetsCamera(camera))
|
|
|
|
|
sampler.RenderSamplingPass();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnEnable()
|
|
|
|
|
{
|
|
|
|
|
if (!ActiveSamplers.Contains(this))
|
|
|
|
|
ActiveSamplers.Add(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnDisable()
|
|
|
|
|
{
|
|
|
|
|
ActiveSamplers.Remove(this);
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-21 21:40:44 +01:00
|
|
|
private void Start()
|
|
|
|
|
{
|
2026-06-30 19:14:49 +01:00
|
|
|
_ownerCamera = GetComponent<Camera>();
|
2022-07-21 21:40:44 +01:00
|
|
|
_keywordFlowGlobal = GlobalKeyword.Create("FLOW_GLOBAL");
|
|
|
|
|
_keywordFlowSlope = GlobalKeyword.Create("FLOW_SLOPE");
|
|
|
|
|
_keywordNoiseSlope = GlobalKeyword.Create("NOISE_SLOPE");
|
2025-07-10 21:05:54 +01:00
|
|
|
|
|
|
|
|
if (GridMesh != null)
|
|
|
|
|
_gridMeshRenderer = GridMesh.GetComponent<MeshRenderer>();
|
2026-06-28 01:18:21 +01:00
|
|
|
|
|
|
|
|
ResolveSplatRenderers();
|
2022-07-21 21:40:44 +01:00
|
|
|
}
|
2021-04-09 14:01:34 +01:00
|
|
|
|
2022-07-10 02:53:09 +01:00
|
|
|
void Update()
|
|
|
|
|
{
|
2022-07-18 08:44:54 +01:00
|
|
|
if(_gridTransform==null)
|
|
|
|
|
_gridTransform = GridCamera?.transform;
|
2022-07-16 23:14:25 +01:00
|
|
|
|
2026-06-30 19:14:49 +01:00
|
|
|
_flowScroll += Settings.DefaultEnvironment.Flow.GlobalScrollSpeed * Time.deltaTime;
|
|
|
|
|
UpdateGridMeshBounds();
|
|
|
|
|
}
|
2026-06-28 01:18:21 +01:00
|
|
|
|
2026-06-30 19:14:49 +01:00
|
|
|
private bool TargetsCamera(Camera camera)
|
|
|
|
|
{
|
|
|
|
|
_ownerCamera ??= GetComponent<Camera>();
|
|
|
|
|
return camera == _ownerCamera;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void RenderSamplingPass()
|
|
|
|
|
{
|
|
|
|
|
if (_gridTransform == null)
|
|
|
|
|
_gridTransform = GridCamera?.transform;
|
|
|
|
|
|
|
|
|
|
RenderSplatTexturesIfNeeded();
|
|
|
|
|
PublishSamplingGlobals();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateGridMeshBounds()
|
|
|
|
|
{
|
2026-06-25 18:09:33 +01:00
|
|
|
var environment = Settings.DefaultEnvironment;
|
2025-01-30 01:58:05 +00:00
|
|
|
|
|
|
|
|
if (GridMesh != null)
|
|
|
|
|
{
|
|
|
|
|
GridMesh.gameObject.SetActive(environment.Grid.Enabled);
|
2025-07-10 21:05:54 +01:00
|
|
|
_gridMeshRenderer.bounds = new Bounds(
|
|
|
|
|
new Vector3(_gridTransform.position.x, environment.Grid.Offset, _gridTransform.position.z),
|
|
|
|
|
new Vector3(GridCamera.orthographicSize * 2, 1000, GridCamera.orthographicSize * 2));
|
|
|
|
|
// GridMesh.position = new Vector3(_gridTransform.position.x, environment.Grid.Offset, _gridTransform.position.z);
|
|
|
|
|
// GridMesh.localScale = new Vector3(GridCamera.orthographicSize, 1, GridCamera.orthographicSize);
|
2025-01-30 01:58:05 +00:00
|
|
|
}
|
2026-06-30 19:14:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void PublishSamplingGlobals()
|
|
|
|
|
{
|
|
|
|
|
var environment = Settings.DefaultEnvironment;
|
|
|
|
|
|
|
|
|
|
ResolveSplatViewport(out var center, out var size);
|
|
|
|
|
_publishedSplatCenter = center;
|
|
|
|
|
_publishedSplatSize = size;
|
|
|
|
|
|
|
|
|
|
// Shader sampling must use the same world-space viewport that produced the splat textures.
|
|
|
|
|
Shader.SetGlobalVector("_GridTransform", new Vector4(center.x, center.y, size.x));
|
|
|
|
|
|
2022-07-16 23:14:25 +01:00
|
|
|
// Volumetric sampling parameters are used by several shaders
|
2021-06-18 12:32:03 +01:00
|
|
|
Shader.SetGlobalTexture("_NebulaSurfaceHeight", NebulaSurfaceHeight);
|
|
|
|
|
Shader.SetGlobalTexture("_NebulaPatchHeight", NebulaPatchHeight);
|
|
|
|
|
Shader.SetGlobalTexture("_NebulaPatch", NebulaPatch);
|
|
|
|
|
Shader.SetGlobalTexture("_NebulaTint", NebulaTint);
|
|
|
|
|
|
2022-07-21 21:40:44 +01:00
|
|
|
Shader.SetGlobalFloat("_NebulaFillDensity", environment.Nebula.FillDensity);
|
|
|
|
|
Shader.SetGlobalFloat("_NebulaFillDistance", environment.Nebula.FillDistance);
|
|
|
|
|
Shader.SetGlobalFloat("_NebulaFillExponent", environment.Nebula.FillExponent);
|
|
|
|
|
Shader.SetGlobalFloat("_NebulaFillOffset", environment.Nebula.FillOffset);
|
|
|
|
|
Shader.SetGlobalFloat("_NebulaPatchDensity", environment.Nebula.PatchDensity);
|
|
|
|
|
Shader.SetGlobalFloat("_NebulaFloorOffset", environment.Nebula.FloorOffset);
|
|
|
|
|
Shader.SetGlobalFloat("_NebulaFloorBlend", environment.Nebula.FloorBlend);
|
|
|
|
|
Shader.SetGlobalFloat("_NebulaPatchBlend", environment.Nebula.PatchBlend);
|
|
|
|
|
Shader.SetGlobalFloat("_NebulaLuminance", environment.Nebula.Luminance);
|
|
|
|
|
Shader.SetGlobalFloat("_ExtinctionCoefficient", environment.Nebula.Extinction);
|
|
|
|
|
Shader.SetGlobalFloat("_TintLodExponent", environment.Nebula.TintLodExponent);
|
|
|
|
|
Shader.SetGlobalFloat("_SafetyDistance", environment.Nebula.SafetyDistance);
|
|
|
|
|
|
|
|
|
|
Shader.SetGlobalFloat("_DynamicSkyBoost", environment.Lighting.DynamicSkyBoost);
|
|
|
|
|
Shader.SetGlobalFloat("_DynamicLodHigh", environment.Lighting.DynamicLodHigh);
|
|
|
|
|
Shader.SetGlobalFloat("_DynamicLodLow", environment.Lighting.DynamicLodLow);
|
|
|
|
|
Shader.SetGlobalFloat("_DynamicIntensity", environment.Lighting.DynamicIntensity);
|
2022-07-10 02:53:09 +01:00
|
|
|
|
2022-07-21 21:40:44 +01:00
|
|
|
Shader.SetGlobalFloat("_NebulaNoiseScale", environment.Noise.Scale);
|
|
|
|
|
Shader.SetGlobalFloat("_NebulaNoiseExponent", environment.Noise.Exponent);
|
|
|
|
|
Shader.SetGlobalFloat("_NebulaNoiseAmplitude", environment.Noise.Amplitude);
|
|
|
|
|
Shader.SetGlobalFloat("_NebulaNoiseSpeed", environment.Noise.Speed);
|
|
|
|
|
Shader.SetGlobalFloat("_NebulaNoiseSlopeExponent", environment.Noise.SlopeExponent);
|
2022-07-10 02:53:09 +01:00
|
|
|
|
2022-07-21 21:40:44 +01:00
|
|
|
Shader.SetGlobalFloat("_FlowScale", environment.Flow.GlobalScale);
|
|
|
|
|
Shader.SetGlobalFloat("_FlowAmplitude", environment.Flow.GlobalAmplitude);
|
|
|
|
|
Shader.SetGlobalFloat("_FlowSlopeAmplitude", environment.Flow.SlopeAmplitude);
|
|
|
|
|
Shader.SetGlobalFloat("_FlowSwirlAmplitude", environment.Flow.SwirlAmplitude);
|
2021-06-18 12:32:03 +01:00
|
|
|
Shader.SetGlobalFloat("_FlowScroll", _flowScroll);
|
2022-07-21 21:40:44 +01:00
|
|
|
Shader.SetGlobalFloat("_FlowPeriod", environment.Flow.Period);
|
|
|
|
|
|
|
|
|
|
Shader.SetKeyword(_keywordFlowGlobal, environment.Flow.GlobalAmplitude != 0);
|
|
|
|
|
Shader.SetKeyword(_keywordFlowSlope, environment.Flow.SlopeAmplitude != 0 || environment.Flow.SwirlAmplitude != 0);
|
|
|
|
|
Shader.SetKeyword(_keywordNoiseSlope, environment.Noise.SlopeExponent != 0);
|
2020-11-27 22:47:33 +00:00
|
|
|
}
|
2026-06-28 01:18:21 +01:00
|
|
|
|
2026-06-30 19:14:49 +01:00
|
|
|
private void RenderSplatTexturesIfNeeded()
|
2026-06-28 01:18:21 +01:00
|
|
|
{
|
|
|
|
|
ResolveSplatRenderers();
|
2026-06-30 19:14:49 +01:00
|
|
|
if (SplatLayerRenderer == null)
|
2026-06-28 01:18:21 +01:00
|
|
|
return;
|
2026-07-15 23:57:48 +01:00
|
|
|
if (!useRuntimeRenderSplats)
|
|
|
|
|
return;
|
2026-06-28 01:18:21 +01:00
|
|
|
|
2026-06-30 19:14:49 +01:00
|
|
|
ResolveSplatViewport(out var center, out var size);
|
2026-07-15 23:57:48 +01:00
|
|
|
var document = ResolveRuntimeSplatDocument(center, size);
|
2026-06-30 19:14:49 +01:00
|
|
|
if (document == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
var viewportChanged = !_hasRenderedSplats ||
|
|
|
|
|
!Approximately(center, _lastSplatCenter) ||
|
|
|
|
|
!Approximately(size, _lastSplatSize);
|
|
|
|
|
var intervalElapsed = Time.unscaledTime >= _nextSplatRenderTime;
|
|
|
|
|
if (!renderSplatsEveryFrame && !viewportChanged && !intervalElapsed)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
SplatLayerRenderer.Render(document, Mathf.RoundToInt(size.x), Mathf.RoundToInt(size.y));
|
|
|
|
|
_lastSplatCenter = center;
|
|
|
|
|
_lastSplatSize = size;
|
|
|
|
|
_publishedSplatCenter = center;
|
|
|
|
|
_publishedSplatSize = size;
|
|
|
|
|
_nextSplatRenderTime = Time.unscaledTime + Mathf.Max(0.02f, splatRenderIntervalSeconds);
|
|
|
|
|
_hasRenderedSplats = true;
|
2026-06-28 01:18:21 +01:00
|
|
|
|
|
|
|
|
if (SplatLayerRenderer == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
NebulaSurfaceHeight = ResolveLayerTexture(AetheriaRuntimeRenderSplatLayerKeys.FogSurfaceHeight, NebulaSurfaceHeight);
|
|
|
|
|
NebulaPatchHeight = ResolveLayerTexture(AetheriaRuntimeRenderSplatLayerKeys.FogPatchHeight, NebulaPatchHeight);
|
|
|
|
|
NebulaPatch = ResolveLayerTexture(AetheriaRuntimeRenderSplatLayerKeys.FogPatch, NebulaPatch);
|
|
|
|
|
NebulaTint = ResolveLayerTexture(AetheriaRuntimeRenderSplatLayerKeys.FogTint, NebulaTint);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-15 20:17:05 +01:00
|
|
|
private EveFieldsSplatsDocument ResolveRuntimeSplatDocument(Vector2 center, Vector2 size)
|
2026-06-30 19:14:49 +01:00
|
|
|
{
|
|
|
|
|
if (SplatViewportSource == null)
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
SplatViewportSource.SetViewport(center, size);
|
|
|
|
|
return SplatViewportSource.CurrentDocument;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ResolveSplatViewport(out Vector2 center, out Vector2 size)
|
|
|
|
|
{
|
|
|
|
|
size = GridCamera != null
|
|
|
|
|
? new Vector2(GridCamera.orthographicSize * 2, GridCamera.orthographicSize * 2)
|
|
|
|
|
: new Vector2(1024, 1024);
|
|
|
|
|
center = _gridTransform != null
|
|
|
|
|
? new Vector2(_gridTransform.position.x, _gridTransform.position.z)
|
|
|
|
|
: Vector2.zero;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool Approximately(Vector2 lhs, Vector2 rhs)
|
|
|
|
|
{
|
|
|
|
|
return Mathf.Abs(lhs.x - rhs.x) < 0.001f &&
|
|
|
|
|
Mathf.Abs(lhs.y - rhs.y) < 0.001f;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-28 01:18:21 +01:00
|
|
|
private RenderTexture ResolveLayerTexture(string layerKey, RenderTexture fallback)
|
|
|
|
|
{
|
|
|
|
|
return SplatLayerRenderer.TryGetTexture(layerKey, out var texture) && texture != null
|
|
|
|
|
? texture
|
|
|
|
|
: fallback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ResolveSplatRenderers()
|
|
|
|
|
{
|
|
|
|
|
if (SplatLayerRenderer == null)
|
|
|
|
|
SplatLayerRenderer = GetComponent<AetheriaRenderSplatLayerRenderer>();
|
|
|
|
|
if (SplatLayerRenderer == null)
|
|
|
|
|
SplatLayerRenderer = gameObject.AddComponent<AetheriaRenderSplatLayerRenderer>();
|
|
|
|
|
|
2026-06-30 19:14:49 +01:00
|
|
|
if (!useRuntimeRenderSplats)
|
|
|
|
|
return;
|
|
|
|
|
|
2026-06-28 01:18:21 +01:00
|
|
|
if (SplatViewportSource == null)
|
|
|
|
|
SplatViewportSource = GetComponent<AetheriaUnityRenderSplatViewportSource>();
|
|
|
|
|
if (SplatViewportSource == null)
|
|
|
|
|
SplatViewportSource = gameObject.AddComponent<AetheriaUnityRenderSplatViewportSource>();
|
2026-06-30 19:14:49 +01:00
|
|
|
SplatViewportSource.RenderInLateUpdate = false;
|
2026-06-28 01:18:21 +01:00
|
|
|
}
|
2020-11-27 22:47:33 +00:00
|
|
|
}
|