/// <summary> /// Loops through each voxel data point that is contained in <paramref name="dataChunk"/> AND in <paramref name="worldSpaceQuery"/>, and performs <paramref name="function"/> on it /// </summary> /// <param name="worldSpaceQuery">The query that determines whether or not a voxel data point is contained.</param> /// <param name="chunkCoordinate">The coordinate of <paramref name="dataChunk"/></param> /// <param name="dataChunk">The voxel datas of the chunk</param> /// <param name="function">The function that will be performed on each voxel data point. The arguments are as follows: 1) The world space position of the voxel data point, 2) The chunk space position of the voxel data point, 3) The index of the voxel data point inside of <paramref name="dataChunk"/>, 4) The value of the voxel data</param> public void ForEachVoxelDataInQueryInChunk(BoundsInt worldSpaceQuery, int3 chunkCoordinate, VoxelDataVolume <T> dataChunk, Action <int3, int3, int, T> function) { int3 chunkBoundsSize = VoxelWorld.WorldSettings.ChunkSize; int3 chunkWorldSpaceOrigin = chunkCoordinate * VoxelWorld.WorldSettings.ChunkSize; BoundsInt chunkWorldSpaceBounds = new BoundsInt(chunkWorldSpaceOrigin.ToVectorInt(), chunkBoundsSize.ToVectorInt()); BoundsInt 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; int voxelDataIndex = IndexUtilities.XyzToIndex(voxelDataLocalPosition, chunkBoundsSize.x + 1, chunkBoundsSize.y + 1); if (dataChunk.TryGetVoxelData(voxelDataIndex, out T voxelData)) { function(voxelDataWorldPosition, voxelDataLocalPosition, voxelDataIndex, voxelData); } } } } }
/// <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; } } } } }
public void Test4() { Bounds a = new Bounds(new Vector3(-10, -10, -10), new Vector3(4, 4, 4)); Bounds b = new Bounds(new Vector3(-1, -1, -1), new Vector3(16, 16, 16)); Bounds intersection = IntersectionUtilities.GetIntersectionVolume(a, b); Assert.AreEqual(new Bounds(new Vector3(-8.5f, -8.5f, -8.5f), new Vector3(1, 1, 1)), intersection); }
public void Test3_Swapped() { Bounds a = new Bounds(new Vector3(-10, -10, -10), new Vector3(4, 4, 4)); Bounds b = new Bounds(new Vector3(0, 0, 0), new Vector3(16, 16, 16)); Bounds intersection = IntersectionUtilities.GetIntersectionVolume(b, a); Assert.AreEqual(new Bounds(new Vector3(-8, -8, -8), new Vector3(0, 0, 0)), intersection); }
public void Bounds_Are_Same_Should_Return_Same() { Bounds a = new Bounds(new Vector3(41, 24, 85), new Vector3(48, 26, 23)); Bounds b = a; Bounds intersection = IntersectionUtilities.GetIntersectionVolume(a, b); Assert.AreEqual(a, intersection); }
public void Test2_Swapped() { Bounds a = new Bounds(new Vector3(0, 0, 0), new Vector3(2, 3, 3)); Bounds b = new Bounds(new Vector3(2, 0, 0), new Vector3(2, 3, 3)); Bounds intersection = IntersectionUtilities.GetIntersectionVolume(b, a); Assert.AreEqual(new Bounds(new Vector3(1, 0, 0), new Vector3(0, 3, 3)), intersection); }
public void Test1() { Bounds a = new Bounds(new Vector3(0, 0, 0), new Vector3(4, 4, 4)); Bounds b = new Bounds(new Vector3(2, 2, 2), new Vector3(4, 4, 4)); Bounds intersection = IntersectionUtilities.GetIntersectionVolume(a, b); Assert.AreEqual(new Bounds(new Vector3(1, 1, 1), new Vector3(2, 2, 2)), intersection); }
/// <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> /// Gets the list of new coordinates that should be generated when the player moved from <paramref name="oldChunks"/> to <paramref name="newChunks"/>; Every coordinate in <paramref name="newChunks"/> that is not in <paramref name="oldChunks"/> /// </summary> /// <param name="oldChunks">The old rendering bounds of the chunks the player saw</param> /// <param name="newChunks">The new rendering bounds of the chunks the player sees</param> /// <returns>Returns the new coordinates that need chunks</returns> public static int3[] GetCoordinatesThatNeedChunks(BoundsInt oldChunks, BoundsInt newChunks) { // Cache the min/max values because accessing them repeatedly in a loop is surprisingly costly int newChunksMinX = newChunks.xMin; int newChunksMaxX = newChunks.xMax; int newChunksMinY = newChunks.yMin; int newChunksMaxY = newChunks.yMax; int newChunksMinZ = newChunks.zMin; int newChunksMaxZ = newChunks.zMax; int oldChunksMinX = oldChunks.xMin; int oldChunksMaxX = oldChunks.xMax; int oldChunksMinY = oldChunks.yMin; int oldChunksMaxY = oldChunks.yMax; int oldChunksMinZ = oldChunks.zMin; int oldChunksMaxZ = oldChunks.zMax; int count = newChunks.CalculateVolume(); BoundsInt intersection = IntersectionUtilities.GetIntersectionVolume(oldChunks, newChunks); if (math.all(intersection.size.ToInt3() > 0)) { count -= intersection.CalculateVolume(); } int3[] coordinates = new int3[count]; int i = 0; for (int x = newChunksMinX; x < newChunksMaxX; x++) { for (int y = newChunksMinY; y < newChunksMaxY; y++) { for (int z = newChunksMinZ; z < newChunksMaxZ; z++) { if (oldChunksMinX <= x && x < oldChunksMaxX && oldChunksMinY <= y && y < oldChunksMaxY && oldChunksMinZ <= z && z < oldChunksMaxZ) { continue; } coordinates[i] = new int3(x, y, z); i++; } } } return(coordinates); }
private static void TestIntersection(Vector3Int aPosition, Vector3Int aSize, Vector3Int bPosition, Vector3Int bSize, Vector3Int expectedPosition, Vector3Int expectedSize) { BoundsInt a = new BoundsInt(aPosition, aSize); BoundsInt b = new BoundsInt(bPosition, bSize); BoundsInt expected = new BoundsInt(expectedPosition, expectedSize); string message = $"Test failed with objects {a} and {b}. Expected {expected}"; BoundsInt intersection = IntersectionUtilities.GetIntersectionVolume(a, b); Assert.AreEqual(expected, intersection, "Regular: " + message); BoundsInt intersectionSwapped = IntersectionUtilities.GetIntersectionVolume(b, a); Assert.AreEqual(expected, intersectionSwapped, "Swapped: " + message); }