public LoadChunkDataFromFileJob(Chunk.ID chunkID, string levelName) { this.chunkID = chunkID; this.levelName = levelName; solidVoxelCount = new NativeArray <int>(1, Allocator.TempJob); outVoxels = new NativeArray <byte>(Chunk.Diameter * Chunk.Diameter * Chunk.Diameter, Allocator.TempJob); }
public SaveChunkDataToFileJob(Chunk.ID chunkID, string levelName, NativeArray <byte> inputVoxels, int solidVoxelCount) { this.chunkID = chunkID; this.levelName = levelName; this.solidVoxelCount = solidVoxelCount; saveVoxels = inputVoxels; }
/// <summary> /// Schedule the job for loading new data from file /// </summary> /// <param name="chunkID"></param> /// <returns></returns> public override ApertureJobHandle getJobFor(Chunk.ID chunkID, FocusAdjustmentType adjustmentType) { IJob job; if (adjustmentType == FocusAdjustmentType.InFocus) { if (LevelDAO.ChunkFileExists(chunkID, level)) { job = new LevelDAO.LoadChunkDataFromFileJob(chunkID, level.name); // if there's no file, we need to generate the chunk data from scratch } else { job = BiomeMap.GetTerrainGenerationJob(chunkID, level); } /// if it's out of focus, we want to save the chunk to file } else if (level.chunks.TryGetValue(chunkID, out Chunk chunkToSave)) { job = new LevelDAO.SaveChunkDataToFileJob(chunkID, level.name, chunkToSave.getVoxels(), chunkToSave.solidVoxelCount); } else { throw new System.MissingMemberException( $"VoxelDataAperture is trying to save chunk data for {chunkID} but could not find the chunk data in the level" ); } return(new ApertureJobHandle(job, this)); }
/// <summary> /// Get the voxeldata for a chunk location from file /// </summary> /// <returns>False if the chunk is empty</returns> static bool GetDataForChunkFromFile(Chunk.ID chunkId, string levelName, out ChunkSaveData chunkData) { chunkData = default; IFormatter formatter = new BinaryFormatter(); Stream readStream = new FileStream( GetChunkDataFileName(chunkId, levelName), FileMode.Open, FileAccess.Read, FileShare.Read ) { Position = 0 }; var fileData = formatter.Deserialize(readStream); if (fileData is ChunkSaveData) { chunkData = (ChunkSaveData)fileData; readStream.Close(); return(true); } readStream.Close(); return(false); }
/// <summary> /// If the chunk is loaded but empty, we should just drop it from this queue /// </summary> /// <param name="chunkID"></param> /// <returns></returns> internal override bool validateChunk(Chunk.ID chunkID, out Chunk chunk) { // if this is valid for meshing // and if the chunk is meshed and isn't empty, or just isn't meshed, it's valid for the queue. return(level.getApetureByPriority(Level.AperturePriority.Meshed).validateChunk(chunkID, out chunk) && !(chunk != null && chunk.meshIsGenerated && chunk.meshIsEmpty)); }
/// <summary> /// Make a mesh /// </summary> public VoxelMeshData( Chunk.ID forChunk, bool meshDataIsEmpty, NativeArray <Vector3> vertices, NativeArray <int> triangles, NativeArray <Color> colors ) { chunkID = forChunk; if (meshDataIsEmpty) { this.vertices = null; this.triangles = null; this.colors = null; } else { this.vertices = new Vector3[vertices.Length]; vertices.CopyTo(this.vertices); this.triangles = new int[triangles.Length]; triangles.CopyTo(this.triangles); this.colors = new Color[colors.Length]; colors.CopyTo(this.colors); } }
/// <summary> /// Validate the chunk for this aperture. /// Should it be being managed still? /// </summary> /// <param name="chunkID">The id of the chunk to validate</param> /// <param name="chunk">The chunk grabbed for validation, used to speed up chains/callbacks</param> /// <returns></returns> internal virtual bool validateChunk(Chunk.ID chunkID, out Chunk chunk) { if (!level.chunks.TryGetValue(chunkID, out chunk)) { chunk = null; } return(true); }
/// <summary> /// Only to be used by jobs /// Save a chunk to file /// </summary> /// <param name="chunkLocation"></param> static public void SaveChunkDataToFile(Chunk.ID chunkId, string levelName, NativeArray <byte> voxelsToSave, int solidVoxelCount) { IFormatter formatter = new BinaryFormatter(); CheckForSaveDirectory(levelName); Stream stream = new FileStream(GetChunkDataFileName(chunkId, levelName), FileMode.Create, FileAccess.Write, FileShare.None); formatter.Serialize(stream, new ChunkSaveData(voxelsToSave, solidVoxelCount)); stream.Close(); }
/// <summary> /// Get the distance from this chunk to the closest level focus /// </summary> /// <param name="chunkID"></param> /// <returns></returns> float getDistanceToClosestFocus(Chunk.ID chunkID, float yWeightMultiplier = 5.0f) { float closestFocusDistance = float.MaxValue; level.forEachFocus(focus => { float focusDistance = focus.currentChunk.Coordinate.distanceYFlattened(chunkID.Coordinate, yWeightMultiplier); closestFocusDistance = focusDistance < closestFocusDistance ? focusDistance : closestFocusDistance; }); return(closestFocusDistance); }
/// <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> /// Schedule the activate chunk job /// </summary> /// <param name="chunkID"></param> /// <returns></returns> public override ApertureJobHandle getJobFor(Chunk.ID chunkID, FocusAdjustmentType adjustmentType) { IJob job; if (adjustmentType == FocusAdjustmentType.InFocus) { job = new ActivateChunkObjectJob(chunkID); } else { job = new DeactivateChunkObjectJob(chunkID); } return(new ApertureJobHandle(job, this)); }
/// <summary> /// Do things for different jobs /// </summary> /// <param name="chunkID"></param> /// <param name="finishedJobHandle"></param> protected override void handleFinishedJob(Chunk.ID chunkID, ref ApertureJobHandle finishedJobHandle) { Chunk newlyLoadedChunk = new Chunk(); switch (finishedJobHandle.job) { /// same for both, but they're structs so can't inherit. case LevelDAO.LoadChunkDataFromFileJob lcdffj: // if we didn't generate an empty chunk, copy the voxels over. if (lcdffj.solidVoxelCount[0] > 0) { newlyLoadedChunk.setVoxels( lcdffj.outVoxels, lcdffj.solidVoxelCount[0] ); } // also dispose of the native array lcdffj.outVoxels.Dispose(finishedJobHandle.jobHandle); lcdffj.solidVoxelCount.Dispose(finishedJobHandle.jobHandle); break; case BiomeMap.GenerateChunkDataFromSourceJob gcdfsj: if (gcdfsj.solidVoxelCount[0] > 0) { newlyLoadedChunk.setVoxels( gcdfsj.outVoxels, gcdfsj.solidVoxelCount[0] ); } gcdfsj.outVoxels.Dispose(finishedJobHandle.jobHandle); gcdfsj.solidVoxelCount.Dispose(finishedJobHandle.jobHandle); break; /// once the chunk data is saved, remove it from the level case LevelDAO.SaveChunkDataToFileJob scdtfj: level.chunks.Remove(scdtfj.chunkID); break; default: return; } // add chunk to the level newlyLoadedChunk.isLoaded = true; level.chunks.Add(chunkID, newlyLoadedChunk); }
/// <summary> /// Generate all the voxels for the given chunk id using the provided biome /// </summary> /// <param name="biome"></param> /// <param name="chunkID"></param> /// <param name="generatedVoxels"></param> /// <returns>the number of solid voxels generated</returns> static int GenerateTerrainDataForChunk(VoxelSource biome, Chunk.ID chunkID, out byte[] generatedVoxels) { int solidVoxelCount = 0; generatedVoxels = null; byte[] voxels = new byte[Chunk.Diameter * Chunk.Diameter * Chunk.Diameter]; Coordinate chunkWorldLocation = chunkID.toWorldLocation(); chunkWorldLocation.until(chunkWorldLocation + Chunk.Diameter, currentWorldLocation => { byte voxelValue = biome.getVoxelValueAt(currentWorldLocation); if (voxelValue != Voxel.Types.Empty.Id) { solidVoxelCount++; Coordinate localChunkVoxelLocation = currentWorldLocation - chunkWorldLocation; voxels[localChunkVoxelLocation.flatten(Chunk.Diameter)] = voxelValue; } }); generatedVoxels = voxels; return solidVoxelCount; }
/// <summary> /// Get the biome for the given level seed and chunk id /// </summary> /// <param name="chunkID"></param> /// <param name="levelSeed"></param> /// <returns></returns> static VoxelSource GetBiomeForChunk(Chunk.ID chunkID, int levelSeed) { return new PerlinSource(levelSeed); }
/// <summary> /// Get the file name a chunk is saved to based on it's location /// </summary> /// <param name="chunkLocation">the location of the chunk</param> /// <returns></returns> static string GetChunkDataFileName(Chunk.ID chunk, string levelName) { return($"{GetChunkDataFolder(levelName)}{chunk.Coordinate}.evxch"); }
/// <summary> /// Get a chunk terrain generation job from the biome map /// </summary> /// <param name="chunkID"></param> /// <param name="level"></param> /// <returns></returns> public static GenerateChunkDataFromSourceJob GetTerrainGenerationJob(Chunk.ID chunkID, Level level) { return new GenerateChunkDataFromSourceJob(chunkID, level.seed); }
///// PUBLIC FUNCTIONS /// <summary> /// Create and schedule the child job for this chunk using a unity IJob /// </summary> /// <param name="chunkID"></param> public abstract ApertureJobHandle getJobFor(Chunk.ID chunkID, FocusAdjustmentType adjustmentType);
public SetChunkInactiveEvent(Chunk.ID chunkID) { this.chunkID = chunkID; name = $"Setting chunk active: {chunkID.Coordinate}"; }
/// <summary> /// Get a file load job /// </summary> /// <param name="chunkID"></param> /// <param name="level"></param> /// <returns></returns> public static LoadChunkDataFromFileJob GetFileLoadJob(Chunk.ID chunkID, Level level) { return(new LoadChunkDataFromFileJob(chunkID, level.name)); }
/// <summary> /// Check if the chunk save file exists /// </summary> /// <param name=""></param> /// <param name="level"></param> /// <returns></returns> public static bool ChunkFileExists(Chunk.ID chunkID, Level level) { return(File.Exists(GetChunkDataFileName(chunkID, level.name))); }
/// <summary> /// The chunk is ready to mesh when the mesh is generated but not empty /// </summary> /// <param name="chunkID"></param> /// <returns></returns> public override bool chunkIsReady(Chunk.ID chunkID) { return(level.chunks.TryGetValue(chunkID, out Chunk chunk) && chunk.meshIsGenerated && !chunk.meshIsEmpty); }
public GenerateChunkDataFromSourceJob(Chunk.ID chunkID, int levelSeed) { this.chunkID = chunkID; this.levelSeed = levelSeed; solidVoxelCount = new NativeArray<int>(1, Allocator.Persistent); outVoxels = new NativeArray<byte>(Chunk.Diameter * Chunk.Diameter * Chunk.Diameter, Allocator.Persistent); }
/// <summary> /// Make a new adjustment /// </summary> /// <param name="chunkID"></param> /// <param name="adjustmentType"></param> public ApetureChunkAdjustment(Chunk.ID chunkID, FocusAdjustmentType adjustmentType = FocusAdjustmentType.InFocus) { this.chunkID = chunkID; type = adjustmentType; }
/// <summary> /// Set the previous to the current chunk the level thinks we're in, to keep it up to date /// </summary> public void onFocusUpdatedForLevel(Chunk.ID toNewChunk) { previousChunk = toNewChunk; }
/// <summary> /// Schedule a mesh job. Can only be called from the main thread! /// </summary> /// <param name="chunkID"></param> /// <returns></returns> public override ApertureJobHandle getJobFor(Chunk.ID chunkID, FocusAdjustmentType adjustmentType) { IJob job; /// if it's an in focus job if (adjustmentType == FocusAdjustmentType.InFocus) { // get the prep'd data and send it to the job if (preparedVoxelData.TryGetValue(chunkID, out byte[] marchVoxelData)) {
/// <summary> /// Do nothing on job complete /// </summary> /// <param name="chunkID"></param> /// <param name="finishedJob"></param> protected override void handleFinishedJob(Chunk.ID chunkID, ref ApertureJobHandle finishedJob) { }
/// <summary> /// If the chunk is already loaded, we don't need to load it again /// </summary> /// <param name="chunkID"></param> /// <param name="chunk"></param> /// <returns></returns> internal override bool validateChunk(Chunk.ID chunkID, out Chunk chunk) { return(base.validateChunk(chunkID, out chunk) && (chunk == null || !chunk.isLoaded)); }
/// <summary> /// Set the world position of the focus. Also sets the chunk position. /// </summary> public void setPosition(Coordinate worldPosition) { transform.position = worldLocation = previousWorldLocation = worldPosition.vec3; currentChunk = previousChunk = worldLocation / Chunk.Diameter; }
public ActivateChunkObjectJob(Chunk.ID chunkID) { this.chunkID = chunkID; }