public void ProcessChunk(Chunk chunk) { int xd = Helpers.Abs((m_viewerPos.x - chunk.pos.x) >> Env.ChunkPower); int yd = Helpers.Abs((m_viewerPos.y - chunk.pos.y) >> Env.ChunkPower); int zd = Helpers.Abs((m_viewerPos.z - chunk.pos.z) >> Env.ChunkPower); // Remove the chunk if it is too far away if ( xd * xd + zd * zd >= HorizontalChunkDeleteRadius * HorizontalChunkDeleteRadius || yd * yd >= VerticalChunkDeleteRadius * VerticalChunkDeleteRadius ) { chunk.stateManager.RequestState(ChunkState.Remove); return; } // Dummy collider example - create a collider for chunks directly surrounding the viewer chunk.NeedsCollider = xd <= 1 && yd <= 1 && zd <= 1; // Update visibility information bool isInsideFrustum = FullLoadOnStartUp || IsChunkInViewFrustum(chunk); ChunkStateManagerClient stateManager = (ChunkStateManagerClient)chunk.stateManager; stateManager.Visible = isInsideFrustum; stateManager.PossiblyVisible = isInsideFrustum; }
public void ProcessChunks() { Profiler.BeginSample("ProcessChunks"); HandleVisibility(); // Process removal requests for (int i = 0; i < m_updateRequests.Count;) { Chunk chunk = m_updateRequests[i]; ProcessChunk(chunk); // Update the chunk if possible if (chunk.CanUpdate) { chunk.UpdateState(); chunk.UpdateRenderGeometry(); chunk.UpdateCollisionGeometry(); } // Automatically collect chunks which are ready to be removed from the world ChunkStateManagerClient stateManager = chunk.stateManager; if (stateManager.IsStateCompleted(ChunkState.Remove)) { // Remove the chunk from our provider and unregister it from chunk storage world.chunks.RemoveChunk(chunk); // Unregister from updates m_updateRequests.RemoveAt(i); continue; } ++i; } world.PerformBlockActions(); FullLoadOnStartUp = false; Profiler.EndSample(); // Memory savings diagnostic /*{ * long compressedMem = 0; * foreach (var chunk in world.chunks.chunkCollection) * { * compressedMem += chunk.blocks.BlocksCompressed.Count* * StructSerialization.TSSize<BlockDataAABB>.ValueSize; * } * * long uncompressedMem = (long)world.chunks.Count*Env.ChunkSizeWithPaddingPow3* * StructSerialization.TSSize<BlockData>.ValueSize; * Debug.LogFormat("mem: {0}/{1} ({2}%)", compressedMem, uncompressedMem, (int)((double)compressedMem/(double)uncompressedMem*100.0)); * }*/ }
public void ProcessChunks() { Profiler.BeginSample("ProcessChunks"); HandleVisibility(); // Process removal requests for (int i = 0; i < m_updateRequests.Count;) { Chunk chunk = m_updateRequests[i]; ProcessChunk(chunk); // Update the chunk if possible if (chunk.CanUpdate) { chunk.UpdateState(); // Build colliders if there is enough time if (Globals.GeometryBudget.HasTimeBudget) { Globals.GeometryBudget.StartMeasurement(); bool wasBuilt = chunk.UpdateRenderGeometry(); wasBuilt |= chunk.UpdateCollisionGeometry(); if (wasBuilt) { Globals.GeometryBudget.StopMeasurement(); } } } // Automatically collect chunks which are ready to be removed from the world ChunkStateManagerClient stateManager = chunk.stateManager; if (stateManager.IsStateCompleted(ChunkState.Remove)) { // Remove the chunk from our provider and unregister it from chunk storage world.chunks.RemoveChunk(chunk); // Unregister from updates m_updateRequests.RemoveAt(i); continue; } ++i; } world.PerformBlockActions(); FullLoadOnStartUp = false; Profiler.EndSample(); }
public Chunk(int sideSize = Env.ChunkSize) { m_sideSize = sideSize; // Associate Chunk with a certain thread and make use of its memory pool // This is necessary in order to have lock-free caches ThreadID = Globals.WorkPool.GetThreadIDFromIndex(s_id++); pools = Globals.WorkPool.GetPool(ThreadID); stateManager = new ChunkStateManagerClient(this); blocks = new ChunkBlocks(this, sideSize); }
private void OnDrawGizmosSelected() { int size = Mathf.FloorToInt(Env.ChunkSize * Env.BlockSize); int halfSize = size >> 1; int smallSize = size >> 4; if (world != null && world.chunks != null && (Diag_DrawWorldBounds || Diag_DrawLoadRange)) { foreach (Chunk chunk in world.chunks.chunkCollection) { if (Diag_DrawWorldBounds) { Gizmos.color = Color.blue; Gizmos.DrawWireCube(chunk.WorldBounds.center, chunk.WorldBounds.size); } if (Diag_DrawLoadRange) { Vector3Int pos = chunk.pos; int xd = Helpers.Abs((m_viewerPos.x - pos.x) >> Env.ChunkPower); int zd = Helpers.Abs((m_viewerPos.z - pos.z) >> Env.ChunkPower); if (xd * xd + zd * zd >= HorizontalChunkDeleteRadius * HorizontalChunkDeleteRadius) { Gizmos.color = Color.red; Gizmos.DrawWireCube( new Vector3(chunk.pos.x + halfSize, 0, chunk.pos.z + halfSize), new Vector3(size - 0.05f, 0, size - 0.05f) ); } else { Gizmos.color = Color.green; Gizmos.DrawWireCube( new Vector3(chunk.pos.x + halfSize, 0, chunk.pos.z + halfSize), new Vector3(size - 0.05f, 0, size - 0.05f) ); } // Show generated chunks ChunkStateManagerClient stateManager = (ChunkStateManagerClient)chunk.stateManager; if (stateManager.IsStateCompleted(ChunkState.Generate)) { Gizmos.color = Color.magenta; Gizmos.DrawWireCube( new Vector3(chunk.pos.x + halfSize, 0, chunk.pos.z + halfSize), new Vector3(smallSize - 0.05f, 0, smallSize - 0.05f) ); } } } } }
private void FinishChunkDataReceive() { if (!InitFromBytes()) { Reset(); } ChunkStateManagerClient stateManager = chunk.stateManager; ChunkStateManagerClient.OnGenerateDataOverNetworkDone(stateManager); receiveBuffer = null; receiveIndex = 0; }
public Chunk() { // Associate Chunk with a certain thread and make use of its memory pool // This is necessary in order to have lock-free caches ThreadID = Globals.WorkPool.GetThreadIDFromIndex(s_id++); pools = Globals.WorkPool.GetPool(ThreadID); stateManager = new ChunkStateManagerClient(this); blocks = new ChunkBlocks(this); logic = new ChunkLogic(this); GeometryHandler = new ChunkRenderGeometryHandler(this); ChunkColliderGeometryHandler = new ChunkColliderGeometryHandler(this); }
public Chunk() { Blocks = new BlockStorage(); // Associate Chunk with a certain thread and make use of its memory pool // This is necessary in order to have lock-free caches ThreadID = Globals.WorkPool.GetThreadIDFromIndex(s_id++); Pools = Globals.WorkPool.GetPool(ThreadID); RenderGeometryBatcher = new RenderGeometryBatcher(Globals.CubeMeshGeometryBuilder, this); BBoxVertices = new List <Vector3>(); BBoxVerticesTransformed = new List <Vector3>(); StateManager = new ChunkStateManagerClient(this); }
private void FinishChunkDataReceive() { GenerateContentsFromBytes(); ChunkStateManagerClient stateManager = (ChunkStateManagerClient)chunk.stateManager; ChunkStateManagerClient.OnGenerateDataOverNetworkDone(stateManager); receiveBuffer = null; receiveIndex = 0; if (debugRecieve) { Debug.Log("ChunkBlocks.FinishChunkDataReceive (" + Thread.CurrentThread.ManagedThreadId + "): " + chunk.pos); } }
void IEventListener <ChunkStateExternal> .OnNotified(IEventSource <ChunkStateExternal> source, ChunkStateExternal evt) { // Unsubscribe from any further events ChunkStateManagerClient stateManager = (ChunkStateManagerClient)source; stateManager.Subscribe(this, evt, false); Assert.IsTrue(evt == ChunkStateExternal.Saved); if (evt == ChunkStateExternal.Saved) { if (!chunksToSave.Contains(stateManager.chunk)) { return; } SaveCompleteForChunk(stateManager.chunk); } }
public void ProcessChunk(Chunk chunk) { Profiler.BeginSample("ProcessChunk"); ChunkStateManagerClient stateManager = chunk.stateManager; int tx = m_clipmap.TransformX(chunk.pos.x / Env.ChunkSize); int ty = m_clipmap.TransformY(chunk.pos.y / Env.ChunkSize); int tz = m_clipmap.TransformZ(chunk.pos.z / Env.ChunkSize); // Chunk is too far away. Remove it if (!m_clipmap.IsInsideBounds_Transformed(tx, ty, tz)) { stateManager.RequestState(ChunkState.Remove); } else { // Dummy collider example - create a collider for chunks directly surrounding the viewer int xd = Helpers.Abs((m_viewerPos.x - chunk.pos.x) / Env.ChunkSize); int yd = Helpers.Abs((m_viewerPos.y - chunk.pos.y) / Env.ChunkSize); int zd = Helpers.Abs((m_viewerPos.z - chunk.pos.z) / Env.ChunkSize); chunk.NeedsCollider = xd <= 1 && yd <= 1 && zd <= 1; if (!UseFrustumCulling) { ClipmapItem item = m_clipmap.Get_Transformed(tx, ty, tz); // Chunk is in visibilty range. Full update with geometry generation is possible if (item.IsInVisibleRange) { //chunk.LOD = item.LOD; stateManager.PossiblyVisible = true; stateManager.Visible = true; } // Chunk is in cached range. Full update except for geometry generation else { //chunk.LOD = item.LOD; stateManager.PossiblyVisible = true; stateManager.Visible = false; } } } }
public void Init(World world, Vector3Int pos) { this.world = world; this.pos = pos; stateManager = new ChunkStateManagerClient(this); if (world != null) { logic = world.config.randomUpdateFrequency > 0.0f ? new ChunkLogic(this) : null; if (GeometryHandler == null) { GeometryHandler = new ChunkRenderGeometryHandler(this, world.renderMaterials); } if (ChunkColliderGeometryHandler == null) { ChunkColliderGeometryHandler = new ChunkColliderGeometryHandler(this, world.physicsMaterials); } } else { if (GeometryHandler == null) { GeometryHandler = new ChunkRenderGeometryHandler(this, null); } if (ChunkColliderGeometryHandler == null) { ChunkColliderGeometryHandler = new ChunkColliderGeometryHandler(this, null); } } WorldBounds = new AABB( pos.x, pos.y, pos.z, pos.x + m_sideSize, pos.y + m_sideSize, pos.z + m_sideSize ); minBounds = maxBounds = 0; minBoundsC = maxBoundsC = 0; Reset(); blocks.Init(); stateManager.Init(); }
public SaveProgress(ICollection <Chunk> chunks) { if (chunks.Count <= 0) { progress = 100; return; } chunksToSave.AddRange(chunks); totalChunksToSave = chunksToSave.Count; // Register at each chunk for (int i = 0; i < chunksToSave.Count; i++) { Chunk chunk = chunksToSave[i]; ChunkStateManagerClient stateManager = (ChunkStateManagerClient)chunk.stateManager; stateManager.Subscribe(this, ChunkStateExternal.Saved, true); } }
private ChunkBlocks HandleNeighborBack(ref Vector3Int pos) { ChunkStateManagerClient stateManager = chunk.stateManager; int i = DirectionUtils.Get(Direction.south); // If it is an edge position, notify neighbor as well // Iterate over neighbors and decide which ones should be notified to rebuild var listeners = stateManager.Listeners; ChunkEvent listener = listeners[i]; if (listener == null) { return(null); } ChunkStateManagerClient listenerClient = (ChunkStateManagerClient)listener; Chunk listenerChunk = listenerClient.chunk; int cx = chunk.pos.x; int cy = chunk.pos.y; int cz = chunk.pos.z; int lx = listenerChunk.pos.x; int ly = listenerChunk.pos.y; int lz = listenerChunk.pos.z; if (ly != cy && lx != cx) { return(null); } if (pos.z != 0 || (lz + m_sideSize != cz)) { return(null); } rebuildMaskGeometry = rebuildMaskGeometry | (1 << i); return(listenerChunk.blocks); }
public void ProcessChunk(Chunk chunk) { Profiler.BeginSample("ProcessChunk"); ChunkStateManagerClient stateManager = chunk.stateManager; int xd = (m_viewerPos.x - chunk.pos.x) / Env.ChunkSize; int yd = (m_viewerPos.y - chunk.pos.y) / Env.ChunkSize; int zd = (m_viewerPos.z - chunk.pos.z) / Env.ChunkSize; int hRadius = HorizontalChunkLoadRadius; int vRadius = VerticalChunkLoadRadius; int xDist = xd * xd + zd * zd; int yDist = yd * yd; // Remove the chunk if it is too far away if (xDist > hRadius * hRadius || yDist > vRadius * vRadius) { stateManager.RequestState(ChunkState.Remove); } else { // Dummy collider example - create a collider for chunks directly surrounding the viewer chunk.NeedsCollider = Helpers.Abs(xd) <= 1 && Helpers.Abs(yd) <= 1 && Helpers.Abs(zd) <= 1; if (!UseFrustumCulling) { // Update visibility information stateManager.Visible = xDist <= HorizontalChunkLoadRadius * HorizontalChunkLoadRadius && yDist <= VerticalChunkLoadRadius * VerticalChunkLoadRadius; stateManager.PossiblyVisible = true; } } Profiler.EndSample(); }
/// <summary> /// Sends a save request to all chunk currently loaded /// </summary> /// <param name="world">World holding chunks</param> /// <returns>List of chunks waiting to be saved.</returns> public static List <Chunk> SaveAll(World world) { if (world == null || !Features.UseSerialization) { return(null); } List <Chunk> chunksToSave = new List <Chunk> (); foreach (Chunk chunk in world.chunks.chunkCollection) { // Ignore chunks that can't be saved at the moment ChunkStateManagerClient stateManager = chunk.stateManager; if (!stateManager.IsSavePossible) { continue; } chunksToSave.Add(chunk); stateManager.RequestState(ChunkState.PrepareSaveData); } return(chunksToSave); }
public void Update() { ChunkStateManagerClient stateManager = chunk.stateManager; if (!stateManager.IsUpdateBlocksPossible) { return; } //UnityEngine.Debug.Log(m_setBlockQueue.Count); if (m_setBlockQueue.Count > 0) { if (rebuildMaskGeometry < 0) { rebuildMaskGeometry = 0; } if (rebuildMaskCollider < 0) { rebuildMaskCollider = 0; } var timeBudget = Globals.SetBlockBudget; // Modify blocks int j; for (j = 0; j < m_setBlockQueue.Count; j++) { timeBudget.StartMeasurement(); m_setBlockQueue[j].Apply(this); timeBudget.StopMeasurement(); // Sync edges if there's enough time /*if (!timeBudget.HasTimeBudget) * { ++j; * break; * }*/ } rebuildMaskCollider |= rebuildMaskGeometry; if (j == m_setBlockQueue.Count) { m_setBlockQueue.Clear(); } else { m_setBlockQueue.RemoveRange(0, j); return; } } long now = Globals.Watch.ElapsedMilliseconds; // Request a geometry update at most 10 times a second if (rebuildMaskGeometry >= 0 && now - lastUpdateTimeGeometry >= 100) { lastUpdateTimeGeometry = now; // Request rebuild on this chunk stateManager.RequestState(ChunkState.BuildVerticesNow); // Notify neighbors that they need to rebuild their geometry if (rebuildMaskGeometry > 0) { var listeners = stateManager.Listeners; for (int j = 0; j < listeners.Length; j++) { ChunkStateManagerClient listener = (ChunkStateManagerClient)listeners[j]; if (listener != null && ((rebuildMaskGeometry >> j) & 1) != 0) { // Request rebuild on neighbor chunks listener.RequestState(ChunkState.BuildVerticesNow); } } } rebuildMaskGeometry = -1; } // Request a collider update at most 4 times a second if (chunk.NeedsCollider && rebuildMaskCollider >= 0 && now - lastUpdateTimeCollider >= 250) { lastUpdateTimeCollider = now; // Request rebuild on this chunk stateManager.RequestState(ChunkState.BuildColliderNow); // Notify neighbors that they need to rebuilt their geometry if (rebuildMaskCollider > 0) { var listeners = stateManager.Listeners; for (int j = 0; j < listeners.Length; j++) { ChunkStateManagerClient listener = (ChunkStateManagerClient)listeners[j]; if (listener != null && ((rebuildMaskCollider >> j) & 1) != 0) { // Request rebuild on neighbor chunks if (listener.chunk.NeedsCollider) { listener.RequestState(ChunkState.BuildColliderNow); } } } } rebuildMaskCollider = -1; } }
public void HandleNeighbors(BlockData block, Vector3Int pos) { if (!NeedToHandleNeighbors(ref pos)) { return; } int cx = chunk.pos.x; int cy = chunk.pos.y; int cz = chunk.pos.z; ChunkStateManagerClient stateManager = chunk.stateManager; // If it is an edge position, notify neighbor as well // Iterate over neighbors and decide which ones should be notified to rebuild their geometry var listeners = stateManager.Listeners; for (int i = 0; i < listeners.Length; i++) { ChunkEvent listener = listeners[i]; if (listener == null) { continue; } ChunkStateManagerClient listenerClient = (ChunkStateManagerClient)listener; Chunk listenerChunk = listenerClient.chunk; ChunkBlocks listenerChunkBlocks = listenerChunk.blocks; int lx = listenerChunk.pos.x; int ly = listenerChunk.pos.y; int lz = listenerChunk.pos.z; if (ly == cy || lz == cz) { // Section to the left if ((pos.x == 0) && (lx + m_sideSize == cx)) { rebuildMaskGeometry = rebuildMaskGeometry | (1 << i); // Mirror the block to the neighbor edge int neighborIndex = Helpers.GetChunkIndex1DFrom3D(m_sideSize, pos.y, pos.z, m_pow); listenerChunkBlocks[neighborIndex] = block; } // Section to the right else if ((pos.x == (m_sideSize - 1)) && (lx - m_sideSize == cx)) { rebuildMaskGeometry = rebuildMaskGeometry | (1 << i); // Mirror the block to the neighbor edge int neighborIndex = Helpers.GetChunkIndex1DFrom3D(-1, pos.y, pos.z, m_pow); listenerChunkBlocks[neighborIndex] = block; } } if (lx == cx || lz == cz) { // Section to the bottom if ((pos.y == 0) && (ly + m_sideSize == cy)) { rebuildMaskGeometry = rebuildMaskGeometry | (1 << i); // Mirror the block to the neighbor edge int neighborIndex = Helpers.GetChunkIndex1DFrom3D(pos.x, m_sideSize, pos.z, m_pow); listenerChunkBlocks[neighborIndex] = block; } // Section to the top else if ((pos.y == (m_sideSize - 1)) && (ly - m_sideSize == cy)) { rebuildMaskGeometry = rebuildMaskGeometry | (1 << i); // Mirror the block to the neighbor edge int neighborIndex = Helpers.GetChunkIndex1DFrom3D(pos.x, -1, pos.z, m_pow); listenerChunkBlocks[neighborIndex] = block; } } if (ly == cy || lx == cx) { // Section to the back if ((pos.z == 0) && (lz + m_sideSize == cz)) { rebuildMaskGeometry = rebuildMaskGeometry | (1 << i); // Mirror the block to the neighbor edge int neighborIndex = Helpers.GetChunkIndex1DFrom3D(pos.x, pos.y, m_sideSize, m_pow); listenerChunkBlocks[neighborIndex] = block; } // Section to the front else if ((pos.z == (m_sideSize - 1)) && (lz - m_sideSize == cz)) { rebuildMaskGeometry = rebuildMaskGeometry | (1 << i); // Mirror the block to the neighbor edge int neighborIndex = Helpers.GetChunkIndex1DFrom3D(pos.x, pos.y, -1, m_pow); listenerChunkBlocks[neighborIndex] = block; } } // No further checks needed once we know all neighbors need to be notified if (rebuildMaskGeometry == 0x3f) { break; } } }
private void OnDrawGizmosSelected() { int size = Mathf.FloorToInt(Env.ChunkSize * Env.BlockSize); int halfSize = size >> 1; int smallSize = size >> 4; if (world != null && world.chunks != null && (Diag_DrawWorldBounds || Diag_DrawLoadRange)) { foreach (Chunk chunk in world.chunks.chunkCollection) { if (Diag_DrawWorldBounds) { Gizmos.color = Color.blue; Gizmos.DrawWireCube(chunk.WorldBounds.center, chunk.WorldBounds.size); } if (Diag_DrawLoadRange) { Vector3Int pos = chunk.pos; if (chunk.pos.y == 0) { int tx = m_clipmap.TransformX(pos.x >> Env.ChunkPower); int ty = m_clipmap.TransformY(pos.y >> Env.ChunkPower); int tz = m_clipmap.TransformZ(pos.z >> Env.ChunkPower); if (!m_clipmap.IsInsideBounds_Transformed(tx, ty, tz)) { Gizmos.color = Color.red; Gizmos.DrawWireCube( new Vector3(pos.x + halfSize, 0, pos.z + halfSize), new Vector3(size - 0.05f, 0, size - 0.05f) ); } else { ClipmapItem item = m_clipmap.Get_Transformed(tx, ty, tz); if (item.IsWithinVisibleRange) { Gizmos.color = Color.green; Gizmos.DrawWireCube( new Vector3(pos.x + halfSize, 0, pos.z + halfSize), new Vector3(size - 0.05f, 0, size - 0.05f) ); } else // if (item.IsWithinCachedRange) { Gizmos.color = Color.yellow; Gizmos.DrawWireCube( new Vector3(pos.x + halfSize, 0, pos.z + halfSize), new Vector3(size - 0.05f, 0, size - 0.05f) ); } } } // Show generated chunks ChunkStateManagerClient stateManager = (ChunkStateManagerClient)chunk.stateManager; if (stateManager.IsStateCompleted(ChunkState.Generate)) { Gizmos.color = Color.magenta; Gizmos.DrawWireCube( new Vector3(pos.x + halfSize, pos.y + halfSize, pos.z + halfSize), new Vector3(smallSize - 0.05f, smallSize - 0.05f, smallSize - 0.05f) ); } } } } }
public void ProcessChunk(Chunk chunk) { int xd = Helpers.Abs((m_viewerPos.x - chunk.pos.x) >> Env.ChunkPower); int yd = Helpers.Abs((m_viewerPos.y - chunk.pos.y) >> Env.ChunkPower); int zd = Helpers.Abs((m_viewerPos.z - chunk.pos.z) >> Env.ChunkPower); int tx = m_clipmap.TransformX(chunk.pos.x >> Env.ChunkPower); int ty = m_clipmap.TransformY(chunk.pos.y >> Env.ChunkPower); int tz = m_clipmap.TransformZ(chunk.pos.z >> Env.ChunkPower); ChunkStateManagerClient stateManager = (ChunkStateManagerClient)chunk.stateManager; // Chunk is too far away. Remove it if (!m_clipmap.IsInsideBounds_Transformed(tx, ty, tz)) { stateManager.RequestState(ChunkState.Remove); } else { // Dummy collider example - create a collider for chunks directly surrounding the viewer chunk.NeedsCollider = xd <= 1 && yd <= 1 && zd <= 1; // Chunk is within view frustum if (FullLoadOnStartUp || IsChunkInViewFrustum(chunk)) { ClipmapItem item = m_clipmap.Get_Transformed(tx, ty, tz); // Chunk is within visibilty range. Full update with geometry generation is possible if (item.IsWithinVisibleRange) { //chunk.LOD = item.LOD; stateManager.PossiblyVisible = true; stateManager.Visible = true; } // Chunk is within cached range. Full update except for geometry generation else // if (item.IsWithinCachedRange) { //chunk.LOD = item.LOD; stateManager.PossiblyVisible = true; stateManager.Visible = false; } } else { ClipmapItem item = m_clipmap.Get_Transformed(tx, ty, tz); // Chunk is not in the view frustum but still within cached range if (item.IsWithinCachedRange) { //chunk.LOD = item.LOD; stateManager.PossiblyVisible = false; stateManager.Visible = false; } else // Weird state { Assert.IsFalse(true); stateManager.RequestState(ChunkState.Remove); } } } }
private void ProcessSetBlockQueue() { if (m_setBlockQueue.Count <= 0) { return; } StateManager.RequestState(ChunkState.FinalizeData | ChunkState.BuildVerticesNow); int rebuildMask = 0; // Modify blocks for (int j = 0; j < m_setBlockQueue.Count; j++) { SetBlockContext context = m_setBlockQueue[j]; int x, y, z; Helpers.GetIndex3DFrom1D(context.Index, out x, out y, out z); this[x, y, z] = context.Block; if ( // Only check neighbors if it is still needed rebuildMask == 0x3f || // Only check neighbors when it is a change of a block on a chunk's edge (((x + 1) & EngineSettings.ChunkConfig.Mask) > 1 && ((y + 1) & EngineSettings.ChunkConfig.Mask) > 1 && ((z + 1) & EngineSettings.ChunkConfig.Mask) > 1) ) { continue; } int cx = Pos.X; int cy = Pos.Y; int cz = Pos.Z; // If it is an edge position, notify neighbor as well // Iterate over neighbors and decide which ones should be notified to rebuild for (int i = 0; i < StateManager.Listeners.Length; i++) { ChunkEvent listener = StateManager.Listeners[i]; if (listener == null) { continue; } // No further checks needed once we know all neighbors need to be notified if (rebuildMask == 0x3f) { break; } ChunkStateManagerClient listenerChunk = (ChunkStateManagerClient)listener; int lx = listenerChunk.chunk.Pos.X; int ly = listenerChunk.chunk.Pos.Y; int lz = listenerChunk.chunk.Pos.Z; if ((ly == cy || lz == cz) && ( // Section to the left ((x == 0) && (lx + EngineSettings.ChunkConfig.Mask == cx)) || // Section to the right ((x == EngineSettings.ChunkConfig.Mask) && (lx - EngineSettings.ChunkConfig.Mask == cx)) )) { rebuildMask = rebuildMask | (1 << i); } if ((lx == cx || lz == cz) && ( // Section to the bottom ((y == 0) && (ly + EngineSettings.ChunkConfig.Mask == cy)) || // Section to the top ((y == EngineSettings.ChunkConfig.Mask) && (ly - EngineSettings.ChunkConfig.Mask == cy)) )) { rebuildMask = rebuildMask | (1 << i); } if ((ly == cy || lx == cx) && ( // Section to the back ((z == 0) && (lz + EngineSettings.ChunkConfig.Mask == cz)) || // Section to the front ((z == EngineSettings.ChunkConfig.Mask) && (lz - EngineSettings.ChunkConfig.Mask == cz)) )) { rebuildMask = rebuildMask | (1 << i); } } } m_setBlockQueue.Clear(); // Notify neighbors that they need to rebuilt their geometry if (rebuildMask > 0) { for (int j = 0; j < StateManager.Listeners.Length; j++) { ChunkStateManagerClient listener = (ChunkStateManagerClient)StateManager.Listeners[j]; if (listener != null && ((rebuildMask >> j) & 1) != 0) { listener.RequestState(ChunkState.FinalizeData | ChunkState.BuildVerticesNow); } } } }
public void Update() { ChunkStateManagerClient stateManager = (ChunkStateManagerClient)chunk.stateManager; if (m_setBlockQueue.Count > 0) { if (rebuildMaskGeometry < 0) { rebuildMaskGeometry = 0; } if (rebuildMaskCollider < 0) { rebuildMaskCollider = 0; } // Modify blocks for (int j = 0; j < m_setBlockQueue.Count; j++) { SetBlockContext context = m_setBlockQueue[j]; if (!context.IsRange()) { ProcessSetBlockQueue(context.Block, context.IndexFrom, context.SetBlockModified); } else { int sx, sy, sz, ex, ey, ez; Helpers.GetChunkIndex3DFrom1D(context.IndexFrom, out sx, out sy, out sz); Helpers.GetChunkIndex3DFrom1D(context.IndexTo, out ex, out ey, out ez); for (int y = sy; y <= ey; y++) { for (int z = sz; z <= ez; z++) { for (int x = sx; x <= ex; x++) { int index = Helpers.GetChunkIndex1DFrom3D(x, y, z); ProcessSetBlockQueue(context.Block, index, context.SetBlockModified); } } } } } rebuildMaskCollider |= rebuildMaskGeometry; m_setBlockQueue.Clear(); } long now = Globals.Watch.ElapsedMilliseconds; // Request a geometry update at most 10 times a second if (rebuildMaskGeometry >= 0 && now - lastUpdateTimeGeometry >= 100) { lastUpdateTimeGeometry = now; // Request rebuild on this chunk stateManager.RequestState(ChunkState.BuildVerticesNow);; // Notify neighbors that they need to rebuilt their geometry if (rebuildMaskGeometry > 0) { for (int j = 0; j < stateManager.Listeners.Length; j++) { ChunkStateManagerClient listener = (ChunkStateManagerClient)stateManager.Listeners[j]; if (listener != null && ((rebuildMaskGeometry >> j) & 1) != 0) { // Request rebuild on neighbor chunks listener.RequestState(ChunkState.BuildVerticesNow); } } } rebuildMaskGeometry = -1; } // Request a collider update at most 4 times a second if (chunk.NeedsCollider && rebuildMaskCollider >= 0 && now - lastUpdateTimeCollider >= 250) { lastUpdateTimeCollider = now; // Request rebuild on this chunk stateManager.RequestState(ChunkState.BuildCollider); // Notify neighbors that they need to rebuilt their geometry if (rebuildMaskCollider > 0) { for (int j = 0; j < stateManager.Listeners.Length; j++) { ChunkStateManagerClient listener = (ChunkStateManagerClient)stateManager.Listeners[j]; if (listener != null && ((rebuildMaskCollider >> j) & 1) != 0) { // Request rebuild on neighbor chunks if (listener.chunk.NeedsCollider) { listener.RequestState(ChunkState.BuildCollider); } } } } rebuildMaskCollider = -1; } }
private void ProcessSetBlockQueue(BlockData block, int index, bool setBlockModified) { int x, y, z; Helpers.GetChunkIndex3DFrom1D(index, out x, out y, out z); if (x < 0 || y < 0 || z < 0 || x > Env.ChunkMask || y > Env.ChunkMask || z > Env.ChunkMask) { Assert.IsTrue(false, "Chunk index out of range in setBlockQueue"); return; } Vector3Int pos = new Vector3Int(x, y, z); Vector3Int globalPos = pos + chunk.pos; BlockData oldBlockData = blocks[index]; Block oldBlock = m_blockTypes[oldBlockData.Type]; Block newBlock = m_blockTypes[block.Type]; oldBlock.OnDestroy(chunk, pos); newBlock.OnCreate(chunk, pos); SetInternal(index, ref pos, block); if (setBlockModified) { BlockModified(new BlockPos(x, y, z), globalPos, block); chunk.blocks.contentsInvalidated = true; } if ( // Only check neighbors if it is still needed rebuildMaskGeometry == 0x3f || // Only check neighbors when it is a change of a block on a chunk's edge (((pos.x + 1) & Env.ChunkMask) > 1 && ((pos.y + 1) & Env.ChunkMask) > 1 && ((pos.z + 1) & Env.ChunkMask) > 1) ) { return; } int cx = chunk.pos.x; int cy = chunk.pos.y; int cz = chunk.pos.z; ChunkStateManagerClient stateManager = (ChunkStateManagerClient)chunk.stateManager; // If it is an edge position, notify neighbor as well // Iterate over neighbors and decide which ones should be notified to rebuild for (int i = 0; i < stateManager.Listeners.Length; i++) { ChunkEvent listener = stateManager.Listeners[i]; if (listener == null) { continue; } // No further checks needed once we know all neighbors need to be notified if (rebuildMaskGeometry == 0x3f) { break; } ChunkStateManagerClient listenerClient = (ChunkStateManagerClient)listener; Chunk listenerChunk = listenerClient.chunk; int lx = listenerChunk.pos.x; int ly = listenerChunk.pos.y; int lz = listenerChunk.pos.z; if (ly == cy || lz == cz) { // Section to the left if ((pos.x == 0) && (lx + Env.ChunkSize == cx)) { rebuildMaskGeometry = rebuildMaskGeometry | (1 << i); // Mirror the block to the neighbor edge int neighborIndex = Helpers.GetChunkIndex1DFrom3D(Env.ChunkSize, y, z); listenerChunk.blocks.blocks[neighborIndex] = block; } // Section to the right else if ((pos.x == Env.ChunkMask) && (lx - Env.ChunkSize == cx)) { rebuildMaskGeometry = rebuildMaskGeometry | (1 << i); // Mirror the block to the neighbor edge int neighborIndex = Helpers.GetChunkIndex1DFrom3D(-1, y, z); listenerChunk.blocks.blocks[neighborIndex] = block; } } if (lx == cx || lz == cz) { // Section to the bottom if ((pos.y == 0) && (ly + Env.ChunkSize == cy)) { rebuildMaskGeometry = rebuildMaskGeometry | (1 << i); // Mirror the block to the neighbor edge int neighborIndex = Helpers.GetChunkIndex1DFrom3D(x, Env.ChunkSize, y); listenerChunk.blocks.blocks[neighborIndex] = block; } // Section to the top else if ((pos.y == Env.ChunkMask) && (ly - Env.ChunkSize == cy)) { rebuildMaskGeometry = rebuildMaskGeometry | (1 << i); // Mirror the block to the neighbor edge int neighborIndex = Helpers.GetChunkIndex1DFrom3D(x, -1, y); listenerChunk.blocks.blocks[neighborIndex] = block; } } if (ly == cy || lx == cx) { // Section to the back if ((pos.z == 0) && (lz + Env.ChunkSize == cz)) { rebuildMaskGeometry = rebuildMaskGeometry | (1 << i); // Mirror the block to the neighbor edge int neighborIndex = Helpers.GetChunkIndex1DFrom3D(x, y, Env.ChunkSize); listenerChunk.blocks.blocks[neighborIndex] = block; } // Section to the front else if ((pos.z == Env.ChunkMask) && (lz - Env.ChunkSize == cz)) { rebuildMaskGeometry = rebuildMaskGeometry | (1 << i); // Mirror the block to the neighbor edge int neighborIndex = Helpers.GetChunkIndex1DFrom3D(x, y, -1); listenerChunk.blocks.blocks[neighborIndex] = block; } } } }
public void Update() { ChunkStateManagerClient stateManager = (ChunkStateManagerClient)chunk.stateManager; if (m_setBlockQueue.Count > 0) { if (rebuildMaskGeometry < 0) { rebuildMaskGeometry = 0; } if (rebuildMaskCollider < 0) { rebuildMaskCollider = 0; } // Modify blocks for (int j = 0; j < m_setBlockQueue.Count; j++) { SetBlockContext context = m_setBlockQueue[j]; int x, y, z; Helpers.GetChunkIndex3DFrom1D(context.Index, out x, out y, out z); Vector3Int pos = new Vector3Int(x, y, z); Vector3Int globalPos = pos + chunk.pos; BlockData oldBlockData = blocks.Get(context.Index); Block oldBlock = m_blockTypes[oldBlockData.Type]; Block newBlock = m_blockTypes[context.Block.Type]; oldBlock.OnDestroy(chunk, pos, globalPos); newBlock.OnCreate(chunk, pos, globalPos); blocks.Set(context.Index, context.Block); if (context.SetBlockModified) { BlockModified(new BlockPos(x, y, z), globalPos, context.Block); chunk.blocks.contentsInvalidated = true; } if ( // Only check neighbors if it is still needed rebuildMaskGeometry == 0x3f || // Only check neighbors when it is a change of a block on a chunk's edge (((pos.x + 1) & Env.ChunkMask) > 1 && ((pos.y + 1) & Env.ChunkMask) > 1 && ((pos.z + 1) & Env.ChunkMask) > 1) ) { continue; } int cx = chunk.pos.x; int cy = chunk.pos.y; int cz = chunk.pos.z; // If it is an edge position, notify neighbor as well // Iterate over neighbors and decide which ones should be notified to rebuild for (int i = 0; i < stateManager.Listeners.Length; i++) { ChunkEvent listener = stateManager.Listeners[i]; if (listener == null) { continue; } // No further checks needed once we know all neighbors need to be notified if (rebuildMaskGeometry == 0x3f) { break; } ChunkStateManagerClient listenerChunk = (ChunkStateManagerClient)listener; int lx = listenerChunk.chunk.pos.x; int ly = listenerChunk.chunk.pos.y; int lz = listenerChunk.chunk.pos.z; if ((ly == cy || lz == cz) && ( // Section to the left ((pos.x == 0) && (lx + Env.ChunkSize == cx)) || // Section to the right ((pos.x == Env.ChunkMask) && (lx - Env.ChunkSize == cx)) )) { rebuildMaskGeometry = rebuildMaskGeometry | (1 << i); } if ((lx == cx || lz == cz) && ( // Section to the bottom ((pos.y == 0) && (ly + Env.ChunkSize == cy)) || // Section to the top ((pos.y == Env.ChunkMask) && (ly - Env.ChunkSize == cy)) )) { rebuildMaskGeometry = rebuildMaskGeometry | (1 << i); } if ((ly == cy || lx == cx) && ( // Section to the back ((pos.z == 0) && (lz + Env.ChunkSize == cz)) || // Section to the front ((pos.z == Env.ChunkMask) && (lz - Env.ChunkSize == cz)) )) { rebuildMaskGeometry = rebuildMaskGeometry | (1 << i); } } rebuildMaskCollider |= rebuildMaskGeometry; } m_setBlockQueue.Clear(); } long now = Globals.Watch.ElapsedMilliseconds; // Request a geometry update at most 10 times a second if (rebuildMaskGeometry >= 0 && now - lastUpdateTimeGeometry >= 100) { lastUpdateTimeGeometry = now; // Request rebuild on this chunk stateManager.RequestState(ChunkState.BuildVerticesNow);; // Notify neighbors that they need to rebuilt their geometry if (rebuildMaskGeometry > 0) { for (int j = 0; j < stateManager.Listeners.Length; j++) { ChunkStateManagerClient listener = (ChunkStateManagerClient)stateManager.Listeners[j]; if (listener != null && ((rebuildMaskGeometry >> j) & 1) != 0) { // Request rebuild on neighbor chunks listener.RequestState(ChunkState.BuildVerticesNow); } } } rebuildMaskGeometry = -1; } // Request a collider update at most 4 times a second if (chunk.NeedsCollider && rebuildMaskCollider >= 0 && now - lastUpdateTimeCollider >= 250) { lastUpdateTimeCollider = now; // Request rebuild on this chunk stateManager.RequestState(ChunkState.BuildCollider); // Notify neighbors that they need to rebuilt their geometry if (rebuildMaskCollider > 0) { for (int j = 0; j < stateManager.Listeners.Length; j++) { ChunkStateManagerClient listener = (ChunkStateManagerClient)stateManager.Listeners[j]; if (listener != null && ((rebuildMaskCollider >> j) & 1) != 0) { // Request rebuild on neighbor chunks if (listener.chunk.NeedsCollider) { listener.RequestState(ChunkState.BuildCollider); } } } } rebuildMaskCollider = -1; } }
private void OnDrawGizmosSelected() { if (!enabled) { return; } float size = Env.ChunkSize * Env.BlockSize; float halfSize = size * 0.5f; float smallSize = size * 0.25f; if (world != null && world.chunks != null && (Diag_DrawWorldBounds || Diag_DrawLoadRange)) { foreach (Chunk chunk in world.chunks.chunkCollection) { if (Diag_DrawWorldBounds) { // Make central chunks more apparent by using yellow color bool isCentral = chunk.pos.x == m_viewerPos.x || chunk.pos.y == m_viewerPos.y || chunk.pos.z == m_viewerPos.z; Gizmos.color = isCentral ? Color.yellow : Color.blue; Vector3 chunkCenter = new Vector3( chunk.pos.x + (Env.ChunkSize >> 1), chunk.pos.y + (Env.ChunkSize >> 1), chunk.pos.z + (Env.ChunkSize >> 1) ); Vector3 chunkSize = new Vector3(Env.ChunkSize, Env.ChunkSize, Env.ChunkSize); Gizmos.DrawWireCube(chunkCenter, chunkSize); } if (Diag_DrawLoadRange) { Vector3Int pos = chunk.pos; if (chunk.pos.y == 0) { int tx = m_clipmap.TransformX(pos.x / Env.ChunkSize); int ty = m_clipmap.TransformY(pos.y / Env.ChunkSize); int tz = m_clipmap.TransformZ(pos.z / Env.ChunkSize); if (!m_clipmap.IsInsideBounds_Transformed(tx, ty, tz)) { Gizmos.color = Color.red; Gizmos.DrawWireCube( new Vector3(pos.x + halfSize, 0, pos.z + halfSize), new Vector3(size - 1f, 0, size - 1f) ); } else { ClipmapItem item = m_clipmap.Get_Transformed(tx, ty, tz); if (item.IsInVisibleRange) { Gizmos.color = Color.green; Gizmos.DrawWireCube( new Vector3(pos.x + halfSize, 0, pos.z + halfSize), new Vector3(size - 1f, 0, size - 1f) ); } } } // Show generated chunks ChunkStateManagerClient stateManager = chunk.stateManager; if (stateManager.IsStateCompleted(ChunkState.Generate)) { Gizmos.color = Color.magenta; Gizmos.DrawWireCube( new Vector3(pos.x + halfSize, pos.y + halfSize, pos.z + halfSize), new Vector3(smallSize - 0.05f, smallSize - 0.05f, smallSize - 0.05f) ); } } } } }
private void UpdateVisibility(int x, int y, int z, int rangeX, int rangeY, int rangeZ) { if (rangeX == 0 || rangeY == 0 || rangeZ == 0) { return; } bool isLast = rangeX == 1 && rangeY == 1 && rangeZ == 1; int wx = x * Env.ChunkSize; int wy = y * Env.ChunkSize; int wz = z * Env.ChunkSize; // Stop if there is no further subdivision possible if (isLast) { // Update chunk's visibility information Vector3Int chunkPos = new Vector3Int(wx, wy, wz); Chunk chunk = world.chunks.Get(ref chunkPos); if (chunk == null) { return; } ChunkStateManagerClient stateManager = chunk.stateManager; int xd = (m_viewerPos.x - chunk.pos.x) / Env.ChunkSize; int yd = (m_viewerPos.y - chunk.pos.y) / Env.ChunkSize; int zd = (m_viewerPos.z - chunk.pos.z) / Env.ChunkSize; int xDist = xd * xd + zd * zd; int yDist = yd * yd; // Update visibility information bool isVisible = Planes.TestPlanesAABB(m_cameraPlanes, chunk.WorldBounds); stateManager.Visible = isVisible && xDist <= HorizontalChunkLoadRadius * HorizontalChunkLoadRadius && yDist <= VerticalChunkLoadRadius * VerticalChunkLoadRadius; stateManager.PossiblyVisible = isVisible || FullLoadOnStartUp; return; } int rx = rangeX * Env.ChunkSize; int ry = rangeY * Env.ChunkSize; int rz = rangeZ * Env.ChunkSize; /*AABB bounds2 = new AABB(wx, wy, wz, wx+rx, wy+ry, wz+rz); * int inside = 0; * * // if the camera position lies inside the bounding box we'll assume partial visibility automatically * if (bounds2.IsInside(m_camera.transform.position)) * inside = 3; * else * { * // Check whether the bouding box lies inside the camera's frustum * inside = Geometry.TestPlanesAABB2(m_cameraPlanes, bounds2); * }*/ // Check whether the bouding box lies inside the camera's frustum AABB bounds2 = new AABB(wx, wy, wz, wx + rx, wy + ry, wz + rz); int inside = Planes.TestPlanesAABB2(m_cameraPlanes, bounds2); #region Full invisibility if (inside == 0) { // Full invisibility. All chunks in this area need to be made invisible for (int cy = wy; cy < wy + ry; cy += Env.ChunkSize) { for (int cz = wz; cz < wz + rz; cz += Env.ChunkSize) { for (int cx = wx; cx < wx + rx; cx += Env.ChunkSize) { // Update chunk's visibility information Vector3Int chunkPos = new Vector3Int(cx, cy, cz); Chunk chunk = world.chunks.Get(ref chunkPos); if (chunk == null) { continue; } ChunkStateManagerClient stateManager = chunk.stateManager; // Update visibility information stateManager.PossiblyVisible = FullLoadOnStartUp; stateManager.Visible = false; } } } return; } #endregion #region Full visibility if (inside == 6) { // Full visibility. All chunks in this area need to be made visible for (int cy = wy; cy < wy + ry; cy += Env.ChunkSize) { for (int cz = wz; cz < wz + rz; cz += Env.ChunkSize) { for (int cx = wx; cx < wx + rx; cx += Env.ChunkSize) { // Update chunk's visibility information Vector3Int chunkPos = new Vector3Int(cx, cy, cz); Chunk chunk = world.chunks.Get(ref chunkPos); if (chunk == null) { continue; } ChunkStateManagerClient stateManager = chunk.stateManager; int xd = (m_viewerPos.x - chunk.pos.x) / Env.ChunkSize; int yd = (m_viewerPos.y - chunk.pos.y) / Env.ChunkSize; int zd = (m_viewerPos.z - chunk.pos.z) / Env.ChunkSize; int xDist = xd * xd + zd * zd; int yDist = yd * yd; // Update visibility information stateManager.Visible = xDist <= HorizontalChunkLoadRadius * HorizontalChunkLoadRadius && yDist <= VerticalChunkLoadRadius * VerticalChunkLoadRadius; stateManager.PossiblyVisible = true; } } } return; } #endregion #region Partial visibility int offX = rangeX; if (rangeX > 1) { offX = rangeX >> 1; rangeX = (rangeX + 1) >> 1; // ceil the number } int offY = rangeY; if (rangeY > 1) { offY = rangeY >> 1; rangeY = (rangeY + 1) >> 1; // ceil the number } int offZ = rangeZ; if (rangeZ > 1) { offZ = rangeZ >> 1; rangeZ = (rangeZ + 1) >> 1; // ceil the number } // Subdivide if possible // TODO: Avoid the recursion UpdateVisibility(x, y, z, offX, offY, offZ); UpdateVisibility(x + offX, y, z, rangeX, offY, offZ); UpdateVisibility(x, y, z + offZ, offX, offY, rangeZ); UpdateVisibility(x + offX, y, z + offZ, rangeX, offY, rangeZ); UpdateVisibility(x, y + offY, z, offX, rangeY, offZ); UpdateVisibility(x + offX, y + offY, z, rangeX, rangeY, offZ); UpdateVisibility(x, y + offY, z + offZ, offX, rangeY, rangeZ); UpdateVisibility(x + offX, y + offY, z + offZ, rangeX, rangeY, rangeZ); #endregion }
private void OnDrawGizmosSelected() { if (!enabled) { return; } float size = Env.ChunkSize * Env.BlockSize; float halfSize = size * 0.5f; float smallSize = size * 0.25f; if (world != null && world.chunks != null && (Diag_DrawWorldBounds || Diag_DrawLoadRange)) { foreach (Chunk chunk in world.chunks.chunkCollection) { if (Diag_DrawWorldBounds) { // Make central chunks more apparent by using yellow color bool isCentral = chunk.pos.x == m_viewerPos.x || chunk.pos.y == m_viewerPos.y || chunk.pos.z == m_viewerPos.z; Gizmos.color = isCentral ? Color.yellow : Color.blue; Vector3 chunkCenter = new Vector3( chunk.pos.x + (Env.ChunkSize >> 1), chunk.pos.y + (Env.ChunkSize >> 1), chunk.pos.z + (Env.ChunkSize >> 1) ); Vector3 chunkSize = new Vector3(Env.ChunkSize, Env.ChunkSize, Env.ChunkSize); Gizmos.DrawWireCube(chunkCenter, chunkSize); } if (Diag_DrawLoadRange) { Vector3Int pos = chunk.pos; int xd = Helpers.Abs((m_viewerPos.x - pos.x) / Env.ChunkSize); int zd = Helpers.Abs((m_viewerPos.z - pos.z) / Env.ChunkSize); int dist = xd * xd + zd * zd; if (dist <= HorizontalChunkLoadRadius * HorizontalChunkLoadRadius) { Gizmos.color = Color.green; Gizmos.DrawWireCube( new Vector3(chunk.pos.x + halfSize, 0, chunk.pos.z + halfSize), new Vector3(size - 1f, 0, size - 1f) ); } else { Gizmos.color = Color.red; Gizmos.DrawWireCube( new Vector3(chunk.pos.x + halfSize, 0, chunk.pos.z + halfSize), new Vector3(size - 1f, 0, size - 1f) ); } // Show generated chunks ChunkStateManagerClient stateManager = chunk.stateManager; if (stateManager.IsStateCompleted(ChunkState.Generate)) { Gizmos.color = Color.magenta; Gizmos.DrawWireCube( new Vector3(chunk.pos.x + halfSize, 0, chunk.pos.z + halfSize), new Vector3(smallSize - 0.05f, 0, smallSize - 0.05f) ); } } } } }
public void PostProcessChunks() { int minX = m_viewerPos.x - (HorizontalChunkLoadRadius * Env.ChunkSize); int maxX = m_viewerPos.x + (HorizontalChunkLoadRadius * Env.ChunkSize); int minY = m_viewerPos.y - (VerticalChunkLoadRadius * Env.ChunkSize); int maxY = m_viewerPos.y + (VerticalChunkLoadRadius * Env.ChunkSize); int minZ = m_viewerPos.z - (HorizontalChunkLoadRadius * Env.ChunkSize); int maxZ = m_viewerPos.z + (HorizontalChunkLoadRadius * Env.ChunkSize); world.CapCoordXInsideWorld(ref minX, ref maxX); world.CapCoordYInsideWorld(ref minY, ref maxY); world.CapCoordZInsideWorld(ref minZ, ref maxZ); world.Bounds = new AABBInt(minX, minY, minZ, maxX, maxY, maxZ); int expectedChunks = m_chunkPositions.Length * ((maxY - minY + Env.ChunkSize) / Env.ChunkSize); if (// No update necessary if there was no movement m_viewerPos == m_viewerPosPrev && // However, we need to make sure that we have enough chunks loaded world.chunks.Count >= expectedChunks) { return; } // Unregister any non-necessary pending structures Profiler.BeginSample("UnregisterStructures"); { world.UnregisterPendingStructures(); } Profiler.EndSample(); // Cycle through the array of positions Profiler.BeginSample("PostProcessChunks"); { WorldChunks chunks = world.chunks; // Cycle through the array of positions for (int y = maxY; y >= minY; y -= Env.ChunkSize) { for (int i = 0; i < m_chunkPositions.Length; i++) { // Skip loading chunks which are off limits int cx = (m_chunkPositions[i].x * Env.ChunkSize) + m_viewerPos.x; if (cx > maxX || cx < minX) { continue; } int cy = (m_chunkPositions[i].y * Env.ChunkSize) + y; if (cy > maxY || cy < minY) { continue; } int cz = (m_chunkPositions[i].z * Env.ChunkSize) + m_viewerPos.z; if (cz > maxZ || cz < minZ) { continue; } // Create a new chunk if possible Vector3Int newChunkPos = new Vector3Int(cx, cy, cz); Chunk chunk; if (!chunks.CreateOrGetChunk(ref newChunkPos, out chunk)) { continue; } if (FullLoadOnStartUp) { ChunkStateManagerClient stateManager = chunk.stateManager; stateManager.PossiblyVisible = true; stateManager.Visible = false; } m_updateRequests.Add(chunk); } } } Profiler.EndSample(); }
private void UpdateVisibility(int x, int y, int z, int rangeX, int rangeY, int rangeZ) { if (rangeX == 0 || rangeY == 0 || rangeZ == 0) { return; } bool isLast = rangeX == 1 && rangeY == 1 && rangeZ == 1; int wx = m_viewerPos.x + (x * Env.ChunkSize); int wy = m_viewerPos.y + (y * Env.ChunkSize); int wz = m_viewerPos.z + (z * Env.ChunkSize); int rx = rangeX * Env.ChunkSize; int ry = rangeY * Env.ChunkSize; int rz = rangeZ * Env.ChunkSize; // Stop if there is no further subdivision possible if (isLast) { // Update chunk's visibility information Vector3Int chunkPos = new Vector3Int(wx, wy, wz); Chunk chunk = world.chunks.Get(ref chunkPos); if (chunk == null) { return; } ChunkStateManagerClient stateManager = chunk.stateManager; int tx = m_clipmap.TransformX(x); int ty = m_clipmap.TransformY(y); int tz = m_clipmap.TransformZ(z); // Skip chunks which are too far away if (!m_clipmap.IsInsideBounds_Transformed(tx, ty, tz)) { return; } // Update visibility information ClipmapItem item = m_clipmap.Get_Transformed(tx, ty, tz); bool isVisible = Planes.TestPlanesAABB(m_cameraPlanes, chunk.WorldBounds); stateManager.Visible = isVisible && item.IsInVisibleRange; stateManager.PossiblyVisible = isVisible || FullLoadOnStartUp; return; } // Check whether the bouding box lies inside the camera's frustum AABB bounds2 = new AABB(wx, wy, wz, wx + rx, wy + ry, wz + rz); int inside = Planes.TestPlanesAABB2(m_cameraPlanes, bounds2); #region Full invisibility if (inside == 0) { // Full invisibility. All chunks in this area need to be made invisible for (int cy = wy; cy < wy + ry; cy += Env.ChunkSize) { for (int cz = wz; cz < wz + rz; cz += Env.ChunkSize) { for (int cx = wx; cx < wx + rx; cx += Env.ChunkSize) { // Update chunk's visibility information Vector3Int chunkPos = new Vector3Int(cx, cy, cz); Chunk chunk = world.chunks.Get(ref chunkPos); if (chunk == null) { continue; } ChunkStateManagerClient stateManager = chunk.stateManager; // Update visibility information stateManager.PossiblyVisible = FullLoadOnStartUp; stateManager.Visible = false; } } } return; } #endregion #region Full visibility if (inside == 6) { // Full visibility. All chunks in this area need to be made visible for (int cy = wy; cy < wy + ry; cy += Env.ChunkSize) { for (int cz = wz; cz < wz + rz; cz += Env.ChunkSize) { for (int cx = wx; cx < wx + rx; cx += Env.ChunkSize) { // Update chunk's visibility information Vector3Int chunkPos = new Vector3Int(cx, cy, cz); Chunk chunk = world.chunks.Get(ref chunkPos); if (chunk == null) { continue; } ChunkStateManagerClient stateManager = chunk.stateManager; int tx = m_clipmap.TransformX(x); int ty = m_clipmap.TransformY(y); int tz = m_clipmap.TransformZ(z); // Update visibility information ClipmapItem item = m_clipmap.Get_Transformed(tx, ty, tz); stateManager.Visible = item.IsInVisibleRange; stateManager.PossiblyVisible = true; } } } return; } #endregion #region Partial visibility int offX = rangeX; if (rangeX > 1) { offX = rangeX >> 1; rangeX = (rangeX + 1) >> 1; // ceil the number } int offY = rangeY; if (rangeY > 1) { offY = rangeY >> 1; rangeY = (rangeY + 1) >> 1; // ceil the number } int offZ = rangeZ; if (rangeZ > 1) { offZ = rangeZ >> 1; rangeZ = (rangeZ + 1) >> 1; // ceil the number } // Subdivide if possible // TODO: Avoid the recursion UpdateVisibility(x, y, z, offX, offY, offZ); UpdateVisibility(x + offX, y, z, rangeX, offY, offZ); UpdateVisibility(x, y, z + offZ, offX, offY, rangeZ); UpdateVisibility(x + offX, y, z + offZ, rangeX, offY, rangeZ); UpdateVisibility(x, y + offY, z, offX, rangeY, offZ); UpdateVisibility(x + offX, y + offY, z, rangeX, rangeY, offZ); UpdateVisibility(x, y + offY, z + offZ, offX, rangeY, rangeZ); UpdateVisibility(x + offX, y + offY, z + offZ, rangeX, rangeY, rangeZ); #endregion }