Пример #1
0
        /// <summary>
        /// Initialises a new instance of the ChunkJobQueue class.
        /// </summary>
        /// <param name="chunk">The chunk.</param>
        /// <param name="masterJobs">The current master jobs which must be immediately queued.</param>
        public ChunkJobQueue(Vector2I chunk, List<Job> masterJobs)
        {
            this.Chunk = chunk;
            this.State = new ChunkJobQueueState(chunk);

            // Enqueue the existing master jobs
            if (masterJobs.Count > 0)
            {
                foreach (Job job in masterJobs)
                {
                    job.AddOwners(this);
                    this.Enqueue(job);
                }
            }
        }
Пример #2
0
        /// <summary>
        /// Gets the chunk indices within the given bounds.
        /// </summary>
        /// <param name="bounds">The bounds.</param>
        /// <returns>The chunk indices.</returns>
        public static Vector2I[] GetChunks(RectangleI bounds)
        {
            var chunks = new Vector2I[bounds.Width * bounds.Height];

            int i = 0;
            for (int x = bounds.X; x < bounds.X + bounds.Width; x++)
            {
                for (int y = bounds.Y; y > bounds.Y - bounds.Height; y--)
                {
                    chunks[i++] = new Vector2I(x, y);
                }
            }

            return chunks;
        }
Пример #3
0
        /// <summary>
        /// Initialises a new instance of the TerrainChunk class.
        /// </summary>
        /// <param name="chunkIndex">The chunk index.</param>
        public TerrainChunk(Vector2I chunkIndex)
        {
            this.Index = chunkIndex;
            this.Points = new TerrainPoint[Metrics.ChunkWidth, Metrics.ChunkHeight];
            this.Mesh = new TerrainChunkMesh();

            // Initialise the points
            for (int x = 0; x < Metrics.ChunkWidth; x++)
            {
                for (int y = 0; y < Metrics.ChunkHeight; y++)
                {
                    this.Points[x, y] = new TerrainPoint();
                }
            }
        }
Пример #4
0
        /// <summary>
        /// Rebuild the MeshData for the given chunk. This is a heavy operation which takes a few frames to complete,
        /// so is best handled asynchronously.
        /// </summary>
        /// <param name="chunkIndex">The chunk index.</param>
        public void RebuildMesh(Vector2I chunkIndex)
        {
            TerrainChunk chunk = this.terrain.GetChunk(chunkIndex);

            // Initialise the arrays of shared indices
            var sharedIndices = new SharedIndices(Metrics.ChunkWidth, Metrics.ChunkWidth);

            // Clear the mesh data
            chunk.Mesh.Data.Clear();

            // Create the mesh for each cell in the chunk
            var chunkOrigin = Metrics.GetChunkOrigin(chunkIndex);
            for (int z = -1; z < Metrics.ChunkDepth; z++)
            {
                for (int x = chunkOrigin.X; x < chunkOrigin.X + Metrics.ChunkWidth; x++)
                {
                    for (int y = chunkOrigin.Y; y < chunkOrigin.Y + Metrics.ChunkHeight; y++)
                    {
                        this.CreateMeshCell(new Vector3I(x, y, z), chunk.Mesh.Data, sharedIndices);
                    }
                }
            }
        }
Пример #5
0
 /// <summary>
 /// Gets the chunks being synchronised.
 /// </summary>
 /// <returns>The chunks being synchronised.</returns>
 public Vector2I[] GetChunks()
 {
     var chunks = new Vector2I[this.Chunks.Count];
     this.Chunks.Keys.CopyTo(chunks, 0);
     return chunks;
 }
Пример #6
0
 /// <summary>
 /// Indicates that a chunk is ready.
 /// </summary>
 /// <param name="chunk">The chunk.</param>
 public void SetReady(Vector2I chunk)
 {
     this.content.SetMeshFilterUpdateReady(chunk);
 }
Пример #7
0
        /// <summary>
        /// Remove a chunk.
        /// </summary>
        /// <param name="chunkIndex">The chunk index.</param>
        public void RemoveChunk(Vector2I chunkIndex)
        {
            bool removed;
            lock (this.chunksLock)
            {
                removed = this.chunks.Remove(chunkIndex);
            }

            if (removed && this.ChunkRemoved != null)
            {
                this.ChunkRemoved(this, chunkIndex);
            }
        }
Пример #8
0
 /// <summary>
 /// Gets the chunks. This is a thread-safe operation to be used when accessing the terrain from outside of a
 /// scheduled job.
 /// </summary>
 /// <returns>The chunks.</returns>
 public Vector2I[] GetChunksThreadSafe()
 {
     lock (this.chunksLock)
     {
         var chunks = new Vector2I[this.chunks.Count];
         this.chunks.Keys.CopyTo(chunks, 0);
         return chunks;
     }
 }
 /// <summary>
 /// Gets the label for the given chunk.
 /// </summary>
 /// <param name="chunkIndex">The chunk index.</param>
 /// <returns>The chunk label.</returns>
 public static string GetLabel(Vector2I chunkIndex)
 {
     return "Chunk[" + chunkIndex.X + "," + chunkIndex.Y + "]";
 }
        /// <summary>
        /// Update the MeshFilter data for this chunk.
        /// </summary>
        /// <param name="chunksToSync">The chunks that need to have their mesh filter updated in the same frame as
        /// this. Null if no chunk sync is required.</param>
        private void UpdateMeshFilterJob(Vector2I[] chunksToSync)
        {
            // The chunk GameObject (this) is destroyed after the logical instance, so check that it still exists
            TerrainChunk chunk;
            if (chunksToSync == null)
            {
                if (TerrainSystem.Instance.Terrain.TryGetChunk(this.Chunk, out chunk))
                {
                    // Copy the mesh data into arrays
                    var mesh = new MeshArrays(
                        this.Chunk,
                        chunk.Mesh.Data.Vertices.ToArray(),
                        chunk.Mesh.Data.Normals.ToArray(),
                        chunk.Mesh.Data.Indices.ToArray(),
                        chunk.Mesh.Data.Light.ToArray());

                    // Update the mesh filter geometry
                    GameScheduler.Instance.Invoke(
                        () =>
                        {
                            this.cMeshFilter.mesh.Clear();
                            this.cMeshFilter.mesh.vertices = mesh.Vertices;
                            this.cMeshFilter.mesh.normals = mesh.Normals;
                            this.cMeshFilter.mesh.triangles = mesh.Triangles;
                            this.cMeshFilter.mesh.colors = mesh.Colors;
                        });
                }
            }
            else
            {
                var meshes = new MeshArrays[chunksToSync.Length];
                for (int i = 0; i < meshes.Length; i++)
                {
                    Vector2I chunkIndex = chunksToSync[i];
                    if (TerrainSystem.Instance.Terrain.TryGetChunk(chunkIndex, out chunk))
                    {
                        // Copy the mesh data into arrays
                        meshes[i] = new MeshArrays(
                            chunkIndex,
                            chunk.Mesh.Data.Vertices.ToArray(),
                            chunk.Mesh.Data.Normals.ToArray(),
                            chunk.Mesh.Data.Indices.ToArray(),
                            chunk.Mesh.Data.Light.ToArray());
                    }
                }

                // Update the mesh filter geometry
                GameScheduler.Instance.Invoke(
                    () =>
                    {
                        foreach (MeshArrays mesh in meshes)
                        {
                            if (mesh == null)
                            {
                                continue;
                            }

                            Transform chunkTransform =
                                this.transform.parent.FindChild(TerrainChunkComponent.GetLabel(mesh.Chunk));
                            if (chunkTransform == null)
                            {
                                continue;
                            }

                            TerrainChunkComponent cChunk = chunkTransform.GetComponent<TerrainChunkComponent>();
                            cChunk.cMeshFilter.mesh.Clear();
                            cChunk.cMeshFilter.mesh.vertices = mesh.Vertices;
                            cChunk.cMeshFilter.mesh.normals = mesh.Normals;
                            cChunk.cMeshFilter.mesh.triangles = mesh.Triangles;
                            cChunk.cMeshFilter.mesh.colors = mesh.Colors;
                        }
                    });
            }
        }
Пример #11
0
 /// <summary>
 /// Check whether a LoadPoints job can execute.
 /// </summary>
 /// <param name="chunk">The chunk being loaded.</param>
 /// <returns>True if the job can be enqueued.</returns>
 public bool CanLoadPoints(Vector2I chunk)
 {
     return this.Chunk != chunk || !this.loadPointsInProgress;
 }
Пример #12
0
        /// <summary>
        /// Check whether a DigCircle job can execute.
        /// </summary>
        /// <param name="chunk">The chunk in which the origin lies.</param>
        /// <param name="origin">The circle origin.</param>
        /// <param name="radius">The circle radius.</param>
        /// <returns>True if the job can be enqueued.</returns>
        public bool CanDigCircle(Vector2I chunk, Vector2I origin, int radius)
        {
            if (chunk == this.Chunk)
            {
                bool exists;
                int existing;
                lock ((this.digCircleInProgress as ICollection).SyncRoot)
                {
                    exists = this.digCircleInProgress.TryGetValue(origin, out existing);
                }

                return !exists || radius > existing;
            }
            else
            {
                return true;
            }
        }
Пример #13
0
 /// <summary>
 /// Determine if the vector is equal.
 /// </summary>
 /// <param name="other">The vector to test.</param>
 /// <returns>True if the vectors are equal.</returns>
 public bool Equals(Vector2I other)
 {
     return other.X == this.X && other.Y == this.Y;
 }
Пример #14
0
        /// <summary>
        /// Dig a circle in the terrain with the given origin and radius.
        /// </summary>
        /// <param name="origin">The origin.</param>
        /// <param name="radius">The radius.</param>
        public void DigCircle(Vector2 origin, float radius)
        {
            Vector2I originI = new Vector2I((int)Math.Round(origin.x), (int)Math.Round(origin.y));
            int radiusI = (int)radius;
            Vector2I chunk = Metrics.ChunkIndex(originI.X, originI.Y);

            // Get the affected chunks
            var worldBounds = new RectangleI(
                (int)(origin.x - radius - 0.5f) - 1,
                (int)(origin.y + radius + 0.5f) + 1,
                (int)((radius * 2) + 0.5f) + 3,
                (int)((radius * 2) + 0.5f) + 3);
            RectangleI chunkBounds = Metrics.WorldToChunk(worldBounds);
            Vector2I[] chunks = TerrainChunk.GetChunks(chunkBounds);
            SynchronisedUpdate toSync = chunks.Length > 1 ? new SynchronisedUpdate(chunks) : null;

            // Enqueue the job
            JobSystem.Instance.Scheduler.BeginEnqueueChunks();
            try
            {
                if (JobSystem.Instance.Scheduler.ForAllChunks(
                    chunks,
                    (q) => q.State.CanDigCircle(chunk, originI, radiusI),
                    MissingQueue.Skip))
                {
                    JobSystem.Instance.Scheduler.EnqueueChunks(
                        () => this.DigCircleJob(origin, radius),
                        (q) => q.State.ReserveDigCircle(chunk, originI, radiusI, toSync),
                        (q) => q.State.UnreserveDigCircle(chunk, originI, radiusI),
                        false,
                        chunks);
                }
            }
            finally
            {
                JobSystem.Instance.Scheduler.EndEnqueueChunks();
            }
        }
Пример #15
0
        /// <summary>
        /// Set the foreground density of the given point.
        /// </summary>
        /// <param name="position">The position.</param>
        /// <param name="density">The density.</param>
        private void SetDensityPoint(Vector2I position, byte density)
        {
            Vector2I chunkIndex = Metrics.ChunkIndex(position.X, position.Y);

            // Get the chunk
            TerrainChunk chunk;
            if (this.terrain.TryGetChunk(chunkIndex, out chunk))
            {
                Vector2I chunkPos = Metrics.WorldToChunk(position);
                TerrainPoint point = chunk.Points[chunkPos.X, chunkPos.Y];
                if (point != null)
                {
                    if (point.Foreground < density)
                    {
                        point.Foreground = density;
                    }
                }
            }
        }
Пример #16
0
 /// <summary>
 /// Un-reserves a RebuildMesh job.
 /// </summary>
 /// <param name="chunk">The chunk being rebuilt.</param>
 public void UnreserveRebuildMesh(Vector2I chunk)
 {
     if (this.Chunk == chunk)
     {
         this.rebuildMeshInProgress = false;
     }
 }
Пример #17
0
 /// <summary>
 /// Initialises a new instance of the ChunkJobQueueState class.
 /// </summary>
 /// <param name="chunk">The chunk.</param>
 public ChunkJobQueueState(Vector2I chunk)
 {
     this.Chunk = chunk;
     this.digCircleInProgress = new Dictionary<Vector2I, int>();
 }
Пример #18
0
 /// <summary>
 /// Check whether a RebuildMesh job can execute.
 /// </summary>
 /// <param name="chunk">The chunk being rebuilt.</param>
 /// <returns>True if the job can be enqueued.</returns>
 public bool CanRebuildMesh(Vector2I chunk)
 {
     return this.Chunk != chunk || (!this.rebuildMeshInProgress && this.rebuildMeshRequired);
 }
 /// <summary>
 /// Initialises a new instance of the MeshArrays class.
 /// </summary>
 /// <param name="chunk">The chunk.</param>
 /// <param name="vertices">The vertices.</param>
 /// <param name="normals">The normal vectors.</param>
 /// <param name="triangles">The triangles.</param>
 /// <param name="colors">The colours.</param>
 public MeshArrays(Vector2I chunk, Vector3[] vertices, Vector3[] normals, int[] triangles, Color[] colors)
 {
     this.Chunk = chunk;
     this.Vertices = vertices;
     this.Normals = normals;
     this.Triangles = triangles;
     this.Colors = colors;
 }
Пример #20
0
        /// <summary>
        /// Check whether a UpdateMeshFilter job can execute.
        /// </summary>
        /// <param name="chunksToSync">The chunks that need to have their mesh filter updated in the same frame as
        /// this. Null if no chunk sync is required.</param>
        /// <returns>True if the job can be enqueued.</returns>
        public bool CanUpdateMeshFilter(out Vector2I[] chunksToSync)
        {
            if (!this.updateMeshFilterInProgress && this.updateMeshFilterRequired)
            {
                if (this.meshFilterSync == null)
                {
                    chunksToSync = null;
                    return true;
                }
                else if (this.meshFilterSync.IsSynchronised)
                {
                    chunksToSync = this.meshFilterSync.GetChunks();
                    return true;
                }
            }

            chunksToSync = null;
            return false;
        }
Пример #21
0
 /// <summary>
 /// Get the chunk.
 /// </summary>
 /// <param name="chunkIndex">The chunk index.</param>
 /// <returns>The chunk.</returns>
 public TerrainChunk GetChunk(Vector2I chunkIndex)
 {
     return this.chunks[chunkIndex];
 }
Пример #22
0
        /// <summary>
        /// Reserves a DigCircle job.
        /// </summary>
        /// <param name="chunk">The chunk in which the origin lies.</param>
        /// <param name="origin">The circle origin.</param>
        /// <param name="radius">The circle radius.</param>
        /// <param name="toSync">The chunks requiring synchronisation such that their mesh filters are updated in
        /// the same frame.</param>
        public void ReserveDigCircle(Vector2I chunk, Vector2I origin, int radius, SynchronisedUpdate toSync)
        {
            if (chunk == this.Chunk)
            {
                lock ((this.digCircleInProgress as ICollection).SyncRoot)
                {
                    if (this.digCircleInProgress.ContainsKey(origin))
                    {
                        this.digCircleInProgress[origin] = radius;
                    }
                    else
                    {
                        this.digCircleInProgress.Add(origin, radius);
                    }
                }
            }

            this.rebuildMeshRequired = true;
            this.meshFilterSync = SynchronisedUpdate.MergeAndReset(this.meshFilterSync, toSync);
        }
Пример #23
0
 /// <summary>
 /// Determine if the given chunk exists.
 /// </summary>
 /// <param name="chunkIndex">The chunk index.</param>
 /// <returns>True if the chunk exists.</returns>
 public bool HasChunk(Vector2I chunkIndex)
 {
     return this.chunks.ContainsKey(chunkIndex);
 }
Пример #24
0
        /// <summary>
        /// Reserves a LoadPoints job.
        /// </summary>
        /// <param name="chunk">The chunk being loaded.</param>
        public void ReserveLoadPoints(Vector2I chunk)
        {
            if (this.Chunk == chunk)
            {
                this.loadPointsInProgress = true;
            }

            this.rebuildMeshRequired = true;
        }
Пример #25
0
 /// <summary>
 /// Try to get the chunk.
 /// </summary>
 /// <param name="chunkIndex">The chunk index.</param>
 /// <param name="chunk">The chunk.</param>
 /// <returns>True if the chunk exists.</returns>
 public bool TryGetChunk(Vector2I chunkIndex, out TerrainChunk chunk)
 {
     return this.chunks.TryGetValue(chunkIndex, out chunk);
 }
Пример #26
0
        /// <summary>
        /// Reserves a RebuildMesh job.
        /// </summary>
        /// <param name="chunk">The chunk being rebuilt.</param>
        public void ReserveRebuildMesh(Vector2I chunk)
        {
            if (this.Chunk == chunk)
            {
                this.rebuildMeshInProgress = true;

                // Merge the rebuild mesh sync chunks into the update mesh filter chunks
                this.updateMeshFilterRequired = true;
                if (this.meshFilterSync != null)
                {
                    this.meshFilterSync.SetReady(this.Chunk);
                }

                // Reset the rebuild mesh required flags
                this.rebuildMeshRequired = false;
            }
        }
Пример #27
0
 /// <summary>
 /// Initialises a new instance of the Content class.
 /// </summary>
 /// <param name="chunks">The chunks being synchronised.</param>
 public Content(Vector2I[] chunks)
 {
     this.Chunks = new Dictionary<Vector2I, bool>();
     foreach (Vector2I chunk in chunks)
     {
         this.Chunks.Add(chunk, false);
     }
 }
Пример #28
0
 /// <summary>
 /// Un-reserves a DigCircle job.
 /// </summary>
 /// <param name="chunk">The chunk in which the origin lies.</param>
 /// <param name="origin">The circle origin.</param>
 /// <param name="radius">The circle radius.</param>
 public void UnreserveDigCircle(Vector2I chunk, Vector2I origin, int radius)
 {
     if (chunk == this.Chunk)
     {
         lock ((this.digCircleInProgress as ICollection).SyncRoot)
         {
             if (this.digCircleInProgress[origin] == radius)
             {
                 this.digCircleInProgress.Remove(origin);
             }
         }
     }
 }
Пример #29
0
 /// <summary>
 /// Indicates that a chunk is ready for a mesh filter update.
 /// </summary>
 /// <param name="chunk">The chunk.</param>
 public void SetMeshFilterUpdateReady(Vector2I chunk)
 {
     if (!this.Chunks[chunk])
     {
         this.Chunks[chunk] = true;
         this.meshFilterReadyCount++;
     }
 }
Пример #30
0
 /// <summary>
 /// Un-reserves a LoadPoints job.
 /// </summary>
 /// <param name="chunk">The chunk being loaded.</param>
 public void UnreserveLoadPoints(Vector2I chunk)
 {
     if (this.Chunk == chunk)
     {
         this.loadPointsInProgress = false;
         this.LoadPointsCompleted = true;
     }
 }