// Perfect Culling (C) 2021 Patrick König // using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Linq.Expressions; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.Serialization; namespace Koenigz.PerfectCulling { /// /// This is the base class that provides an easy interface for baking. /// Hopefully the built-in baking scripts are sufficient but if you need something else you can inherit it and roll your own. /// public abstract class PerfectCullingBakingBehaviour : MonoBehaviour { public enum EOutOfBoundsBehaviour { ClampToNearestCell, Cull, IgnoreDoNothing, } [Tooltip("Try to find a non-empty cell if we hit an empty one?")] public bool searchForNonEmptyCells = false; [Tooltip("What should happen if we encounter an empty cell? Cull everything or make everything visible?")] public EEmptyCellCullBehaviour emptyCellCullBehaviour = EEmptyCellCullBehaviour.CullEverything; [Tooltip("Should this volume be culled if the camera is not inside it or should the camera position be clamped to the nearest cell?")] public EOutOfBoundsBehaviour outOfBoundsBehaviour = EOutOfBoundsBehaviour.ClampToNearestCell; private IActiveSamplingProvider[] m_activeSamplingProviders; public IActiveSamplingProvider[] QuerySamplingProviders() { return GetComponentsInChildren(); } public void InitializeAllSamplingProviders() { m_activeSamplingProviders = GetComponentsInChildren(); DefaultSamplingProvider.InitializeSamplingProvider(); foreach (var provider in m_activeSamplingProviders) { provider.InitializeSamplingProvider(); } } public bool SamplingProvidersIsPositionActive(Vector3 pos) { switch (DefaultSamplingProvider.IsSamplingPositionActive(this, pos)) { case DefaultSamplingProvider.Result.IncludeCell: return true; case DefaultSamplingProvider.Result.ExcludeCell: return false; } foreach (IActiveSamplingProvider provider in m_activeSamplingProviders) { if (!provider.IsSamplingPositionActive(this, pos)) { return false; } } return true; } [SerializeField] public PerfectCullingBakeGroup[] bakeGroups = System.Array.Empty(); // Important to initialize or AddRange will fail [SerializeField] public List additionalOccluders = new List(); public virtual PerfectCullingBakeData BakeData { get; } = null; [System.NonSerialized] public int TotalVertexCount = 0; private bool[] m_renderersState; public virtual void Start() { TotalVertexCount = 0; m_renderersState = new bool[bakeGroups.Length]; foreach (PerfectCullingBakeGroup group in bakeGroups) { group.Init(); #if UNITY_EDITOR TotalVertexCount += group.vertexCount; #endif } } public void QueueToggleAllRenderers(bool state) { try { for (var index = 0; index < bakeGroups.Length; index++) { m_renderersState[index] = state; } } catch (Exception e) { // ignored } } /// /// Queue up renderer state change. This will not take effect until ExecuteQueue was called. /// public void QueueToggleRenderer(int index, bool state, out PerfectCullingBakeGroup modifiedBakeGroup) { m_renderersState[index] = state; modifiedBakeGroup = bakeGroups[index]; } /// /// Applies renderers state changes. /// public void ExecuteQueue(bool forceNullCheck = false) { for (var index = 0; index < bakeGroups.Length; index++) { PerfectCullingBakeGroup r = bakeGroups[index]; r?.Toggle(m_renderersState[index], forceNullCheck); } } public virtual void SetBakeData(PerfectCullingBakeData bakeData) => throw new System.NotImplementedException(); public virtual List GetSamplingPositions(Space space = Space.Self) => throw new System.NotImplementedException(); public virtual void GetIndicesForWorldPos(Vector3 worldPos, List indices) => throw new System.NotImplementedException(); public virtual int GetIndexForWorldPos(Vector3 worldPos, out bool isOutOfBounds) => throw new System.NotImplementedException(); public virtual void GetIndicesForIndex(int index, List indices) => BakeData.SampleAtIndex(index, indices); public virtual bool PreBake() => throw new System.NotImplementedException(); public virtual void PostBake() => throw new System.NotImplementedException(); public virtual int GetBakeHash() => throw new System.NotImplementedException(); public virtual void CullAdditionalOccluders(ref HashSet additionalOccluders) => throw new System.NotImplementedException(); } }