248 lines
5.8 KiB
C#
248 lines
5.8 KiB
C#
using System.Collections.Generic;
|
|
using Assets.Code.Scripts;
|
|
using Obvious.Soap;
|
|
using Unity.Burst;
|
|
using Unity.Collections;
|
|
using Unity.Jobs;
|
|
using Unity.Mathematics;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering.HighDefinition;
|
|
using UnityEngine.VFX;
|
|
|
|
public sealed class WaterFXManager : MonoBehaviour, IBatchUpdate
|
|
{
|
|
[BurstCompile(CompileSynchronously = true)]
|
|
private struct CalculateFXVisibilityJob : IJobFor
|
|
{
|
|
[ReadOnly]
|
|
public NativeArray<float3> Positions;
|
|
|
|
[ReadOnly]
|
|
public NativeArray<int> Hashes;
|
|
|
|
[ReadOnly]
|
|
public float3 CameraPosition;
|
|
|
|
[ReadOnly]
|
|
public float CullingDistance;
|
|
|
|
[WriteOnly]
|
|
public NativeHashMap<int, bool> ChangeMap;
|
|
|
|
public NativeHashMap<int, bool> VisibilityMap;
|
|
|
|
public void Execute(int i)
|
|
{
|
|
float num = math.distance(CameraPosition, Positions[i]);
|
|
int key = Hashes[i];
|
|
if (VisibilityMap[key])
|
|
{
|
|
if (num > CullingDistance)
|
|
{
|
|
VisibilityMap[key] = false;
|
|
ChangeMap[key] = true;
|
|
}
|
|
}
|
|
else if (num <= CullingDistance)
|
|
{
|
|
VisibilityMap[key] = true;
|
|
ChangeMap[key] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
private IntVariable _waterQuality;
|
|
|
|
[SerializeField]
|
|
private UpdateManager.UpdateMode _updateMode = UpdateManager.UpdateMode.Always;
|
|
|
|
[SerializeField]
|
|
private Transform _playerCameraTransform;
|
|
|
|
public float MaxSpawnDistanceFromCamera = 20f;
|
|
|
|
[SerializeField]
|
|
private VisualEffect _splashPrefab;
|
|
|
|
private const float VFX_DISABLE_DELAY = 0.2f;
|
|
|
|
private static WaterFXManager _instance;
|
|
|
|
private Camera _camera;
|
|
|
|
private Pool<VisualEffect> _splashPool;
|
|
|
|
private List<(VisualEffect splash, float timeActive)> _activeSplashes;
|
|
|
|
private Dictionary<int, Pool<WaterDecal>> _decalPools;
|
|
|
|
private Dictionary<int, WaterFXInstance> _fxInstances;
|
|
|
|
private NativeHashMap<int, bool> _visibilityMap;
|
|
|
|
private NativeHashMap<int, bool> _changeMap;
|
|
|
|
public static WaterFXManager GetInstance()
|
|
{
|
|
return _instance;
|
|
}
|
|
|
|
public void SetCamera(Camera camera)
|
|
{
|
|
_camera = camera;
|
|
}
|
|
|
|
public void AddFXInstance(int hash, WaterFXInstance instance)
|
|
{
|
|
if (_changeMap.IsCreated && _visibilityMap.IsCreated)
|
|
{
|
|
_fxInstances.Add(hash, instance);
|
|
_visibilityMap.Add(hash, item: false);
|
|
_changeMap.Add(hash, item: false);
|
|
}
|
|
}
|
|
|
|
public void RemoveFXInstance(int id)
|
|
{
|
|
if (_changeMap.IsCreated && _visibilityMap.IsCreated)
|
|
{
|
|
_fxInstances.Remove(id);
|
|
_visibilityMap.Remove(id);
|
|
_changeMap.Remove(id);
|
|
}
|
|
}
|
|
|
|
private void Awake()
|
|
{
|
|
_instance = this;
|
|
_camera = Camera.main;
|
|
InitializePools();
|
|
InitializeCollections();
|
|
InitializeNativeCollections();
|
|
}
|
|
|
|
private void InitializePools()
|
|
{
|
|
_splashPool = Pool<VisualEffect>.CreateInstance(_splashPrefab, 2, null, "Splash pool");
|
|
}
|
|
|
|
private void InitializeCollections()
|
|
{
|
|
_activeSplashes = new List<(VisualEffect, float)>();
|
|
_decalPools = new Dictionary<int, Pool<WaterDecal>>();
|
|
_fxInstances = new Dictionary<int, WaterFXInstance>();
|
|
}
|
|
|
|
private void InitializeNativeCollections()
|
|
{
|
|
_visibilityMap = new NativeHashMap<int, bool>(_fxInstances.Count, Allocator.Persistent);
|
|
_changeMap = new NativeHashMap<int, bool>(_fxInstances.Count, Allocator.Persistent);
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
if (_playerCameraTransform == null)
|
|
{
|
|
_playerCameraTransform = _camera.transform;
|
|
}
|
|
UpdateManager.RegisterSlicedUpdate(this, _updateMode);
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
UpdateManager.DeregisterSlicedUpdate(this);
|
|
Dispose();
|
|
}
|
|
|
|
private void Dispose()
|
|
{
|
|
_visibilityMap.Dispose();
|
|
_changeMap.Dispose();
|
|
}
|
|
|
|
public void BatchUpdate()
|
|
{
|
|
UpdateSplashes();
|
|
if (_waterQuality.Value != 0 && _changeMap.IsCreated && _visibilityMap.IsCreated)
|
|
{
|
|
UpdateDecals();
|
|
}
|
|
}
|
|
|
|
private void UpdateSplashes()
|
|
{
|
|
if (_activeSplashes == null)
|
|
{
|
|
return;
|
|
}
|
|
for (int num = _activeSplashes.Count - 1; num >= 0; num--)
|
|
{
|
|
if (_activeSplashes[num].timeActive < 0.2f)
|
|
{
|
|
_activeSplashes[num] = (_activeSplashes[num].splash, _activeSplashes[num].timeActive + Time.deltaTime);
|
|
}
|
|
else if (!_activeSplashes[num].splash.HasAnySystemAwake())
|
|
{
|
|
_splashPool.Reclaim(_activeSplashes[num].splash);
|
|
_activeSplashes.RemoveAt(num);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdateDecals()
|
|
{
|
|
NativeArray<float3> positions = new NativeArray<float3>(_fxInstances.Count, Allocator.Persistent);
|
|
NativeArray<int> hashes = new NativeArray<int>(_fxInstances.Count, Allocator.Persistent);
|
|
int num = 0;
|
|
foreach (KeyValuePair<int, WaterFXInstance> fxInstance in _fxInstances)
|
|
{
|
|
positions[num] = fxInstance.Value.transform.position;
|
|
hashes[num] = fxInstance.Key;
|
|
num++;
|
|
}
|
|
new CalculateFXVisibilityJob
|
|
{
|
|
Positions = positions,
|
|
Hashes = hashes,
|
|
CameraPosition = _playerCameraTransform.position,
|
|
CullingDistance = MaxSpawnDistanceFromCamera,
|
|
ChangeMap = _changeMap,
|
|
VisibilityMap = _visibilityMap
|
|
}.Schedule(_fxInstances.Count, default(JobHandle)).Complete();
|
|
foreach (KVPair<int, bool> item in _changeMap)
|
|
{
|
|
if (item.Value)
|
|
{
|
|
_fxInstances[item.Key].IsVisible = _visibilityMap[item.Key];
|
|
item.Value = false;
|
|
}
|
|
}
|
|
positions.Dispose();
|
|
hashes.Dispose();
|
|
}
|
|
|
|
public Pool<WaterDecal> GetPool(WaterDecal decalPrefab)
|
|
{
|
|
int hashCode = decalPrefab.GetHashCode();
|
|
if (!_decalPools.ContainsKey(hashCode))
|
|
{
|
|
_decalPools.Add(hashCode, Pool<WaterDecal>.CreateInstance(decalPrefab, 2, null, $"Water decal pool {_decalPools.Count + 1}"));
|
|
}
|
|
return _decalPools[hashCode];
|
|
}
|
|
|
|
public void CreateSplash(Vector3 position, float scaleMultiplier)
|
|
{
|
|
if (!(Vector3.Distance(position, _playerCameraTransform.position) > MaxSpawnDistanceFromCamera))
|
|
{
|
|
VisualEffect instance = _splashPool.GetInstance();
|
|
_activeSplashes.Add((instance, 0f));
|
|
instance.transform.position = position;
|
|
instance.transform.rotation = Quaternion.identity;
|
|
instance.transform.localScale = Vector3.one * scaleMultiplier;
|
|
instance.gameObject.SetActive(value: true);
|
|
}
|
|
}
|
|
}
|