using System.Collections.Generic; using AwesomeTechnologies.Utility; using AwesomeTechnologies.Utility.Quadtree; using AwesomeTechnologies.Vegetation; using AwesomeTechnologies.VegetationStudio; using AwesomeTechnologies.VegetationSystem.Biomes; using Unity.Burst; using Unity.Collections; using Unity.Jobs; using Unity.Mathematics; using UnityEngine; namespace AwesomeTechnologies.VegetationSystem { public class RaycastContainers { public NativeArray RaycastHits; public NativeArray RaycastCommands; } [BurstCompile(CompileSynchronously = true)] public struct CreateRaycastCommandsJob : IJob { [ReadOnly] public NativeArray SpawnLocationList; public NativeArray RaycastCommands; public int LayerMask; public int MaxHits; public float3 FloatingOriginOffset; public void Execute() { for (int i = 0; i <= SpawnLocationList.Length - 1; i++) { RaycastCommand raycastCommand = new RaycastCommand { distance = 20000, from = SpawnLocationList[i].Position + new float3(0, 10000, 0) + FloatingOriginOffset, direction = new Vector3(0, -1, 0), layerMask = LayerMask, maxHits = MaxHits }; RaycastCommands[i] = raycastCommand; } } } [BurstCompile(CompileSynchronously = true)] public struct CreateRaycastCommandsListJob : IJob { [ReadOnly] public NativeList SpawnLocationList; public NativeList RaycastCommands; public int LayerMask; public int MaxHits; public void Execute() { for (int i = 0; i <= SpawnLocationList.Length - 1; i++) { RaycastCommand raycastCommand = new RaycastCommand { distance = 20000, from = SpawnLocationList[i].Position + new float3(0, 10000, 0), direction = new Vector3(0, -1, 0), layerMask = LayerMask, maxHits = MaxHits }; RaycastCommands.Add(raycastCommand); } } } [BurstCompile(CompileSynchronously = true)] public struct UpdateInstanceListJob : IJob { [ReadOnly] public NativeArray RaycastHits; [ReadOnly] public NativeArray SpawnLocationList; public Rect TerrainRect; public float3 FloatingOriginOffset; public byte TerrainSourceID; public NativeList Position; public NativeList Rotation; public NativeList Scale; public NativeList TerrainNormal; public NativeList BiomeDistance; public NativeList TerrainTextureData; public NativeList RandomNumberIndex; public NativeList DistanceFalloff; public NativeList VegetationMaskDensity; public NativeList VegetationMaskScale; public NativeList TerrainSourceIDs; public NativeList TextureMaskData; public NativeList Excluded; public NativeList HeightmapSampled; public void Execute() { for (int i = 0; i <= RaycastHits.Length - 1; i++) { if (SpawnLocationList[i].SpawnChance < 0) continue; RaycastHit raycastHit = RaycastHits[i]; if (raycastHit.distance > 0) { float3 position = raycastHit.point; position -= FloatingOriginOffset; Vector2 point2D = new Vector2(position.x, position.z); if (TerrainRect.Contains(point2D)) { Position.Add(position); Rotation.Add(Quaternion.Euler(0, 45, 0)); Scale.Add(new float3(1, 1, 1)); TerrainNormal.Add(raycastHit.normal); BiomeDistance.Add(SpawnLocationList[i].BiomeDistance); TerrainTextureData.Add(0); RandomNumberIndex.Add(SpawnLocationList[i].RandomNumberIndex); DistanceFalloff.Add(1); VegetationMaskDensity.Add(1); VegetationMaskScale.Add(1); TerrainSourceIDs.Add(TerrainSourceID); TextureMaskData.Add(0); Excluded.Add(0); HeightmapSampled.Add(0); } } } } } [BurstCompile(CompileSynchronously = true)] public struct RaycastTerranCellSampleJob : IJobParallelFor { public NativeArray VegetationCellBoundsList; public Rect TerrainRect; public float TerrainMinHeight; public float TerrainMaxHeight; public void Execute(int index) { Bounds vegetationCellBounds = VegetationCellBoundsList[index]; Rect cellRect = RectExtension.CreateRectFromBounds(vegetationCellBounds); if (!TerrainRect.Overlaps(cellRect)) return; float minHeight; float maxHeight = vegetationCellBounds.center.y + vegetationCellBounds.extents.y; if (vegetationCellBounds.center.y < 99999) { minHeight = TerrainMinHeight; } else { minHeight = vegetationCellBounds.center.y - vegetationCellBounds.extents.y; } if (TerrainMinHeight < minHeight) minHeight = TerrainMinHeight; if (TerrainMaxHeight > maxHeight) maxHeight = TerrainMaxHeight; float centerY = (maxHeight + minHeight) / 2f; float height = maxHeight - minHeight; vegetationCellBounds = new Bounds(new Vector3(vegetationCellBounds.center.x, centerY, vegetationCellBounds.center.z), new Vector3(vegetationCellBounds.size.x, height, vegetationCellBounds.size.z)); VegetationCellBoundsList[index] = vegetationCellBounds; } } [ExecuteInEditMode] public class RaycastTerrain : MonoBehaviour, IVegetationStudioTerrain { public string TerrainType => "Raycast terrain"; public Bounds RaycastTerrainBounds = new Bounds(Vector3.zero, new Vector3(100, 20, 100)); public Bounds TerrainBounds => new Bounds(RaycastTerrainBounds.center + TerrainPosition, RaycastTerrainBounds.size); public LayerMask RaycastLayerMask; public int MaxHits = 4; public List RaycastContainerList = new List(); public ObjectPool RaycastContainerPool = new ObjectPool(); public TerrainSourceID TerrainSourceID; public bool AutoAddToVegegetationSystem; //public bool ApplyFloatingOriginOffset = true; private bool _initDone; //private Vector3 _terrainPosition; public Vector3 TerrainPosition = Vector3.zero; // ReSharper disable once UnusedMember.Local void Reset() { RaycastTerrainBounds = new Bounds(Vector3.zero, new Vector3(100, 20, 100)); TerrainPosition = transform.position; } public void RefreshTerrainData() { } public void RefreshTerrainData(Bounds bounds) { } public JobHandle SampleCellHeight(NativeArray vegetationCellBoundsList, float worldspaceHeightCutoff, Rect cellBoundsRect, JobHandle dependsOn = default(JobHandle)) { if (!_initDone) return dependsOn; Rect terrainRect = RectExtension.CreateRectFromBounds(TerrainBounds); if (!cellBoundsRect.Overlaps(terrainRect)) return dependsOn; RaycastTerranCellSampleJob raycastTerranCellSampleJob = new RaycastTerranCellSampleJob { VegetationCellBoundsList = vegetationCellBoundsList, TerrainMinHeight = TerrainBounds.center.y - TerrainBounds.extents.y, TerrainMaxHeight = TerrainBounds.center.y + TerrainBounds.extents.y, TerrainRect = terrainRect }; JobHandle handle = raycastTerranCellSampleJob.Schedule(vegetationCellBoundsList.Length, 32, dependsOn); return handle; } public JobHandle SampleTerrain(NativeList spawnLocationList, VegetationInstanceData instanceData, int sampleCount, Rect spawnRect, JobHandle dependsOn) { if (!_initDone) return dependsOn; Vector3 floatingOriginOffset = VegetationStudioManager.GetFloatingOriginOffset(); Rect terrainRect = RectExtension.CreateRectFromBounds(TerrainBounds); if (!spawnRect.Overlaps(terrainRect)) return dependsOn; MaxHits = 1; RaycastContainers raycastContainers = RaycastContainerPool.Get(); raycastContainers.RaycastCommands = new NativeArray(sampleCount, Allocator.TempJob); raycastContainers.RaycastHits = new NativeArray(sampleCount * MaxHits, Allocator.TempJob); RaycastContainerList.Add(raycastContainers); CreateRaycastCommandsJob createRaycastCommandsJob = new CreateRaycastCommandsJob { SpawnLocationList = spawnLocationList, LayerMask = RaycastLayerMask, MaxHits = MaxHits, RaycastCommands = raycastContainers.RaycastCommands, FloatingOriginOffset = floatingOriginOffset }; dependsOn = createRaycastCommandsJob.Schedule(dependsOn); dependsOn = RaycastCommand.ScheduleBatch(raycastContainers.RaycastCommands, raycastContainers.RaycastHits, 32, dependsOn); UpdateInstanceListJob updateInstanceListJob = new UpdateInstanceListJob { Position = instanceData.Position, Rotation = instanceData.Rotation, Scale = instanceData.Scale, TerrainNormal = instanceData.TerrainNormal, BiomeDistance = instanceData.BiomeDistance, TerrainTextureData = instanceData.TerrainTextureData, RandomNumberIndex = instanceData.RandomNumberIndex, DistanceFalloff = instanceData.DistanceFalloff, VegetationMaskDensity = instanceData.VegetationMaskDensity, VegetationMaskScale = instanceData.VegetationMaskScale, TerrainSourceIDs = instanceData.TerrainSourceID, TextureMaskData = instanceData.TextureMaskData, Excluded = instanceData.Excluded, RaycastHits = raycastContainers.RaycastHits, HeightmapSampled = instanceData.HeightmapSampled, SpawnLocationList = spawnLocationList, TerrainRect = terrainRect, FloatingOriginOffset = floatingOriginOffset, TerrainSourceID = (byte) TerrainSourceID }; dependsOn = updateInstanceListJob.Schedule(dependsOn); return dependsOn; } public bool NeedsSplatMapUpdate(Bounds updateBounds) { return false; } public void PrepareSplatmapGeneration(bool clearLockedTextures) { } public void GenerateSplatMapBiome(Bounds updateBounds, BiomeType biomeType, List polygonBiomeMaskList, List terrainTextureSettingsList, float heightCurveSampleHeight, float worldSpaceSeaLevel, bool clearLockedTextures) { } public void CompleteSplatmapGeneration() { } public JobHandle SampleConcaveLocation(VegetationInstanceData instanceData, float minHeightDifference, float distance, bool inverse, bool average, Rect spawnRect, JobHandle dependsOn) { if (!_initDone) return dependsOn; //TODO implement concave sampling for raycast terrain return dependsOn; } public void Init() { } public void DisposeTemporaryMemory() { for (int i = 0; i <= RaycastContainerList.Count - 1; i++) { if (RaycastContainerList[i].RaycastCommands.IsCreated) { RaycastContainerList[i].RaycastCommands.Dispose(); } RaycastContainerList[i].RaycastHits.Dispose(); RaycastContainerPool.Release(RaycastContainerList[i]); } RaycastContainerList.Clear(); } public void OverrideTerrainMaterial() { } public void RestoreTerrainMaterial() { } public void VerifySplatmapAccess() { } public void UpdateTerrainMaterial(float worldspaceSeaLevel, float worldspaceMaxTerrainHeight, TerrainTextureSettings terrainTextureSettings) { } public JobHandle ProcessSplatmapRules(List terrainTextureRuleList, VegetationInstanceData instanceData, bool include, Rect cellRect, JobHandle dependsOn) { return dependsOn; } public bool HasTerrainTextures() { return false; } public Texture2D GetTerrainTexture(int index) { return null; } #if UNITY_2018_3_OR_NEWER public TerrainLayer[] GetTerrainLayers() { return new TerrainLayer[0]; } public void SetTerrainLayers(TerrainLayer[] terrainLayers) { } #else public SplatPrototype[] GetSplatPrototypes() { return new SplatPrototype[0]; } public void SetSplatPrototypes(SplatPrototype[] splatPrototypes) { } #endif // ReSharper disable once UnusedMember.Local void OnDrawGizmosSelected() { Gizmos.color = Color.blue; Gizmos.DrawWireCube( RaycastTerrainBounds.center + TerrainPosition + VegetationStudioManager.GetFloatingOriginOffset(), RaycastTerrainBounds.size); } // ReSharper disable once UnusedMember.Local void Update() { if (!Application.isPlaying) { TerrainPosition = transform.position; } //if (!Application.isPlaying && _terrainPosition != transform.position) //{ // SetTerrainPosition(); //} } // ReSharper disable once UnusedMember.Local void OnEnable() { _initDone = true; //SetTerrainPosition(); if (AutoAddToVegegetationSystem && Application.isPlaying) { VegetationStudioManager.AddTerrain(gameObject, false); } else { VegetationStudioManager.RefreshTerrainArea(TerrainBounds); } } //void SetTerrainPosition() //{ // if (ApplyFloatingOriginOffset) // { // _terrainPosition = transform.position - VegetationStudioManager.GetFloatingOriginOffset(); // } // else // { // _terrainPosition = transform.position; // } //} public void RefreshTerrain() { VegetationStudioManager.RefreshTerrainArea(TerrainBounds); VegetationStudioManager.ClearCache(TerrainBounds); } public void RefreshTerrain(Bounds bounds) { VegetationStudioManager.RefreshTerrainArea(bounds); VegetationStudioManager.ClearCache(bounds); } // ReSharper disable once UnusedMember.Local void OnDisable() { _initDone = false; if (AutoAddToVegegetationSystem && Application.isPlaying) { VegetationStudioManager.RemoveTerrain(gameObject); } else { VegetationStudioManager.RefreshTerrainArea(TerrainBounds); } } } }