using System; using System.Collections.Generic; using AwesomeTechnologies.Utility.Culling; using Unity.Collections; using Unity.Jobs; using UnityEngine; using UnityEngine.Profiling; using AwesomeTechnologies.BillboardSystem; using AwesomeTechnologies.Utility; namespace AwesomeTechnologies.VegetationSystem { public class VegetationStudioCameraRenderList { [NonSerialized] public readonly List> VegetationItemMergeMatrixList = new List>(); [NonSerialized] public readonly List> VegetationItemLOD0MatrixList = new List>(); [NonSerialized] public readonly List> VegetationItemLOD1MatrixList = new List>(); [NonSerialized] public readonly List> VegetationItemLOD2MatrixList = new List>(); [NonSerialized] public readonly List> VegetationItemLOD3MatrixList = new List>(); [NonSerialized] public readonly List> VegetationItemLOD0ShadowMatrixList = new List>(); [NonSerialized] public readonly List> VegetationItemLOD1ShadowMatrixList = new List>(); [NonSerialized] public readonly List> VegetationItemLOD2ShadowMatrixList = new List>(); [NonSerialized] public readonly List> VegetationItemLOD3ShadowMatrixList = new List>(); [NonSerialized] public readonly List> VegetationItemLOD0LodFadeList = new List>(); [NonSerialized] public readonly List> VegetationItemLOD1LodFadeList = new List>(); [NonSerialized] public readonly List> VegetationItemLOD2LodFadeList = new List>(); [NonSerialized] public readonly List> VegetationItemLOD3LodFadeList = new List>(); public VegetationStudioCameraRenderList(int vegetationItemCount) { for (int i = 0; i <= vegetationItemCount - 1; i++) { NativeList newMatrixList = new NativeList(1024, Allocator.Persistent) { Capacity = 1024 }; VegetationItemMergeMatrixList.Add(newMatrixList); NativeList newMatrixLOD0List = new NativeList(1024, Allocator.Persistent) { Capacity = 1024 }; VegetationItemLOD0MatrixList.Add(newMatrixLOD0List); NativeList newMatrixLOD1List = new NativeList(1024, Allocator.Persistent) { Capacity = 1024 }; VegetationItemLOD1MatrixList.Add(newMatrixLOD1List); NativeList newMatrixLOD2List = new NativeList(1024, Allocator.Persistent) { Capacity = 1024 }; VegetationItemLOD2MatrixList.Add(newMatrixLOD2List); NativeList newMatrixLOD3List = new NativeList(1024, Allocator.Persistent) { Capacity = 1024 }; VegetationItemLOD3MatrixList.Add(newMatrixLOD3List); NativeList newMatrixLOD0ShadowList = new NativeList(1024, Allocator.Persistent) { Capacity = 1024 }; VegetationItemLOD0ShadowMatrixList.Add(newMatrixLOD0ShadowList); NativeList newMatrixLOD1ShadowList = new NativeList(1024, Allocator.Persistent) { Capacity = 1024 }; VegetationItemLOD1ShadowMatrixList.Add(newMatrixLOD1ShadowList); NativeList newMatrixLOD2ShadowList = new NativeList(1024, Allocator.Persistent) { Capacity = 1024 }; VegetationItemLOD2ShadowMatrixList.Add(newMatrixLOD2ShadowList); NativeList newMatrixLOD3ShadowList = new NativeList(1024, Allocator.Persistent) { Capacity = 1024 }; VegetationItemLOD3ShadowMatrixList.Add(newMatrixLOD3ShadowList); NativeList newLOD0LodFadeList = new NativeList(1024, Allocator.Persistent) { Capacity = 1024 }; VegetationItemLOD0LodFadeList.Add(newLOD0LodFadeList); NativeList newLOD1LodFadeList = new NativeList(1024, Allocator.Persistent) { Capacity = 1024 }; VegetationItemLOD1LodFadeList.Add(newLOD1LodFadeList); NativeList newLOD2LodFadeList = new NativeList(1024, Allocator.Persistent) { Capacity = 1024 }; VegetationItemLOD2LodFadeList.Add(newLOD2LodFadeList); NativeList newLOD3LodFadeList = new NativeList(1024, Allocator.Persistent) { Capacity = 1024 }; VegetationItemLOD3LodFadeList.Add(newLOD3LodFadeList); } } public void Dispose() { DisposeMatrixInstanceList(VegetationItemMergeMatrixList); DisposeMatrixList(VegetationItemLOD0MatrixList); DisposeMatrixList(VegetationItemLOD1MatrixList); DisposeMatrixList(VegetationItemLOD2MatrixList); DisposeMatrixList(VegetationItemLOD3MatrixList); DisposeMatrixList(VegetationItemLOD0ShadowMatrixList); DisposeMatrixList(VegetationItemLOD1ShadowMatrixList); DisposeMatrixList(VegetationItemLOD2ShadowMatrixList); DisposeMatrixList(VegetationItemLOD3ShadowMatrixList); DisposeVector4List(VegetationItemLOD0LodFadeList); DisposeVector4List(VegetationItemLOD1LodFadeList); DisposeVector4List(VegetationItemLOD2LodFadeList); DisposeVector4List(VegetationItemLOD3LodFadeList); } void DisposeMatrixList(List> list) { for (int i = 0; i <= list.Count - 1; i++) { list[i].Dispose(); } } void DisposeMatrixInstanceList(List> list) { for (int i = 0; i <= list.Count - 1; i++) { list[i].Dispose(); } } void DisposeVector4List(List> list) { for (int i = 0; i <= list.Count - 1; i++) { list[i].Dispose(); } } } [Serializable] public enum VegetationStudioCameraType { Normal, SceneView } [Serializable] public enum CameraCullingMode { Frustum = 0, Complete360 = 1 } [Serializable] public class VegetationStudioCamera { [SerializeField] public Camera SelectedCamera; public VegetationStudioCameraType VegetationStudioCameraType = VegetationStudioCameraType.Normal; public JobCullingGroup JobCullingGroup; public JobCullingGroup BillboardJobCullingGroup; public delegate void MultiOnVegetationCellVisibityChangedDelegate(VegetationStudioCamera vegetationStudioCamera, VegetationCell vegetationCell); public MultiOnVegetationCellVisibityChangedDelegate OnVegetationCellVisibleDelegate; public MultiOnVegetationCellVisibityChangedDelegate OnVegetationCellInvisibleDelegate; public MultiOnVegetationCellVisibityChangedDelegate OnPotentialCellInvisibleDelegate; public delegate void MultiOnVegetationDistanceBandChangeDelegate(VegetationStudioCamera vegetationStudioCamera, VegetationCell vegetationCell, int distanceBand, int previousDistanceBand); public MultiOnVegetationDistanceBandChangeDelegate OnVegetationCellDistanceBandChangeDelegate; public bool RenderDirectToCamera; public bool RenderBillboardsOnly; public CameraCullingMode CameraCullingMode = CameraCullingMode.Frustum; public VegetationSystemPro VegetationSystemPro; private Vector3 _potentialCellsCenterPosition = new Vector3(0,-10000,0); private float _potentialCellPadding = 100; private float _lastVegetationDistance; private bool _dirty; private Vector3 _floatingOriginOffset = new Vector3(0,0,0); public GameObject WindSampler; private JobHandle _currentJobHandle; [NonSerialized] public List VegetationStudioCameraRenderList; [NonSerialized] public List PotentialVisibleVegetationCellList; //[NonSerialized] private List _lastVisibleVegetationCellList; //[NonSerialized] private List _tempVisibleVegetationCellList; public bool Enabled => IsEnabled(); bool IsEnabled() { bool isPlaying = Application.isPlaying; if (VegetationStudioCameraRenderList == null) return false; if (JobCullingGroup == null) return false; if (BillboardJobCullingGroup == null) return false; if (!isPlaying && VegetationStudioCameraType == VegetationStudioCameraType.SceneView) { return true; } if (VegetationStudioCameraType == VegetationStudioCameraType.Normal && !isPlaying) { return false; } if (SelectedCamera == null) { Debug.Log("no camera"); return false; } return (SelectedCamera && SelectedCamera.enabled && SelectedCamera.gameObject.activeInHierarchy); } public VegetationStudioCamera(Camera selectedCamera) { SelectedCamera = selectedCamera; } Vector3 GetCameraPosition() { return SelectedCamera.transform.position - _floatingOriginOffset; } public void SetFloatingOriginOffset(Vector3 floatingOriginOffset) { _floatingOriginOffset = floatingOriginOffset; JobCullingGroup?.SetFloatingOriginOffset(floatingOriginOffset); BillboardJobCullingGroup?.SetFloatingOriginOffset(floatingOriginOffset); } public VegetationStudioCamera(VegetationStudioCameraType vegetationStudioCameraType) { #if UNITY_EDITOR if (vegetationStudioCameraType == VegetationStudioCameraType.SceneView) { VegetationStudioCameraType = vegetationStudioCameraType; SelectedCamera = SceneViewDetector.GetCurrentSceneViewCamera(); SceneViewDetector.OnChangedSceneViewCameraDelegate += OnChangedSceneViewCameraDelegate; } #endif } void OnChangedSceneViewCameraDelegate(Camera camera) { SelectedCamera = camera; Dispose(); } public void SetDirty() { _dirty = true; } public void PreCullVegetation(bool forceUpdate) { if (!SelectedCamera) return; if (JobCullingGroup == null) CreateCullingGroup(); if (BillboardJobCullingGroup == null) CreateBillboardCullingGroup(); UpdatePotentialVisibleCells(forceUpdate); JobCullingGroup.CameraCullingMode = CameraCullingMode; BillboardJobCullingGroup.CameraCullingMode = CameraCullingMode; BillboardJobCullingGroup.AddShadowCells = false; } public JobHandle ScheduleCullVegetationJob(JobHandle dependsOn) { if (JobCullingGroup == null) return default(JobHandle); _currentJobHandle = JobCullingGroup.Cull(dependsOn); _currentJobHandle = BillboardJobCullingGroup.Cull(_currentJobHandle); return _currentJobHandle; } public void ProcessEvents() { //ProcessPotentialVegetationCellsVisibilityEvents(); JobCullingGroup?.ProcessEvents(); JobCullingGroup?.ProcessDistanceBandEvents(); } public void PrepareRenderLists(List vegetationSystemProList) { if (!ValidateVegetationStudioCameraRenderList(vegetationSystemProList)) { DisposeVegetationStudioCameraRenderList(); } if (VegetationStudioCameraRenderList == null) { VegetationStudioCameraRenderList = new List(vegetationSystemProList.Count); for (int i = 0; i <= vegetationSystemProList.Count - 1; i++) { VegetationStudioCameraRenderList.Add(new VegetationStudioCameraRenderList(vegetationSystemProList[i].VegetationInfoList.Count)); } } } void UpdatePotentialVisibleCells(bool forceUpdate) { Vector3 selectedCameraPosition = GetCameraPosition(); _potentialCellPadding = VegetationSystemPro.VegetationCellSize * 2; bool needsUpdate = forceUpdate; if (PotentialVisibleVegetationCellList == null) { PotentialVisibleVegetationCellList = new List(); needsUpdate = true; } float distance = Vector3.Distance(_potentialCellsCenterPosition, selectedCameraPosition); if (distance > VegetationSystemPro.VegetationCellSize || Math.Abs(_lastVegetationDistance - VegetationSystemPro.VegetationSettings.GetTreeDistance()) > 0.1f || _dirty) { needsUpdate = true; _potentialCellsCenterPosition = selectedCameraPosition; _lastVegetationDistance = VegetationSystemPro.VegetationSettings.GetTreeDistance(); } if (needsUpdate) { _dirty = false; // if (_lastVisibleVegetationCellList == null) _lastVisibleVegetationCellList = new List(); // for (int i = 0; i <= JobCullingGroup.VisibleCellIndexList.Length - 1; i++) // { // _lastVisibleVegetationCellList.Add(PotentialVisibleCellList[JobCullingGroup.VisibleCellIndexList[i]]); // } JobCullingGroup.VisibleCellIndexList.Clear(); Profiler.BeginSample("Potential Cell Selection"); float areaSize = VegetationSystemPro.VegetationSettings.GetTreeDistance() * 2 + _potentialCellPadding * 2; Vector2 position = new Vector2(selectedCameraPosition.x - areaSize / 2f, selectedCameraPosition.z - areaSize / 2f); Rect selectedAreaRect = new Rect(position, new Vector2(areaSize, areaSize)); if (OnPotentialCellInvisibleDelegate != null) { for (int i = 0; i <= PotentialVisibleVegetationCellList.Count - 1; i++) { VegetationCell vegetationCell = PotentialVisibleVegetationCellList[i]; if (!vegetationCell.Rectangle.Overlaps(selectedAreaRect)) { OnPotentialCellInvisibleDelegate(this, vegetationCell); } } } PotentialVisibleVegetationCellList.Clear(); VegetationSystemPro.VegetationCellQuadTree.Query(selectedAreaRect, PotentialVisibleVegetationCellList); Profiler.EndSample(); UpdateCullingGroup(); if (VegetationSystemPro.LoadPotentialVegetationCells) { VegetationSystemPro.PredictiveCellLoader.ClearNonImportant(); VegetationSystemPro.PredictiveCellLoader.PreloadArea(PotentialVisibleVegetationCellList, false); } } } void CreateCullingGroup() { JobCullingGroup?.Dispose(); JobCullingGroup = new JobCullingGroup { TargetCamera = SelectedCamera }; JobCullingGroup.OnStateChanged += OnStateChanged; JobCullingGroup.OnDistanceBandStateChanged += OnDistanceBandStateChanged; } void CreateBillboardCullingGroup() { BillboardJobCullingGroup?.Dispose(); BillboardJobCullingGroup = new JobCullingGroup { TargetCamera = SelectedCamera }; //BillboardJobCullingGroup.OnStateChanged += OnStateChanged; UpdateBillboardCullingGroup(); } public void UpdateBillboardCullingGroup() { if (BillboardJobCullingGroup == null) return; var currentBillboardCellSize = VegetationSystemPro.BillboardCellSize; if (!Application.isPlaying) { currentBillboardCellSize = 200; } BillboardJobCullingGroup.DistanceBandList.Clear(); BillboardJobCullingGroup.DistanceBandList.Add(VegetationSystemPro.VegetationSettings.GetBillboardDistance() + currentBillboardCellSize); BillboardJobCullingGroup.BundingSphereInfoList.Clear(); if (BillboardJobCullingGroup.BundingSphereInfoList.Capacity < VegetationSystemPro.BillboardCellList.Count) { BillboardJobCullingGroup.BundingSphereInfoList.Capacity = VegetationSystemPro.BillboardCellList.Count; } for (int i = 0; i <= VegetationSystemPro.BillboardCellList.Count - 1; i++) { BoundingSphereInfo boundingSphereInfo = new BoundingSphereInfo { BoundingSphere = VegetationSystemPro.BillboardCellList[i].GetBoundingSphere(), LastVisisbility = (int)BoundingSphereVisibility.Invisible, CurrentDistanceBand = -1, //PreviousDistance = -1, Enabled = 1 }; BillboardJobCullingGroup.BundingSphereInfoList.Add(boundingSphereInfo); } } void UpdateCullingGroup() { JobCullingGroup.DistanceBandList.Clear(); JobCullingGroup.DistanceBandList.Add(VegetationSystemPro.VegetationSettings.GetVegetationDistance() + VegetationSystemPro.VegetationCellSize); JobCullingGroup.DistanceBandList.Add(VegetationSystemPro.VegetationSettings.GetTreeDistance() + VegetationSystemPro.VegetationCellSize); JobCullingGroup.BundingSphereInfoList.Clear(); if (JobCullingGroup.BundingSphereInfoList.Capacity < PotentialVisibleVegetationCellList.Count) { JobCullingGroup.BundingSphereInfoList.Capacity = PotentialVisibleVegetationCellList.Count; } float additionalBoundingSphereRadius = VegetationSystemPro.AdditionalBoundingSphereRadius; for (int i = 0; i <= PotentialVisibleVegetationCellList.Count - 1; i++) { BoundingSphere boundingSphere = PotentialVisibleVegetationCellList[i].GetBoundingSphere(); boundingSphere.radius += additionalBoundingSphereRadius; BoundingSphereInfo boundingSphereInfo = new BoundingSphereInfo { BoundingSphere = boundingSphere, LastVisisbility = 0,//(int)BoundingSphereVisibility.Invisible, CurrentDistanceBand = -1, PreviousDistanceBand = -1, Enabled = PotentialVisibleVegetationCellList[i].EnabledInt }; JobCullingGroup.BundingSphereInfoList.Add(boundingSphereInfo); } } // private void ProcessPotentialVegetationCellsVisibilityEvents() // { // if (_lastVisibleVegetationCellList.Count == 0) return; // if (_tempVisibleVegetationCellList == null) _tempVisibleVegetationCellList = new List(); // // for (int i = 0; i <= JobCullingGroup.VisibleCellIndexList.Length - 1; i++) // { // _tempVisibleVegetationCellList.Add(PotentialVisibleCellList[JobCullingGroup.VisibleCellIndexList[i]]); // } // // for (int i = 0; i <= _lastVisibleVegetationCellList.Count - 1; i++) // { // if (!_tempVisibleVegetationCellList.Contains(_lastVisibleVegetationCellList[i])) ; // { // OnVegetationCellInvisibleDelegate?.Invoke(this, _lastVisibleVegetationCellList[i]); // } // } // _lastVisibleVegetationCellList.Clear(); // _tempVisibleVegetationCellList.Clear(); // } public BoundingSphereInfo GetBoundingSphereInfo(int potentialVisibleVegetationCellIndex) { return JobCullingGroup.BundingSphereInfoList[potentialVisibleVegetationCellIndex]; } void OnStateChanged(JobCullingGroupEvent sphere) { if (sphere.IsVisible ) { OnVegetationCellVisibleDelegate?.Invoke(this, PotentialVisibleVegetationCellList[sphere.Index]); } else { OnVegetationCellInvisibleDelegate?.Invoke(this, PotentialVisibleVegetationCellList[sphere.Index]); } } void OnDistanceBandStateChanged(JobCullingGroupEvent sphere) { OnVegetationCellDistanceBandChangeDelegate?.Invoke(this, PotentialVisibleVegetationCellList[sphere.Index], sphere.CurrentDistanceBand, sphere.PreviousDistanceBand); } void DisposeVegetationStudioCameraRenderList() { if (VegetationStudioCameraRenderList != null) { for (int i = 0; i <= VegetationStudioCameraRenderList.Count - 1; i++) { VegetationStudioCameraRenderList[i].Dispose(); } VegetationStudioCameraRenderList.Clear(); } VegetationStudioCameraRenderList = null; } bool ValidateVegetationStudioCameraRenderList(List vegetationPackageProList) { if (VegetationStudioCameraRenderList?.Count != vegetationPackageProList.Count) return false; for (int i = 0; i <= VegetationStudioCameraRenderList.Count - 1; i++) { if (VegetationStudioCameraRenderList[i].VegetationItemMergeMatrixList.Count != vegetationPackageProList[i].VegetationInfoList.Count) { return false; } } return true; } public void Dispose() { PotentialVisibleVegetationCellList?.Clear(); JobCullingGroup?.Dispose(); JobCullingGroup = null; BillboardJobCullingGroup?.Dispose(); BillboardJobCullingGroup = null; DisposeVegetationStudioCameraRenderList(); _potentialCellsCenterPosition = new Vector3(0,-10000,0); } public void RemoveDelegates() { #if UNITY_EDITOR if (VegetationStudioCameraType == VegetationStudioCameraType.SceneView) { // ReSharper disable once DelegateSubtraction SceneViewDetector.OnChangedSceneViewCameraDelegate -= OnChangedSceneViewCameraDelegate; } #endif } public void DrawVisibleCellGizmos() { if (JobCullingGroup == null) return; Gizmos.color = Color.white; for (int i = 0; i <= JobCullingGroup.VisibleCellIndexList.Length - 1; i++) { int index = JobCullingGroup.VisibleCellIndexList[i]; if (PotentialVisibleVegetationCellList[index].Enabled) { Gizmos.color = GetDistanceBandColor(JobCullingGroup.BundingSphereInfoList[index].CurrentDistanceBand); Gizmos.DrawWireCube(PotentialVisibleVegetationCellList[index].VegetationCellBounds.center, PotentialVisibleVegetationCellList[index].VegetationCellBounds.size); } } //for (int i = 0; i <= _boundingsphereList.Count - 1; i++) //{ // Gizmos.DrawWireSphere(_boundingsphereList[i].position, _boundingsphereList[i].radius); //} } private Color GetDistanceBandColor(int distanceBand) { switch (distanceBand) { case 0: return Color.yellow; case 1: return Color.red; } return Color.white; } public void DrawVisibleBillboardCellGizmos() { if (BillboardJobCullingGroup == null) return; Gizmos.color = Color.green; for (int i = 0; i <= BillboardJobCullingGroup.VisibleCellIndexList.Length - 1; i++) { int index = BillboardJobCullingGroup.VisibleCellIndexList[i]; BillboardCell billboardCell = VegetationSystemPro.BillboardCellList[index]; Gizmos.DrawWireCube(billboardCell.BilllboardCellBounds.center, billboardCell.BilllboardCellBounds.size); } } public void DrawPotentialCellGizmos() { if (PotentialVisibleVegetationCellList == null) return; Gizmos.color = Color.green; //for (int i = 0; i <= JobCullingGroup.VisibleCellIndexList.Length - 1; i++) //{ // int index = JobCullingGroup.VisibleCellIndexList[i]; // BoundingSphereInfo boundingSphereInfo = JobCullingGroup.BundingSphereInfoList[index]; // if (boundingSphereInfo.CurrentDistanceBand == -1) // { // Gizmos.color = Color.red; // Gizmos.DrawWireCube(PotentialVisibleCellList[index].VegetationCellBounds.center, // PotentialVisibleCellList[index].VegetationCellBounds.size); // } //} //return; for (int i = 0; i <= PotentialVisibleVegetationCellList.Count - 1; i++) { //BoundingSphereInfo boundingSphereInfo = JobCullingGroup.BundingSphereInfoList[i]; //if (boundingSphereInfo.CurrentDistanceBand == -1) //{ // Gizmos.color = Color.red; // Gizmos.DrawWireCube(PotentialVisibleCellList[i].VegetationCellBounds.center, // PotentialVisibleCellList[i].VegetationCellBounds.size); //} if (PotentialVisibleVegetationCellList[i].Enabled) { //if (PotentialVisibleCellList[i].Loaded) if (PotentialVisibleVegetationCellList[i].LoadedDistanceBand == 0) { Gizmos.color = Color.red; } else if (PotentialVisibleVegetationCellList[i].LoadedDistanceBand == 1) { Gizmos.color = Color.white; } else { Gizmos.color = Color.green; } Gizmos.DrawWireCube(PotentialVisibleVegetationCellList[i].VegetationCellBounds.center, PotentialVisibleVegetationCellList[i].VegetationCellBounds.size); } } } } }