public override void GenerateDataForChunkUnchecked(int3 chunkCoordinate, VoxelDataVolume <byte> existingData) { int3 chunkWorldOrigin = chunkCoordinate * VoxelWorld.WorldSettings.ChunkSize; JobHandleWithData <IVoxelDataGenerationJob> jobHandleWithData = VoxelWorld.VoxelDataGenerator.GenerateVoxelData(chunkWorldOrigin, existingData); _generationJobHandles.Add(chunkCoordinate, jobHandleWithData); }
/// <summary> /// Starts generating the voxel data for a specified volume /// </summary> /// <param name="bounds">The world-space volume to generate the voxel data for</param> /// <param name="allocator">The allocator for the new voxel data array</param> /// <returns>The job handle and the voxel data generation job</returns> public JobHandleWithData <IVoxelDataGenerationJob> GenerateVoxelData(BoundsInt bounds, Allocator allocator) { VoxelDataVolume <byte> voxelDataArray = new VoxelDataVolume <byte>(bounds.size, allocator); int3 worldSpaceOrigin = bounds.min.ToInt3(); return(GenerateVoxelData(worldSpaceOrigin, voxelDataArray)); }
/// <summary> /// Sets the voxel data for a volume in the world /// </summary> /// <param name="voxelDataVolume">The new voxel data volume</param> /// <param name="originPosition">The world position of the origin where the voxel data should be set</param> public void SetVoxelDataCustom(VoxelDataVolume voxelDataVolume, int3 originPosition) { Bounds worldSpaceQuery = new Bounds(); worldSpaceQuery.SetMinMax(originPosition.ToVectorInt(), (originPosition + voxelDataVolume.Size - new int3(1, 1, 1)).ToVectorInt()); int chunkSize = VoxelWorld.WorldSettings.ChunkSize; int3 minChunkCoordinate = VectorUtilities.WorldPositionToCoordinate(worldSpaceQuery.min - Vector3Int.one, chunkSize); int3 maxChunkCoordinate = VectorUtilities.WorldPositionToCoordinate(worldSpaceQuery.max + Vector3Int.one, chunkSize); for (int chunkCoordinateX = minChunkCoordinate.x; chunkCoordinateX <= maxChunkCoordinate.x; chunkCoordinateX++) { for (int chunkCoordinateY = minChunkCoordinate.y; chunkCoordinateY <= maxChunkCoordinate.y; chunkCoordinateY++) { for (int chunkCoordinateZ = minChunkCoordinate.z; chunkCoordinateZ <= maxChunkCoordinate.z; chunkCoordinateZ++) { int3 chunkCoordinate = new int3(chunkCoordinateX, chunkCoordinateY, chunkCoordinateZ); if (!TryGetVoxelDataChunk(chunkCoordinate, out VoxelDataVolume voxelDataChunk)) { continue; } Vector3 chunkBoundsSize = new Vector3(voxelDataChunk.Width - 1, voxelDataChunk.Height - 1, voxelDataChunk.Depth - 1); int3 chunkWorldSpaceOrigin = chunkCoordinate * chunkSize; Bounds chunkWorldSpaceBounds = new Bounds(); chunkWorldSpaceBounds.SetMinMax(chunkWorldSpaceOrigin.ToVectorInt(), chunkWorldSpaceOrigin.ToVectorInt() + chunkBoundsSize); Bounds intersectionVolume = IntersectionUtilities.GetIntersectionVolume(worldSpaceQuery, chunkWorldSpaceBounds); int3 intersectionVolumeMin = intersectionVolume.min.ToInt3(); int3 intersectionVolumeMax = intersectionVolume.max.ToInt3(); for (int voxelDataWorldPositionX = intersectionVolumeMin.x; voxelDataWorldPositionX <= intersectionVolumeMax.x; voxelDataWorldPositionX++) { for (int voxelDataWorldPositionY = intersectionVolumeMin.y; voxelDataWorldPositionY <= intersectionVolumeMax.y; voxelDataWorldPositionY++) { for (int voxelDataWorldPositionZ = intersectionVolumeMin.z; voxelDataWorldPositionZ <= intersectionVolumeMax.z; voxelDataWorldPositionZ++) { int3 voxelDataWorldPosition = new int3(voxelDataWorldPositionX, voxelDataWorldPositionY, voxelDataWorldPositionZ); if (voxelDataChunk.TryGetVoxelData(voxelDataWorldPosition - worldSpaceQuery.min.ToInt3(), out float voxelData)) { voxelDataChunk.SetVoxelData(voxelData, voxelDataWorldPosition - chunkWorldSpaceOrigin); } } } } if (VoxelWorld.ChunkStore.TryGetChunkAtCoordinate(chunkCoordinate, out Chunk chunk)) { chunk.HasChanges = true; } } } } }
/// <summary> /// Gets the voxel data of a custom volume in the world /// </summary> /// <param name="bounds">The world-space volume to get the voxel data for</param> /// <param name="allocator">How the new voxel data volume should be allocated</param> /// <returns>The voxel data volume inside the bounds</returns> public VoxelDataVolume GetVoxelDataCustom(Bounds bounds, Allocator allocator) { VoxelDataVolume voxelDataVolume = new VoxelDataVolume(bounds.size.ToInt3(), allocator); Bounds worldSpaceQuery = bounds; int chunkSize = VoxelWorld.WorldSettings.ChunkSize; int3 minChunkCoordinate = VectorUtilities.WorldPositionToCoordinate(worldSpaceQuery.min - Vector3Int.one, chunkSize); int3 maxChunkCoordinate = VectorUtilities.WorldPositionToCoordinate(worldSpaceQuery.max + Vector3Int.one, chunkSize); for (int chunkCoordinateX = minChunkCoordinate.x; chunkCoordinateX <= maxChunkCoordinate.x; chunkCoordinateX++) { for (int chunkCoordinateY = minChunkCoordinate.y; chunkCoordinateY <= maxChunkCoordinate.y; chunkCoordinateY++) { for (int chunkCoordinateZ = minChunkCoordinate.z; chunkCoordinateZ <= maxChunkCoordinate.z; chunkCoordinateZ++) { int3 chunkCoordinate = new int3(chunkCoordinateX, chunkCoordinateY, chunkCoordinateZ); if (!TryGetVoxelDataChunk(chunkCoordinate, out VoxelDataVolume voxelDataChunk)) { continue; } Vector3 chunkBoundsSize = new Vector3(voxelDataChunk.Width - 1, voxelDataChunk.Height - 1, voxelDataChunk.Depth - 1); int3 chunkWorldSpaceOrigin = chunkCoordinate * chunkSize; Bounds chunkWorldSpaceBounds = new Bounds(); chunkWorldSpaceBounds.SetMinMax(chunkWorldSpaceOrigin.ToVectorInt(), chunkWorldSpaceOrigin.ToVectorInt() + chunkBoundsSize); Bounds intersectionVolume = IntersectionUtilities.GetIntersectionVolume(worldSpaceQuery, chunkWorldSpaceBounds); int3 intersectionVolumeMin = intersectionVolume.min.ToInt3(); int3 intersectionVolumeMax = intersectionVolume.max.ToInt3(); for (int voxelDataWorldPositionX = intersectionVolumeMin.x; voxelDataWorldPositionX < intersectionVolumeMax.x; voxelDataWorldPositionX++) { for (int voxelDataWorldPositionY = intersectionVolumeMin.y; voxelDataWorldPositionY < intersectionVolumeMax.y; voxelDataWorldPositionY++) { for (int voxelDataWorldPositionZ = intersectionVolumeMin.z; voxelDataWorldPositionZ < intersectionVolumeMax.z; voxelDataWorldPositionZ++) { int3 voxelDataWorldPosition = new int3(voxelDataWorldPositionX, voxelDataWorldPositionY, voxelDataWorldPositionZ); int3 voxelDataLocalPosition = voxelDataWorldPosition - chunkWorldSpaceOrigin; if (voxelDataChunk.TryGetVoxelData(voxelDataLocalPosition, out float voxelData)) { voxelDataVolume.SetVoxelData(voxelData, voxelDataWorldPosition - bounds.min.ToInt3()); } } } } } } } return(voxelDataVolume); }
/// <summary> /// Copies the voxel data from the source volume if the volumes are the same size /// </summary> /// <param name="sourceVolume">The source volume, which should be the same size as this volume</param> public void CopyFrom(VoxelDataVolume <T> sourceVolume) { if (Width == sourceVolume.Width && Height == sourceVolume.Height && Depth == sourceVolume.Depth) { _voxelData.CopyFrom(sourceVolume._voxelData); } else { throw new ArgumentException($"The chunks are not the same size! Width: {Width}/{sourceVolume.Width}, Height: {Height}/{sourceVolume.Height}, Depth: {Depth}/{sourceVolume.Depth}"); } }
/// <summary> /// Sets a chunk's voxel data /// </summary> /// <param name="chunkVoxelData">The new voxel data</param> /// <param name="chunkCoordinate">The coordinate of the chunk whose voxel data should be set</param> public void SetVoxelDataChunk(VoxelDataVolume chunkVoxelData, int3 chunkCoordinate) { if (_chunks.TryGetValue(chunkCoordinate, out VoxelDataVolume voxelDataVolume)) { voxelDataVolume.CopyFrom(chunkVoxelData); } else { _chunks.Add(chunkCoordinate, chunkVoxelData); } if (VoxelWorld.ChunkStore.TryGetChunkAtCoordinate(chunkCoordinate, out Chunk chunk)) { chunk.HasChanges = true; } }
public override void SetDataChunk(int3 chunkCoordinate, VoxelDataVolume <byte> newData) { if (TryGetDataChunkWithoutApplying(chunkCoordinate, out VoxelDataVolume <byte> oldData)) { oldData.CopyFrom(newData); newData.Dispose(); } else { AddChunkUnchecked(chunkCoordinate, newData); } if (VoxelWorld.ChunkStore.TryGetDataChunk(chunkCoordinate, out ChunkProperties chunkProperties)) { chunkProperties.HasChanges = true; } }
/// <summary> /// Starts generating the voxel data for a specified volume /// </summary> /// <param name="bounds">The volume to generate the voxel data for</param> /// <param name="allocator">The allocator for the new <see cref="VoxelDataVolume"/></param> /// <returns>The job handle and the voxel data generation job</returns> public override JobHandleWithData <IVoxelDataGenerationJob> GenerateVoxelData(Bounds bounds, Allocator allocator) { VoxelDataVolume voxelData = new VoxelDataVolume(bounds.size.ToInt3(), allocator); ProceduralTerrainVoxelDataCalculationJob job = new ProceduralTerrainVoxelDataCalculationJob { WorldPositionOffset = bounds.min.ToInt3(), OutputVoxelData = voxelData, ProceduralTerrainSettings = proceduralTerrainSettings }; JobHandle jobHandle = job.Schedule(voxelData.Length, 256); JobHandleWithData <IVoxelDataGenerationJob> jobHandleWithData = new JobHandleWithData <IVoxelDataGenerationJob>(); jobHandleWithData.JobHandle = jobHandle; jobHandleWithData.JobData = job; return(jobHandleWithData); }
/// <summary> /// Starts generating the voxel data for a specified volume /// </summary> /// <param name="bounds">The volume to generate the voxel data for</param> /// <param name="allocator">The allocator for the new <see cref="VoxelDataVolume"/></param> /// <returns>The job handle and the voxel data generation job</returns> public override JobHandleWithData <IVoxelDataGenerationJob> GenerateVoxelData(Bounds bounds, Allocator allocator) { VoxelDataVolume voxelData = new VoxelDataVolume(bounds.size.ToInt3(), allocator); HeightmapTerrainVoxelDataCalculationJob job = new HeightmapTerrainVoxelDataCalculationJob { WorldPositionOffset = bounds.min.ToInt3(), OutputVoxelData = voxelData, HeightmapData = heightmapWorldGenerator.HeightmapTerrainSettings.HeightmapData, HeightmapWidth = heightmapWorldGenerator.HeightmapTerrainSettings.Width, HeightmapHeight = heightmapWorldGenerator.HeightmapTerrainSettings.Height, Amplitude = heightmapWorldGenerator.HeightmapTerrainSettings.Amplitude, HeightOffset = heightmapWorldGenerator.HeightmapTerrainSettings.HeightOffset }; JobHandle jobHandle = job.Schedule(voxelData.Length, 256); JobHandleWithData <IVoxelDataGenerationJob> jobHandleWithData = new JobHandleWithData <IVoxelDataGenerationJob>(); jobHandleWithData.JobHandle = jobHandle; jobHandleWithData.JobData = job; return(jobHandleWithData); }
/// <summary> /// Tries to get the <see cref="VoxelDataVolume"/> for one chunk with a persistent allocator. If a chunk doesn't exist there, false will be returned and <paramref name="chunk"/> will be set to null. If a chunk exists there, true will be returned and <paramref name="chunk"/> will be set to the chunk. /// </summary> /// <param name="chunkCoordinate">The coordinate of the chunk whose voxel data should be gotten</param> /// <param name="chunk">The voxel data of a chunk at the coordinate</param> /// <returns>Does a chunk exists at that coordinate</returns> public bool TryGetVoxelDataChunk(int3 chunkCoordinate, out VoxelDataVolume chunk) { ApplyChunkChanges(chunkCoordinate); return(_chunks.TryGetValue(chunkCoordinate, out chunk)); }
/// <summary> /// Generates the colors for a chunk at <paramref name="chunkCoordinate"/>, where the output array is <paramref name="outputColors"/> to save memory by not needing to allocate a new array. This does not check if a color array already exists at <paramref name="chunkCoordinate"/> /// </summary> /// <param name="chunkCoordinate">The coordinate of the chunk which to generate the colors for</param> /// <param name="outputColors">The array that should be filled with the new colors</param> public override unsafe void GenerateDataForChunkUnchecked(int3 chunkCoordinate, VoxelDataVolume <Color32> outputColors) { // Fill the array with the default terrain color Color32 *defaultColorArray = stackalloc Color32[1] { defaultTerrainColor }; unsafe { UnsafeUtility.MemCpyReplicate(outputColors.GetUnsafePtr(), defaultColorArray, sizeof(Color32), outputColors.Length); } SetDataChunkUnchecked(chunkCoordinate, outputColors, false); }
/// <summary> /// Tries to get the voxel data array for one chunk with a persistent allocator. If a chunk doesn't exist there, false will be returned and <paramref name="chunk"/> will be set to null. If a chunk exists there, true will be returned and <paramref name="chunk"/> will be set to the chunk. If the data for that chunk is currently being calculated, the job will NOT be completed. /// <param name="chunkCoordinate">The coordinate of the chunk whose voxel data should be gotten</param> /// <param name="chunk">The voxel data of a chunk at the coordinate</param> /// <returns>Does a chunk exists at that coordinate</returns> private bool TryGetDataChunkWithoutApplying(int3 chunkCoordinate, out VoxelDataVolume <byte> chunk) { return(_chunks.TryGetValue(chunkCoordinate, out chunk)); }
/// <summary> /// Tries to get the voxel data array for one chunk with a persistent allocator. If a chunk doesn't exist there, false will be returned and <paramref name="chunk"/> will be set to null. If a chunk exists there, true will be returned and <paramref name="chunk"/> will be set to the chunk. If the data for that chunk is currently being calculated, the job will complete and the new data will be set to <paramref name="chunk"/> /// </summary> /// <param name="chunkCoordinate">The coordinate of the chunk whose voxel data should be gotten</param> /// <param name="chunk">The voxel data of a chunk at the coordinate</param> /// <returns>Does a chunk exists at that coordinate</returns> public override bool TryGetDataChunk(int3 chunkCoordinate, out VoxelDataVolume <byte> chunk) { ApplyChunkChanges(chunkCoordinate); return(TryGetDataChunkWithoutApplying(chunkCoordinate, out chunk)); }
/// <inheritdoc/> public override JobHandleWithData <IVoxelDataGenerationJob> GenerateVoxelData(int3 worldSpaceOrigin, VoxelDataVolume <byte> outputVoxelDataArray) { HeightmapTerrainVoxelDataCalculationJob job = new HeightmapTerrainVoxelDataCalculationJob { WorldPositionOffset = worldSpaceOrigin, OutputVoxelData = outputVoxelDataArray, HeightmapData = heightmapWorldGenerator.HeightmapTerrainSettings.HeightmapData, HeightmapWidth = heightmapWorldGenerator.HeightmapTerrainSettings.Width, HeightmapHeight = heightmapWorldGenerator.HeightmapTerrainSettings.Height, Amplitude = heightmapWorldGenerator.HeightmapTerrainSettings.Amplitude, HeightOffset = heightmapWorldGenerator.HeightmapTerrainSettings.HeightOffset }; JobHandle jobHandle = job.Schedule(); JobHandleWithData <IVoxelDataGenerationJob> jobHandleWithData = new JobHandleWithData <IVoxelDataGenerationJob> { JobHandle = jobHandle, JobData = job }; return(jobHandleWithData); }
/// <inheritdoc/> public override JobHandleWithData <IVoxelDataGenerationJob> GenerateVoxelData(int3 worldSpaceOrigin, VoxelDataVolume <byte> outputVoxelDataArray) { ProceduralTerrainVoxelDataCalculationJob job = new ProceduralTerrainVoxelDataCalculationJob { WorldPositionOffset = worldSpaceOrigin, OutputVoxelData = outputVoxelDataArray, ProceduralTerrainSettings = proceduralTerrainSettings }; JobHandle jobHandle = job.Schedule(); JobHandleWithData <IVoxelDataGenerationJob> jobHandleWithData = new JobHandleWithData <IVoxelDataGenerationJob> { JobHandle = jobHandle, JobData = job }; return(jobHandleWithData); }
/// <summary> /// Starts generating the voxel data for the given volume, where the origin of the volume is at <paramref name="worldSpaceOrigin"/> /// </summary> /// <param name="worldSpaceOrigin">The world space origin of <paramref name="outputVoxelDataArray"/></param> /// <param name="outputVoxelDataArray">The volume where the new voxel data should be generated to</param> /// <returns>The job handle and the voxel data generation job</returns> public abstract JobHandleWithData <IVoxelDataGenerationJob> GenerateVoxelData(int3 worldSpaceOrigin, VoxelDataVolume <byte> outputVoxelDataArray);