public void ClearCache()
        {
            CompleteCellLoading();

            for (int i = 0; i <= LoadedVegetationCellList.Count - 1; i++)
            {
                LoadedVegetationCellList[i].ClearCache();
            }

            LoadedVegetationCellList.Clear();
            ClearBillboardCellsCache();
            OnClearCacheDelegate?.Invoke(this);
        }
        public void ClearCache(Bounds bounds)
        {
            Rect clearRect = RectExtension.CreateRectFromBounds(bounds);

            CompleteCellLoading();

            //TODO Use quadtree here
            for (int i = LoadedVegetationCellList.Count - 1; i >= 0; i--)
            {
                VegetationCell vegetationCell = LoadedVegetationCellList[i];
                if (!vegetationCell.Rectangle.Overlaps(clearRect))
                {
                    continue;
                }

                vegetationCell.ClearCache();
                LoadedVegetationCellList.RemoveAtSwapBack(i);

                OnClearCacheVegetationCellDelegate?.Invoke(this, vegetationCell);
            }

            ClearBillboardCellsCache(bounds);
        }
        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 CreateVegetationCells()
        {
            DisposeVegetationCells();
            Bounds expandedBounds = new Bounds(VegetationSystemBounds.center, VegetationSystemBounds.size);

            expandedBounds.Expand(new Vector3(VegetationCellSize * 2f, 0, VegetationCellSize * 2f));

            Rect expandedRect = RectExtension.CreateRectFromBounds(expandedBounds);

            VegetationCellQuadTree = new QuadTree <VegetationCell>(expandedRect);
            int cellXCount = Mathf.CeilToInt(VegetationSystemBounds.size.x / VegetationCellSize);
            int cellZCount = Mathf.CeilToInt(VegetationSystemBounds.size.z / VegetationCellSize);

            Vector2 corner = new Vector2(VegetationSystemBounds.center.x - VegetationSystemBounds.size.x / 2f,
                                         VegetationSystemBounds.center.z - VegetationSystemBounds.size.z / 2f);

            for (int x = 0; x <= cellXCount - 1; x++)
            {
                for (int z = 0; z <= cellZCount - 1; z++)
                {
                    VegetationCell vegetationCell = new VegetationCell(new Rect(
                                                                           new Vector2(VegetationCellSize * x + corner.x, VegetationCellSize * z + corner.y),
                                                                           new Vector2(VegetationCellSize, VegetationCellSize)));
                    VegetationCellList.Add(vegetationCell);
                    vegetationCell.Index = VegetationCellList.Count - 1;
                    VegetationCellQuadTree.Insert(vegetationCell);
                }
            }

            LoadedVegetationCellList.Clear();
            LoadedVegetationCellList.Capacity = VegetationCellList.Count;

            NativeArray <Bounds> vegetationCellBounds =
                new NativeArray <Bounds>(VegetationCellList.Count, Allocator.Persistent);

            for (int i = 0; i <= VegetationCellList.Count - 1; i++)
            {
                vegetationCellBounds[i] = VegetationCellList[i].VegetationCellBounds;
            }

            float minBoundsHeight    = VegetationSystemBounds.center.y - VegetationSystemBounds.extents.y;
            float worldspaceSealevel = minBoundsHeight + SeaLevel;

            if (!ExcludeSeaLevelCells)
            {
                worldspaceSealevel = minBoundsHeight;
            }

            JobHandle jobHandle = default(JobHandle);

            for (int i = 0; i <= VegetationStudioTerrainList.Count - 1; i++)
            {
                jobHandle = VegetationStudioTerrainList[i]
                            .SampleCellHeight(vegetationCellBounds, worldspaceSealevel, expandedRect, jobHandle);
            }

            jobHandle.Complete();

            for (int i = 0; i <= VegetationCellList.Count - 1; i++)
            {
                VegetationCellList[i].VegetationCellBounds = vegetationCellBounds[i];
            }

            vegetationCellBounds.Dispose();

            PrepareVegetationCells();

            VegetationStudioManager.OnVegetationCellRefresh(this);
        }