/// <summary>
        /// Initilize the level queue manager to follow the foci and appetures of the level
        /// </summary>
        /// <param name="level"></param>
        public void initializeFor(Level level)
        {
            if (chunkObjectPrefab == null)
            {
                World.Debugger.logError("UnityLevelController Missing chunk prefab, can't work");
            }
            else if (level == null)
            {
                World.Debugger.logError("No level provided to level controller");
            }
            else
            {
                /// init
                this.level = level;

                // build the controller pool based on the maxed meshed chunk area we should ever have:
                ChunkResolutionAperture meshResolutionAperture = level.getApetureByPriority(Level.AperturePriority.Meshed);
                int diameterToMeshManager = meshResolutionAperture != null ? meshResolutionAperture.managedChunkRadius : level.chunkBounds.x;
                chunkControllerPool = new ChunkController[diameterToMeshManager * diameterToMeshManager * level.chunkBounds.y * 2];
                for (int index = 0; index < chunkControllerPool.Length; index++)
                {
                    // for each chunk we want to be able to render at once, create a new pooled gameobject for it with the prefab that has a unitu chunk controller on it
                    GameObject chunkObject = Instantiate(chunkObjectPrefab);
                    chunkObject.transform.parent = gameObject.transform;
                    ChunkController chunkController = chunkObject.GetComponent <ChunkController>();
                    if (chunkController == null)
                    {
                        World.Debugger.logError($"No chunk controller on {chunkObject.name}");
                    }
                    else
                    {
                        chunkControllerPool[index]   = chunkController;
                        chunkController.levelManager = this;
                        chunkObject.SetActive(false);
                    }
                }

                /// this controller is now loaded
                isLoaded = true;

                /// add the focus initilization jobs to the queue for each apeture
                level.forEachFocus(focus => {
                    level.forEachAperture(aperture => {
                        foreach (ChunkResolutionAperture.ApetureChunkAdjustment chunkAdjustment in aperture.getAdjustmentsForFocusInitilization(focus))
                        {
                            apertureJobQueue.Enqueue(getCurrentPriorityForChunk(chunkAdjustment.chunkID, aperture), chunkAdjustment);
                        }
                    });
                });

                /// start the manager job in a seperate thread
                apertureJobQueueManagerThread = new Thread(() => ManageQueue())
                {
                    Name = "Level Aperture Queue Manager"
                };
                apertureJobQueueManagerThread.Start();
            }
        }
        /// <summary>
        /// if the queue item is ready to go, or should be put back in the queue
        /// </summary>
        /// <returns></returns>
        bool itemIsReady(Chunk.ID chunkID, ChunkResolutionAperture aperture)
        {
            bool isReady = aperture.chunkIsReady(chunkID);

            if (isReady)
            {
                aperture.prepareChunkJobData(chunkID);
            }

            return(isReady);
        }
 /// <summary>
 /// Get the priority object for a given chunk being loaded by the given apeture
 /// </summary>
 /// <param name="chunkID"></param>
 /// <param name="aperture"></param>
 /// <returns></returns>
 ApertureWorkQueuePriority getCurrentPriorityForChunk(
     Chunk.ID chunkID,
     ChunkResolutionAperture aperture,
     ChunkResolutionAperture.FocusAdjustmentType adjustmentType = ChunkResolutionAperture.FocusAdjustmentType.InFocus
     )
 {
     return(new ApertureWorkQueuePriority(
                aperture.priority,
                (int)getDistanceToClosestFocus(chunkID, aperture.yWeightMultiplier),
                adjustmentType
                ));
 }
        /// <summary>
        /// Manage which chunks are queued for loading with what level apertures.
        /// </summary>
        void ManageQueue()
        {
            while (runLevelManagerQueue)
            {
                /// Step 1: listen for focus changes.
                level.forEachFocus(focus => {
                    if (focus.currentChunk != focus.previousChunk)
                    {
                        /// Step 2: get all the aperture jobs for the focus changes into the queue.
                        level.forEachAperture(aperture => {
                            foreach (ChunkResolutionAperture.ApetureChunkAdjustment adjustment in aperture.getAdjustmentsForFocusLocationChange(focus))
                            {
                                apertureJobQueue.Enqueue(getCurrentPriorityForChunk(adjustment.chunkID, aperture, adjustment.type), adjustment);
                            }
                        });

                        // callback, to update the focus' chunk location
                        focus.onFocusUpdatedForLevel(focus.currentChunk);
                    }
                });

                /// Step 3: Iterate over the next item in the queue and try to schedule a job for it
                if (apertureJobQueue.TryDequeue(out KeyValuePair <ApertureWorkQueuePriority, ChunkResolutionAperture.ApetureChunkAdjustment> queueItemWithPriority))
                {
                    // if the item was cancled or is invalid, skip it.
                    ChunkResolutionAperture apertureForQueueItem = level.getApetureByPriority(queueItemWithPriority.Key.aperturePriority);
                    if (!isAValidQueueItem(queueItemWithPriority.Value, apertureForQueueItem))
                    {
                        continue;
                    }

                    // if the item is ready, offer it up to the running jobs to pick up.
                    // we'll try to add it to the running jobs list
                    if (itemIsReady(queueItemWithPriority.Value.chunkID, apertureForQueueItem) &&
                        !runningJobs.ContainsKey(queueItemWithPriority.Value)
                        )
                    {
                        adjustmentJobsReadyToRun.TryAdd(queueItemWithPriority.Value, apertureForQueueItem);
                        World.Debugger.log($"Apeture Job type {apertureForQueueItem.GetType()} ready for {queueItemWithPriority.Value.chunkID}");
                        // if it's not ready, or there's a conflict requeue
                        // if there's a conflict, it means a job is already running on this chunk and we should wait for that one to finish
                    }
                    else
                    {
                        apertureJobQueue.Enqueue(
                            getCurrentPriorityForChunk(queueItemWithPriority.Value.chunkID, apertureForQueueItem, queueItemWithPriority.Key.adjustmentType),
                            queueItemWithPriority.Value
                            );
                    }
                }
            }
        }
Exemple #5
0
        /// <summary>
        /// Draw the managed apetures around this focus
        /// </summary>
        void OnDrawGizmos()
        {
            // ignore gizmo if inactive
            if (!isActive)
            {
                return;
            }

            Level   level = World.Current.activeLevel;
            Vector3 worldChunkLocation = ((currentChunk.Coordinate * Chunk.Diameter) + (Chunk.Diameter / 2)).vec3;

            /// draw the chunk this focus is in
            Gizmos.color = new Color(1.0f, 0.64f, 0.0f);
            Gizmos.DrawWireCube(worldChunkLocation, new Vector3(Chunk.Diameter, Chunk.Diameter, Chunk.Diameter));
            worldChunkLocation -= new Vector3((Chunk.Diameter / 2), (Chunk.Diameter / 2), (Chunk.Diameter / 2));

            /// draw the active chunk area
            ChunkResolutionAperture activeAperture = level.getApetureByPriority(Level.AperturePriority.Active);

            Gizmos.color = Color.green;
            Gizmos.DrawWireCube(worldChunkLocation, new Vector3(
                                    activeAperture.managedChunkRadius * 2,
                                    Mathf.Min(activeAperture.managedChunkHeightRadius * 2, level.chunkBounds.y),
                                    activeAperture.managedChunkRadius * 2
                                    ) * Chunk.Diameter);

            /// draw the meshed chunk area
            ChunkResolutionAperture meshAperture = level.getApetureByPriority(Level.AperturePriority.Meshed);

            Gizmos.color = Color.blue;
            Gizmos.DrawWireCube(worldChunkLocation, new Vector3(
                                    meshAperture.managedChunkRadius * 2,
                                    Mathf.Min(meshAperture.managedChunkHeightRadius * 2, level.chunkBounds.y),
                                    meshAperture.managedChunkRadius * 2
                                    ) * Chunk.Diameter);

            /// draw the meshed chunk area
            ChunkResolutionAperture loadedAperture = level.getApetureByPriority(Level.AperturePriority.Loaded);

            Gizmos.color = Color.yellow;
            Gizmos.DrawWireCube(worldChunkLocation, new Vector3(
                                    loadedAperture.managedChunkRadius * 2,
                                    Mathf.Min(loadedAperture.managedChunkHeightRadius * 2, level.chunkBounds.y),
                                    loadedAperture.managedChunkRadius * 2
                                    ) * Chunk.Diameter);
        }
 /// <summary>
 /// validate queue items
 /// </summary>
 /// <returns></returns>
 bool isAValidQueueItem(ChunkResolutionAperture.ApetureChunkAdjustment adjustment, ChunkResolutionAperture aperture)
 {
     return(aperture.chunkIsValid(adjustment));
 }