/// <summary> /// Get a point on the flattening plane and flatten the terrain around it /// </summary> private void FlattenTerrain() { PlaneLineIntersectionResult result = IntersectionUtilities.PlaneLineIntersection(_flatteningOrigin, _flatteningNormal, playerCamera.position, playerCamera.forward, out float3 intersectionPoint); if (result != PlaneLineIntersectionResult.OneHit) { return; } float flattenOffset = 0; // This is a bit hacky. One fix could be that the VoxelMesher class has a flattenOffset property, but I'm not sure if that's a good idea either. if (voxelWorld.VoxelMesher is MarchingCubesMesher marchingCubesMesher) { flattenOffset = marchingCubesMesher.Isolevel; } int intRange = (int)math.ceil(deformRange); int size = 2 * intRange + 1; int3 queryPosition = (int3)(intersectionPoint - new int3(intRange)); BoundsInt worldSpaceQuery = new BoundsInt(queryPosition.ToVectorInt(), new Vector3Int(size, size, size)); voxelWorld.VoxelDataStore.SetVoxelDataCustom(worldSpaceQuery, (voxelDataWorldPosition, voxelData) => { float distance = math.distance(voxelDataWorldPosition, intersectionPoint); if (distance > deformRange) { return(voxelData); } float voxelDataChange = (math.dot(_flatteningNormal, voxelDataWorldPosition) - math.dot(_flatteningNormal, _flatteningOrigin)) / deformRange; return((byte)math.clamp(((voxelDataChange * 0.5f + voxelData / 255f - flattenOffset) * 0.8f + flattenOffset) * 255, 0, 255)); }); }
/// <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); }
public override void UpdateObservation() { foreach (var transform1 in this._transforms) { if (IntersectionUtilities.ConeSphereIntersection(this._light, transform1)) { //Debug.Log("Intersect"); } } }
/// <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); }
public void Line_At_1_0_0_Plane_At_0_0_0_Custom_Angle_Should_Be_1_Negative2_0() { float3 planeOrigin = new float3(0, 0, 0); float3 planeNormal = math.normalize(new float3(1, 0.5f, 0)); float3 lineOrigin = new float3(1, 0, 0); float3 lineDirection = new float3(0, -1, 0); PlaneLineIntersectionResult result = IntersectionUtilities.PlaneLineIntersection(planeOrigin, planeNormal, lineOrigin, lineDirection, out float3 intersectionPoint); Assert.AreEqual(new float3(1, -2, 0), intersectionPoint); }
public void Line_Above_Directly_Down_Result_Should_Be_OneHit() { float3 planeOrigin = new float3(0, 0, 0); float3 planeNormal = math.normalize(new float3(0, 1, 0)); float3 lineOrigin = new float3(0, 10, 0); float3 lineDirection = new float3(0, -1, 0); PlaneLineIntersectionResult result = IntersectionUtilities.PlaneLineIntersection(planeOrigin, planeNormal, lineOrigin, lineDirection, out float3 intersectionPoint); Assert.AreEqual(PlaneLineIntersectionResult.OneHit, result); }
public void Line_Parallel_To_Plane_Inside_Result_Should_Be_ParallelInsidePlane() { float3 planeOrigin = new float3(0, 4, 0); float3 planeNormal = math.normalize(new float3(0, 1, 0)); float3 lineOrigin = new float3(0, 4, 0); float3 lineDirection = new float3(1, 0, 0); PlaneLineIntersectionResult result = IntersectionUtilities.PlaneLineIntersection(planeOrigin, planeNormal, lineOrigin, lineDirection, out float3 intersectionPoint); Assert.AreEqual(PlaneLineIntersectionResult.ParallelInsidePlane, result); }
public void Line_At_10_4_5_Directly_Down_Should_Hit_10_0_5() { float3 planeOrigin = new float3(0, 0, 0); float3 planeNormal = math.normalize(new float3(0, 1, 0)); float3 lineOrigin = new float3(10, 4, 5); float3 lineDirection = new float3(0, -1, 0); PlaneLineIntersectionResult result = IntersectionUtilities.PlaneLineIntersection(planeOrigin, planeNormal, lineOrigin, lineDirection, out float3 intersectionPoint); Assert.AreEqual(new float3(10, 0, 5), intersectionPoint); }
/// <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); }
/// <summary> /// Get a point on the flattening plane and flatten the terrain around it /// </summary> private void FlattenTerrain() { PlaneLineIntersectionResult result = IntersectionUtilities.PlaneLineIntersection(_flatteningOrigin, _flatteningNormal, playerCamera.position, playerCamera.forward, out float3 intersectionPoint); if (result != PlaneLineIntersectionResult.OneHit) { return; } int intRange = (int)math.ceil(deformRange); for (int x = -intRange; x <= intRange; x++) { for (int y = -intRange; y <= intRange; y++) { for (int z = -intRange; z <= intRange; z++) { int3 localPosition = new int3(x, y, z); float3 offsetPoint = intersectionPoint + localPosition; float distance = math.distance(offsetPoint, intersectionPoint); if (distance > deformRange) { continue; } int3 voxelDataWorldPosition = (int3)offsetPoint; if (voxelDataStore.TryGetVoxelData(voxelDataWorldPosition, out float oldVoxelData)) { float voxelDataChange = (math.dot(_flatteningNormal, voxelDataWorldPosition) - math.dot(_flatteningNormal, _flatteningOrigin)) / deformRange; voxelDataStore.SetVoxelData((voxelDataChange * 0.5f + oldVoxelData) * 0.8f, voxelDataWorldPosition); } } } } }
private static void TestPlaneLineIntersectionPoint(float3 planeOrigin, float3 planeNormal, float3 lineOrigin, float3 lineDirection, float3 expected) { _ = IntersectionUtilities.PlaneLineIntersection(planeOrigin, planeNormal, lineOrigin, lineDirection, out float3 intersectionPoint); Assert.AreEqual(expected, intersectionPoint); }
private static void TestPlaneLineIntersectionResult(float3 planeOrigin, float3 planeNormal, float3 lineOrigin, float3 lineDirection, PlaneLineIntersectionResult expected) { PlaneLineIntersectionResult result = IntersectionUtilities.PlaneLineIntersection(planeOrigin, math.normalize(planeNormal), lineOrigin, lineDirection, out _); Assert.AreEqual(expected, result); }