using System; using System.Collections.Generic; using AwesomeTechnologies.Common; using AwesomeTechnologies.VegetationSystem; using Unity.Burst; using Unity.Collections; using Unity.Jobs; using Unity.Mathematics; using UnityEngine; // ReSharper disable DelegateSubtraction namespace AwesomeTechnologies.Utility { public struct ItemSelectorInstanceInfo { public int VegetationCellIndex; public int VegetationCellItemIndex; public Vector3 Position; public Quaternion Rotation; public Vector3 Scale; public int Visible; public int LastVisible; public int Remove; } public class VegetationItemSelector { private readonly VisibleVegetationCellSelector _visibleVegetationCellSelector; [NonSerialized] public readonly List ReadyToLoadVegetationCellList = new List(); [NonSerialized] public readonly List ReadyToUnloadVegetationCellList = new List(); [NonSerialized] public readonly List LoadedVegetationCellList = new List(); public NativeList InstanceList; public delegate void MultiOnVegetationItemVisibilityChangeDelegate(ItemSelectorInstanceInfo itemSelectorInstanceInfo,VegetationItemIndexes vegetationItemIndexes,string vegetationItemID); public MultiOnVegetationItemVisibilityChangeDelegate OnVegetationItemVisibleDelegate; public MultiOnVegetationItemVisibilityChangeDelegate OnVegetationItemInvisibleDelegate; public delegate void MultiOnVegetationCellVisibilityChangeDelegate(int vegetationCellIndex); public MultiOnVegetationCellVisibilityChangeDelegate OnVegetationCellInvisibleDelegate; private NativeList _removeVegetationCellIndexList; private NativeList _visibilityChangedIndexList; private readonly VegetationSystemPro _vegetationSystemPro; public readonly string VegetationItemID; private readonly VegetationItemIndexes _vegetationItemIndexes; public float CullingDistance = 50f; private readonly bool _useSpawnChance; private readonly float _spawnChance; private readonly int _spawnSeed; public VegetationItemSelector(VisibleVegetationCellSelector visibleVegetationCellSelector, VegetationSystemPro vegetationSystemPro, VegetationItemInfoPro vegetationItemInfoPro, bool useSpawnChance, float spawnChance, int spawnSeed) { _useSpawnChance = useSpawnChance; _spawnChance = spawnChance; _spawnSeed = spawnSeed; _visibleVegetationCellSelector = visibleVegetationCellSelector; _vegetationSystemPro = vegetationSystemPro; VegetationItemID = vegetationItemInfoPro.VegetationItemID; _vegetationItemIndexes = _vegetationSystemPro.GetVegetationItemIndexes(VegetationItemID); _visibleVegetationCellSelector.OnVegetationCellVisibleDelegate += OnVegetationCellVisible; _visibleVegetationCellSelector.OnVegetationCellInvisibleDelegate += OnVegetationCellInvisible; _vegetationSystemPro.OnVegetationCellLoaded += OnVegetationCellLoaded; InstanceList = new NativeList(512,Allocator.Persistent); _removeVegetationCellIndexList = new NativeList(64,Allocator.Persistent); _visibilityChangedIndexList = new NativeList(512,Allocator.Persistent); } public void Dispose() { _visibleVegetationCellSelector.OnVegetationCellVisibleDelegate -= OnVegetationCellVisible; _visibleVegetationCellSelector.OnVegetationCellInvisibleDelegate -= OnVegetationCellInvisible; _vegetationSystemPro.OnVegetationCellLoaded -= OnVegetationCellLoaded; if (InstanceList.IsCreated) InstanceList.Dispose(); if (_removeVegetationCellIndexList.IsCreated) _removeVegetationCellIndexList.Dispose(); if (_visibilityChangedIndexList.IsCreated) _visibilityChangedIndexList.Dispose(); } public void OnVegetationCellLoaded(VegetationCell vegetationCell) { if (LoadedVegetationCellList.Contains(vegetationCell)) { if (!ReadyToUnloadVegetationCellList.Contains(vegetationCell)) { ReadyToUnloadVegetationCellList.Add(vegetationCell); } if (!ReadyToLoadVegetationCellList.Contains(vegetationCell)) { ReadyToLoadVegetationCellList.Add(vegetationCell); } } } public void OnVegetationCellVisible(VegetationCell vegetationCell) { ReadyToLoadVegetationCellList.Add(vegetationCell); } public void OnVegetationCellInvisible(VegetationCell vegetationCell) { ReadyToUnloadVegetationCellList.Add(vegetationCell); } public void RefreshVegetationCell(VegetationCell vegetationCell) { if (!LoadedVegetationCellList.Contains(vegetationCell)) return; if (!ReadyToUnloadVegetationCellList.Contains(vegetationCell)) { ReadyToUnloadVegetationCellList.Add(vegetationCell); } if (!ReadyToLoadVegetationCellList.Contains(vegetationCell)) { ReadyToLoadVegetationCellList.Add(vegetationCell); } } public void RefreshAllVegetationCells() { for (int i = 0; i <= LoadedVegetationCellList.Count - 1; i++) { VegetationCell vegetationCell = LoadedVegetationCellList[i]; if (!ReadyToUnloadVegetationCellList.Contains(vegetationCell)) { ReadyToUnloadVegetationCellList.Add(vegetationCell); } if (!ReadyToLoadVegetationCellList.Contains(vegetationCell)) { ReadyToLoadVegetationCellList.Add(vegetationCell); } } } public JobHandle ProcessVisibleCells(JobHandle processCullingHandle) { processCullingHandle = LoadVisibleCells(processCullingHandle); return processCullingHandle; } public JobHandle ProcessInvisibleCells(JobHandle processCullingHandle) { processCullingHandle = RemoveInvisibleCells(processCullingHandle); return processCullingHandle; } public JobHandle ProcessCulling(JobHandle processCullingHandle) { ResetVisibilityJob resetVisibilityJob = new ResetVisibilityJob { InstanceList = InstanceList }; processCullingHandle = resetVisibilityJob.Schedule(processCullingHandle); for (int i = 0; i <= _vegetationSystemPro.VegetationStudioCameraList.Count - 1; i++) { VegetationStudioCamera vegetationStudioCamera = _vegetationSystemPro.VegetationStudioCameraList[i]; if (!vegetationStudioCamera.Enabled) continue; if (vegetationStudioCamera.SelectedCamera == null) continue; DistanceCullingJob distanceCullingJob = new DistanceCullingJob { InstanceList = InstanceList, CameraPosition = vegetationStudioCamera.SelectedCamera.transform.position - _vegetationSystemPro.FloatingOriginOffset, CullingDistance = CullingDistance, }; processCullingHandle = distanceCullingJob.Schedule(processCullingHandle); } _visibilityChangedIndexList.Clear(); VisibilityChangedFilterManualJob visibilityChangedFilterManualJob = new VisibilityChangedFilterManualJob { InstanceList = InstanceList, VisibilityChangedIndexList = _visibilityChangedIndexList }; processCullingHandle = visibilityChangedFilterManualJob.Schedule(processCullingHandle); return processCullingHandle; } public void ProcessEvents() { for (int i = 0; i <= _visibilityChangedIndexList.Length - 1; i++) { ItemSelectorInstanceInfo itemSelectorInstanceInfo = InstanceList[_visibilityChangedIndexList[i]]; if (itemSelectorInstanceInfo.Visible == 1) { OnVegetationItemVisibleDelegate?.Invoke(itemSelectorInstanceInfo,_vegetationItemIndexes,VegetationItemID); } else { OnVegetationItemInvisibleDelegate?.Invoke(itemSelectorInstanceInfo,_vegetationItemIndexes,VegetationItemID); } } } JobHandle LoadVisibleCells(JobHandle processCullingHandle) { for (int i = 0; i <= ReadyToLoadVegetationCellList.Count - 1; i++) { VegetationCell vegetationCell = ReadyToLoadVegetationCellList[i]; if (!vegetationCell.Prepared) { Debug.Log("Unprepared cell" + vegetationCell.Index); continue; } //NativeList matrixInstanceArray = vegetationCell.GetVegetationPackageInstancesList(_vegetationItemIndexes.VegetationPackageIndex,_vegetationItemIndexes.VegetationItemIndex); NativeList matrixInstanceArray = vegetationCell .VegetationPackageInstancesList[_vegetationItemIndexes.VegetationPackageIndex] .VegetationItemMatrixList[_vegetationItemIndexes.VegetationItemIndex]; // if (!matrixInstanceArray.IsCreated) // { // Debug.Log("not created"); // continue; // } if (_useSpawnChance) { AddInstancesSpawnChanceJob addInstancesSpawnChanceJob = new AddInstancesSpawnChanceJob { InstanceList = InstanceList, RandomNumbers = _vegetationSystemPro.VegetationCellSpawner.RandomNumbers, SpawnChance = _spawnChance, MatrixInstanceList = matrixInstanceArray, RandomNumberIndex = vegetationCell.Index + _spawnSeed, VegetationCellIndex = vegetationCell.Index }; processCullingHandle = addInstancesSpawnChanceJob.Schedule(processCullingHandle); } else { AddInstancesJob addInstancesJob = new AddInstancesJob { InstanceList = InstanceList, MatrixInstanceList = matrixInstanceArray, VegetationCellIndex = vegetationCell.Index }; processCullingHandle = addInstancesJob.Schedule(processCullingHandle); } LoadedVegetationCellList.Add(vegetationCell); } ReadyToLoadVegetationCellList.Clear(); return processCullingHandle; } JobHandle RemoveInvisibleCells(JobHandle processCullingHandle) { _removeVegetationCellIndexList.Clear(); bool needsRemoval = false; for (int i = 0; i <= ReadyToUnloadVegetationCellList.Count - 1; i++) { _removeVegetationCellIndexList.Add(ReadyToUnloadVegetationCellList[i].Index); VegetationCell vegetationCell = ReadyToUnloadVegetationCellList[i]; // ReSharper disable once InlineOutVariableDeclaration int index = LoadedVegetationCellList.IndexOf(ReadyToUnloadVegetationCellList[i]); if (index > -1) { LoadedVegetationCellList.RemoveAtSwapBack(index); } OnVegetationCellInvisibleDelegate?.Invoke(vegetationCell.Index); needsRemoval = true; } ReadyToUnloadVegetationCellList.Clear(); if (needsRemoval) { FlagInstancesForRemovalJob flagInstancesForRemovalJob = new FlagInstancesForRemovalJob { InstanceList = InstanceList, RemoveCellIndexList = _removeVegetationCellIndexList }; processCullingHandle = flagInstancesForRemovalJob.Schedule(processCullingHandle); RemoveInstancesJob removeInstancesJob = new RemoveInstancesJob { InstanceList = InstanceList }; processCullingHandle = removeInstancesJob.Schedule(processCullingHandle); } return processCullingHandle; } } [BurstCompile] public struct DistanceCullingJob : IJob { public NativeList InstanceList; public float3 CameraPosition; public float CullingDistance; public void Execute() { for (int i = 0; i <= InstanceList.Length - 1; i++) { ItemSelectorInstanceInfo itemSelectorInstanceInfo = InstanceList[i]; float distance = math.distance(itemSelectorInstanceInfo.Position, CameraPosition); if (distance <= CullingDistance) { itemSelectorInstanceInfo.Visible = 1; } else { itemSelectorInstanceInfo.Visible = -1; } InstanceList[i] = itemSelectorInstanceInfo; } } } [BurstCompile] public struct ResetVisibilityJob : IJob { public NativeList InstanceList; public void Execute() { for (int i = 0; i <= InstanceList.Length - 1; i++) { ItemSelectorInstanceInfo itemSelectorInstanceInfo = InstanceList[i]; itemSelectorInstanceInfo.LastVisible = itemSelectorInstanceInfo.Visible; itemSelectorInstanceInfo.Visible = 0; InstanceList[i] = itemSelectorInstanceInfo; } } } [BurstCompile] public struct VisibilityChangedFilterManualJob : IJob { [ReadOnly] public NativeList InstanceList; public NativeList VisibilityChangedIndexList; public void Execute() { for (int i = 0; i <= InstanceList.Length - 1; i++) { ItemSelectorInstanceInfo itemSelectorInstanceInfo = InstanceList[i]; if (itemSelectorInstanceInfo.Visible != itemSelectorInstanceInfo.LastVisible) { VisibilityChangedIndexList.Add(i); } } } } [BurstCompile] public struct RemoveInstancesJob : IJob { public NativeList InstanceList; public void Execute() { for (int i = InstanceList.Length - 1; i >= 0; i--) { if (InstanceList[i].Remove == 1) { InstanceList.RemoveAtSwapBack(i); } } } } [BurstCompile] public struct AddInstancesSpawnChanceJob : IJob { public NativeList InstanceList; public NativeList MatrixInstanceList; [ReadOnly] public NativeArray RandomNumbers; public int RandomNumberIndex; public float SpawnChance; public int VegetationCellIndex; public void Execute() { for (int i = MatrixInstanceList.Length - 1; i >= 0; i--) { MatrixInstance matrixInstance = MatrixInstanceList[i]; if (!RandomCutoff(SpawnChance, RandomNumberIndex)) { ItemSelectorInstanceInfo itemSelectorInstanceInfo = new ItemSelectorInstanceInfo { VegetationCellIndex = VegetationCellIndex, VegetationCellItemIndex = i, Position = ExtractTranslationFromMatrix(matrixInstance.Matrix), Scale = ExtractScaleFromMatrix(matrixInstance.Matrix), Rotation = ExtractRotationFromMatrix(matrixInstance.Matrix), LastVisible = -1, Visible = -1 }; InstanceList.Add(itemSelectorInstanceInfo); } RandomNumberIndex++; } } private bool RandomCutoff(float value, int randomNumberIndex) { float randomNumber = RandomRange(randomNumberIndex, 0, 1); return !(value > randomNumber); } public float RandomRange(int randomNumberIndex, float min, float max) { while (randomNumberIndex > 9999) randomNumberIndex = randomNumberIndex - 10000; return Mathf.Lerp(min, max, RandomNumbers[randomNumberIndex]); } private static float3 ExtractTranslationFromMatrix(Matrix4x4 matrix) { float3 translate; translate.x = matrix.m03; translate.y = matrix.m13; translate.z = matrix.m23; return translate; } private static Quaternion ExtractRotationFromMatrix(Matrix4x4 matrix) { Vector3 forward; forward.x = matrix.m02; forward.y = matrix.m12; forward.z = matrix.m22; if (forward == Vector3.zero) return Quaternion.identity; Vector3 upwards; upwards.x = matrix.m01; upwards.y = matrix.m11; upwards.z = matrix.m21; return Quaternion.LookRotation(forward, upwards); } private static float3 ExtractScaleFromMatrix(Matrix4x4 matrix) { return new float3(matrix.GetColumn(0).magnitude, matrix.GetColumn(1).magnitude, matrix.GetColumn(2).magnitude); } } [BurstCompile] public struct AddInstancesJob : IJob { public NativeList InstanceList; public NativeList MatrixInstanceList; public int VegetationCellIndex; public void Execute() { for (int i = MatrixInstanceList.Length - 1; i >= 0; i--) { MatrixInstance matrixInstance = MatrixInstanceList[i]; ItemSelectorInstanceInfo itemSelectorInstanceInfo = new ItemSelectorInstanceInfo { VegetationCellIndex = VegetationCellIndex, VegetationCellItemIndex = i, Position = ExtractTranslationFromMatrix(matrixInstance.Matrix), Scale = ExtractScaleFromMatrix(matrixInstance.Matrix), Rotation = ExtractRotationFromMatrix(matrixInstance.Matrix), LastVisible = -1, Visible = -1 }; InstanceList.Add(itemSelectorInstanceInfo); } } private static float3 ExtractTranslationFromMatrix(Matrix4x4 matrix) { float3 translate; translate.x = matrix.m03; translate.y = matrix.m13; translate.z = matrix.m23; return translate; } private static Quaternion ExtractRotationFromMatrix(Matrix4x4 matrix) { Vector3 forward; forward.x = matrix.m02; forward.y = matrix.m12; forward.z = matrix.m22; if (forward == Vector3.zero) return Quaternion.identity; Vector3 upwards; upwards.x = matrix.m01; upwards.y = matrix.m11; upwards.z = matrix.m21; return Quaternion.LookRotation(forward, upwards); } private static float3 ExtractScaleFromMatrix(Matrix4x4 matrix) { return new float3(matrix.GetColumn(0).magnitude, matrix.GetColumn(1).magnitude, matrix.GetColumn(2).magnitude); } } [BurstCompile] public struct FlagInstancesForRemovalJob : IJob { public NativeList InstanceList; [ReadOnly] public NativeList RemoveCellIndexList; public void Execute() { for (int j = 0; j <= InstanceList.Length - 1; j++) { ItemSelectorInstanceInfo instanceInfo = InstanceList[j]; for (int i = 0; i <= RemoveCellIndexList.Length - 1; i++) { if (instanceInfo.VegetationCellIndex == RemoveCellIndexList[i]) { instanceInfo.Remove = 1; InstanceList[j] = instanceInfo; break; } } } } } }