/// <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 ); } } } }
/// <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)); }