1
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
[RequireComponent(typeof(Camera))]
|
||||
public partial class DC_Camera : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
private int _raysCount = 1500;
|
||||
|
||||
[SerializeField]
|
||||
private DistributionMethod _raysDistribution = DistributionMethod.R2;
|
||||
|
||||
[Space]
|
||||
|
||||
[Range(0, 90)]
|
||||
[SerializeField]
|
||||
private int _fovAddition = 5;
|
||||
|
||||
[SerializeField]
|
||||
private bool _autoCheckChanges = false;
|
||||
|
||||
private IReadOnlyDictionary<Collider, IHitable> _hitablesDic;
|
||||
|
||||
private Camera _camera;
|
||||
private DC_CameraSettings _settings;
|
||||
private bool _updateSettings;
|
||||
private bool _updateRaysCount;
|
||||
private int _newRaysCount;
|
||||
|
||||
private Vector3[] _rayDirs;
|
||||
|
||||
private NativeArray<RaycastCommand> _rayCommands;
|
||||
private NativeArray<RaycastHit> _rayHits;
|
||||
private JobHandle _jobHandle;
|
||||
private int _layerMask;
|
||||
private int _currentRay;
|
||||
private bool _cameraEnabled;
|
||||
|
||||
#if UNITY_2022_2_OR_NEWER
|
||||
|
||||
private QueryParameters _query;
|
||||
|
||||
#endif
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_camera = GetComponent<Camera>();
|
||||
_newRaysCount = _raysCount;
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_hitablesDic = DC_Controller.GetHitables();
|
||||
_layerMask = LayerMask.GetMask(DC_Controller.GetCullingLayerName());
|
||||
|
||||
_updateRaysCount = true;
|
||||
_updateSettings = true;
|
||||
|
||||
#if UNITY_2022_2_OR_NEWER
|
||||
|
||||
_query = new QueryParameters(_layerMask, false, QueryTriggerInteraction.Ignore, false);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
_cameraEnabled = _camera.enabled && gameObject.activeInHierarchy;
|
||||
|
||||
if (!_cameraEnabled)
|
||||
return;
|
||||
|
||||
UpdateIfNeeded();
|
||||
|
||||
int totalCount = _rayDirs.Length;
|
||||
float distance = _settings.farPlane;
|
||||
|
||||
Vector3 origin = _camera.transform.position;
|
||||
Matrix4x4 matrix = _camera.transform.localToWorldMatrix;
|
||||
|
||||
for (int i = 0; i < _raysCount; i++)
|
||||
{
|
||||
|
||||
#if UNITY_2022_2_OR_NEWER
|
||||
|
||||
_rayCommands[i] = new RaycastCommand(origin,
|
||||
matrix.MultiplyVector(_rayDirs[_currentRay]), _query, distance);
|
||||
|
||||
#else
|
||||
|
||||
_rayCommands[i] = new RaycastCommand(origin,
|
||||
matrix.MultiplyVector(_rayDirs[_currentRay]), distance, _layerMask);
|
||||
|
||||
#endif
|
||||
|
||||
_currentRay++;
|
||||
|
||||
if (_currentRay >= totalCount)
|
||||
_currentRay = 0;
|
||||
}
|
||||
|
||||
_jobHandle = RaycastCommand.ScheduleBatch(_rayCommands, _rayHits, 1);
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (!_cameraEnabled)
|
||||
return;
|
||||
|
||||
_jobHandle.Complete();
|
||||
|
||||
for (int i = 0; i < _raysCount; i++)
|
||||
{
|
||||
Collider collider = _rayHits[i].collider;
|
||||
|
||||
if (collider != null)
|
||||
{
|
||||
if (_hitablesDic.TryGetValue(collider, out IHitable hitable))
|
||||
hitable.OnHit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (_rayCommands.IsCreated)
|
||||
_rayCommands.Dispose();
|
||||
|
||||
if (_rayHits.IsCreated)
|
||||
_rayHits.Dispose();
|
||||
}
|
||||
|
||||
|
||||
public void CameraSettingsChanged()
|
||||
{
|
||||
_updateSettings = true;
|
||||
}
|
||||
|
||||
public void SetRaysCount(int count)
|
||||
{
|
||||
_updateRaysCount = true;
|
||||
_newRaysCount = count;
|
||||
}
|
||||
|
||||
|
||||
private bool IsCameraSettingsChanged()
|
||||
{
|
||||
if (_settings.width != _camera.pixelWidth)
|
||||
return true;
|
||||
|
||||
if (_settings.height != _camera.pixelHeight)
|
||||
return true;
|
||||
|
||||
if (_settings.fov != _camera.fieldOfView)
|
||||
return true;
|
||||
|
||||
if (_settings.farPlane != _camera.farClipPlane)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void UpdateIfNeeded()
|
||||
{
|
||||
if (!_updateSettings && _autoCheckChanges)
|
||||
{
|
||||
if (IsCameraSettingsChanged())
|
||||
_updateSettings = true;
|
||||
}
|
||||
|
||||
if (_updateSettings)
|
||||
{
|
||||
UpdateCameraSettings();
|
||||
_updateSettings = false;
|
||||
}
|
||||
|
||||
if (_updateRaysCount)
|
||||
{
|
||||
UpdateRaysCount(_newRaysCount);
|
||||
_updateRaysCount = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateRaysCount(int count)
|
||||
{
|
||||
if (_rayCommands.IsCreated)
|
||||
_rayCommands.Dispose();
|
||||
|
||||
if (_rayHits.IsCreated)
|
||||
_rayHits.Dispose();
|
||||
|
||||
_rayCommands = new NativeArray<RaycastCommand>(count, Allocator.Persistent);
|
||||
_rayHits = new NativeArray<RaycastHit>(count, Allocator.Persistent);
|
||||
_raysCount = _newRaysCount;
|
||||
}
|
||||
|
||||
private void UpdateCameraSettings()
|
||||
{
|
||||
_rayDirs = DC_CameraUtil.GetRaysDirections(_camera, _raysDistribution, _fovAddition);
|
||||
_settings = new DC_CameraSettings(_camera);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public struct DC_CameraSettings
|
||||
{
|
||||
public int width;
|
||||
public int height;
|
||||
public float fov;
|
||||
public float farPlane;
|
||||
|
||||
public DC_CameraSettings(Camera camera)
|
||||
{
|
||||
width = camera.pixelWidth;
|
||||
height = camera.pixelHeight;
|
||||
fov = camera.fieldOfView;
|
||||
farPlane = camera.farClipPlane;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,98 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public partial class DC_Camera : MonoBehaviour
|
||||
{
|
||||
public enum DistributionMethod { Halton, R2 }
|
||||
|
||||
private static class DC_CameraUtil
|
||||
{
|
||||
private static Dictionary<DC_CameraSettings, Vector3[]> _rayDirsTable;
|
||||
|
||||
private static double _r2a1;
|
||||
private static double _r2a2;
|
||||
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
private static void ReloadDomain()
|
||||
{
|
||||
_rayDirsTable?.Clear();
|
||||
}
|
||||
|
||||
static DC_CameraUtil()
|
||||
{
|
||||
_rayDirsTable = new Dictionary<DC_CameraSettings, Vector3[]>();
|
||||
|
||||
double g = 1.32471795724474602596;
|
||||
|
||||
_r2a1 = 1.0 / g;
|
||||
_r2a2 = 1.0 / (g * g);
|
||||
}
|
||||
|
||||
public static Vector3[] GetRaysDirections(Camera camera, DistributionMethod distribution, int fovAddition)
|
||||
{
|
||||
DC_CameraSettings settings = new DC_CameraSettings(camera);
|
||||
|
||||
if (_rayDirsTable.TryGetValue(settings, out Vector3[] result))
|
||||
return result;
|
||||
|
||||
float cameraFov = camera.fieldOfView;
|
||||
Matrix4x4 cameraInvTransform = camera.transform.localToWorldMatrix.inverse;
|
||||
|
||||
int count = (Screen.width * Screen.height) / 8;
|
||||
Vector3[] dirs = new Vector3[count];
|
||||
|
||||
camera.fieldOfView = cameraFov + fovAddition;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Vector2 viewPoint;
|
||||
|
||||
if (distribution == DistributionMethod.Halton)
|
||||
viewPoint = new Vector2(HaltonSequence(i, 2), HaltonSequence(i, 3));
|
||||
else
|
||||
viewPoint = R2Distribution(i);
|
||||
|
||||
Ray ray = camera.ViewportPointToRay(viewPoint);
|
||||
|
||||
dirs[i] = cameraInvTransform.MultiplyVector(ray.direction);
|
||||
}
|
||||
|
||||
camera.fieldOfView = cameraFov;
|
||||
|
||||
_rayDirsTable.Add(settings, dirs);
|
||||
|
||||
return dirs;
|
||||
}
|
||||
|
||||
|
||||
private static float HaltonSequence(int index, int b)
|
||||
{
|
||||
float res = 0f;
|
||||
float f = 1f / b;
|
||||
|
||||
int i = index;
|
||||
|
||||
while (i > 0)
|
||||
{
|
||||
res = res + f * (i % b);
|
||||
i = Mathf.FloorToInt(i / b);
|
||||
f = f / b;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static Vector2 R2Distribution(int index)
|
||||
{
|
||||
float x = (float)((0.5 + _r2a1 * index) % 1);
|
||||
float y = (float)((0.5 + _r2a2 * index) % 1);
|
||||
|
||||
return new Vector2(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,216 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public class DC_Controller : MonoBehaviour
|
||||
{
|
||||
private static Dictionary<int, DC_Controller> _controllersDic;
|
||||
private static Dictionary<Collider, IHitable> _hitablesDic;
|
||||
|
||||
public int ControllerID
|
||||
{
|
||||
get
|
||||
{
|
||||
return _controllerID;
|
||||
}
|
||||
set
|
||||
{
|
||||
_controllerID = value;
|
||||
}
|
||||
}
|
||||
public float ObjectsLifetime
|
||||
{
|
||||
get
|
||||
{
|
||||
return _objectsLifetime;
|
||||
}
|
||||
set
|
||||
{
|
||||
_objectsLifetime = Mathf.Max(0.1f, value);
|
||||
}
|
||||
}
|
||||
public bool MergeInGroups
|
||||
{
|
||||
get
|
||||
{
|
||||
return _mergeInGroups;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_sourcesProvider != null)
|
||||
{
|
||||
Debug.Log("You can set 'MergeInGroups' option only before initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
_mergeInGroups = value;
|
||||
}
|
||||
}
|
||||
public float CellSize
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cellSize;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_sourcesProvider != null)
|
||||
{
|
||||
Debug.Log("You can set 'Cell Size' option only before initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
_cellSize = Mathf.Max(value, 0.1f);
|
||||
}
|
||||
}
|
||||
public bool DrawGizmos { get; set; }
|
||||
|
||||
[SerializeField]
|
||||
private int _controllerID;
|
||||
|
||||
[SerializeField, Min(0.1f)]
|
||||
private float _objectsLifetime = 2f;
|
||||
|
||||
[SerializeField]
|
||||
private bool _mergeInGroups = true;
|
||||
|
||||
[SerializeField]
|
||||
private float _cellSize = 10f;
|
||||
|
||||
private IDC_SourcesProvider _sourcesProvider;
|
||||
private BinaryTreeDrawer _treeDrawer;
|
||||
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
private static void ReloadDomain()
|
||||
{
|
||||
_controllersDic = null;
|
||||
_hitablesDic = null;
|
||||
}
|
||||
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (_controllersDic == null)
|
||||
_controllersDic = new Dictionary<int, DC_Controller>();
|
||||
|
||||
if (_hitablesDic == null)
|
||||
_hitablesDic = new Dictionary<Collider, IHitable>();
|
||||
|
||||
if (!_controllersDic.ContainsKey(_controllerID))
|
||||
_controllersDic.Add(_controllerID, this);
|
||||
else
|
||||
Debug.Log("DynamicCullingController with id : " + _controllerID + " already exists!");
|
||||
|
||||
if (_mergeInGroups)
|
||||
{
|
||||
_sourcesProvider = new DC_SourcesTree(_cellSize);
|
||||
_treeDrawer = new BinaryTreeDrawer();
|
||||
}
|
||||
else
|
||||
{
|
||||
_sourcesProvider = new DC_SingleSourcesProvider();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
if (!DrawGizmos)
|
||||
return;
|
||||
|
||||
if (_treeDrawer == null)
|
||||
return;
|
||||
|
||||
DC_SourcesTree tree = _sourcesProvider as DC_SourcesTree;
|
||||
|
||||
if (tree.Root == null)
|
||||
return;
|
||||
|
||||
_treeDrawer.Color = Color.white;
|
||||
_treeDrawer.DrawTreeGizmos(tree.Root);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
_controllersDic.Remove(_controllerID);
|
||||
}
|
||||
|
||||
|
||||
public DC_Camera AddCamera(Camera camera, int raysPerFrame)
|
||||
{
|
||||
if (camera.TryGetComponent(out DC_Camera cullingCamera))
|
||||
{
|
||||
Debug.Log(camera.name + " already has DynamicCullingCamera component");
|
||||
}
|
||||
else
|
||||
{
|
||||
cullingCamera = camera.gameObject.AddComponent<DC_Camera>();
|
||||
cullingCamera.SetRaysCount(raysPerFrame);
|
||||
}
|
||||
|
||||
return cullingCamera;
|
||||
}
|
||||
|
||||
public DC_SourceSettings AddObjectForCulling(MeshRenderer renderer,
|
||||
CullingMethod cullingMethod = CullingMethod.FullDisable)
|
||||
{
|
||||
DC_SourceSettings settings = renderer.gameObject.AddComponent<DC_SourceSettings>();
|
||||
|
||||
settings.ControllerID = _controllerID;
|
||||
settings.SourceType = SourceType.SingleMesh;
|
||||
settings.GetStrategy<DC_RendererSourceSettingsStrategy>().CullingMethod = cullingMethod;
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
public DC_SourceSettings AddObjectForCulling(LODGroup lodGroup,
|
||||
CullingMethod cullingMethod = CullingMethod.FullDisable)
|
||||
{
|
||||
DC_SourceSettings settings = lodGroup.gameObject.AddComponent<DC_SourceSettings>();
|
||||
|
||||
settings.ControllerID = _controllerID;
|
||||
settings.SourceType = SourceType.LODGroup;
|
||||
settings.GetStrategy<DC_LODGroupSourceSettingsStrategy>().CullingMethod = cullingMethod;
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
public void AddObjectForCulling(ICullingTarget cullingTarget, IEnumerable<Collider> colliders)
|
||||
{
|
||||
DC_Source source = _sourcesProvider.GetSource(cullingTarget);
|
||||
|
||||
source.Lifetime = _objectsLifetime;
|
||||
source.transform.parent = transform;
|
||||
|
||||
DC_CullingTargetObserver observer = cullingTarget.GameObject.AddComponent<DC_CullingTargetObserver>();
|
||||
observer.Initialize(source, cullingTarget);
|
||||
|
||||
foreach (var collider in colliders)
|
||||
_hitablesDic.Add(collider, source);
|
||||
}
|
||||
|
||||
|
||||
public static DC_Controller GetById(int id)
|
||||
{
|
||||
return _controllersDic[id];
|
||||
}
|
||||
|
||||
public static int GetCullingLayer()
|
||||
{
|
||||
return LayerMask.NameToLayer(GetCullingLayerName());
|
||||
}
|
||||
|
||||
public static string GetCullingLayerName()
|
||||
{
|
||||
return "ACSCulling";
|
||||
}
|
||||
|
||||
public static IReadOnlyDictionary<Collider, IHitable> GetHitables()
|
||||
{
|
||||
return _hitablesDic;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public interface ICullingTarget
|
||||
{
|
||||
GameObject GameObject { get; }
|
||||
Bounds Bounds { get; }
|
||||
|
||||
void MakeVisible();
|
||||
|
||||
void MakeInvisible();
|
||||
}
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public class DC_CustomTarget : ICullingTarget
|
||||
{
|
||||
public GameObject GameObject { get; private set; }
|
||||
public Bounds Bounds { get; private set; }
|
||||
|
||||
private DC_CustomTargetEvent _onVisible;
|
||||
private DC_CustomTargetEvent _onInvisible;
|
||||
|
||||
public DC_CustomTarget(GameObject go, Bounds bounds,
|
||||
DC_CustomTargetEvent onVisible,
|
||||
DC_CustomTargetEvent onInvisible)
|
||||
{
|
||||
GameObject = go;
|
||||
Bounds = bounds;
|
||||
|
||||
_onVisible = onVisible != null ? onVisible : new DC_CustomTargetEvent();
|
||||
_onInvisible = onInvisible != null ? onInvisible : new DC_CustomTargetEvent();
|
||||
}
|
||||
|
||||
public void MakeVisible()
|
||||
{
|
||||
_onVisible?.Invoke(this);
|
||||
}
|
||||
|
||||
public void MakeInvisible()
|
||||
{
|
||||
_onInvisible?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class DC_CustomTargetEvent : UnityEvent<DC_CustomTarget>
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public class DC_LODGroupShadowsTarget : DC_LODGroupTargetBase
|
||||
{
|
||||
private Renderer[] _renderers;
|
||||
private ShadowCastingMode _shadowMode;
|
||||
|
||||
public DC_LODGroupShadowsTarget(LODGroup group, Renderer[] renderers, Bounds bounds)
|
||||
: base(group, renderers, bounds)
|
||||
{
|
||||
_renderers = Renderers;
|
||||
_shadowMode = _renderers[0].shadowCastingMode;
|
||||
}
|
||||
|
||||
public override void MakeVisible()
|
||||
{
|
||||
for (int i = 0; i < _renderers.Length; i++)
|
||||
_renderers[i].shadowCastingMode = _shadowMode;
|
||||
}
|
||||
|
||||
public override void MakeInvisible()
|
||||
{
|
||||
for (int i = 0; i < _renderers.Length; i++)
|
||||
_renderers[i].shadowCastingMode = ShadowCastingMode.ShadowsOnly;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public class DC_LODGroupTarget : DC_LODGroupTargetBase
|
||||
{
|
||||
private Renderer[] _renderers;
|
||||
|
||||
public DC_LODGroupTarget(LODGroup group, Renderer[] renderers, Bounds bounds)
|
||||
: base(group, renderers, bounds)
|
||||
{
|
||||
_renderers = Renderers;
|
||||
}
|
||||
|
||||
public override void MakeVisible()
|
||||
{
|
||||
for (int i = 0; i < _renderers.Length; i++)
|
||||
_renderers[i].enabled = true;
|
||||
}
|
||||
|
||||
public override void MakeInvisible()
|
||||
{
|
||||
for (int i = 0; i < _renderers.Length; i++)
|
||||
_renderers[i].enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public abstract class DC_LODGroupTargetBase : ICullingTarget
|
||||
{
|
||||
public GameObject GameObject
|
||||
{
|
||||
get
|
||||
{
|
||||
return Group.gameObject;
|
||||
}
|
||||
}
|
||||
public Bounds Bounds { get; private set; }
|
||||
|
||||
protected LODGroup Group { get; private set; }
|
||||
protected Renderer[] Renderers { get; private set; }
|
||||
|
||||
public DC_LODGroupTargetBase(LODGroup group, Renderer[] renderers, Bounds bounds)
|
||||
{
|
||||
Group = group;
|
||||
Renderers = renderers;
|
||||
Bounds = bounds;
|
||||
}
|
||||
|
||||
public abstract void MakeVisible();
|
||||
|
||||
public abstract void MakeInvisible();
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public class DC_RendererShadowsTarget : DC_RendererTargetBase
|
||||
{
|
||||
private Renderer _renderer;
|
||||
private ShadowCastingMode _shadowMode;
|
||||
|
||||
public DC_RendererShadowsTarget(Renderer renderer)
|
||||
: base(renderer)
|
||||
{
|
||||
_renderer = Renderer;
|
||||
_shadowMode = _renderer.shadowCastingMode;
|
||||
}
|
||||
|
||||
public override void MakeVisible()
|
||||
{
|
||||
_renderer.shadowCastingMode = _shadowMode;
|
||||
}
|
||||
|
||||
public override void MakeInvisible()
|
||||
{
|
||||
_renderer.shadowCastingMode = ShadowCastingMode.ShadowsOnly;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public class DC_RendererTarget : DC_RendererTargetBase
|
||||
{
|
||||
private Renderer _renderer;
|
||||
|
||||
public DC_RendererTarget(Renderer renderer)
|
||||
: base(renderer)
|
||||
{
|
||||
_renderer = Renderer;
|
||||
}
|
||||
|
||||
public override void MakeVisible()
|
||||
{
|
||||
_renderer.enabled = true;
|
||||
}
|
||||
|
||||
public override void MakeInvisible()
|
||||
{
|
||||
_renderer.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public abstract class DC_RendererTargetBase : ICullingTarget
|
||||
{
|
||||
public GameObject GameObject
|
||||
{
|
||||
get
|
||||
{
|
||||
return Renderer.gameObject;
|
||||
}
|
||||
}
|
||||
public Bounds Bounds
|
||||
{
|
||||
get
|
||||
{
|
||||
return Renderer.bounds;
|
||||
}
|
||||
}
|
||||
|
||||
protected Renderer Renderer { get; private set; }
|
||||
|
||||
public DC_RendererTargetBase(Renderer renderer)
|
||||
{
|
||||
Renderer = renderer;
|
||||
}
|
||||
|
||||
public abstract void MakeInvisible();
|
||||
|
||||
public abstract void MakeVisible();
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public class DC_CullingTargetObserver : MonoBehaviour
|
||||
{
|
||||
private DC_Source _source;
|
||||
private ICullingTarget _target;
|
||||
|
||||
public void Initialize(DC_Source source, ICullingTarget target)
|
||||
{
|
||||
_source = source;
|
||||
_target = target;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (!gameObject.scene.isLoaded)
|
||||
return;
|
||||
|
||||
_source?.RemoveCullingTarget(_target);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,133 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public enum OccluderType { Collider, Mesh, LODGroup }
|
||||
|
||||
public class DC_Occluder : MonoBehaviour
|
||||
{
|
||||
[field : SerializeField]
|
||||
public OccluderType OccluderType { get; set; }
|
||||
|
||||
private Bounds? _bounds;
|
||||
private int _layer;
|
||||
|
||||
|
||||
public bool TryGetBounds(ref Bounds bounds)
|
||||
{
|
||||
if (_bounds != null)
|
||||
{
|
||||
bounds = _bounds.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (OccluderType == OccluderType.Collider)
|
||||
{
|
||||
if (TryGetComponent(out Collider collider))
|
||||
{
|
||||
_bounds = bounds = collider.bounds;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (OccluderType == OccluderType.Mesh)
|
||||
{
|
||||
if (TryGetComponent(out MeshRenderer renderer))
|
||||
{
|
||||
_bounds = bounds = renderer.bounds;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TryGetComponent(out LODGroup group))
|
||||
{
|
||||
LOD[] lods = group.GetLODs();
|
||||
|
||||
for (int i = 0; i < lods.Length; i++)
|
||||
{
|
||||
foreach (var renderer in lods[i].renderers)
|
||||
{
|
||||
if (renderer != null)
|
||||
{
|
||||
_bounds = bounds = renderer.bounds;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
if (GetComponent<MeshRenderer>() != null)
|
||||
OccluderType = OccluderType.Mesh;
|
||||
|
||||
else if (GetComponent<LODGroup>() != null)
|
||||
OccluderType = OccluderType.LODGroup;
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_layer = DC_Controller.GetCullingLayer();
|
||||
|
||||
if (OccluderType == OccluderType.Collider)
|
||||
{
|
||||
gameObject.layer = _layer;
|
||||
}
|
||||
else if (OccluderType == OccluderType.Mesh)
|
||||
{
|
||||
MeshFilter filter = GetComponent<MeshFilter>();
|
||||
|
||||
if (filter == null || filter.sharedMesh == null)
|
||||
{
|
||||
Debug.Log(gameObject.name + " unable to create occluder, mesh not found");
|
||||
return;
|
||||
}
|
||||
|
||||
CreateCollider(gameObject, filter.sharedMesh);
|
||||
}
|
||||
else
|
||||
{
|
||||
LODGroup group = GetComponent<LODGroup>();
|
||||
|
||||
if (group == null)
|
||||
{
|
||||
Debug.Log(gameObject.name + " unable to create occluder, LODGroup not found");
|
||||
return;
|
||||
}
|
||||
|
||||
LOD lod = group.GetLODs()[0];
|
||||
|
||||
foreach (var renderer in lod.renderers)
|
||||
{
|
||||
MeshFilter filter = renderer.GetComponent<MeshFilter>();
|
||||
|
||||
if (filter != null && filter.sharedMesh != null)
|
||||
CreateCollider(renderer.gameObject, filter.sharedMesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateCollider(GameObject go, Mesh mesh)
|
||||
{
|
||||
GameObject colliderGO = new GameObject("DC_Collider");
|
||||
colliderGO.layer = _layer;
|
||||
|
||||
Transform colliderTransform = colliderGO.transform;
|
||||
|
||||
colliderTransform.parent = go.transform;
|
||||
colliderTransform.localPosition = Vector3.zero;
|
||||
colliderTransform.localEulerAngles = Vector3.zero;
|
||||
colliderTransform.localScale = Vector3.one;
|
||||
|
||||
MeshCollider collider = colliderGO.AddComponent<MeshCollider>();
|
||||
collider.sharedMesh = mesh;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public abstract class DC_Source : MonoBehaviour, IHitable
|
||||
{
|
||||
public float Lifetime
|
||||
{
|
||||
get
|
||||
{
|
||||
return _lifetime;
|
||||
}
|
||||
set
|
||||
{
|
||||
_lifetime = Mathf.Max(0.01f, value);
|
||||
}
|
||||
}
|
||||
|
||||
private float _lifetime;
|
||||
private float _currentTime;
|
||||
|
||||
|
||||
private void Update()
|
||||
{
|
||||
try
|
||||
{
|
||||
_currentTime += Time.deltaTime;
|
||||
|
||||
if (_currentTime > _lifetime)
|
||||
{
|
||||
OnTimeout();
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError(ex.Message + ex.StackTrace);
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnHit()
|
||||
{
|
||||
try
|
||||
{
|
||||
enabled = true;
|
||||
|
||||
_currentTime = 0;
|
||||
|
||||
OnHitInternal();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError(ex.Message + ex.StackTrace);
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
OnTimeout();
|
||||
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
OnHitInternal();
|
||||
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
|
||||
public abstract void SetCullingTarget(ICullingTarget target);
|
||||
|
||||
public abstract void RemoveCullingTarget(ICullingTarget target);
|
||||
|
||||
|
||||
protected abstract void OnHitInternal();
|
||||
|
||||
protected abstract void OnTimeout();
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public interface IHitable
|
||||
{
|
||||
void OnHit();
|
||||
}
|
||||
}
|
@@ -0,0 +1,79 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public class DC_MultiSource : DC_Source
|
||||
{
|
||||
private List<ICullingTarget> _cullingTargets;
|
||||
private bool _visible;
|
||||
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_cullingTargets = new List<ICullingTarget>();
|
||||
}
|
||||
|
||||
|
||||
public override void SetCullingTarget(ICullingTarget target)
|
||||
{
|
||||
if (_cullingTargets == null)
|
||||
_cullingTargets = new List<ICullingTarget>();
|
||||
|
||||
_cullingTargets.Add(target);
|
||||
|
||||
if (_visible)
|
||||
target.MakeVisible();
|
||||
else
|
||||
target.MakeInvisible();
|
||||
}
|
||||
|
||||
public override void RemoveCullingTarget(ICullingTarget target)
|
||||
{
|
||||
_cullingTargets.Remove(target);
|
||||
}
|
||||
|
||||
|
||||
protected override void OnHitInternal()
|
||||
{
|
||||
if (_visible)
|
||||
return;
|
||||
|
||||
int i = 0;
|
||||
while (i < _cullingTargets.Count)
|
||||
{
|
||||
try
|
||||
{
|
||||
_cullingTargets[i].MakeVisible();
|
||||
i++;
|
||||
}
|
||||
catch(MissingReferenceException)
|
||||
{
|
||||
RemoveCullingTarget(_cullingTargets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
_visible = true;
|
||||
}
|
||||
|
||||
protected override void OnTimeout()
|
||||
{
|
||||
int i = 0;
|
||||
while (i < _cullingTargets.Count)
|
||||
{
|
||||
try
|
||||
{
|
||||
_cullingTargets[i].MakeInvisible();
|
||||
i++;
|
||||
}
|
||||
catch (MissingReferenceException)
|
||||
{
|
||||
RemoveCullingTarget(_cullingTargets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
_visible = false;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public class DC_SingleSource : DC_Source
|
||||
{
|
||||
private ICullingTarget _cullingTarget;
|
||||
private bool _visible;
|
||||
|
||||
|
||||
public override void SetCullingTarget(ICullingTarget target)
|
||||
{
|
||||
_cullingTarget = target;
|
||||
_cullingTarget.MakeInvisible();
|
||||
}
|
||||
|
||||
public override void RemoveCullingTarget(ICullingTarget target)
|
||||
{
|
||||
enabled = false;
|
||||
Destroy(gameObject);
|
||||
}
|
||||
|
||||
|
||||
protected override void OnHitInternal()
|
||||
{
|
||||
if (_visible)
|
||||
return;
|
||||
|
||||
_cullingTarget.MakeVisible();
|
||||
_visible = true;
|
||||
}
|
||||
|
||||
protected override void OnTimeout()
|
||||
{
|
||||
_cullingTarget.MakeInvisible();
|
||||
_visible = false;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,189 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public enum CullingMethod { FullDisable, KeepShadows }
|
||||
public enum SourceType { SingleMesh, LODGroup, Custom }
|
||||
|
||||
[DisallowMultipleComponent]
|
||||
public class DC_SourceSettings : MonoBehaviour
|
||||
{
|
||||
public bool ReadyForCulling
|
||||
{
|
||||
get
|
||||
{
|
||||
return _strategy != null && _strategy.ReadyForCulling;
|
||||
}
|
||||
}
|
||||
public int CullingLayer
|
||||
{
|
||||
get
|
||||
{
|
||||
return DC_Controller.GetCullingLayer();
|
||||
}
|
||||
}
|
||||
public SourceType SourceType
|
||||
{
|
||||
get
|
||||
{
|
||||
return _sourceType;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == _sourceType)
|
||||
return;
|
||||
|
||||
_sourceType = value;
|
||||
|
||||
OnSourceTypeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
[field: SerializeField]
|
||||
public int ControllerID { get; set; }
|
||||
|
||||
[field: SerializeField]
|
||||
public bool IsIncompatible { get; private set; }
|
||||
|
||||
[field: SerializeField]
|
||||
public string IncompatibilityReason { get; private set; }
|
||||
|
||||
[SerializeField]
|
||||
private SourceType _sourceType;
|
||||
|
||||
[SerializeReference]
|
||||
private IDC_SourceSettingsStrategy _strategy;
|
||||
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
DetectSourceType();
|
||||
CheckCompatibility();
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (_strategy == null)
|
||||
CreateStrategy();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!CheckCompatibility())
|
||||
{
|
||||
enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_strategy.ReadyForCulling)
|
||||
_strategy.PrepareForCulling();
|
||||
|
||||
DC_Controller.GetById(ControllerID).AddObjectForCulling(
|
||||
_strategy.CreateCullingTarget(),
|
||||
_strategy.GetColliders());
|
||||
|
||||
Destroy(this);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
IsIncompatible = true;
|
||||
IncompatibilityReason = ex.Message + ex.StackTrace;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public T GetStrategy<T>() where T : IDC_SourceSettingsStrategy
|
||||
{
|
||||
return (T)_strategy;
|
||||
}
|
||||
|
||||
public bool TryGetBounds(ref Bounds bounds)
|
||||
{
|
||||
if (_strategy == null)
|
||||
return false;
|
||||
|
||||
return _strategy.TryGetBounds(ref bounds);
|
||||
}
|
||||
|
||||
public bool CheckCompatibility()
|
||||
{
|
||||
if (_strategy == null)
|
||||
CreateStrategy();
|
||||
|
||||
IsIncompatible = !_strategy.CheckCompatibilityAndGetComponents(out string reason);
|
||||
IncompatibilityReason = reason;
|
||||
|
||||
return !IsIncompatible;
|
||||
}
|
||||
|
||||
public void Bake()
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
Debug.Log("'Bake' can only be called in editor mode");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_strategy != null && _strategy.ReadyForCulling)
|
||||
_strategy.ClearData();
|
||||
|
||||
if (CheckCompatibility())
|
||||
_strategy.PrepareForCulling();
|
||||
}
|
||||
|
||||
public void ClearBakedData()
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
Debug.Log("'ClearBakedData' can only be called in editor mode");
|
||||
return;
|
||||
}
|
||||
|
||||
_strategy?.ClearData();
|
||||
}
|
||||
|
||||
|
||||
private void DetectSourceType()
|
||||
{
|
||||
if (GetComponent<LODGroup>() != null)
|
||||
SourceType = SourceType.LODGroup;
|
||||
|
||||
else if (GetComponent<MeshRenderer>() != null)
|
||||
SourceType = SourceType.SingleMesh;
|
||||
|
||||
else
|
||||
SourceType = SourceType.Custom;
|
||||
}
|
||||
|
||||
private void OnSourceTypeChanged()
|
||||
{
|
||||
if (_strategy != null && _strategy.ReadyForCulling)
|
||||
_strategy.ClearData();
|
||||
|
||||
CreateStrategy();
|
||||
CheckCompatibility();
|
||||
}
|
||||
|
||||
private void CreateStrategy()
|
||||
{
|
||||
if (SourceType == SourceType.SingleMesh)
|
||||
{
|
||||
_strategy = new DC_RendererSourceSettingsStrategy(this);
|
||||
}
|
||||
else if (SourceType == SourceType.LODGroup)
|
||||
{
|
||||
_strategy = new DC_LODGroupSourceSettingsStrategy(this);
|
||||
}
|
||||
else if (SourceType == SourceType.Custom)
|
||||
{
|
||||
_strategy = new DC_CustomSourceSettingsStrategy(this);
|
||||
}
|
||||
else
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
[Serializable]
|
||||
public class DC_CustomSourceSettingsStrategy : IDC_SourceSettingsStrategy
|
||||
{
|
||||
[field: SerializeField]
|
||||
public bool ReadyForCulling { get; private set; }
|
||||
public Bounds LocalBounds
|
||||
{
|
||||
get
|
||||
{
|
||||
return _localBounds;
|
||||
}
|
||||
set
|
||||
{
|
||||
_localBounds = value;
|
||||
}
|
||||
}
|
||||
|
||||
public DC_CustomTargetEvent OnVisibleEvent
|
||||
{
|
||||
get
|
||||
{
|
||||
return _onVisible;
|
||||
}
|
||||
}
|
||||
public DC_CustomTargetEvent OnInvisibleEvent
|
||||
{
|
||||
get
|
||||
{
|
||||
return _onInvisible;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private DC_SourceSettings _context;
|
||||
|
||||
[SerializeField]
|
||||
private Bounds _localBounds;
|
||||
|
||||
[SerializeField]
|
||||
private DC_CustomTargetEvent _onVisible;
|
||||
|
||||
[SerializeField]
|
||||
private DC_CustomTargetEvent _onInvisible;
|
||||
|
||||
[SerializeField]
|
||||
private BoxCollider _collider;
|
||||
|
||||
|
||||
public DC_CustomSourceSettingsStrategy(DC_SourceSettings context)
|
||||
{
|
||||
_context = context;
|
||||
_localBounds = new Bounds(Vector3.zero, Vector3.one);
|
||||
|
||||
_onVisible = new DC_CustomTargetEvent();
|
||||
_onInvisible = new DC_CustomTargetEvent();
|
||||
}
|
||||
|
||||
public bool CheckCompatibilityAndGetComponents(out string incompatibilityReason)
|
||||
{
|
||||
incompatibilityReason = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
public void PrepareForCulling()
|
||||
{
|
||||
if (ReadyForCulling)
|
||||
return;
|
||||
|
||||
GameObject go = new GameObject("DC_Collider");
|
||||
|
||||
go.transform.parent = _context.transform;
|
||||
go.layer = _context.CullingLayer;
|
||||
|
||||
go.transform.localPosition = Vector3.zero;
|
||||
go.transform.localEulerAngles = Vector3.zero;
|
||||
go.transform.localScale = Vector3.one;
|
||||
|
||||
_collider = go.AddComponent<BoxCollider>();
|
||||
_collider.center = _localBounds.center;
|
||||
_collider.size = _localBounds.size;
|
||||
|
||||
ReadyForCulling = true;
|
||||
}
|
||||
|
||||
public void ClearData()
|
||||
{
|
||||
if (!ReadyForCulling)
|
||||
return;
|
||||
|
||||
if (_collider != null)
|
||||
UnityEngine.Object.DestroyImmediate(_collider.gameObject);
|
||||
|
||||
_collider = null;
|
||||
|
||||
ReadyForCulling = false;
|
||||
}
|
||||
|
||||
|
||||
public bool TryGetBounds(ref Bounds bounds)
|
||||
{
|
||||
bounds.center = _context.transform.position + _localBounds.center;
|
||||
bounds.size = _localBounds.size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public ICullingTarget CreateCullingTarget()
|
||||
{
|
||||
Bounds bounds = new Bounds();
|
||||
|
||||
TryGetBounds(ref bounds);
|
||||
|
||||
return new DC_CustomTarget(_context.gameObject, bounds, _onVisible, _onInvisible);
|
||||
}
|
||||
|
||||
public IEnumerable<Collider> GetColliders()
|
||||
{
|
||||
if (_collider == null)
|
||||
yield break;
|
||||
|
||||
yield return _collider;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,191 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public class DC_LODGroupSourceSettingsStrategy : IDC_SourceSettingsStrategy
|
||||
{
|
||||
[field: SerializeField]
|
||||
public bool ReadyForCulling { get; private set; }
|
||||
|
||||
public CullingMethod CullingMethod
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cullingMethod;
|
||||
}
|
||||
set
|
||||
{
|
||||
_cullingMethod = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private DC_SourceSettings _context;
|
||||
|
||||
[SerializeField]
|
||||
private CullingMethod _cullingMethod;
|
||||
|
||||
[SerializeField]
|
||||
private LODGroup _group;
|
||||
|
||||
[SerializeField]
|
||||
private Renderer[] _renderers;
|
||||
|
||||
[SerializeField]
|
||||
private MeshCollider[] _colliders;
|
||||
|
||||
[SerializeField]
|
||||
private Bounds _bounds;
|
||||
|
||||
|
||||
public DC_LODGroupSourceSettingsStrategy(DC_SourceSettings context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public void PrepareForCulling()
|
||||
{
|
||||
if (ReadyForCulling)
|
||||
return;
|
||||
|
||||
_colliders = _group.GetLODs()[0].renderers
|
||||
.Where(r => (r != null && IsCompatibleRenderer(r)))
|
||||
.Select(r =>
|
||||
{
|
||||
GameObject go = new GameObject("DC_Collider");
|
||||
|
||||
go.transform.parent = r.transform;
|
||||
go.layer = _context.CullingLayer;
|
||||
|
||||
go.transform.localPosition = Vector3.zero;
|
||||
go.transform.localEulerAngles = Vector3.zero;
|
||||
go.transform.localScale = Vector3.one;
|
||||
|
||||
MeshCollider collider = go.AddComponent<MeshCollider>();
|
||||
collider.sharedMesh = r.GetComponent<MeshFilter>().sharedMesh;
|
||||
|
||||
return collider;
|
||||
}).ToArray();
|
||||
|
||||
ReadyForCulling = true;
|
||||
}
|
||||
|
||||
public void ClearData()
|
||||
{
|
||||
if (!ReadyForCulling)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < _colliders.Length; i++)
|
||||
{
|
||||
Collider collider = _colliders[i];
|
||||
|
||||
if (collider == null || collider.gameObject == null)
|
||||
continue;
|
||||
|
||||
UnityEngine.Object.DestroyImmediate(collider.gameObject);
|
||||
}
|
||||
_colliders = null;
|
||||
|
||||
ReadyForCulling = false;
|
||||
}
|
||||
|
||||
|
||||
public bool TryGetBounds(ref Bounds bounds)
|
||||
{
|
||||
if (_renderers != null)
|
||||
{
|
||||
bounds = _bounds;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public ICullingTarget CreateCullingTarget()
|
||||
{
|
||||
if (CullingMethod == CullingMethod.KeepShadows)
|
||||
return new DC_LODGroupShadowsTarget(_group, _renderers, _bounds);
|
||||
|
||||
return new DC_LODGroupTarget(_group, _renderers, _bounds);
|
||||
}
|
||||
|
||||
public IEnumerable<Collider> GetColliders()
|
||||
{
|
||||
if (_colliders == null)
|
||||
yield break;
|
||||
|
||||
for (int i = 0; i < _colliders.Length; i++)
|
||||
yield return _colliders[i];
|
||||
}
|
||||
|
||||
|
||||
public bool CheckCompatibilityAndGetComponents(out string incompatibilityReason)
|
||||
{
|
||||
if (_group == null)
|
||||
{
|
||||
if (!_context.TryGetComponent(out _group))
|
||||
{
|
||||
incompatibilityReason = "LODGroup not found";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (_renderers == null)
|
||||
{
|
||||
LOD[] lods = _group.GetLODs();
|
||||
|
||||
int count = lods.Count(IsCompatibleRenderer);
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
incompatibilityReason = "Can't find any compatible renderer";
|
||||
return false;
|
||||
}
|
||||
|
||||
_renderers = new Renderer[count];
|
||||
_bounds = new Bounds(_group.transform.position, Vector3.zero);
|
||||
|
||||
int idx = 0;
|
||||
for (int i = 0; i < lods.Length; i++)
|
||||
{
|
||||
Renderer[] lodRenderers = lods[i].renderers;
|
||||
|
||||
for (int c = 0; c < lodRenderers.Length; c++)
|
||||
{
|
||||
Renderer renderer = lodRenderers[c];
|
||||
|
||||
if (renderer != null && IsCompatibleRenderer(renderer))
|
||||
{
|
||||
_renderers[idx++] = renderer;
|
||||
_bounds.Encapsulate(renderer.bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < _renderers.Length; i++)
|
||||
{
|
||||
if (_renderers[i] == null)
|
||||
{
|
||||
incompatibilityReason = "Missing renderer at index : " + i;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
incompatibilityReason = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsCompatibleRenderer(Renderer renderer)
|
||||
{
|
||||
MeshFilter filter = renderer.GetComponent<MeshFilter>();
|
||||
|
||||
return filter != null && filter.sharedMesh != null;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,175 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
[Serializable]
|
||||
public class DC_RendererSourceSettingsStrategy : IDC_SourceSettingsStrategy
|
||||
{
|
||||
[field: SerializeField]
|
||||
public bool ReadyForCulling { get; private set; }
|
||||
|
||||
public CullingMethod CullingMethod
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cullingMethod;
|
||||
}
|
||||
set
|
||||
{
|
||||
_cullingMethod = value;
|
||||
}
|
||||
}
|
||||
public bool ConvexCollider
|
||||
{
|
||||
get
|
||||
{
|
||||
return _convexCollider;
|
||||
}
|
||||
set
|
||||
{
|
||||
_convexCollider = value;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
private DC_SourceSettings _context;
|
||||
|
||||
[SerializeField]
|
||||
private CullingMethod _cullingMethod;
|
||||
|
||||
[SerializeField]
|
||||
private bool _convexCollider;
|
||||
|
||||
[SerializeField]
|
||||
private MeshRenderer _renderer;
|
||||
|
||||
[SerializeField]
|
||||
private Mesh _mesh;
|
||||
|
||||
[SerializeField]
|
||||
private MeshCollider _collider;
|
||||
|
||||
[SerializeField]
|
||||
private bool _rigibodiesChecked;
|
||||
|
||||
|
||||
public DC_RendererSourceSettingsStrategy(DC_SourceSettings context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public bool CheckCompatibilityAndGetComponents(out string incompatibilityReason)
|
||||
{
|
||||
if (_renderer == null)
|
||||
{
|
||||
if (!_context.TryGetComponent(out _renderer))
|
||||
{
|
||||
incompatibilityReason = "MeshRenderer not found";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (_mesh == null)
|
||||
{
|
||||
MeshFilter filter = _context.GetComponent<MeshFilter>();
|
||||
|
||||
if (filter == null)
|
||||
{
|
||||
incompatibilityReason = "MeshFilter not found";
|
||||
return false;
|
||||
}
|
||||
|
||||
_mesh = filter.sharedMesh;
|
||||
|
||||
if (_mesh == null)
|
||||
{
|
||||
incompatibilityReason = "Mesh not found";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_rigibodiesChecked)
|
||||
{
|
||||
foreach (var rb in _context.GetComponentsInParent<Rigidbody>())
|
||||
{
|
||||
if (!rb.isKinematic)
|
||||
{
|
||||
ConvexCollider = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_rigibodiesChecked = true;
|
||||
}
|
||||
|
||||
incompatibilityReason = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
public void PrepareForCulling()
|
||||
{
|
||||
if (ReadyForCulling)
|
||||
return;
|
||||
|
||||
GameObject go = new GameObject("DC_Collider");
|
||||
|
||||
go.transform.parent = _renderer.transform;
|
||||
go.layer = _context.CullingLayer;
|
||||
|
||||
go.transform.localPosition = Vector3.zero;
|
||||
go.transform.localEulerAngles = Vector3.zero;
|
||||
go.transform.localScale = Vector3.one;
|
||||
|
||||
_collider = go.AddComponent<MeshCollider>();
|
||||
_collider.sharedMesh = _mesh;
|
||||
|
||||
if (ConvexCollider)
|
||||
_collider.convex = true;
|
||||
|
||||
ReadyForCulling = true;
|
||||
}
|
||||
|
||||
public void ClearData()
|
||||
{
|
||||
if (!ReadyForCulling)
|
||||
return;
|
||||
|
||||
if (_collider != null)
|
||||
UnityEngine.Object.DestroyImmediate(_collider.gameObject);
|
||||
|
||||
_collider = null;
|
||||
|
||||
ReadyForCulling = false;
|
||||
}
|
||||
|
||||
|
||||
public bool TryGetBounds(ref Bounds bounds)
|
||||
{
|
||||
if (_renderer != null)
|
||||
{
|
||||
bounds = _renderer.bounds;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public ICullingTarget CreateCullingTarget()
|
||||
{
|
||||
if (CullingMethod == CullingMethod.KeepShadows)
|
||||
return new DC_RendererShadowsTarget(_renderer);
|
||||
|
||||
return new DC_RendererTarget(_renderer);
|
||||
}
|
||||
|
||||
public IEnumerable<Collider> GetColliders()
|
||||
{
|
||||
if (_collider == null)
|
||||
yield break;
|
||||
|
||||
yield return _collider;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public interface IDC_SourceSettingsStrategy
|
||||
{
|
||||
bool ReadyForCulling { get; }
|
||||
|
||||
|
||||
bool CheckCompatibilityAndGetComponents(out string incompatibilityReason);
|
||||
|
||||
void PrepareForCulling();
|
||||
|
||||
void ClearData();
|
||||
|
||||
|
||||
bool TryGetBounds(ref Bounds bounds);
|
||||
|
||||
ICullingTarget CreateCullingTarget();
|
||||
|
||||
IEnumerable<Collider> GetColliders();
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public interface IDC_SourcesProvider
|
||||
{
|
||||
DC_Source GetSource(ICullingTarget cullingTarget);
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public class DC_SingleSourcesProvider : IDC_SourcesProvider
|
||||
{
|
||||
public DC_Source GetSource(ICullingTarget cullingTarget)
|
||||
{
|
||||
GameObject go = new GameObject("DC_SingleSource");
|
||||
|
||||
DC_SingleSource source = go.AddComponent<DC_SingleSource>();
|
||||
source.SetCullingTarget(cullingTarget);
|
||||
|
||||
return source;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public class DC_SourcesTree : BinaryTree<DC_SourcesTreeNode, ICullingTarget>, IDC_SourcesProvider
|
||||
{
|
||||
private DC_Source _lastModifiedSource;
|
||||
|
||||
|
||||
public DC_SourcesTree(float nodeSize)
|
||||
: base(nodeSize)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public DC_Source GetSource(ICullingTarget cullingTarget)
|
||||
{
|
||||
_lastModifiedSource = null;
|
||||
|
||||
Add(cullingTarget);
|
||||
|
||||
return _lastModifiedSource;
|
||||
}
|
||||
|
||||
|
||||
protected override void AddInternal(DC_SourcesTreeNode node, ICullingTarget data, int depth)
|
||||
{
|
||||
if (node.IsLeaf)
|
||||
{
|
||||
AddDataToNode(node, data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!node.HasChilds)
|
||||
GrowTreeDown(node, data, depth + 1);
|
||||
|
||||
if (Intersects(node.Left, data))
|
||||
AddInternal(node.Left, data, depth + 1);
|
||||
else
|
||||
AddInternal(node.Right, data, depth + 1);
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected override Bounds GetBounds(ICullingTarget data)
|
||||
{
|
||||
return data.Bounds;
|
||||
}
|
||||
|
||||
protected override DC_SourcesTreeNode CreateNode(Vector3 center, Vector3 size, bool isLeaf)
|
||||
{
|
||||
return new DC_SourcesTreeNode(center, size, isLeaf);
|
||||
}
|
||||
|
||||
protected override void SetChildsToNode(DC_SourcesTreeNode parent, DC_SourcesTreeNode leftChild, DC_SourcesTreeNode rightChild)
|
||||
{
|
||||
parent.SetChilds(leftChild, rightChild);
|
||||
}
|
||||
|
||||
protected override void AddDataToNode(DC_SourcesTreeNode node, ICullingTarget data)
|
||||
{
|
||||
node.AddCullingTarget(data);
|
||||
_lastModifiedSource = node.Source;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NGS.AdvancedCullingSystem.Dynamic
|
||||
{
|
||||
public class DC_SourcesTreeNode : BinaryTreeNode
|
||||
{
|
||||
public DC_SourcesTreeNode Left { get; private set; }
|
||||
public DC_SourcesTreeNode Right { get; private set; }
|
||||
public DC_MultiSource Source { get; private set; }
|
||||
|
||||
public DC_SourcesTreeNode(Vector3 center, Vector3 size, bool isLeaf)
|
||||
: base(center, size, isLeaf)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override BinaryTreeNode GetLeft()
|
||||
{
|
||||
return Left;
|
||||
}
|
||||
|
||||
public override BinaryTreeNode GetRight()
|
||||
{
|
||||
return Right;
|
||||
}
|
||||
|
||||
|
||||
public void SetChilds(DC_SourcesTreeNode left, DC_SourcesTreeNode right)
|
||||
{
|
||||
Left = left;
|
||||
Right = right;
|
||||
}
|
||||
|
||||
public void AddCullingTarget(ICullingTarget cullingTarget)
|
||||
{
|
||||
if (Source == null)
|
||||
Source = new GameObject("DC_MultiSource").AddComponent<DC_MultiSource>();
|
||||
|
||||
Source.SetCullingTarget(cullingTarget);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user