using System; using System.Collections.Generic; using AwesomeTechnologies.BillboardSystem; using AwesomeTechnologies.Utility; using AwesomeTechnologies.Utility.Culling; using AwesomeTechnologies.Utility.Quadtree; using AwesomeTechnologies.Vegetation.Masks; using AwesomeTechnologies.Vegetation.PersistentStorage; using AwesomeTechnologies.VegetationStudio; using AwesomeTechnologies.VegetationSystem.Wind; using Unity.Collections; using Unity.Jobs; #if UNITY_EDITOR using UnityEditor; #endif using UnityEngine; using UnityEngine.Profiling; using UnityEngine.Rendering; namespace AwesomeTechnologies.VegetationSystem { [AwesomeTechnologiesScriptOrder(100)] [ExecuteInEditMode] public partial class VegetationSystemPro : MonoBehaviour { public QuadTree VegetationCellQuadTree; public QuadTree BillboardCellQuadTree; [NonSerialized] public readonly List VegetationCellList = new List(); [NonSerialized] public readonly List BillboardCellList = new List(); [NonSerialized] public readonly List LoadedVegetationCellList = new List(); [NonSerialized] public readonly List ProcessInstancedIndirectCellList = new List(); [NonSerialized] public readonly List CompactMemoryCellList = new List(); [NonSerialized] public readonly List PredictiveCellLoaderList = new List(); public VegetationCellSpawner VegetationCellSpawner = new VegetationCellSpawner(); public Bounds VegetationSystemBounds; public bool AutomaticBoundsCalculation = true; public PersistentVegetationStorage PersistentVegetationStorage; public PredictiveCellLoader PredictiveCellLoader; public int PredictiveCellLoaderCellsPerFrame = 1; public bool LoadPotentialVegetationCells = true; public int CurrentTabIndex; public int VegetationPackageIndex; public float SeaLevel; public bool ExcludeSeaLevelCells; public float VegetationCellSize = 100; public float BillboardCellSize = 500; public bool UseCacheCompacter = false; [NonSerialized] public float AdditionalBoundingSphereRadius; public int SelectedTextureMaskGroupTextureIndex; public int SelectedTextureMaskGroupIndex; public TextureMask DebugTextureMask; [NonSerialized] public bool InitDone; private JobHandle _prepareVegetationHandle; public VegetationSettings VegetationSettings = new VegetationSettings(); public VegetationRenderSettings VegetationRenderSettings = new VegetationRenderSettings(); public EnvironmentSettings EnvironmentSettings = new EnvironmentSettings(); public List VegetationStudioCameraList = new List(); public bool ShowVegetationCells; public bool ShowBillboardCells; public bool ShowVisibleBillboardCells; public bool ShowPotentialVisibleCells; public bool ShowVisibleCells; public bool ShowBiomeCells; public bool ShowVegetationMaskCells; public bool ShowHeatMap; public bool ShowTerrainTextures = true; public bool ShowLODDebug = false; public bool ShowVegetationPackageGeneralSettingsMenu = true; public bool ShowVegetationPackageNoiseMenu = true; public bool ShowTerrainTextureRulesMenu = true; public bool ShowTextureMaskRulesMenu = true; public bool ShowVegetationMaskRulesMenu = true; public bool ShowShaderSettingsMenu = true; public bool ShowPositionMenu = true; public bool ShowDistanceFalloffMenu = true; public bool ShowBiomeRulesMenu = true; public bool ShowConcaveLocationRulesMenu = true; public bool ShowColliderRulesMenu = true; public bool ShowBillboardsMenu = true; public bool ShowVegetationItemSettingsMenu = true; public bool ShowTerrainSourceSettingsMenu = true; public bool ShowAddVegetationItemMenu = true; public bool ShowLODMenu = true; //public bool ForceBillboardCellLoadinComplete = false; public List VegetationStudioTerrainList = new List(); public List VegetationStudioTerrainObjectList = new List(); public List VegetationPackageProList = new List(); public List VegetationPackageProModelsList = new List(); public List WindControllerSettingsList = new List(); public delegate void MultiOnAddCameraDelegate(VegetationStudioCamera vegetationStudioCamera); public MultiOnAddCameraDelegate OnAddCameraDelegate; public delegate void MultiOnRemoveCameraDelegate(VegetationStudioCamera vegetationStudioCamera); public MultiOnRemoveCameraDelegate OnRemoveCameraDelegate; public delegate void MultiOnVegetationStudioRefreshDelegate(VegetationSystemPro vegetationSystemPro); public MultiOnVegetationStudioRefreshDelegate OnRefreshVegetationSystemDelegate; public MultiOnVegetationStudioRefreshDelegate OnRefreshColliderSystemDelegate; public MultiOnVegetationStudioRefreshDelegate OnRefreshRuntimePrefabSpawnerDelegate; public delegate void MultiOnClearCacheDelegate(VegetationSystemPro vegetationSystemPro); public delegate void MultiOnClearCacheVegetationCellDelegate(VegetationSystemPro vegetationSystemPro, VegetationCell vegetationCell); public delegate void MultiOnClearCacheVegetationItemDelegate(VegetationSystemPro vegetationSystemPro, int vegetationPackageIndex, int vegetationItemIndex); public delegate void MultiOnClearCacheVegetationCellVegetationItemDelegate( VegetationSystemPro vegetationSystemPro, VegetationCell vegetationCell, int vegetationPackageIndex, int vegetationItemIndex); public delegate void MultiOnVegetationCellSpawnedDelegate(VegetationCell vegetationCell); public MultiOnVegetationCellSpawnedDelegate OnVegetationCellLoaded; public MultiOnClearCacheDelegate OnClearCacheDelegate; public MultiOnClearCacheVegetationItemDelegate OnClearCacheVegetationItemDelegate; public MultiOnClearCacheVegetationCellDelegate OnClearCacheVegetationCellDelegate; public MultiOnClearCacheVegetationCellVegetationItemDelegate OnClearCacheVegetationCellVegetatonItemDelegate; public delegate void MultOnRenderCompleteDelegate(VegetationSystemPro vegetationSystemPro); public MultOnRenderCompleteDelegate OnRenderCompleteDelegate; [NonSerialized] private readonly List _windControllerList = new List(); public WindZone SelectedWindZone; public float WindSpeedFactor = 1f; public Light SunDirectionalLight; // ReSharper disable once UnusedMember.Local //private Texture _dummyMaskTexture; private ComputeBuffer _dummyComputeBuffer; public int FrustumKernelHandle; //public int DistanceKernelHandle; public ComputeShader FrusumMatrixShader; private int _cameraFrustumPlan0; private int _cameraFrustumPlan1; private int _cameraFrustumPlan2; private int _cameraFrustumPlan3; private int _cameraFrustumPlan4; private int _cameraFrustumPlan5; public int MergeBufferKernelHandle; public ComputeShader MergeBufferShader; private int _floatingOriginOffsetID = -1; private int _mergeBufferID = -1; private int _mergeSourceBuffer0ID = -1; private int _mergeSourceBuffer1ID = -1; private int _mergeSourceBuffer2ID = -1; private int _mergeSourceBuffer3ID = -1; private int _mergeSourceBuffer4ID = -1; private int _mergeSourceBuffer5ID = -1; private int _mergeSourceBuffer6ID = -1; private int _mergeSourceBuffer7ID = -1; private int _mergeSourceBuffer8ID = -1; private int _mergeSourceBuffer9ID = -1; private int _mergeSourceBuffer10ID = -1; private int _mergeSourceBuffer11ID = -1; private int _mergeSourceBuffer12ID = -1; private int _mergeSourceBuffer13ID = -1; private int _mergeSourceBuffer14ID = -1; private int _mergeInstanceCount0ID = -1; private int _mergeInstanceCount1ID = -1; private int _mergeInstanceCount2ID = -1; private int _mergeInstanceCount3ID = -1; private int _mergeInstanceCount4ID = -1; private int _mergeInstanceCount5ID = -1; private int _mergeInstanceCount6ID = -1; private int _mergeInstanceCount7ID = -1; private int _mergeInstanceCount8ID = -1; private int _mergeInstanceCount9ID = -1; private int _mergeInstanceCount10ID = -1; private int _mergeInstanceCount11ID = -1; private int _mergeInstanceCount12ID = -1; private int _mergeInstanceCount13ID = -1; private int _mergeInstanceCount14ID = -1; private int _visibleBufferLod0ID = -1; private int _visibleBufferLod1ID = -1; private int _visibleBufferLod2ID = -1; private int _visibleBufferLod3ID = -1; private int _shadowBufferLod0ID = -1; private int _shadowBufferLod1ID = -1; private int _shadowBufferLod2ID = -1; private int _shadowBufferLod3ID = -1; private int _sourceBufferID = -1; private int _instanceCountID = -1; private int _boundingSphereRadiusID = -1; private int _useLodsID = -1; private int _noFrustumCullingID = -1; private int _shadowCullingID = -1; private int _cullFarStartID; private int _visibleShaderDataBufferID; private int _indirectShaderDataBufferID; private int _cameraPositionID; private int _cullDistanceID; private int _farCullDistanceID; private static readonly int _nearFadeDistanceID = Shader.PropertyToID("_NearFadeDistance"); private int _unityLODFadeID; private int _lod1Distance = -1; private int _lod2Distance = -1; private int _lod3Distance = -1; private int _lightDirection = -1; private int _planeOrigin = -1; private int _boundsSize = -1; private int _lodFactor = -1; private int _lodBias = -1; private int _lodFadeDistance = -1; private int _lodCount = -1; private readonly List _hasBufferList = new List(); private readonly Matrix4x4[] _renderArray = new Matrix4x4[1000]; private readonly float[] _renderingLayerArray = new float[1000]; private readonly Vector4[] _renderLodFadeArray = new Vector4[1000]; //private Vector4[] _lodFadeSingleArray = new Vector4[1]; //Floating origin variables public Transform FloatingOriginAnchor; public Vector3 FloatingOriginOffset; public Vector3 FloatingOriginStartPosition; private static readonly int UnityRenderingLayerID = Shader.PropertyToID("unity_RenderingLayer"); public Vector3 VegetationSystemPosition { get { Vector3 position = VegetationSystemBounds.center - VegetationSystemBounds.extents; position.y = 0; return position; } } public void DetectPersistentVegetationStorage() { if (!PersistentVegetationStorage) { PersistentVegetationStorage = GetComponent(); } } // ReSharper disable once UnusedMember.Local void Reset() { AutoSelectCamera(); FindWindZone(); FindDirectionalLight(); DetectPersistentVegetationStorage(); } void FindDirectionalLight() { if (SunDirectionalLight) return; Light selectedLight = null; float intensity = float.MinValue; Light[] lights = FindObjectsOfType(); for (int i = 0; i <= lights.Length - 1; i++) { if (lights[i].type == LightType.Directional) { if (lights[i].intensity > intensity) { intensity = lights[i].intensity; selectedLight = lights[i]; } } } SunDirectionalLight = selectedLight; } void AutoSelectCamera() { Camera selectedCamera = Camera.main; if (selectedCamera == null) { Camera[] cameras = FindObjectsOfType(); for (int i = 0; i <= cameras.Length - 1; i++) { if (cameras[i].gameObject.name.Contains("Main Camera") || cameras[i].gameObject.name.Contains("MainCamera")) { selectedCamera = cameras[i]; break; } } } AddCamera(selectedCamera); } public void RefreshVegetationSystem() { SetupVegetationSystem(); } public void RefreshColliderSystem() { OnRefreshColliderSystemDelegate?.Invoke(this); } public void RefreshRuntimePrefabSpawner() { OnRefreshRuntimePrefabSpawnerDelegate?.Invoke(this); } void SetupVegetationSystem() { CompleteCellLoading(); ProcessInstancedIndirectCellList.Clear(); DisposeVegetationStudioCameras(); DisposeVegetationCells(); DisposeBillboardCells(); if (VegetationSystemBounds.size.magnitude < 1) return; SetupWindSamplers(); SetupVegetationItemModels(); RefreshVegetationStudioTerrains(); CreateVegetationCells(); CreateBillboardCells(); SetVegetationStudioCamerasDirty(); RefreshMaterials(); OnRefreshVegetationSystemDelegate?.Invoke(this); } public void CompleteCellLoading() { _prepareVegetationHandle.Complete(); } // ReSharper disable once UnusedMember.Local void OnEnable() { VegetationStudioManager.RegisterVegetationSystem(this); DetectPersistentVegetationStorage(); FindDirectionalLight(); EnableEditorApi(); LoadSettingsFromQualityManager(); SetupPredictiveCellLoader(); SetupVegetationCellSpawner(); SetupSceneviewCamera(); SetupFloatingOrigin(); SetupComputeShaders(); SetupBillboardShaderIDs(); SetupInstancedRenderMaterialPropertiesIDs(); SetupRenderingLayerData(); SetupVegetationSystem(); VegetationCellSpawner.Init(); SetupWind(); InitDone = true; } public void SetupRenderingLayerData() { int flags = VegetationRenderSettings.RenderingLayerMask; float flagsFloat = BitConverter.ToSingle(System.BitConverter.GetBytes(flags), 0); for (int i = 0; i < _renderingLayerArray.Length; i++) { _renderingLayerArray[i] = flagsFloat; } Shader.SetGlobalFloat("VSPRenderingLayerMask", flagsFloat); } void LoadSettingsFromQualityManager() { if (!Application.isPlaying) { return; } QualityManager qualityManager = GetComponent(); if (qualityManager) { qualityManager.SetQualityLevel(false); } } void EnableEditorApi() { #if UNITY_EDITOR if (!Application.isPlaying) { SceneViewDetector.OnSceneViewTransformChangeDelegate += OnSceneviewTransformChanged; } #endif } void DisableEditorApi() { #if UNITY_EDITOR if (!Application.isPlaying) { // ReSharper disable once DelegateSubtraction SceneViewDetector.OnSceneViewTransformChangeDelegate -= OnSceneviewTransformChanged; } #endif } void OnSceneviewTransformChanged(Camera currentCamera) { #if UNITY_EDITOR EditorUtility.SetDirty(this); #endif } void SetupSceneviewCamera() { for (int i = VegetationStudioCameraList.Count - 1; i >= 0; i--) { if (VegetationStudioCameraList[i].VegetationStudioCameraType == VegetationStudioCameraType.SceneView || VegetationStudioCameraList[i].SelectedCamera == null) { RemoveVegetationStudioCamera(VegetationStudioCameraList[i]); } } if (!Application.isPlaying) { VegetationStudioCamera sceneviewCamera = new VegetationStudioCamera(VegetationStudioCameraType.SceneView) { CameraCullingMode = CameraCullingMode.Frustum, RenderDirectToCamera = false, VegetationSystemPro = this }; AddVegetationStudioCamera(sceneviewCamera); //VegetationStudioCameraList.Add(sceneviewCamera); } if (Application.isPlaying && VegetationStudioCameraList.Count == 0) { AutoSelectCamera(); } } void SetupFloatingOrigin() { Transform anchor = GetFloatingOriginAnchor(); FloatingOriginStartPosition = anchor.position; } void UpdateFloatingOrigin() { if (Application.isPlaying) { Transform anchor = GetFloatingOriginAnchor(); FloatingOriginOffset = anchor.transform.position - FloatingOriginStartPosition; } else { FloatingOriginOffset = Vector3.zero; } } Transform GetFloatingOriginAnchor() { if (FloatingOriginAnchor) return FloatingOriginAnchor; return transform; } // void PrepareRenderLists() // { // for (int i = 0; i <= VegetationStudioCameraList.Count - 1; i++) // { // VegetationStudioCameraList[i].PrepareRenderLists(VegetationPackageProList); // } // } // ReSharper disable once UnusedMember.Local void Update() { if (!InitDone) return; UpdateFloatingOrigin(); if (VegetationCellList.Count <= 0) return; JobHandle cullingHandle = default(JobHandle); for (int i = 0; i <= VegetationStudioCameraList.Count - 1; i++) { VegetationStudioCameraList[i].SetFloatingOriginOffset(FloatingOriginOffset); VegetationStudioCameraList[i].PreCullVegetation(false); VegetationStudioCameraList[i].PrepareRenderLists(VegetationPackageProList); } //PrepareRenderLists(); for (int i = 0; i <= VegetationStudioCameraList.Count - 1; i++) { if (!VegetationStudioCameraList[i].Enabled) continue; cullingHandle = VegetationStudioCameraList[i].ScheduleCullVegetationJob(cullingHandle); } Profiler.BeginSample("VegetationCell culling"); cullingHandle.Complete(); Profiler.EndSample(); Profiler.BeginSample("Process culling events"); for (int i = 0; i <= VegetationStudioCameraList.Count - 1; i++) { if (!VegetationStudioCameraList[i].Enabled) continue; VegetationStudioCameraList[i].ProcessEvents(); } Profiler.EndSample(); VerifySplatmapAccess(); Profiler.BeginSample("Prepare cell loading jobs"); VegetationCellSpawner.CellJobHandleList.Clear(); float worldspaceMinHeight = VegetationSystemBounds.center.y - VegetationSystemBounds.extents.y; float worldspaceSeaLevel = worldspaceMinHeight + SeaLevel; VegetationCellSpawner.WorldspaceSeaLevel = worldspaceSeaLevel; PredictiveCellLoaderList.Clear(); PredictiveCellLoader.GetCellsToLoad(PredictiveCellLoaderList); for (int i = 0; i <= PredictiveCellLoaderList.Count - 1; i++) { VegetationCell vegetationCell = PredictiveCellLoaderList[i]; bool hasData = vegetationCell.LoadedDistanceBand != 99; bool hasInstancedIndirect; JobHandle vegetationCellHandle = VegetationCellSpawner.SpawnVegetationCell(vegetationCell, 0, out hasInstancedIndirect, false); OnVegetationCellLoaded?.Invoke(vegetationCell); if (!hasData) { if (!LoadedVegetationCellList.Contains(vegetationCell)) { LoadedVegetationCellList.Add(vegetationCell); } } if (hasInstancedIndirect) { ProcessInstancedIndirectCellList.Add(vegetationCell); } VegetationCellSpawner.CellJobHandleList.Add(vegetationCellHandle); } for (int i = 0; i <= VegetationStudioCameraList.Count - 1; i++) { if (!VegetationStudioCameraList[i].Enabled) continue; for (int j = 0; j <= VegetationStudioCameraList[i].JobCullingGroup.VisibleCellIndexList.Length - 1; j++) { int potentialVisibleCellIndex = VegetationStudioCameraList[i].JobCullingGroup.VisibleCellIndexList[j]; VegetationCell vegetationCell = VegetationStudioCameraList[i] .PotentialVisibleVegetationCellList[potentialVisibleCellIndex]; BoundingSphereInfo boundingSphereInfo = VegetationStudioCameraList[i].GetBoundingSphereInfo(potentialVisibleCellIndex); if (vegetationCell.LoadedDistanceBand <= boundingSphereInfo.CurrentDistanceBand) continue; // if (!Application.isPlaying && !vegetationCell.Prepared) // { // VegetationCellSpawner.PrepareVegetationCell(vegetationCell); // } // ReSharper disable once InlineOutVariableDeclaration bool hasInstancedIndirect; JobHandle vegetationCellHandle = VegetationCellSpawner.SpawnVegetationCell(vegetationCell, boundingSphereInfo.CurrentDistanceBand, out hasInstancedIndirect, false); OnVegetationCellLoaded?.Invoke(vegetationCell); LoadedVegetationCellList.Add(vegetationCell); if (hasInstancedIndirect) { ProcessInstancedIndirectCellList.Add(vegetationCell); } VegetationCellSpawner.CellJobHandleList.Add(vegetationCellHandle); } } _prepareVegetationHandle = JobHandle.CombineDependencies(VegetationCellSpawner.CellJobHandleList); Profiler.EndSample(); JobHandle.ScheduleBatchedJobs(); Profiler.BeginSample("Prepare render list jobs"); float lodBias = QualitySettings.lodBias * VegetationSettings.LODDistanceFactor; Vector3 sunLightDirection = SunDirectionalLight ? SunDirectionalLight.transform.forward : new Vector3(0, 0, 0); float minBoundsHeight = VegetationSystemBounds.center.y - VegetationSystemBounds.extents.y; Vector3 planeOrigin = new Vector3(0, minBoundsHeight, 0); bool shadowCulling = (SunDirectionalLight != null); bool isPlaying = Application.isPlaying; VegetationCellSpawner.CellJobHandleList.Clear(); for (int i = 0; i <= VegetationStudioCameraList.Count - 1; i++) { if (!VegetationStudioCameraList[i].Enabled) continue; if (VegetationStudioCameraList[i].RenderBillboardsOnly) continue; for (int j = 0; j <= VegetationPackageProList.Count - 1; j++) { for (int k = 0; k <= VegetationPackageProList[j].VegetationInfoList.Count - 1; k++) { NativeList vegetationItemMatrixList = VegetationStudioCameraList[i].VegetationStudioCameraRenderList[j] .VegetationItemMergeMatrixList[k]; vegetationItemMatrixList.Clear(); VegetationItemModelInfo vegetationItemModelInfo = VegetationPackageProModelsList[j].VegetationItemModelList[k]; bool useInstancedIndirect = VegetationRenderSettings.UseInstancedIndirect(); if (useInstancedIndirect && vegetationItemModelInfo.VegetationItemInfo.VegetationRenderMode == VegetationRenderMode.InstancedIndirect) continue; //if (isPlaying && vegetationItemModelInfo.VegetationItemInfo.VegetationRenderMode == // VegetationRenderMode.InstancedIndirect) continue; //if (!vegetationItemModelInfo.VegetationItemInfo.EnableRuntimeSpawn) continue; JobHandle vegetationItemMergeJobHandle = _prepareVegetationHandle; for (int l = 0; l <= VegetationStudioCameraList[i].JobCullingGroup.VisibleCellIndexList.Length - 1; l++) { int potentialVisibleCellIndex = VegetationStudioCameraList[i].JobCullingGroup.VisibleCellIndexList[l]; VegetationCell vegetationCell = VegetationStudioCameraList[i] .PotentialVisibleVegetationCellList[potentialVisibleCellIndex]; BoundingSphereInfo boundingSphereInfo = VegetationStudioCameraList[i] .GetBoundingSphereInfo(potentialVisibleCellIndex); int vegetationItemDistanceBand = vegetationItemModelInfo.DistanceBand; // if (vegetationItemModelInfo.VegetationItemInfo.VegetationType == VegetationType.Objects) // { // vegetationItemDistanceBand = 0; // } if (boundingSphereInfo.CurrentDistanceBand > vegetationItemDistanceBand) continue; MergeCellInstancesJob mergeCellInstancesJob = new MergeCellInstancesJob { OutputNativeList = vegetationItemMatrixList, InputNativeList = vegetationCell.VegetationPackageInstancesList[j] .VegetationItemMatrixList[k] }; Profiler.BeginSample("Schedule"); vegetationItemMergeJobHandle = mergeCellInstancesJob.Schedule(vegetationItemMergeJobHandle); Profiler.EndSample(); } NativeList lod0MatrixList = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD0MatrixList[k]; NativeList lod1MatrixList = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD1MatrixList[k]; NativeList lod2MatrixList = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD2MatrixList[k]; NativeList lod3MatrixList = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD3MatrixList[k]; NativeList lod0ShadowMatrixList = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD0ShadowMatrixList[k]; NativeList lod1ShadowMatrixList = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD1ShadowMatrixList[k]; NativeList lod2ShadowMatrixList = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD2ShadowMatrixList[k]; NativeList lod3ShadowMatrixList = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD3ShadowMatrixList[k]; NativeList lod0FadeList = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD0LodFadeList[k]; NativeList lod1FadeList = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD1LodFadeList[k]; NativeList lod2FadeList = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD2LodFadeList[k]; NativeList lod3FadeList = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD3LodFadeList[k]; lod0MatrixList.Clear(); lod1MatrixList.Clear(); lod2MatrixList.Clear(); lod3MatrixList.Clear(); lod0ShadowMatrixList.Clear(); lod1ShadowMatrixList.Clear(); lod2ShadowMatrixList.Clear(); lod3ShadowMatrixList.Clear(); lod0FadeList.Clear(); lod1FadeList.Clear(); lod2FadeList.Clear(); lod3FadeList.Clear(); float vegetationItemCullDistance; float renderDistanceFactor = vegetationItemModelInfo.VegetationItemInfo.RenderDistanceFactor; if (VegetationSettings.DisableRenderDistanceFactor) renderDistanceFactor = 1; //if (vegetationItemModelInfo.DistanceBand == 0) if (vegetationItemModelInfo.VegetationItemInfo.VegetationType == VegetationType.Tree || vegetationItemModelInfo.VegetationItemInfo.VegetationType == VegetationType.LargeObjects) { vegetationItemCullDistance = VegetationSettings.GetTreeDistance() * renderDistanceFactor; } else { vegetationItemCullDistance = VegetationSettings.GetVegetationDistance() * renderDistanceFactor; } ShadowCastingMode shadowCastingMode = VegetationSettings.GetShadowCastingMode(vegetationItemModelInfo.VegetationItemInfo .VegetationType); bool useShadowCulling = shadowCulling && shadowCastingMode == ShadowCastingMode.On; useShadowCulling = useShadowCulling && vegetationItemModelInfo.DistanceBand == 1; if (VegetationStudioCameraList[i].SelectedCamera == null) { VegetationCellSpawner.CellJobHandleList.Add(vegetationItemMergeJobHandle); continue; } float crossFadeDistance = 0; if (vegetationItemModelInfo.VegetationItemInfo.EnableCrossFade) { crossFadeDistance = VegetationRenderSettings.CrossFadeDistance; } #if USING_HDRP if (vegetationItemModelInfo.VegetationItemInfo.VegetationRenderMode == VegetationRenderMode.Instanced) { crossFadeDistance = 0; } #endif VegetationItemLODSplitAndFrustumCullingJob lodJob = new VegetationItemLODSplitAndFrustumCullingJob { BoundingSphereRadius = vegetationItemModelInfo.BoundingSphereRadius, BoundsSize = vegetationItemModelInfo.VegetationItemInfo.Bounds.size, VegetationItemDistanceBand = vegetationItemModelInfo.DistanceBand, VegetationItemMatrixList = vegetationItemMatrixList, VegetationItemLOD0MatrixList = lod0MatrixList, VegetationItemLOD1MatrixList = lod1MatrixList, VegetationItemLOD2MatrixList = lod2MatrixList, VegetationItemLOD3MatrixList = lod3MatrixList, VegetationItemLOD0ShadowMatrixList = lod0ShadowMatrixList, VegetationItemLOD1ShadowMatrixList = lod1ShadowMatrixList, VegetationItemLOD2ShadowMatrixList = lod2ShadowMatrixList, VegetationItemLOD3ShadowMatrixList = lod3ShadowMatrixList, LOD0FadeList = lod0FadeList, LOD1FadeList = lod1FadeList, LOD2FadeList = lod2FadeList, LOD3FadeList = lod3FadeList, LightDirection = sunLightDirection, ShadowCulling = useShadowCulling, PlaneOrigin = planeOrigin, FrustumPlanes = VegetationStudioCameraList[i].JobCullingGroup.FrustumPlanes, CameraPosition = VegetationStudioCameraList[i].SelectedCamera.transform.position, NoFrustumCulling = VegetationStudioCameraList[i].CameraCullingMode == CameraCullingMode.Complete360, CullDistance = vegetationItemCullDistance, LOD1Distance = vegetationItemModelInfo.LOD1Distance, LOD2Distance = vegetationItemModelInfo.LOD2Distance, LOD3Distance = vegetationItemModelInfo.LOD3Distance, LODFactor = vegetationItemModelInfo.VegetationItemInfo.LODFactor, LODBias = lodBias, LODCount = vegetationItemModelInfo.LODCount, LODFadeDistance = crossFadeDistance, LODFadePercentage = vegetationItemModelInfo.LODFadePercentage, LODFadeCrossfade = vegetationItemModelInfo.LODFadeCrossfade, FloatingOriginOffset = FloatingOriginOffset }; vegetationItemMergeJobHandle = lodJob.Schedule(vegetationItemMergeJobHandle); VegetationCellSpawner.CellJobHandleList.Add(vegetationItemMergeJobHandle); //#if UNITY_EDITOR // if (JobsUtility.JobDebuggerEnabled) vegetationItemMergeJobHandle.Complete(); //#endif } } } Profiler.EndSample(); if (VegetationCellSpawner.CellJobHandleList.Length > 0) { _prepareVegetationHandle = JobHandle.CombineDependencies(VegetationCellSpawner.CellJobHandleList); } JobHandle.ScheduleBatchedJobs(); //TODO add merge vegetation instances for buillboards here } void SetShadowMapVariables() { if (SunDirectionalLight) { Vector3 sundirection = -SunDirectionalLight.transform.forward * 2.5f; Vector4 gVsSunDirection = new Vector4(sundirection.x, sundirection.y, sundirection.z, SunDirectionalLight.intensity); Shader.SetGlobalVector("gVSSunDirection", gVsSunDirection); Shader.SetGlobalVector("gVSSunSettings", new Vector4(SunDirectionalLight.shadowStrength, SunDirectionalLight.shadowBias, 0, 0)); } else { Shader.SetGlobalVector("gVSSunDirection", Vector4.zero); Shader.SetGlobalVector("gVSSunSettings", new Vector4(0, 0, 0, 0)); } } void InitGlobalShaderProperties() { float minVegetationDistance = Mathf.Clamp(VegetationSettings.GetVegetationDistance(), 20, VegetationSettings.GetVegetationDistance() - 20); Shader.SetGlobalVector("_VSGrassFade", new Vector4(minVegetationDistance, 20, 0, 0)); Shader.SetGlobalVector("_VSShadowMapFadeScale", new Vector4(QualitySettings.shadowDistance - 30, 20, 1, 1)); } // ReSharper disable once UnusedMember.Local void LateUpdate() { if (!InitDone) return; UpdateWind(); SetShadowMapVariables(); InitGlobalShaderProperties(); Profiler.BeginSample("Complete spawning jobs"); _prepareVegetationHandle.Complete(); Profiler.EndSample(); VerifySplatmapAccess(); LoadBillboardCells(); RenderBillboardCells(); JobHandle prepareInstancedIndirectHandle = default(JobHandle); bool useInstancedIndirect = VegetationRenderSettings.UseInstancedIndirect(); if (Application.isPlaying && useInstancedIndirect) { prepareInstancedIndirectHandle = PrepareInstancedIndirectSetupJobs(); } RenderInstancedVegetation(); if (Application.isPlaying && useInstancedIndirect) { prepareInstancedIndirectHandle.Complete(); SetupInstancedIndirectComputeBuffers(); RenderInstancedIndirectVegetation(); } DisposeTemporaryTerrainMemory(); ReturnVegetationCellTemporaryMemory(); if (UseCacheCompacter) { CompactCache(); } OnRenderCompleteDelegate?.Invoke(this); } JobHandle PrepareInstancedIndirectSetupJobs() { VegetationCellSpawner.CellJobHandleList.Clear(); for (int i = 0; i <= ProcessInstancedIndirectCellList.Count - 1; i++) { VegetationCell vegetationCell = ProcessInstancedIndirectCellList[i]; for (int j = 0; j <= vegetationCell.VegetationPackageInstancesList.Count - 1; j++) { for (int k = 0; k <= vegetationCell.VegetationPackageInstancesList[j].VegetationItemMatrixList.Count - 1; k++) { VegetationItemInfoPro vegetationItemInfoPro = VegetationPackageProList[j].VegetationInfoList[k]; IndirectInstanceInfo indirectInstanceInfo = vegetationCell.VegetationPackageInstancesList[j] .VegetationItemInstancedIndirectInstanceList[k]; VegetationItemModelInfo vegetationItemModelInfo = VegetationPackageProModelsList[j].VegetationItemModelList[k]; bool hasTreesLoaded = vegetationCell.LoadedBillboards && vegetationItemInfoPro.UseBillboards && vegetationItemInfoPro.VegetationType == VegetationType.Tree; if (vegetationItemModelInfo.DistanceBand < vegetationCell.LoadedDistanceBand && !hasTreesLoaded) continue; if (vegetationItemInfoPro.VegetationRenderMode == VegetationRenderMode.InstancedIndirect && !indirectInstanceInfo.Created) { NativeArray vegetationItemMatrixList = vegetationCell.VegetationPackageInstancesList[j].VegetationItemMatrixList[k]; indirectInstanceInfo.InstancedIndirectInstanceList = new NativeArray(vegetationItemMatrixList.Length, Allocator.Persistent); indirectInstanceInfo.Created = true; CreateInstancedIndirectInstancesJob createInstancedIndirectInstancesJob = new CreateInstancedIndirectInstancesJob { InstanceList = vegetationItemMatrixList, IndirectInstanceList = indirectInstanceInfo.InstancedIndirectInstanceList }; JobHandle handle = createInstancedIndirectInstancesJob.Schedule( indirectInstanceInfo.InstancedIndirectInstanceList.Length, 32); VegetationCellSpawner.CellJobHandleList.Add(handle); } } } } JobHandle processHandle = JobHandle.CombineDependencies(VegetationCellSpawner.CellJobHandleList); VegetationCellSpawner.CellJobHandleList.Clear(); JobHandle.ScheduleBatchedJobs(); return processHandle; } void SetupInstancedIndirectComputeBuffers() { bool useInstancedIndirect = VegetationRenderSettings.UseInstancedIndirect(); if (!useInstancedIndirect) return; Profiler.BeginSample("Update compute buffers"); for (int i = 0; i <= ProcessInstancedIndirectCellList.Count - 1; i++) { VegetationCell vegetationCell = ProcessInstancedIndirectCellList[i]; for (int j = 0; j <= vegetationCell.VegetationPackageInstancesList.Count - 1; j++) { for (int k = 0; k <= vegetationCell.VegetationPackageInstancesList[j].VegetationItemMatrixList.Count - 1; k++) { VegetationItemInfoPro vegetationItemInfoPro = VegetationPackageProList[j].VegetationInfoList[k]; IndirectInstanceInfo indirectInstanceInfo = vegetationCell.VegetationPackageInstancesList[j] .VegetationItemInstancedIndirectInstanceList[k]; ComputeBufferInfo computeBufferInfo = vegetationCell.VegetationPackageInstancesList[j] .VegetationItemComputeBufferList[k]; VegetationItemModelInfo vegetationItemModelInfo = VegetationPackageProModelsList[j].VegetationItemModelList[k]; //if (vegetationItemModelInfo.DistanceBand < vegetationCell.LoadedDistanceBand) continue; bool hasTreesLoaded = vegetationCell.LoadedBillboards && vegetationItemInfoPro.UseBillboards && vegetationItemInfoPro.VegetationType == VegetationType.Tree; if (vegetationItemModelInfo.DistanceBand < vegetationCell.LoadedDistanceBand && !hasTreesLoaded) continue; if (vegetationItemInfoPro.VegetationRenderMode == VegetationRenderMode.InstancedIndirect && !computeBufferInfo.Created) { int length = indirectInstanceInfo.InstancedIndirectInstanceList.Length; if (length == 0) length = 1; //TODO handle 0 length cells better computeBufferInfo.ComputeBuffer = new ComputeBuffer(length, 16 * 4 + 4 * 4); //*2 computeBufferInfo.ComputeBuffer.SetData(indirectInstanceInfo.InstancedIndirectInstanceList); computeBufferInfo.Created = true; //TODO clear indirectInstanceInfo.InstancedIndirectInstanceList; //might be needed if unity clears buffers } } } } ProcessInstancedIndirectCellList.Clear(); Profiler.EndSample(); } void RenderInstancedIndirectVegetation() { DrawCellsIndirectComputeShader(); } void DisposeTemporaryTerrainMemory() { for (int i = 0; i <= VegetationStudioTerrainList.Count - 1; i++) { VegetationStudioTerrainList[i].DisposeTemporaryMemory(); } } //public List RenderList = new List(); // private readonly byte[] _renderbyteArray = new byte[1000 * 16 * 4]; // private readonly byte[] _renderbyteLodFadeArray = new byte[1000 * 4 * 4]; void RenderInstancedVegetation() { Profiler.BeginSample("Draw instanced vegetation"); bool isPlaying = Application.isPlaying; for (int i = 0; i <= VegetationStudioCameraList.Count - 1; i++) { if (!VegetationStudioCameraList[i].Enabled) continue; if (VegetationStudioCameraList[i].RenderBillboardsOnly) continue; var targetCamera = VegetationStudioCameraList[i].RenderDirectToCamera ? VegetationStudioCameraList[i].SelectedCamera : null; if (!Application.isPlaying) targetCamera = null; for (int j = 0; j <= VegetationPackageProList.Count - 1; j++) { for (int k = 0; k <= VegetationPackageProList[j].VegetationInfoList.Count - 1; k++) { VegetationItemInfoPro vegetationItemInfoPro = VegetationPackageProList[j].VegetationInfoList[k]; bool useInstancedIndirect = VegetationRenderSettings.UseInstancedIndirect(); if (useInstancedIndirect && vegetationItemInfoPro.VegetationRenderMode == VegetationRenderMode.InstancedIndirect) continue; //if (isPlaying && vegetationItemInfoPro.VegetationRenderMode == VegetationRenderMode.InstancedIndirect) continue; //if (!vegetationItemInfoPro.EnableRuntimeSpawn) continue; ShadowCastingMode shadowCastingMode = VegetationSettings.GetShadowCastingMode(vegetationItemInfoPro.VegetationType); if (vegetationItemInfoPro.DisableShadows) { shadowCastingMode = ShadowCastingMode.Off; } LayerMask layer = VegetationSettings.GetLayer(vegetationItemInfoPro.VegetationType); VegetationItemModelInfo vegetationItemModelInfo = VegetationPackageProModelsList[j].VegetationItemModelList[k]; NativeList lod0MatrixList = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD0MatrixList[k]; NativeList lod1MatrixList = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD1MatrixList[k]; NativeList lod2MatrixList = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD2MatrixList[k]; NativeList lod3MatrixList = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD3MatrixList[k]; NativeList lod0Fadeist = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD0LodFadeList[k]; NativeList lod1Fadeist = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD1LodFadeList[k]; NativeList lod2Fadeist = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD2LodFadeList[k]; NativeList lod3Fadeist = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD3LodFadeList[k]; if (vegetationItemInfoPro.VegetationRenderMode == VegetationRenderMode.Normal) { RenderVegetationItemLODDrawMesh(lod0MatrixList, lod0Fadeist, vegetationItemModelInfo, 0, targetCamera, i, shadowCastingMode, layer); RenderVegetationItemLODDrawMesh(lod1MatrixList, lod1Fadeist, vegetationItemModelInfo, 1, targetCamera, i, shadowCastingMode, layer); RenderVegetationItemLODDrawMesh(lod2MatrixList, lod2Fadeist, vegetationItemModelInfo, 2, targetCamera, i, shadowCastingMode, layer); RenderVegetationItemLODDrawMesh(lod3MatrixList, lod3Fadeist, vegetationItemModelInfo, 3, targetCamera, i, shadowCastingMode, layer); } else { RenderVegetationItemLOD(lod0MatrixList, lod0Fadeist, vegetationItemModelInfo, 0, targetCamera, i, shadowCastingMode, layer); RenderVegetationItemLOD(lod1MatrixList, lod1Fadeist, vegetationItemModelInfo, 1, targetCamera, i, shadowCastingMode, layer); RenderVegetationItemLOD(lod2MatrixList, lod2Fadeist, vegetationItemModelInfo, 2, targetCamera, i, shadowCastingMode, layer); RenderVegetationItemLOD(lod3MatrixList, lod3Fadeist, vegetationItemModelInfo, 3, targetCamera, i, shadowCastingMode, layer); } if (shadowCastingMode == ShadowCastingMode.On) { NativeList lod0ShadowMatrixList = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD0ShadowMatrixList[k]; NativeList lod1ShadowMatrixList = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD1ShadowMatrixList[k]; NativeList lod2ShadowMatrixList = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD2ShadowMatrixList[k]; NativeList lod3ShadowMatrixList = VegetationStudioCameraList[i] .VegetationStudioCameraRenderList[j].VegetationItemLOD3ShadowMatrixList[k]; if (vegetationItemInfoPro.VegetationRenderMode == VegetationRenderMode.Normal) { RenderVegetationItemLODDrawMesh(lod0ShadowMatrixList, lod0Fadeist, vegetationItemModelInfo, 0, targetCamera, i, ShadowCastingMode.ShadowsOnly, layer); RenderVegetationItemLODDrawMesh(lod1ShadowMatrixList, lod1Fadeist, vegetationItemModelInfo, 1, targetCamera, i, ShadowCastingMode.ShadowsOnly, layer); RenderVegetationItemLODDrawMesh(lod2ShadowMatrixList, lod2Fadeist, vegetationItemModelInfo, 2, targetCamera, i, ShadowCastingMode.ShadowsOnly, layer); RenderVegetationItemLODDrawMesh(lod3ShadowMatrixList, lod3Fadeist, vegetationItemModelInfo, 3, targetCamera, i, ShadowCastingMode.ShadowsOnly, layer); } else { RenderVegetationItemLOD(lod0ShadowMatrixList, lod0Fadeist, vegetationItemModelInfo, 0, targetCamera, i, ShadowCastingMode.ShadowsOnly, layer); RenderVegetationItemLOD(lod1ShadowMatrixList, lod1Fadeist, vegetationItemModelInfo, 1, targetCamera, i, ShadowCastingMode.ShadowsOnly, layer); RenderVegetationItemLOD(lod2ShadowMatrixList, lod2Fadeist, vegetationItemModelInfo, 2, targetCamera, i, ShadowCastingMode.ShadowsOnly, layer); RenderVegetationItemLOD(lod3ShadowMatrixList, lod3Fadeist, vegetationItemModelInfo, 3, targetCamera, i, ShadowCastingMode.ShadowsOnly, layer); } } } } } Profiler.EndSample(); } void SetupInstancedRenderMaterialPropertiesIDs() { _unityLODFadeID = Shader.PropertyToID("unity_LODFade"); } //float[] _singleFloatArray = new float[1]; void RenderVegetationItemLODDrawMesh(NativeList matrixList, NativeList lodFadeList, VegetationItemModelInfo vegetationItemModelInfo, int lodIndex, Camera targetCamera, int cameraIndex, ShadowCastingMode shadowCastingMode, LayerMask layer) { if (matrixList.Length == 0) return; if (lodIndex >= vegetationItemModelInfo.LODCount) return; Mesh mesh = vegetationItemModelInfo.GetLODMesh(lodIndex); Material[] materials = vegetationItemModelInfo.GetLODMaterials(lodIndex); MaterialPropertyBlock materialPropertyBlock = vegetationItemModelInfo.GetLODMaterialPropertyBlock(lodIndex); materialPropertyBlock.Clear(); if (vegetationItemModelInfo.ShaderControler != null && vegetationItemModelInfo.ShaderControler.Settings.SampleWind) { MeshRenderer meshRenderer = vegetationItemModelInfo.WindSamplerMeshRendererList[cameraIndex]; if (meshRenderer) { meshRenderer.GetPropertyBlock(materialPropertyBlock); } } for (int i = 0; i <= matrixList.Length - 1; i++) { int submeshesToRender = Mathf.Min(mesh.subMeshCount, materials.Length); for (int m = 0; m <= submeshesToRender - 1; m++) { Graphics.DrawMesh(mesh, matrixList[i], materials[m], layer, targetCamera, m, materialPropertyBlock, shadowCastingMode, true, null, LightProbeUsage.Off); } } } void RenderVegetationItemLOD(NativeList matrixList, NativeList lodFadeList, VegetationItemModelInfo vegetationItemModelInfo, int lodIndex, Camera targetCamera, int cameraIndex, ShadowCastingMode shadowCastingMode, LayerMask layer) { if (matrixList.Length == 0) return; if (lodIndex >= vegetationItemModelInfo.LODCount) return; //bool uniqueMaterialPropertyBlock = matrixList.Length > 999; int count = Mathf.CeilToInt(matrixList.Length / 1000f); // count = Mathf.Clamp(count, 0, 1); int totalCount = matrixList.Length; for (int l = 0; l <= count - 1; l++) { int copyCount = 1000; if (totalCount < 1000) copyCount = totalCount; NativeSlice matrixSlice = new NativeSlice(matrixList, l * 1000, copyCount); matrixSlice.CopyToFast(_renderArray); Mesh mesh = vegetationItemModelInfo.GetLODMesh(lodIndex); Material[] materials = vegetationItemModelInfo.GetLODMaterials(lodIndex); MaterialPropertyBlock materialPropertyBlock = vegetationItemModelInfo.GetLODMaterialPropertyBlock(lodIndex); materialPropertyBlock.Clear(); if (vegetationItemModelInfo.ShaderControler != null && vegetationItemModelInfo.ShaderControler.Settings.SampleWind) { MeshRenderer meshRenderer = vegetationItemModelInfo.WindSamplerMeshRendererList[cameraIndex]; if (meshRenderer) { meshRenderer.GetPropertyBlock(materialPropertyBlock); } } if (shadowCastingMode != ShadowCastingMode.ShadowsOnly) { if (lodFadeList.Length == matrixList.Length) { NativeSlice lodFadeSlice = new NativeSlice(lodFadeList, l * 1000, copyCount); lodFadeSlice.CopyToFast(_renderLodFadeArray); materialPropertyBlock.SetVectorArray(_unityLODFadeID, _renderLodFadeArray); } } if (VegetationRenderSettings.EnableInstancedRenderingLayers) { materialPropertyBlock.SetFloatArray(UnityRenderingLayerID, _renderingLayerArray); } int submeshesToRender = Mathf.Min(mesh.subMeshCount, materials.Length); for (int m = 0; m <= submeshesToRender - 1; m++) { Graphics.DrawMeshInstanced(mesh, m, materials[m], _renderArray, copyCount, materialPropertyBlock, shadowCastingMode, true, layer, targetCamera, LightProbeUsage.Off); } totalCount -= 1000; } } // ReSharper disable once UnusedMember.Local void OnDisable() { VegetationStudioManager.UnregisterVegetationSystem(this); DisableEditorApi(); DisposeVegetationStudioCameras(); RemoveVegetationStudioCameraDelegates(); DisposeVegetationCells(); DisposeBillboardCells(); ClearVegetationItemModels(); DisposeComputeShaders(); VegetationCellSpawner.Dispose(); InitDone = false; } } }