/// <summary> /// Creates a new chunk object based on the given chunk position. /// </summary> /// <param name="chunkPos">The position of the chunk.</param> /// <returns>The newly created chunk game object.</returns> internal BlockChunk LoadChunk(ChunkPosition chunkPos) { var go = new GameObject($"Chunk: ({chunkPos.X}, {chunkPos.Y}, {chunkPos.Z})"); var chunk = go.AddComponent <BlockChunk>(); chunk.Position = chunkPos; go.hideFlags = HideFlags.HideAndDontSave; go.transform.SetParent(m_Transform); go.transform.localPosition = new Vector3(chunkPos.X, chunkPos.Y, chunkPos.Z) * m_ChunkSize.Value; var meshFilter = go.AddComponent <MeshFilter>(); var meshCollider = go.AddComponent <MeshCollider>(); go.AddComponent <MeshRenderer>(); meshFilter.sharedMesh = new Mesh { name = $"Chunk Visual: ({chunkPos.X}, {chunkPos.Y}, {chunkPos.Z})" }; meshCollider.sharedMesh = new Mesh { name = $"Chunk Collision: ({chunkPos.X}, {chunkPos.Y}, {chunkPos.Z})" }; return(chunk); }
/// <summary> /// Gets the chunk at the target chunk position. /// </summary> /// <param name="chunkPos">The chunk position.</param> /// <param name="create">Whether or not to create the chunk if it doesn't currently exist.</param> /// <returns>The chunk, or null if it doesn't exist.</returns> private Chunk GetChunk(ChunkPosition chunkPos, bool create) { var chunk = World.GetChunk(chunkPos); if (chunk != null || !create) { return(chunk); } chunk = World.CreateChunk(chunkPos); bool remesh = ChunkLoader.LoadSync(chunk); if (remesh) { m_DirtyChunks.Add(chunkPos); m_DirtyChunks.Add(chunkPos.ShiftAlongDirection(0)); m_DirtyChunks.Add(chunkPos.ShiftAlongDirection(1)); m_DirtyChunks.Add(chunkPos.ShiftAlongDirection(2)); m_DirtyChunks.Add(chunkPos.ShiftAlongDirection(3)); m_DirtyChunks.Add(chunkPos.ShiftAlongDirection(4)); m_DirtyChunks.Add(chunkPos.ShiftAlongDirection(5)); RemeshDirtyChunks(); } return(chunk); }
/// <summary> /// Determines which chunks should be remeshed based on the given block position. /// </summary> /// <param name="blockPos">The local block position.</param> /// <param name="chunkPos">The chunk position.</param> private void RemeshEffectedChunks(BlockPosition blockPos, ChunkPosition chunkPos) { m_DirtyChunks.Add(chunkPos); if (blockPos.X == 0) { m_DirtyChunks.Add(chunkPos.ShiftAlongDirection(1)); } if (blockPos.X == World.ChunkSize.Mask) { m_DirtyChunks.Add(chunkPos.ShiftAlongDirection(0)); } if (blockPos.Y == 0) { m_DirtyChunks.Add(chunkPos.ShiftAlongDirection(3)); } if (blockPos.Y == World.ChunkSize.Mask) { m_DirtyChunks.Add(chunkPos.ShiftAlongDirection(2)); } if (blockPos.Z == 0) { m_DirtyChunks.Add(chunkPos.ShiftAlongDirection(5)); } if (blockPos.Z == World.ChunkSize.Mask) { m_DirtyChunks.Add(chunkPos.ShiftAlongDirection(4)); } }
/// <summary> /// Destroys the chunk at the given chunk coordinates if it exists. /// </summary> /// <param name="pos">The chunk position.</param> internal void DestroyChunk(ChunkPosition pos) { var chunk = GetChunk(pos); if (chunk != null) { m_Chunks.Remove(chunk); } }
/// <summary> /// Analyses the given chunk and starts a set of remesh tasks for handling that chunk. /// This method blocks until all remesh tasks have been completed. /// </summary> /// <param name="worldContainer">The world to operate on.</param> /// <param name="chunkPos">The chunk target position.</param> internal RemeshTaskStack RemeshChunk(WorldContainer worldContainer, ChunkPosition chunkPos) { var taskStack = new RemeshTaskStack(chunkPos); var chunkGroup = new ChunkGroup(worldContainer, chunkPos); foreach (var dis in m_Distributors) { dis.CreateTasks(chunkGroup, taskStack); } return(taskStack); }
/// <summary> /// Gets the chunk at the given chunk position. /// </summary> /// <param name="chunkPos">The chunk position.</param> /// <returns>The Block Chunk.</returns> private BlockChunk GetChunk(ChunkPosition chunkPos) { foreach (var chunk in m_Chunks) { if (chunk.Position.Equals(chunkPos)) { return(chunk); } } return(null); }
/// <summary> /// Gets the chunk at the given chunk position. /// </summary> /// <param name="pos">The position of the chunk.</param> /// <returns>The block container, or null if it doesn't exist.</returns> internal Chunk GetChunk(ChunkPosition pos) { for (int i = 0; i < m_Chunks.Count; i++) { if (m_Chunks[i].Position.Equals(pos)) { return(m_Chunks[i]); } } return(null); }
/// <summary> /// Marks a given chunk to be remeshed. /// </summary> /// <param name="chunkPos">The position of the chunk to remesh.</param> private void MarkForRemesh(ChunkPosition chunkPos) { if (!World.DoesChunkExist(chunkPos)) { return; } if (m_DirtyChunks.Contains(chunkPos)) { return; } m_DirtyChunks.Add(chunkPos); }
/// <summary> /// Requests the chunk at the given position to start loading in the background. /// </summary> /// <param name="chunkPos">The chunk position.</param> /// <returns>True if the operation was started. False if the chunk is already loaded.</returns> public bool LoadChunkAsync(ChunkPosition chunkPos) { var chunk = World.GetChunk(chunkPos); if (chunk != null) { return(false); } chunk = World.CreateChunk(chunkPos); ChunkLoader.LoadAsync(chunk); return(true); }
/// <summary> /// Creates a new chunk at the given chunk position if it doesn't currently exist. /// </summary> /// <param name="pos">The chunk position.</param> /// <returns> /// The newly created chunk, or the current chunk if if already exists. /// </returns> internal Chunk CreateChunk(ChunkPosition pos) { var oldChunk = GetChunk(pos); if (oldChunk != null) { return(oldChunk); } var newChunk = new Chunk(ChunkSize, pos); m_Chunks.Add(newChunk); return(newChunk); }
/// <summary> /// Checks for changes in the camera's chunk position and updates the field /// and resets the iterator as needed. /// </summary> private void UpdateCameraPosition() { var pos = m_Camera.transform.position; var chunkPos = new ChunkPosition { X = Mathf.FloorToInt(pos.x) >> m_BlockWorld.ChunkSize.IntBits, Y = Mathf.FloorToInt(pos.y) >> m_BlockWorld.ChunkSize.IntBits, Z = Mathf.FloorToInt(pos.z) >> m_BlockWorld.ChunkSize.IntBits, }; if (!chunkPos.Equals(m_LastCameraPosition)) { m_LastCameraPosition = chunkPos; m_ChunkLoadPatternIterator.Reset(); } }
/// <summary> /// Triggers the given at the given position to be remeshed, if not already in the list. /// Preforms no action if the chunk does not exist. /// </summary> /// <param name="chunkPos">The chunk position.</param> /// <param name="later">Whether to remesh the chunk now or later.</param> private void RemeshChunkAt(ChunkPosition chunkPos, bool later) { if (!World.DoesChunkExist(chunkPos)) { return; } if (later) { RemeshHandler.RemeshChunkLater(chunkPos); } else { RemeshHandler.RemeshChunk(chunkPos); } }
/// <summary> /// Gets or creates the chunk at the given position. /// </summary> /// <param name="chunkPos">The chunk position.</param> /// <returns>The block chunk.</returns> private BlockChunk GetChunk(ChunkPosition chunkPos) { foreach (var chunk in m_Chunks) { if (chunk.Position.Equals(chunkPos)) { return(chunk); } } var c = m_ChunkCreator.LoadChunk(chunkPos); m_Chunks.Add(c); return(c); }
/// <inheritdoc cref="ChunkLoadingPattern"/> protected override void GeneratePositions(ChunkPosition[] positions, Vector3Int extents) { int i = 0; for (int x = -extents.x; x <= extents.x; x++) { for (int y = -extents.y; y <= extents.y; y++) { for (int z = -extents.z; z <= extents.z; z++) { positions[i++] = new ChunkPosition(x, y, z); } } } System.Array.Sort(positions, (a, b) => (a.X * a.X + a.Y * a.Y + a.Z * a.Z) - (b.X * b.X + b.Y * b.Y + b.Z * b.Z)); }
/// <summary> /// Schedules the chunk to be remeshed at a later point in time, after all primary /// remesh tasks are finished. /// </summary> /// <param name="chunkPos">The target chunk position.</param> /// <see cref="RemeshChunk(ChunkProperties)"/> internal void RemeshChunkLater(ChunkPosition chunkPos) { foreach (var task in m_ActiveTasks) { if (task.ChunkPosition.Equals(chunkPos)) { return; } } foreach (var task in m_PendingRemesh) { if (task.Equals(chunkPos)) { return; } } m_PendingRemesh.Add(chunkPos); }
/// <summary> /// Force loads all chunks within a given region, if not already loaded. /// </summary> /// <param name="center">The center of the bounding region.</param> /// <param name="extents">The radius of each axis.</param> /// <returns>True if any additional chunks were loaded.</returns> internal bool LoadChunkRegion(ChunkPosition center, Vector3Int extents) { // TODO Remove "was loaded" check, to stop tasks from being run synchronously. var min = new Vector3Int { x = center.X - extents.x, y = center.Y - extents.y, z = center.Z - extents.z, }; var max = new Vector3Int { x = center.X + extents.x, y = center.Y + extents.y, z = center.Z + extents.z, }; bool loaded = false; for (int x = min.x; x <= max.x; x++) { for (int y = min.y; y <= max.y; y++) { for (int z = min.z; z <= max.z; z++) { var chunkPos = new ChunkPosition(x, y, z); var task = new LoadChunkTask(chunkPos); m_ServerThread.RunTaskSync(task); loaded |= task.WasJustLoaded; } } } return(loaded); }
/// <summary> /// Analyses the given chunk and starts a set of remesh tasks for handling that chunk. /// If the chunk was scheduled to be remeshed later, it will be immediately be remeshed. /// If the chunk is already being remeshed, this method does nothing. /// </summary> /// <param name="chunkPos">The chunk target position.</param> /// <param name="pendingTask"> /// If true, this task is allowed to task any number of frames to finish. Otherwise, the task /// is required to finish the next frame. /// </param> internal void RemeshChunk(ChunkPosition chunkPos, bool pendingTask = false) { for (int i = 0; i < m_ActiveTasks.Count; i++) { if (m_ActiveTasks[i].ChunkPosition.Equals(chunkPos)) { m_ActiveTasks[i].IsPendingTask = false; return; } } for (int i = 0; i < m_PendingRemesh.Count; i++) { if (m_PendingRemesh[i].Equals(chunkPos)) { m_PendingRemesh.RemoveAt(i); break; } } var properties = m_ChunkPropertiesPool.Pull(); var blockList = m_BlockWorld.BlockList; var world = m_BlockWorld.WorldContainer.World; ChunkAnalyzer.LoadProperties(properties, blockList, world, chunkPos); var taskStack = new RemeshTaskStack(properties); taskStack.IsPendingTask = pendingTask; m_ActiveTasks.Add(taskStack); foreach (var dis in m_Distributors) { dis.CreateTasks(properties, taskStack); } }
/// <summary> /// Requests the chunk at the given position to start loading in the background. /// </summary> /// <param name="chunkPos">The chunk position.</param> internal void LoadChunkAsync(ChunkPosition chunkPos) => m_ServerThread.RunTask(new LoadChunkTask(chunkPos));
/// <summary> /// Checks if the given chunk exists or not. /// </summary> /// <returns>True if the chunk exists, false otherwise.</returns> public bool DoesChunkExist(ChunkPosition chunkPos) => World.DoesChunkExist(chunkPos);
/// <summary> /// Causes the target chunk to be loaded, if not already loaded. /// </summary> public void LoadChunk(ChunkPosition chunkPos) => GetChunk(chunkPos, true);
/// <summary> /// Gets whether or not the target chunk exists. /// </summary> /// <param name="pos">The position of the chunk.</param> /// <returns>True if the chunk exists, false otherwise.</returns> internal bool DoesChunkExist(ChunkPosition pos) => GetChunk(pos) != null;
/// <summary> /// Creates a new chunk object. /// </summary> /// <param name="chunkSize">The chunk size.</param> /// <param name="position">The size of this chunk in the world.</param> internal Chunk(GridSize chunkSize, ChunkPosition position) { Size = chunkSize; Position = position; Blocks = new ushort[Size.Volume]; }
/// <summary> /// Requests the chunk at the given position to start loading in the background. /// </summary> /// <param name="chunkPos">The chunk position.</param> /// <returns>True if the operation was started. False if the chunk is already loaded.</returns> public bool LoadChunkAsync(ChunkPosition chunkPos) => m_WorldContainer.LoadChunkAsync(chunkPos);
/// <summary> /// Creates a new remesh task stack. /// </summary> /// <param name="chunkPos">The chunk position.</param> internal RemeshTaskStack(ChunkPosition chunkPos) { ChunkPosition = chunkPos; }