Skip to content

Commit

Permalink
Fix terrain area neighbor system
Browse files Browse the repository at this point in the history
  • Loading branch information
jeickhoff committed Jul 3, 2023
1 parent 6fb971c commit fc0650d
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 73 deletions.
2 changes: 1 addition & 1 deletion Assets/Scenes/AuthoringScene/AuthoringScene.unity
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ MonoBehaviour:
x: 10
y: 1
z: 10
maxChunkSpawnsPerTick: 25
maxChunkSpawnsPerTick: 1
blocksPerAreaSide: 4
YBounds:
x: 0
Expand Down
26 changes: 13 additions & 13 deletions Assets/Scripts/Rendering/TerrainChangeMonitorSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,23 @@ protected override void OnUpdate()
Entities
.WithChangeFilter<TerrainBlocks>()
.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<Remesh>(entity, true);
if(terrainArea.neighborXN!= Entity.Null)
ecb.SetComponentEnabled<Remesh>(terrainArea.neighborXN,true);
if(terrainArea.neighborXP!= Entity.Null)
ecb.SetComponentEnabled<Remesh>(terrainArea.neighborXP,true);
if(terrainArea.neighborYN!= Entity.Null)
ecb.SetComponentEnabled<Remesh>(terrainArea.neighborYN,true);
if(terrainArea.neighborYP!= Entity.Null)
ecb.SetComponentEnabled<Remesh>(terrainArea.neighborYP,true);
if(terrainArea.neighborZN!= Entity.Null)
ecb.SetComponentEnabled<Remesh>(terrainArea.neighborZN,true);
if(terrainArea.neighborZP!= Entity.Null)
ecb.SetComponentEnabled<Remesh>(terrainArea.neighborZP,true);
if(terrainNeighbors.neighborXN!= Entity.Null)
ecb.SetComponentEnabled<Remesh>(terrainNeighbors.neighborXN,true);
if(terrainNeighbors.neighborXP!= Entity.Null)
ecb.SetComponentEnabled<Remesh>(terrainNeighbors.neighborXP,true);
if(terrainNeighbors.neighborYN!= Entity.Null)
ecb.SetComponentEnabled<Remesh>(terrainNeighbors.neighborYN,true);
if(terrainNeighbors.neighborYP!= Entity.Null)
ecb.SetComponentEnabled<Remesh>(terrainNeighbors.neighborYP,true);
if(terrainNeighbors.neighborZN!= Entity.Null)
ecb.SetComponentEnabled<Remesh>(terrainNeighbors.neighborZN,true);
if(terrainNeighbors.neighborZP!= Entity.Null)
ecb.SetComponentEnabled<Remesh>(terrainNeighbors.neighborZP,true);
}).Run();
}

Expand Down
32 changes: 19 additions & 13 deletions Assets/Scripts/Rendering/TerrainMeshingSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ protected override void OnCreate()
_terrainSpawnerQuery = GetEntityQuery(ComponentType.ReadOnly<TerrainSpawner>());
// Fetch terrain that needs to be remeshed
_terrainAreaQuery = new EntityQueryBuilder(Allocator.Temp)
.WithAll<TerrainBlocks, TerrainArea, LocalTransform, RenderMeshArray, Remesh>()
.WithAll<TerrainBlocks, TerrainArea, TerrainNeighbors, LocalTransform, RenderMeshArray, Remesh>()
.Build(EntityManager);
// Set layout object for creating VBO
_vertexLayout = new NativeArray<VertexAttributeDescriptor>(2, Allocator.Persistent);
Expand All @@ -56,6 +56,8 @@ protected override void OnUpdate()
NativeArray<Entity> chunksToUpdate = _terrainAreaQuery.ToEntityArray(Allocator.TempJob);
NativeArray<TerrainArea> terrainAreas =
_terrainAreaQuery.ToComponentDataArray<TerrainArea>(Allocator.TempJob);
NativeArray<TerrainNeighbors> terrainNeighbors =
_terrainAreaQuery.ToComponentDataArray<TerrainNeighbors>(Allocator.TempJob);
_terrainBufferLookup = GetBufferLookup<TerrainBlocks>(true);
// Construct our unmanaged mesh array that can be passed to the job
Mesh.MeshDataArray meshDataArray = Mesh.AllocateWritableMeshData(chunksToUpdate.Length);
Expand All @@ -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
Expand All @@ -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<Remesh>(_terrainAreaQuery, false);
Expand All @@ -110,12 +114,14 @@ public partial struct MeshTerrainChunkJob : IJobParallelFor
public Mesh.MeshDataArray meshDataArray;
public NativeArray<Entity> areasToUpdate;
public NativeArray<TerrainArea> terrainAreas;
public NativeArray<TerrainNeighbors> terrainNeighbors;

[ReadOnly] public BufferLookup<TerrainBlocks> 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;
Expand All @@ -129,23 +135,23 @@ public void Execute(int index)

// References to neighbor areas
DynamicBuffer<TerrainBlocks> neighborXP = default;
if (terrainArea.neighborXP != Entity.Null)
neighborXP = terrainBufferLookup[terrainArea.neighborXP];
if (terrainNeighbor.neighborXP != Entity.Null)
neighborXP = terrainBufferLookup[terrainNeighbor.neighborXP];
DynamicBuffer<TerrainBlocks> neighborXN = default;
if (terrainArea.neighborXN != Entity.Null)
neighborXN = terrainBufferLookup[terrainArea.neighborXN];
if (terrainNeighbor.neighborXN != Entity.Null)
neighborXN = terrainBufferLookup[terrainNeighbor.neighborXN];
DynamicBuffer<TerrainBlocks> neighborYP = default;
if (terrainArea.neighborYP != Entity.Null)
neighborYP = terrainBufferLookup[terrainArea.neighborYP];
if (terrainNeighbor.neighborYP != Entity.Null)
neighborYP = terrainBufferLookup[terrainNeighbor.neighborYP];
DynamicBuffer<TerrainBlocks> neighborYN = default;
if (terrainArea.neighborYN != Entity.Null)
neighborYN = terrainBufferLookup[terrainArea.neighborYN];
if (terrainNeighbor.neighborYN != Entity.Null)
neighborYN = terrainBufferLookup[terrainNeighbor.neighborYN];
DynamicBuffer<TerrainBlocks> neighborZP = default;
if (terrainArea.neighborZP != Entity.Null)
neighborZP = terrainBufferLookup[terrainArea.neighborZP];
if (terrainNeighbor.neighborZP != Entity.Null)
neighborZP = terrainBufferLookup[terrainNeighbor.neighborZP];
DynamicBuffer<TerrainBlocks> 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<bool> visitedXN = new NativeArray<bool>(blocksPerSideCubed, Allocator.Temp);
Expand Down
26 changes: 13 additions & 13 deletions Assets/Scripts/Rendering/TerrainRenderInitSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<MaterialBank>();
foreach (var (terrainArea, entity) in SystemAPI.Query<RefRO<TerrainArea>>().WithAll<NewSpawn>()
foreach (var (terrainArea, terrainNeighbors, entity) in SystemAPI.Query<RefRO<TerrainArea>,RefRO<TerrainNeighbors>>().WithAll<NewSpawn>()
.WithEntityAccess())
{
int3 loc = terrainArea.ValueRO.location;
Expand All @@ -63,18 +63,18 @@ protected override void OnUpdate()
}));

// Remesh neighbors of the new area to eliminate shared faces
if (EntityManager.HasComponent<Remesh>(terrainArea.ValueRO.neighborXN))
ecb.SetComponentEnabled<Remesh>(terrainArea.ValueRO.neighborXN, true);
if (EntityManager.HasComponent<Remesh>(terrainArea.ValueRO.neighborXP))
ecb.SetComponentEnabled<Remesh>(terrainArea.ValueRO.neighborXP, true);
if (EntityManager.HasComponent<Remesh>(terrainArea.ValueRO.neighborYN))
ecb.SetComponentEnabled<Remesh>(terrainArea.ValueRO.neighborYN, true);
if (EntityManager.HasComponent<Remesh>(terrainArea.ValueRO.neighborYP))
ecb.SetComponentEnabled<Remesh>(terrainArea.ValueRO.neighborYP, true);
if (EntityManager.HasComponent<Remesh>(terrainArea.ValueRO.neighborZN))
ecb.SetComponentEnabled<Remesh>(terrainArea.ValueRO.neighborZN, true);
if (EntityManager.HasComponent<Remesh>(terrainArea.ValueRO.neighborZP))
ecb.SetComponentEnabled<Remesh>(terrainArea.ValueRO.neighborZP, true);
if (EntityManager.HasComponent<Remesh>(terrainNeighbors.ValueRO.neighborXN))
ecb.SetComponentEnabled<Remesh>(terrainNeighbors.ValueRO.neighborXN, true);
if (EntityManager.HasComponent<Remesh>(terrainNeighbors.ValueRO.neighborXP))
ecb.SetComponentEnabled<Remesh>(terrainNeighbors.ValueRO.neighborXP, true);
if (EntityManager.HasComponent<Remesh>(terrainNeighbors.ValueRO.neighborYN))
ecb.SetComponentEnabled<Remesh>(terrainNeighbors.ValueRO.neighborYN, true);
if (EntityManager.HasComponent<Remesh>(terrainNeighbors.ValueRO.neighborYP))
ecb.SetComponentEnabled<Remesh>(terrainNeighbors.ValueRO.neighborYP, true);
if (EntityManager.HasComponent<Remesh>(terrainNeighbors.ValueRO.neighborZN))
ecb.SetComponentEnabled<Remesh>(terrainNeighbors.ValueRO.neighborZN, true);
if (EntityManager.HasComponent<Remesh>(terrainNeighbors.ValueRO.neighborZP))
ecb.SetComponentEnabled<Remesh>(terrainNeighbors.ValueRO.neighborZP, true);
ecb.SetComponentEnabled<NewSpawn>(entity, false);
}

Expand Down
9 changes: 7 additions & 2 deletions Assets/Scripts/Terrain/Authoring/TerrainAreaAuthoring.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -22,7 +22,8 @@ public override void Bake(TerrainAreaAuthoring authoring)
neighborZP = Entity.Null,
neighborZN = Entity.Null,
};
AddComponent(entity, terrainArea);
AddComponent(entity, terrainNeighbors);
AddComponent<TerrainArea>(entity);
AddComponent<NewSpawn>(entity);
AddComponent<Remesh>(entity);
AddBuffer<TerrainBlocks>(entity);
Expand All @@ -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;
Expand Down
7 changes: 7 additions & 0 deletions Assets/Scripts/Terrain/TerrainGenerationSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,24 @@ public partial struct TerrainGenerationSystem : ISystem
{
private EntityQuery _newSpawnQuery;
private ProfilerMarker markerTerrainGen;
//private double lastUpdate;

public void OnCreate(ref SystemState state)
{
// Wait for scene load/baking to occur before updates.
state.RequireForUpdate<TerrainSpawner>();
_newSpawnQuery = SystemAPI.QueryBuilder().WithAll<NewSpawn>().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<NewSpawn>(_newSpawnQuery, false);
Expand Down
70 changes: 39 additions & 31 deletions Assets/Scripts/Terrain/TerrainNeighborSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public partial class TerrainNeighborSystem : SystemBase
private EntityQuery _terrainChunkQuery;

private EntityQuery _newSpawnQuery;
private BufferLookup<TerrainBlocks> _terrainBufferLookup;
private ComponentLookup<TerrainNeighbors> _terrainNeighborsLookup;

protected override void OnCreate()
{
Expand All @@ -28,6 +28,7 @@ protected override void OnCreate()
.WithAll<TerrainArea>()
.Build(EntityManager);
_newSpawnQuery = SystemAPI.QueryBuilder().WithAll<NewSpawn>().Build();
_terrainNeighborsLookup = GetComponentLookup<TerrainNeighbors>(false);
}


Expand All @@ -36,19 +37,22 @@ protected override void OnUpdate()
{
if (_newSpawnQuery.IsEmpty)
return;
NativeArray<Entity> terrainChunkEntities = _terrainChunkQuery.ToEntityArray(Allocator.TempJob);
CompleteDependency();
_terrainNeighborsLookup.Update(ref CheckedStateRef);
NativeArray<Entity> terrainAreaEntities = _terrainChunkQuery.ToEntityArray(Allocator.TempJob);
NativeArray<TerrainArea> terrainAreas =
_terrainChunkQuery.ToComponentDataArray<TerrainArea>(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();

}
}
Expand All @@ -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<Entity> terrainChunkEntities;
[NativeDisableParallelForRestriction] public NativeArray<TerrainArea> terrainChunks;
[NativeDisableParallelForRestriction] public NativeArray<Entity> terrainAreaEntities;
[NativeDisableParallelForRestriction] public NativeArray<TerrainArea> terrainAreas;
[NativeDisableParallelForRestriction] public ComponentLookup<TerrainNeighbors> 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;
}
}
}
Expand Down

0 comments on commit fc0650d

Please sign in to comment.