From fc0650d66a40559c5c56f45e76d04885b3982e2a Mon Sep 17 00:00:00 2001 From: jeickhoff Date: Mon, 3 Jul 2023 13:10:56 +0200 Subject: [PATCH] Fix terrain area neighbor system --- .../AuthoringScene/AuthoringScene.unity | 2 +- .../Rendering/TerrainChangeMonitorSystem.cs | 26 +++---- .../Scripts/Rendering/TerrainMeshingSystem.cs | 32 +++++---- .../Rendering/TerrainRenderInitSystem.cs | 26 +++---- .../Terrain/Authoring/TerrainAreaAuthoring.cs | 9 ++- .../Terrain/TerrainGenerationSystem.cs | 7 ++ .../Scripts/Terrain/TerrainNeighborSystem.cs | 70 +++++++++++-------- 7 files changed, 99 insertions(+), 73 deletions(-) diff --git a/Assets/Scenes/AuthoringScene/AuthoringScene.unity b/Assets/Scenes/AuthoringScene/AuthoringScene.unity index 0700ba46..f8edf6af 100644 --- a/Assets/Scenes/AuthoringScene/AuthoringScene.unity +++ b/Assets/Scenes/AuthoringScene/AuthoringScene.unity @@ -176,7 +176,7 @@ MonoBehaviour: x: 10 y: 1 z: 10 - maxChunkSpawnsPerTick: 25 + maxChunkSpawnsPerTick: 1 blocksPerAreaSide: 4 YBounds: x: 0 diff --git a/Assets/Scripts/Rendering/TerrainChangeMonitorSystem.cs b/Assets/Scripts/Rendering/TerrainChangeMonitorSystem.cs index 51befbac..4a3e4d26 100644 --- a/Assets/Scripts/Rendering/TerrainChangeMonitorSystem.cs +++ b/Assets/Scripts/Rendering/TerrainChangeMonitorSystem.cs @@ -23,23 +23,23 @@ protected override void OnUpdate() Entities .WithChangeFilter() .WithImmediatePlayback() - .ForEach((Entity entity, EntityCommandBuffer ecb, ref TerrainArea terrainArea) => + .ForEach((Entity entity, EntityCommandBuffer ecb, in TerrainNeighbors terrainNeighbors) => { // todo- currently we remesh all 6 neighbor areas regardless of where the change was // todo- worst case, we should only need 3 neighbors remeshed, depending on where the change is ecb.SetComponentEnabled(entity, true); - if(terrainArea.neighborXN!= Entity.Null) - ecb.SetComponentEnabled(terrainArea.neighborXN,true); - if(terrainArea.neighborXP!= Entity.Null) - ecb.SetComponentEnabled(terrainArea.neighborXP,true); - if(terrainArea.neighborYN!= Entity.Null) - ecb.SetComponentEnabled(terrainArea.neighborYN,true); - if(terrainArea.neighborYP!= Entity.Null) - ecb.SetComponentEnabled(terrainArea.neighborYP,true); - if(terrainArea.neighborZN!= Entity.Null) - ecb.SetComponentEnabled(terrainArea.neighborZN,true); - if(terrainArea.neighborZP!= Entity.Null) - ecb.SetComponentEnabled(terrainArea.neighborZP,true); + if(terrainNeighbors.neighborXN!= Entity.Null) + ecb.SetComponentEnabled(terrainNeighbors.neighborXN,true); + if(terrainNeighbors.neighborXP!= Entity.Null) + ecb.SetComponentEnabled(terrainNeighbors.neighborXP,true); + if(terrainNeighbors.neighborYN!= Entity.Null) + ecb.SetComponentEnabled(terrainNeighbors.neighborYN,true); + if(terrainNeighbors.neighborYP!= Entity.Null) + ecb.SetComponentEnabled(terrainNeighbors.neighborYP,true); + if(terrainNeighbors.neighborZN!= Entity.Null) + ecb.SetComponentEnabled(terrainNeighbors.neighborZN,true); + if(terrainNeighbors.neighborZP!= Entity.Null) + ecb.SetComponentEnabled(terrainNeighbors.neighborZP,true); }).Run(); } diff --git a/Assets/Scripts/Rendering/TerrainMeshingSystem.cs b/Assets/Scripts/Rendering/TerrainMeshingSystem.cs index aadb677e..65e351b4 100644 --- a/Assets/Scripts/Rendering/TerrainMeshingSystem.cs +++ b/Assets/Scripts/Rendering/TerrainMeshingSystem.cs @@ -31,7 +31,7 @@ protected override void OnCreate() _terrainSpawnerQuery = GetEntityQuery(ComponentType.ReadOnly()); // Fetch terrain that needs to be remeshed _terrainAreaQuery = new EntityQueryBuilder(Allocator.Temp) - .WithAll() + .WithAll() .Build(EntityManager); // Set layout object for creating VBO _vertexLayout = new NativeArray(2, Allocator.Persistent); @@ -56,6 +56,8 @@ protected override void OnUpdate() NativeArray chunksToUpdate = _terrainAreaQuery.ToEntityArray(Allocator.TempJob); NativeArray terrainAreas = _terrainAreaQuery.ToComponentDataArray(Allocator.TempJob); + NativeArray terrainNeighbors = + _terrainAreaQuery.ToComponentDataArray(Allocator.TempJob); _terrainBufferLookup = GetBufferLookup(true); // Construct our unmanaged mesh array that can be passed to the job Mesh.MeshDataArray meshDataArray = Mesh.AllocateWritableMeshData(chunksToUpdate.Length); @@ -69,6 +71,7 @@ protected override void OnUpdate() meshDataArray = meshDataArray, areasToUpdate = chunksToUpdate, terrainAreas = terrainAreas, + terrainNeighbors= terrainNeighbors, terrainBufferLookup = _terrainBufferLookup }; // todo we can potentially have the handling of meshJob output happen on later frames to reduce @@ -90,6 +93,7 @@ protected override void OnUpdate() Mesh.ApplyAndDisposeWritableMeshData(meshDataArray, meshes, MeshUpdateFlags.DontValidateIndices); chunksToUpdate.Dispose(); terrainAreas.Dispose(); + terrainNeighbors.Dispose(); // Mark that these areas have been (re)meshed EntityManager.SetComponentEnabled(_terrainAreaQuery, false); @@ -110,12 +114,14 @@ public partial struct MeshTerrainChunkJob : IJobParallelFor public Mesh.MeshDataArray meshDataArray; public NativeArray areasToUpdate; public NativeArray terrainAreas; + public NativeArray terrainNeighbors; [ReadOnly] public BufferLookup terrainBufferLookup; public void Execute(int index) { Entity entity = areasToUpdate[index]; + TerrainNeighbors terrainNeighbor = terrainNeighbors[index]; TerrainArea terrainArea = terrainAreas[index]; // When area is remeshed, outline it in red float3 loc = terrainArea.location * blocksPerSide; @@ -129,23 +135,23 @@ public void Execute(int index) // References to neighbor areas DynamicBuffer neighborXP = default; - if (terrainArea.neighborXP != Entity.Null) - neighborXP = terrainBufferLookup[terrainArea.neighborXP]; + if (terrainNeighbor.neighborXP != Entity.Null) + neighborXP = terrainBufferLookup[terrainNeighbor.neighborXP]; DynamicBuffer neighborXN = default; - if (terrainArea.neighborXN != Entity.Null) - neighborXN = terrainBufferLookup[terrainArea.neighborXN]; + if (terrainNeighbor.neighborXN != Entity.Null) + neighborXN = terrainBufferLookup[terrainNeighbor.neighborXN]; DynamicBuffer neighborYP = default; - if (terrainArea.neighborYP != Entity.Null) - neighborYP = terrainBufferLookup[terrainArea.neighborYP]; + if (terrainNeighbor.neighborYP != Entity.Null) + neighborYP = terrainBufferLookup[terrainNeighbor.neighborYP]; DynamicBuffer neighborYN = default; - if (terrainArea.neighborYN != Entity.Null) - neighborYN = terrainBufferLookup[terrainArea.neighborYN]; + if (terrainNeighbor.neighborYN != Entity.Null) + neighborYN = terrainBufferLookup[terrainNeighbor.neighborYN]; DynamicBuffer neighborZP = default; - if (terrainArea.neighborZP != Entity.Null) - neighborZP = terrainBufferLookup[terrainArea.neighborZP]; + if (terrainNeighbor.neighborZP != Entity.Null) + neighborZP = terrainBufferLookup[terrainNeighbor.neighborZP]; DynamicBuffer neighborZN = default; - if (terrainArea.neighborZN != Entity.Null) - neighborZN = terrainBufferLookup[terrainArea.neighborZN]; + if (terrainNeighbor.neighborZN != Entity.Null) + neighborZN = terrainBufferLookup[terrainNeighbor.neighborZN]; // Bitmasks that mark a terrain block as visited NativeArray visitedXN = new NativeArray(blocksPerSideCubed, Allocator.Temp); diff --git a/Assets/Scripts/Rendering/TerrainRenderInitSystem.cs b/Assets/Scripts/Rendering/TerrainRenderInitSystem.cs index 85af45ba..cd189ded 100644 --- a/Assets/Scripts/Rendering/TerrainRenderInitSystem.cs +++ b/Assets/Scripts/Rendering/TerrainRenderInitSystem.cs @@ -37,7 +37,7 @@ protected override void OnUpdate() EntityCommandBuffer ecb = new EntityCommandBuffer(Allocator.Temp); float3 boundsExtents = new float3(0.5 * terrainSpawner.blocksPerSide); MaterialBank materialBank = _terrainSpawnerQuery.GetSingleton(); - foreach (var (terrainArea, entity) in SystemAPI.Query>().WithAll() + foreach (var (terrainArea, terrainNeighbors, entity) in SystemAPI.Query,RefRO>().WithAll() .WithEntityAccess()) { int3 loc = terrainArea.ValueRO.location; @@ -63,18 +63,18 @@ protected override void OnUpdate() })); // Remesh neighbors of the new area to eliminate shared faces - if (EntityManager.HasComponent(terrainArea.ValueRO.neighborXN)) - ecb.SetComponentEnabled(terrainArea.ValueRO.neighborXN, true); - if (EntityManager.HasComponent(terrainArea.ValueRO.neighborXP)) - ecb.SetComponentEnabled(terrainArea.ValueRO.neighborXP, true); - if (EntityManager.HasComponent(terrainArea.ValueRO.neighborYN)) - ecb.SetComponentEnabled(terrainArea.ValueRO.neighborYN, true); - if (EntityManager.HasComponent(terrainArea.ValueRO.neighborYP)) - ecb.SetComponentEnabled(terrainArea.ValueRO.neighborYP, true); - if (EntityManager.HasComponent(terrainArea.ValueRO.neighborZN)) - ecb.SetComponentEnabled(terrainArea.ValueRO.neighborZN, true); - if (EntityManager.HasComponent(terrainArea.ValueRO.neighborZP)) - ecb.SetComponentEnabled(terrainArea.ValueRO.neighborZP, true); + if (EntityManager.HasComponent(terrainNeighbors.ValueRO.neighborXN)) + ecb.SetComponentEnabled(terrainNeighbors.ValueRO.neighborXN, true); + if (EntityManager.HasComponent(terrainNeighbors.ValueRO.neighborXP)) + ecb.SetComponentEnabled(terrainNeighbors.ValueRO.neighborXP, true); + if (EntityManager.HasComponent(terrainNeighbors.ValueRO.neighborYN)) + ecb.SetComponentEnabled(terrainNeighbors.ValueRO.neighborYN, true); + if (EntityManager.HasComponent(terrainNeighbors.ValueRO.neighborYP)) + ecb.SetComponentEnabled(terrainNeighbors.ValueRO.neighborYP, true); + if (EntityManager.HasComponent(terrainNeighbors.ValueRO.neighborZN)) + ecb.SetComponentEnabled(terrainNeighbors.ValueRO.neighborZN, true); + if (EntityManager.HasComponent(terrainNeighbors.ValueRO.neighborZP)) + ecb.SetComponentEnabled(terrainNeighbors.ValueRO.neighborZP, true); ecb.SetComponentEnabled(entity, false); } diff --git a/Assets/Scripts/Terrain/Authoring/TerrainAreaAuthoring.cs b/Assets/Scripts/Terrain/Authoring/TerrainAreaAuthoring.cs index 1885909d..d13514c7 100644 --- a/Assets/Scripts/Terrain/Authoring/TerrainAreaAuthoring.cs +++ b/Assets/Scripts/Terrain/Authoring/TerrainAreaAuthoring.cs @@ -13,7 +13,7 @@ public override void Bake(TerrainAreaAuthoring authoring) { var entity = GetEntity(TransformUsageFlags.Dynamic); // Initialize with no neighbors - var terrainArea = new TerrainArea() + var terrainNeighbors = new TerrainNeighbors() { neighborXP = Entity.Null, neighborXN = Entity.Null, @@ -22,7 +22,8 @@ public override void Bake(TerrainAreaAuthoring authoring) neighborZP = Entity.Null, neighborZN = Entity.Null, }; - AddComponent(entity, terrainArea); + AddComponent(entity, terrainNeighbors); + AddComponent(entity); AddComponent(entity); AddComponent(entity); AddBuffer(entity); @@ -35,6 +36,10 @@ public override void Bake(TerrainAreaAuthoring authoring) public struct TerrainArea : IComponentData { [GhostField] public int3 location; + } + + public struct TerrainNeighbors : IComponentData + { public Entity neighborXP; public Entity neighborXN; public Entity neighborYP; diff --git a/Assets/Scripts/Terrain/TerrainGenerationSystem.cs b/Assets/Scripts/Terrain/TerrainGenerationSystem.cs index d710b778..dc1b7d17 100644 --- a/Assets/Scripts/Terrain/TerrainGenerationSystem.cs +++ b/Assets/Scripts/Terrain/TerrainGenerationSystem.cs @@ -27,6 +27,7 @@ public partial struct TerrainGenerationSystem : ISystem { private EntityQuery _newSpawnQuery; private ProfilerMarker markerTerrainGen; + //private double lastUpdate; public void OnCreate(ref SystemState state) { @@ -34,10 +35,16 @@ public void OnCreate(ref SystemState state) state.RequireForUpdate(); _newSpawnQuery = SystemAPI.QueryBuilder().WithAll().Build(); markerTerrainGen = new ProfilerMarker("TerrainGeneration"); + //lastUpdate = -1.0; } public void OnUpdate(ref SystemState state) { + /*if (state.World.Time.ElapsedTime - lastUpdate < 1.0) + { + return; + } + lastUpdate = state.World.Time.ElapsedTime;*/ // Disable the NewSpawn tag component from the areas we populated in the previous tick state.EntityManager.SetComponentEnabled(_newSpawnQuery, false); diff --git a/Assets/Scripts/Terrain/TerrainNeighborSystem.cs b/Assets/Scripts/Terrain/TerrainNeighborSystem.cs index 2d1ee1fa..400e1c5a 100644 --- a/Assets/Scripts/Terrain/TerrainNeighborSystem.cs +++ b/Assets/Scripts/Terrain/TerrainNeighborSystem.cs @@ -18,7 +18,7 @@ public partial class TerrainNeighborSystem : SystemBase private EntityQuery _terrainChunkQuery; private EntityQuery _newSpawnQuery; - private BufferLookup _terrainBufferLookup; + private ComponentLookup _terrainNeighborsLookup; protected override void OnCreate() { @@ -28,6 +28,7 @@ protected override void OnCreate() .WithAll() .Build(EntityManager); _newSpawnQuery = SystemAPI.QueryBuilder().WithAll().Build(); + _terrainNeighborsLookup = GetComponentLookup(false); } @@ -36,19 +37,22 @@ protected override void OnUpdate() { if (_newSpawnQuery.IsEmpty) return; - NativeArray terrainChunkEntities = _terrainChunkQuery.ToEntityArray(Allocator.TempJob); + CompleteDependency(); + _terrainNeighborsLookup.Update(ref CheckedStateRef); + NativeArray terrainAreaEntities = _terrainChunkQuery.ToEntityArray(Allocator.TempJob); NativeArray terrainAreas = _terrainChunkQuery.ToComponentDataArray(Allocator.TempJob); - JobHandle handle = new SetChunkNeighborsJob() + JobHandle handle = new SetAreaNeighborsJob() { - terrainChunks = terrainAreas, - terrainChunkEntities = terrainChunkEntities + terrainAreas = terrainAreas, + terrainAreaEntities = terrainAreaEntities, + terrainNeighborsLookup = _terrainNeighborsLookup }.ScheduleParallel(Dependency); handle.Complete(); terrainAreas.Dispose(); - terrainChunkEntities.Dispose(); + terrainAreaEntities.Dispose(); } } @@ -57,52 +61,56 @@ protected override void OnUpdate() // When a new terrain area has been spawned, set it's neighbors, and update it's neighbors neighbors. [BurstCompile] [WithAll(typeof(NewSpawn))] - public partial struct SetChunkNeighborsJob : IJobEntity + public partial struct SetAreaNeighborsJob : IJobEntity { // thread safe as long as no terrain areas have the same location! - [NativeDisableParallelForRestriction] public NativeArray terrainChunkEntities; - [NativeDisableParallelForRestriction] public NativeArray terrainChunks; + [NativeDisableParallelForRestriction] public NativeArray terrainAreaEntities; + [NativeDisableParallelForRestriction] public NativeArray terrainAreas; + [NativeDisableParallelForRestriction] public ComponentLookup terrainNeighborsLookup; - public void Execute(Entity entity, ref TerrainArea terrainChunk) + public void Execute(Entity entity, ref TerrainArea terrainArea) { - for (int i = 0; i < terrainChunks.Length; i++) + var terrainNeighbors = terrainNeighborsLookup.GetRefRW(entity); + for (int i = 0; i < terrainAreaEntities.Length; i++) { - var otherTerrainChunk = terrainChunks[i]; - int3 otherLoc = otherTerrainChunk.location; - if (otherLoc.Equals(terrainChunk.location + new int3(1, 0, 0))) + var otherTerrainEntity = terrainAreaEntities[i]; + var otherTerrainArea = terrainAreas[i]; + var otherTerrainNeighbors = terrainNeighborsLookup.GetRefRW(otherTerrainEntity); + int3 otherLoc = otherTerrainArea.location; + if (otherLoc.Equals(terrainArea.location + new int3(1, 0, 0))) { - terrainChunk.neighborXP = terrainChunkEntities[i]; - otherTerrainChunk.neighborXN = entity; + terrainNeighbors.ValueRW.neighborXP = otherTerrainEntity; + otherTerrainNeighbors.ValueRW.neighborXN = entity; } - if (otherLoc.Equals(terrainChunk.location + new int3(-1, 0, 0))) + if (otherLoc.Equals(terrainArea.location + new int3(-1, 0, 0))) { - terrainChunk.neighborXN = terrainChunkEntities[i]; - otherTerrainChunk.neighborXP = entity; + terrainNeighbors.ValueRW.neighborXN = otherTerrainEntity; + otherTerrainNeighbors.ValueRW.neighborXP = entity; } - if (otherLoc.Equals(terrainChunk.location + new int3(0, 1, 0))) + if (otherLoc.Equals(terrainArea.location + new int3(0, 1, 0))) { - terrainChunk.neighborYP = terrainChunkEntities[i]; - otherTerrainChunk.neighborYN = entity; + terrainNeighbors.ValueRW.neighborYP = otherTerrainEntity; + otherTerrainNeighbors.ValueRW.neighborYN = entity; } - if (otherLoc.Equals(terrainChunk.location + new int3(0, -1, 0))) + if (otherLoc.Equals(terrainArea.location + new int3(0, -1, 0))) { - terrainChunk.neighborYN = terrainChunkEntities[i]; - otherTerrainChunk.neighborYP = entity; + terrainNeighbors.ValueRW.neighborYN = otherTerrainEntity; + otherTerrainNeighbors.ValueRW.neighborYP = entity; } - if (otherLoc.Equals(terrainChunk.location + new int3(0, 0, 1))) + if (otherLoc.Equals(terrainArea.location + new int3(0, 0, 1))) { - terrainChunk.neighborZP = terrainChunkEntities[i]; - otherTerrainChunk.neighborZN = entity; + terrainNeighbors.ValueRW.neighborZP = otherTerrainEntity; + otherTerrainNeighbors.ValueRW.neighborZN = entity; } - if (otherLoc.Equals(terrainChunk.location + new int3(0, 0, -1))) + if (otherLoc.Equals(terrainArea.location + new int3(0, 0, -1))) { - terrainChunk.neighborZN = terrainChunkEntities[i]; - otherTerrainChunk.neighborZP = entity; + terrainNeighbors.ValueRW.neighborZN = otherTerrainEntity; + otherTerrainNeighbors.ValueRW.neighborZP = entity; } } }