using System; using System.Collections.Generic; using AwesomeTechnologies.Billboards; using AwesomeTechnologies.BillboardSystem; using AwesomeTechnologies.Utility.Quadtree; using Unity.Jobs; using Unity.Jobs.LowLevel.Unsafe; using UnityEngine; using UnityEngine.Profiling; using UnityEngine.Rendering; namespace AwesomeTechnologies.VegetationSystem { public partial class VegetationSystemPro { [NonSerialized] private readonly List _billboardTempVegetationCellList = new List(); [NonSerialized] private readonly List _loadBillboardCellList = new List(); private void DisposeBillboardCells() { _prepareVegetationHandle.Complete(); for (var i = 0; i <= BillboardCellList.Count - 1; i++) BillboardCellList[i].Dispose(); BillboardCellList.Clear(); } public void UpdateBillboardCulling() { for (var i = 0; i <= VegetationStudioCameraList.Count - 1; i++) VegetationStudioCameraList[i].UpdateBillboardCullingGroup(); } public void RefreshBillboards() { } private void LoadBillboardCells() { _loadBillboardCellList.Clear(); Profiler.BeginSample("Load billboard cells"); _billboardTempVegetationCellList.Clear(); VegetationCellSpawner.CellJobHandleList.Clear(); Profiler.BeginSample("Find billboard cells with quadtree"); for (var i = 0; i <= VegetationStudioCameraList.Count - 1; i++) { if (!VegetationStudioCameraList[i].Enabled) continue; for (var j = 0; j <= VegetationStudioCameraList[i].BillboardJobCullingGroup.VisibleCellIndexList.Length - 1; j++) { var index = VegetationStudioCameraList[i].BillboardJobCullingGroup.VisibleCellIndexList[j]; var billboardCell = BillboardCellList[index]; if (billboardCell.Loaded) continue; billboardCell.OverlapVegetationCells.Clear(); VegetationCellQuadTree.Query(billboardCell.Rectangle, billboardCell.OverlapVegetationCells); _billboardTempVegetationCellList.AddRange(billboardCell.OverlapVegetationCells); if (!_loadBillboardCellList.Contains(billboardCell)) _loadBillboardCellList.Add(billboardCell); } } Profiler.EndSample(); Profiler.BeginSample("Load needed cells"); for (var i = 0; i <= _billboardTempVegetationCellList.Count - 1; i++) { var vegetationCell = _billboardTempVegetationCellList[i]; if (vegetationCell.LoadedDistanceBand <= 1) continue; if (vegetationCell.LoadedBillboards) continue; if (!Application.isPlaying && !vegetationCell.Prepared) VegetationCellSpawner.PrepareVegetationCell(vegetationCell); // ReSharper disable once InlineOutVariableDeclaration bool hasInstancedIndirect; var spawnVegetationCellHandle = VegetationCellSpawner.SpawnVegetationCell(vegetationCell, 1, out hasInstancedIndirect, true); CompactMemoryCellList.Add(vegetationCell); LoadedVegetationCellList.Add(vegetationCell); #if UNITY_EDITOR if (JobsUtility.JobDebuggerEnabled) { spawnVegetationCellHandle.Complete(); ReturnVegetationCellTemporaryMemory(); } #endif VegetationCellSpawner.CellJobHandleList.Add(spawnVegetationCellHandle); if (hasInstancedIndirect) ProcessInstancedIndirectCellList.Add(vegetationCell); } var loadAllCellsHandle = JobHandle.CombineDependencies(VegetationCellSpawner.CellJobHandleList); VegetationCellSpawner.CellJobHandleList.Clear(); Profiler.EndSample(); Profiler.BeginSample("Merge cells and create mesh data"); for (var i = 0; i <= _loadBillboardCellList.Count - 1; i++) { var billboardCell = _loadBillboardCellList[i]; if (billboardCell.Loaded) continue; for (var j = 0; j <= billboardCell.VegetationPackageBillboardInstancesList.Count - 1; j++) for (var k = 0; k <= billboardCell.VegetationPackageBillboardInstancesList[j].BillboardInstanceList.Count - 1; k++) { var vegetationItemInfoPro = VegetationPackageProList[j].VegetationInfoList[k]; if (vegetationItemInfoPro.VegetationType != VegetationType.Tree) continue; var billboardInstance = billboardCell.VegetationPackageBillboardInstancesList[j] .BillboardInstanceList[k]; if (billboardInstance.Loaded) continue; var billboardPrepareMeshDataJobHandle = loadAllCellsHandle; for (var l = 0; l <= billboardCell.OverlapVegetationCells.Count - 1; l++) { var vegetationCell = billboardCell.OverlapVegetationCells[l]; var cellInstanceList = vegetationCell.VegetationPackageInstancesList[j].VegetationItemMatrixList[k]; var mergeCellInstancesJob = new MergeCellInstancesJob { OutputNativeList = billboardInstance.InstanceList, InputNativeList = cellInstanceList }; billboardPrepareMeshDataJobHandle = mergeCellInstancesJob.Schedule(billboardPrepareMeshDataJobHandle); } var vegetationItemSize = Mathf.Max(vegetationItemInfoPro.Bounds.extents.x, vegetationItemInfoPro.Bounds.extents.y, vegetationItemInfoPro.Bounds.extents.z) * 2f; var createBillboardMeshJob = new BillboardGenerator.CreateBillboardMeshJob { InstanceList = billboardInstance.InstanceList, VerticeList = billboardInstance.VerticeList, NormalList = billboardInstance.NormalList, UvList = billboardInstance.UvList, Uv2List = billboardInstance.Uv2List, Uv3List = billboardInstance.Uv3List, IndexList = billboardInstance.IndexList, BoundsYExtent = vegetationItemInfoPro.Bounds.extents.y, VegetationItemSize = vegetationItemSize }; billboardPrepareMeshDataJobHandle = createBillboardMeshJob.Schedule(billboardPrepareMeshDataJobHandle); VegetationCellSpawner.CellJobHandleList.Add(billboardPrepareMeshDataJobHandle); } } var combinedMergeJobHandle = JobHandle.CombineDependencies(VegetationCellSpawner.CellJobHandleList); VegetationCellSpawner.CellJobHandleList.Clear(); loadAllCellsHandle.Complete(); combinedMergeJobHandle.Complete(); Profiler.EndSample(); Profiler.BeginSample("Create Mesh Objects"); for (var i = 0; i <= _loadBillboardCellList.Count - 1; i++) { var billboardCell = _loadBillboardCellList[i]; if (billboardCell.Loaded) continue; for (var j = 0; j <= billboardCell.VegetationPackageBillboardInstancesList.Count - 1; j++) for (var k = 0; k <= billboardCell.VegetationPackageBillboardInstancesList[j].BillboardInstanceList.Count - 1; k++) { var billboardInstance = billboardCell.VegetationPackageBillboardInstancesList[j] .BillboardInstanceList[k]; if (billboardInstance.Loaded) continue; billboardInstance.InstanceCount = billboardInstance.InstanceList.Length; if (billboardInstance.InstanceCount > 0) billboardInstance.Mesh = BillboardGenerator.CreateMeshFromBillboardInstance(billboardInstance); billboardInstance.Loaded = true; } billboardCell.Loaded = true; } _loadBillboardCellList.Clear(); Profiler.EndSample(); Profiler.EndSample(); } private void ClearBillboardCellsCache() { for (var i = 0; i <= BillboardCellList.Count - 1; i++) { var billboardCell = BillboardCellList[i]; billboardCell.ClearCache(); } } private void ClearBillboardCellsCache(int vegetationPackageIndex, int vegetationItemIndex) { for (var i = 0; i <= BillboardCellList.Count - 1; i++) { var billboardCell = BillboardCellList[i]; billboardCell.ClearCache(vegetationPackageIndex, vegetationItemIndex); } } private void ClearBillboardCellsCache(Bounds bounds) { _prepareVegetationHandle.Complete(); if (BillboardCellQuadTree == null) return; var clearRect = RectExtension.CreateRectFromBounds(bounds); var overlapBillboardCellList = new List(); BillboardCellQuadTree.Query(clearRect, overlapBillboardCellList); for (var i = 0; i <= overlapBillboardCellList.Count - 1; i++) { var billboardCell = overlapBillboardCellList[i]; billboardCell.ClearCache(); } } private void ClearBillboardCellsCache(Bounds bounds,int vegetationPackageIndex, int vegetationItemIndex) { var clearRect = RectExtension.CreateRectFromBounds(bounds); _prepareVegetationHandle.Complete(); var overlapBillboardCellList = new List(); BillboardCellQuadTree.Query(clearRect, overlapBillboardCellList); for (var i = 0; i <= overlapBillboardCellList.Count - 1; i++) { var billboardCell = overlapBillboardCellList[i]; billboardCell.ClearCache(vegetationPackageIndex, vegetationItemIndex); } } private void SetupBillboardShaderIDs() { _cameraPositionID = Shader.PropertyToID("_CameraPosition"); _cullDistanceID = Shader.PropertyToID("_CullDistance"); _farCullDistanceID = Shader.PropertyToID("_FarCullDistance"); } public void RenderBillboardCells() { Profiler.BeginSample("Draw billboards"); var farCullDistance = Mathf.RoundToInt(VegetationSettings.GetBillboardDistance()); var isPlaying = Application.isPlaying; var shadowCastingMode = VegetationSettings.GetBillboardShadowCastingMode(); var layer = VegetationSettings.GetBillboardLayer(); var positionMatrix = Matrix4x4.TRS(FloatingOriginOffset, Quaternion.identity, Vector3.one); for (var i = 0; i <= VegetationStudioCameraList.Count - 1; i++) { if (!VegetationStudioCameraList[i].Enabled) continue; var targetCamera = VegetationStudioCameraList[i].RenderDirectToCamera ? VegetationStudioCameraList[i].SelectedCamera : null; if (!isPlaying) targetCamera = null; for (var j = 0; j <= VegetationStudioCameraList[i].BillboardJobCullingGroup.VisibleCellIndexList.Length - 1; j++) { var index = VegetationStudioCameraList[i].BillboardJobCullingGroup.VisibleCellIndexList[j]; var billboardCell = BillboardCellList[index]; for (var k = 0; k <= billboardCell.VegetationPackageBillboardInstancesList.Count - 1; k++) for (var l = 0; l <= billboardCell.VegetationPackageBillboardInstancesList[k].BillboardInstanceList.Count - 1; l++) { var billboardInstance = billboardCell.VegetationPackageBillboardInstancesList[k] .BillboardInstanceList[l]; if (billboardInstance.Loaded && billboardInstance.InstanceCount > 0) { if (VegetationStudioCameraList[i].SelectedCamera == null) continue; var vegetationItemModelInfo = VegetationPackageProModelsList[k].VegetationItemModelList[l]; var vegetationItemInfoPro = VegetationPackageProList[k].VegetationInfoList[l]; if (!vegetationItemInfoPro.UseBillboards) continue; var camPos = VegetationStudioCameraList[i].SelectedCamera.transform.position; var renderDistanceFactor = vegetationItemInfoPro.RenderDistanceFactor; if (VegetationSettings.DisableRenderDistanceFactor) renderDistanceFactor = 1; var cullDistance = Mathf.RoundToInt(VegetationSettings.GetTreeDistance() * renderDistanceFactor); if (vegetationItemModelInfo.BillboardLODFadeCrossfade) cullDistance -= 10; //vegetationItemModelInfo.BillboardMaterial.SetVector(_cameraPositionID, camPos); MaterialPropertyBlock materialPropertyBlock = vegetationItemModelInfo.CameraBillboardMaterialPropertyBlockList[i]; materialPropertyBlock.SetVector(_cameraPositionID, camPos); materialPropertyBlock.SetInt(_cullDistanceID, VegetationStudioCameraList[i].RenderBillboardsOnly ? 0 : cullDistance); materialPropertyBlock.SetInt(_farCullDistanceID, farCullDistance); materialPropertyBlock.SetFloat(_nearFadeDistanceID, VegetationRenderSettings.CrossFadeDistance); Graphics.DrawMesh(billboardInstance.Mesh, positionMatrix, vegetationItemModelInfo.BillboardMaterial, layer, targetCamera, 0, materialPropertyBlock, shadowCastingMode, true); } } } } Profiler.EndSample(); } private void PrepareAllBillboardCells() { for (var i = 0; i <= VegetationStudioCameraList.Count - 1; i++) for (var j = 0; j <= BillboardCellList.Count - 1; j++) { var billboardCell = BillboardCellList[j]; if (!billboardCell.Prepared) billboardCell.PrepareBillboardCell(VegetationPackageProList); } } private void CreateBillboardCells() { DisposeBillboardCells(); var expandedBounds = new Bounds(VegetationSystemBounds.center, VegetationSystemBounds.size); var currentBillboardCellSize = BillboardCellSize; if (!Application.isPlaying) currentBillboardCellSize = 400; expandedBounds.Expand(new Vector3(currentBillboardCellSize * 2f, 0, currentBillboardCellSize * 2f)); BillboardCellQuadTree = new QuadTree(RectExtension.CreateRectFromBounds(expandedBounds)); var cellXCount = Mathf.CeilToInt(VegetationSystemBounds.size.x / currentBillboardCellSize); var cellZCount = Mathf.CeilToInt(VegetationSystemBounds.size.z / currentBillboardCellSize); var corner = new Vector2(VegetationSystemBounds.center.x - VegetationSystemBounds.size.x / 2f, VegetationSystemBounds.center.z - VegetationSystemBounds.size.z / 2f); for (var x = 0; x <= cellXCount - 1; x++) for (var z = 0; z <= cellZCount - 1; z++) { var billboardCell = new BillboardCell( new Rect( new Vector2(currentBillboardCellSize * x + corner.x, currentBillboardCellSize * z + corner.y), new Vector2(currentBillboardCellSize, currentBillboardCellSize)), VegetationSystemBounds.center.y, VegetationSystemBounds.size.y); BillboardCellList.Add(billboardCell); billboardCell.Index = BillboardCellList.Count - 1; BillboardCellQuadTree.Insert(billboardCell); } PrepareAllBillboardCells(); } } }