// This is same as normal cut-out sphere, but cache is invalidated like in cut-out sphere fast(so that voxel cut-out will be immediately visible) public static void CutOutSphereInvalidateCache(MyVoxelMap voxelMap, BoundingSphere sphere, ref bool changed) { // Get min corner of the explosion MyMwcVector3Int minCorner = voxelMap.GetVoxelCoordinateFromMeters(new Vector3( sphere.Center.X - sphere.Radius - MyVoxelConstants.VOXEL_SIZE_IN_METRES, sphere.Center.Y - sphere.Radius - MyVoxelConstants.VOXEL_SIZE_IN_METRES, sphere.Center.Z - sphere.Radius - MyVoxelConstants.VOXEL_SIZE_IN_METRES)); // Get max corner of the explosion MyMwcVector3Int maxCorner = voxelMap.GetVoxelCoordinateFromMeters(new Vector3( sphere.Center.X + sphere.Radius + MyVoxelConstants.VOXEL_SIZE_IN_METRES, sphere.Center.Y + sphere.Radius + MyVoxelConstants.VOXEL_SIZE_IN_METRES, sphere.Center.Z + sphere.Radius + MyVoxelConstants.VOXEL_SIZE_IN_METRES)); voxelMap.FixVoxelCoord(ref minCorner); voxelMap.FixVoxelCoord(ref maxCorner); // We are tracking which voxels were changed, so we can invalidate only needed cells in the cache MyMwcVector3Int minChanged = maxCorner; MyMwcVector3Int maxChanged = minCorner; int removedVoxelContent = 0; MyMwcVector3Int tempVoxelCoord; for (tempVoxelCoord.X = minCorner.X; tempVoxelCoord.X <= maxCorner.X; tempVoxelCoord.X++) { for (tempVoxelCoord.Y = minCorner.Y; tempVoxelCoord.Y <= maxCorner.Y; tempVoxelCoord.Y++) { for (tempVoxelCoord.Z = minCorner.Z; tempVoxelCoord.Z <= maxCorner.Z; tempVoxelCoord.Z++) { Vector3 voxelPosition = voxelMap.GetVoxelCenterPositionAbsolute(ref tempVoxelCoord); float dist = (voxelPosition - sphere.Center).Length(); float diff = dist - sphere.Radius; byte contentToRemove; if (diff > MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { contentToRemove = MyVoxelConstants.VOXEL_CONTENT_EMPTY; } else if (diff < -MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { contentToRemove = MyVoxelConstants.VOXEL_CONTENT_FULL; } else { // This formula will work even if diff is positive or negative contentToRemove = (byte)(MyVoxelConstants.VOXEL_ISO_LEVEL - diff / MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF * MyVoxelConstants.VOXEL_ISO_LEVEL); } int originalContent = voxelMap.GetVoxelContent(ref tempVoxelCoord); if (originalContent > MyVoxelConstants.VOXEL_CONTENT_EMPTY && contentToRemove > MyVoxelConstants.VOXEL_CONTENT_EMPTY) { changed = true; int newVal = originalContent - contentToRemove; if (newVal < MyVoxelConstants.VOXEL_CONTENT_EMPTY) newVal = MyVoxelConstants.VOXEL_CONTENT_EMPTY; voxelMap.SetVoxelContent((byte)newVal, ref tempVoxelCoord); removedVoxelContent += originalContent - newVal; if (tempVoxelCoord.X < minChanged.X) minChanged.X = tempVoxelCoord.X; if (tempVoxelCoord.Y < minChanged.Y) minChanged.Y = tempVoxelCoord.Y; if (tempVoxelCoord.Z < minChanged.Z) minChanged.Z = tempVoxelCoord.Z; if (tempVoxelCoord.X > maxChanged.X) maxChanged.X = tempVoxelCoord.X; if (tempVoxelCoord.Y > maxChanged.Y) maxChanged.Y = tempVoxelCoord.Y; if (tempVoxelCoord.Z > maxChanged.Z) maxChanged.Z = tempVoxelCoord.Z; } } } } if (removedVoxelContent > 0) { // Extend borders for cleaning, so it's one pixel on both sides minChanged.X -= 1; minChanged.Y -= 1; minChanged.Z -= 1; maxChanged.X += 1; maxChanged.Y += 1; maxChanged.Z += 1; voxelMap.FixVoxelCoord(ref minChanged); voxelMap.FixVoxelCoord(ref maxChanged); voxelMap.InvalidateCache(minChanged, maxChanged); } }
// Cut outs a sphere from voxel map (e.g. explosion in a voxel map). We modify only voxels inside the sphere - set them to full or partialy full. // Other voxel are untouched. Sphere coordinates are in world space, not relative to voxel map and is in metres. // // Method returns percent of how much voxels were removed by this explosion (value 0.0 means no voxels; value 1.0 means all voxels) // // IMPORTANT: // This is optimized version that uses cache for accessing voxels. But the cache has limits so it can be used only for not extremely large cut-outs. // Non-optimized version is: CutOutSphere - but it doesn't mean it's slow... the difference is probably just 10-20% // // Returns true if indestructible voxels has been hit (otherwise false) public static bool CutOutSphereFast(MyVoxelMap voxelMap, BoundingSphere explosion, out float voxelsCountInPercent, out MyMwcVoxelMaterialsEnum? voxelMaterial, bool isPlayerExplosion = false, float removeRatio = 1, Dictionary<MyMwcVoxelMaterialsEnum, int> exactCutOutMaterials = null) { explosion.Radius = System.Math.Min(explosion.Radius, MyExplosionsConstants.EXPLOSION_RADIUS_MAX); InvalidateCache = true; voxelMaterial = null; MyMwcVoxelMaterialsEnum newVoxelMaterialTemp; byte tempminContentValueByte; MyMwcVector3Int exactCenterOfExplosion = voxelMap.GetVoxelCoordinateFromMeters(new Vector3(explosion.Center.X, explosion.Center.Y, explosion.Center.Z)); voxelMap.FixVoxelCoord(ref exactCenterOfExplosion); voxelMap.GetMaterialAndIndestructibleContent(ref exactCenterOfExplosion, out newVoxelMaterialTemp, out tempminContentValueByte); if (voxelMaterial == null) voxelMaterial = newVoxelMaterialTemp; // We need to replace this only once int originalVoxelContentsSum = 0; // Sum of all voxel contents affected by this explosion before we extract any voxel. This value is increasing per every voxel, no matter if we realy extract it. int removedVoxelContentsSum = 0; // Sum of all voxel contents we removed by this explosion. This value increases only if we extract something from voxel map. // Get min corner of the explosion MyMwcVector3Int minCorner = voxelMap.GetVoxelCoordinateFromMeters(new Vector3( explosion.Center.X - explosion.Radius - MyVoxelConstants.VOXEL_SIZE_IN_METRES, explosion.Center.Y - explosion.Radius - MyVoxelConstants.VOXEL_SIZE_IN_METRES, explosion.Center.Z - explosion.Radius - MyVoxelConstants.VOXEL_SIZE_IN_METRES)); // Get max corner of the explosion MyMwcVector3Int maxCorner = voxelMap.GetVoxelCoordinateFromMeters(new Vector3( explosion.Center.X + explosion.Radius + MyVoxelConstants.VOXEL_SIZE_IN_METRES, explosion.Center.Y + explosion.Radius + MyVoxelConstants.VOXEL_SIZE_IN_METRES, explosion.Center.Z + explosion.Radius + MyVoxelConstants.VOXEL_SIZE_IN_METRES)); voxelMap.FixVoxelCoord(ref minCorner); voxelMap.FixVoxelCoord(ref maxCorner); MyMwcVector3Int cachedSubtract; cachedSubtract.X = minCorner.X - CACHED_BOUNDARY_IN_VOXELS; cachedSubtract.Y = minCorner.Y - CACHED_BOUNDARY_IN_VOXELS; cachedSubtract.Z = minCorner.Z - CACHED_BOUNDARY_IN_VOXELS; // We are tracking which voxels were changed, so we can invalidate only needed cells in the cache MyMwcVector3Int minChanged = maxCorner; MyMwcVector3Int maxChanged = minCorner; MyMwcVector3Int tempVoxelCoord; bool indestructible = true; for (tempVoxelCoord.X = minCorner.X; tempVoxelCoord.X <= maxCorner.X; tempVoxelCoord.X++) { for (tempVoxelCoord.Y = minCorner.Y; tempVoxelCoord.Y <= maxCorner.Y; tempVoxelCoord.Y++) { for (tempVoxelCoord.Z = minCorner.Z; tempVoxelCoord.Z <= maxCorner.Z; tempVoxelCoord.Z++) { Vector3 voxelPosition = voxelMap.GetVoxelCenterPositionAbsolute(ref tempVoxelCoord); float dist = (voxelPosition - explosion.Center).Length(); float diff = dist - explosion.Radius; // This number will tell us how much of this voxel we will remove (can be zero, can be full voxel, or between) int contentToRemove; if (diff > MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { contentToRemove = MyVoxelConstants.VOXEL_CONTENT_EMPTY; } else if (diff < -MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { contentToRemove = MyVoxelConstants.VOXEL_CONTENT_FULL; } else { // This formula will work even if diff is positive or negative contentToRemove = (int)(MyVoxelConstants.VOXEL_ISO_LEVEL - diff / MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF * MyVoxelConstants.VOXEL_ISO_LEVEL); } contentToRemove = (int)(contentToRemove * removeRatio); // Only if we have to remove something (e.g. voxels that are outside of the radius aren't affected, or voxels that are // in cornes are always out of the radius, even if they are in bounding box) if (contentToRemove > MyVoxelConstants.VOXEL_CONTENT_EMPTY) { byte minContentValueByte; MyMwcVoxelMaterialsEnum voxelMaterialTemp; voxelMap.GetMaterialAndIndestructibleContent(ref tempVoxelCoord, out voxelMaterialTemp, out minContentValueByte); int minContentValue = minContentValueByte; if (minContentValue != MyVoxelConstants.VOXEL_CONTENT_FULL) { indestructible = false; } // Alter only non-empty voxels (because we can't remove empty voxel...) int originalContent = GetCachedVoxelContent(voxelMap, ref tempVoxelCoord, ref cachedSubtract); if (originalContent > minContentValue) { // IMPORTANT: When doing transformations on 'content' value, cast it to int always!!! // It's because you can easily forget that result will be negative and if you put negative into byte, it will // be overflown and you will be surprised by results!! int newVal = originalContent - contentToRemove; if (newVal < minContentValue) newVal = minContentValue; voxelMap.SetVoxelContent((byte)newVal, ref tempVoxelCoord); SetCachedVoxelContent(voxelMap, ref tempVoxelCoord, ref cachedSubtract, (byte)newVal); // Sum of all voxel contents affected by this explosion before we extract any voxel. This value is increasing per every voxel, no matter if we realy extract it. originalVoxelContentsSum += originalContent; // Sum of all voxel contents we removed by this explosion. This value increases only if we extract something from voxel map. removedVoxelContentsSum += originalContent - newVal; if (exactCutOutMaterials != null) { int oldValue = 0; exactCutOutMaterials.TryGetValue(voxelMaterialTemp, out oldValue); exactCutOutMaterials[voxelMaterialTemp] = oldValue + removedVoxelContentsSum; } if (tempVoxelCoord.X < minChanged.X) minChanged.X = tempVoxelCoord.X; if (tempVoxelCoord.Y < minChanged.Y) minChanged.Y = tempVoxelCoord.Y; if (tempVoxelCoord.Z < minChanged.Z) minChanged.Z = tempVoxelCoord.Z; if (tempVoxelCoord.X > maxChanged.X) maxChanged.X = tempVoxelCoord.X; if (tempVoxelCoord.Y > maxChanged.Y) maxChanged.Y = tempVoxelCoord.Y; if (tempVoxelCoord.Z > maxChanged.Z) maxChanged.Z = tempVoxelCoord.Z; } } } } } InvalidateCache = false; if (removedVoxelContentsSum > 0) { // Extend borders for cleaning, so it's one pixel on both sides minChanged.X -= 1; minChanged.Y -= 1; minChanged.Z -= 1; maxChanged.X += 1; maxChanged.Y += 1; maxChanged.Z += 1; voxelMap.FixVoxelCoord(ref minChanged); voxelMap.FixVoxelCoord(ref maxChanged); // Clear all small voxel that may have been created during explosion. They can be created even outside the range of // explosion sphere, e.g. if you have three voxels in a row A, B, C, where A is 255, B is 60, and C is 255. During the // explosion you change C to 0, so now we have 255, 60, 0. Than another explosion that will change A to 0, so we // will have 0, 60, 0. But B was always outside the range of the explosion. So this is why we need to do -1/+1 and remove // B voxels too. RemoveSmallVoxelsUsingChachedVoxels(voxelMap, ref minCorner, ref maxCorner, ref cachedSubtract); // Extend borders for invalidating the cache, so it's one pixel on both sides minChanged.X -= 1; minChanged.Y -= 1; minChanged.Z -= 1; maxChanged.X += 1; maxChanged.Y += 1; maxChanged.Z += 1; voxelMap.FixVoxelCoord(ref minChanged); voxelMap.FixVoxelCoord(ref maxChanged); // Invalidate cache for voxels cells covered by explosion and some boundary voxels too voxelMap.InvalidateCache(minCorner, maxCorner); voxelMap.AddExplosion(explosion); } if (originalVoxelContentsSum > 0f) { voxelsCountInPercent = (float)removedVoxelContentsSum / (float)originalVoxelContentsSum; } else { voxelsCountInPercent = 0f; } return indestructible; }
// This is same as normal CreateSphere method, but cache is invalidated (so that voxel changes be immediately visible) public static void CreateSphereInvalidateCache(MyVoxelMap voxelMap, BoundingSphere sphere, ref bool changed, MyMwcVoxelMaterialsEnum? material) { MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("MyVoxelGenerator.CreateSphereInvalidateCache"); // Get min corner of the explosion MyMwcVector3Int minCorner = voxelMap.GetVoxelCoordinateFromMeters(new Vector3( sphere.Center.X - sphere.Radius - MyVoxelConstants.VOXEL_SIZE_IN_METRES, sphere.Center.Y - sphere.Radius - MyVoxelConstants.VOXEL_SIZE_IN_METRES, sphere.Center.Z - sphere.Radius - MyVoxelConstants.VOXEL_SIZE_IN_METRES)); // Get max corner of the explosion MyMwcVector3Int maxCorner = voxelMap.GetVoxelCoordinateFromMeters(new Vector3( sphere.Center.X + sphere.Radius + MyVoxelConstants.VOXEL_SIZE_IN_METRES, sphere.Center.Y + sphere.Radius + MyVoxelConstants.VOXEL_SIZE_IN_METRES, sphere.Center.Z + sphere.Radius + MyVoxelConstants.VOXEL_SIZE_IN_METRES)); voxelMap.FixVoxelCoord(ref minCorner); voxelMap.FixVoxelCoord(ref maxCorner); // We are tracking which voxels were changed, so we can invalidate only needed cells in the cache MyMwcVector3Int minChanged = maxCorner; MyMwcVector3Int maxChanged = minCorner; bool sphereAdded = false; MyMwcVector3Int tempVoxelCoord; for (tempVoxelCoord.X = minCorner.X; tempVoxelCoord.X <= maxCorner.X; tempVoxelCoord.X++) { for (tempVoxelCoord.Y = minCorner.Y; tempVoxelCoord.Y <= maxCorner.Y; tempVoxelCoord.Y++) { for (tempVoxelCoord.Z = minCorner.Z; tempVoxelCoord.Z <= maxCorner.Z; tempVoxelCoord.Z++) { Vector3 voxelPosition = voxelMap.GetVoxelCenterPositionAbsolute(ref tempVoxelCoord); float dist = (voxelPosition - sphere.Center).Length(); float diff = dist - sphere.Radius; byte newContent; if (diff > MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { newContent = MyVoxelConstants.VOXEL_CONTENT_EMPTY; } else if (diff < -MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { newContent = MyVoxelConstants.VOXEL_CONTENT_FULL; } else { // This formula will work even if diff is positive or negative newContent = (byte)(MyVoxelConstants.VOXEL_ISO_LEVEL - diff / MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF * MyVoxelConstants.VOXEL_ISO_LEVEL); } byte originalContent = voxelMap.GetVoxelContent(ref tempVoxelCoord); if (newContent > originalContent) { if (material.HasValue) { MyMwcVoxelMaterialsEnum originalMaterial; byte originalIndestructibleContent; voxelMap.GetMaterialAndIndestructibleContent(ref tempVoxelCoord, out originalMaterial, out originalIndestructibleContent); voxelMap.SetVoxelMaterialAndIndestructibleContent(material.Value, originalIndestructibleContent, ref tempVoxelCoord); } changed = true; voxelMap.SetVoxelContent(newContent, ref tempVoxelCoord); sphereAdded = true; if (tempVoxelCoord.X < minChanged.X) minChanged.X = tempVoxelCoord.X; if (tempVoxelCoord.Y < minChanged.Y) minChanged.Y = tempVoxelCoord.Y; if (tempVoxelCoord.Z < minChanged.Z) minChanged.Z = tempVoxelCoord.Z; if (tempVoxelCoord.X > maxChanged.X) maxChanged.X = tempVoxelCoord.X; if (tempVoxelCoord.Y > maxChanged.Y) maxChanged.Y = tempVoxelCoord.Y; if (tempVoxelCoord.Z > maxChanged.Z) maxChanged.Z = tempVoxelCoord.Z; } } } } if (sphereAdded == true) { // Extend borders for cleaning, so it's one pixel on both sides minChanged.X -= 1; minChanged.Y -= 1; minChanged.Z -= 1; maxChanged.X += 1; maxChanged.Y += 1; maxChanged.Z += 1; voxelMap.FixVoxelCoord(ref minChanged); voxelMap.FixVoxelCoord(ref maxChanged); voxelMap.InvalidateCache(minChanged, maxChanged); } MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock(); }
// Cut-outs a sphere from a voxel map. // Generates inverse sphere in a voxel map (extract matters, create hole, etc - it's like explosion). We modify only voxels inside the sphere - set them to full or partialy full. // Other voxel are untouched. Center is relative to voxel map (not world coordinates). Radius is in metres. // // IMPORTANT: // This is non-optimized version (faster one is CutOutSphereFast). But it doesn't mean this one is slow... the difference is probably just 10-20% public static void CutOutSphere(MyVoxelMap voxelMap, BoundingSphere sphere) { // Get min corner of the explosion MyMwcVector3Int minCorner = voxelMap.GetVoxelCoordinateFromMeters(new Vector3( sphere.Center.X - sphere.Radius - MyVoxelConstants.VOXEL_SIZE_IN_METRES, sphere.Center.Y - sphere.Radius - MyVoxelConstants.VOXEL_SIZE_IN_METRES, sphere.Center.Z - sphere.Radius - MyVoxelConstants.VOXEL_SIZE_IN_METRES)); // Get max corner of the explosion MyMwcVector3Int maxCorner = voxelMap.GetVoxelCoordinateFromMeters(new Vector3( sphere.Center.X + sphere.Radius + MyVoxelConstants.VOXEL_SIZE_IN_METRES, sphere.Center.Y + sphere.Radius + MyVoxelConstants.VOXEL_SIZE_IN_METRES, sphere.Center.Z + sphere.Radius + MyVoxelConstants.VOXEL_SIZE_IN_METRES)); voxelMap.FixVoxelCoord(ref minCorner); voxelMap.FixVoxelCoord(ref maxCorner); MyMwcVector3Int tempVoxelCoord; for (tempVoxelCoord.X = minCorner.X; tempVoxelCoord.X <= maxCorner.X; tempVoxelCoord.X++) { for (tempVoxelCoord.Y = minCorner.Y; tempVoxelCoord.Y <= maxCorner.Y; tempVoxelCoord.Y++) { for (tempVoxelCoord.Z = minCorner.Z; tempVoxelCoord.Z <= maxCorner.Z; tempVoxelCoord.Z++) { Vector3 voxelPosition = voxelMap.GetVoxelCenterPositionAbsolute(ref tempVoxelCoord); float dist = (voxelPosition - sphere.Center).Length(); float diff = dist - sphere.Radius; byte contentToRemove; if (diff > MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { contentToRemove = MyVoxelConstants.VOXEL_CONTENT_EMPTY; } else if (diff < -MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { contentToRemove = MyVoxelConstants.VOXEL_CONTENT_FULL; } else { // This formula will work even if diff is positive or negative contentToRemove = (byte)(MyVoxelConstants.VOXEL_ISO_LEVEL - diff / MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF * MyVoxelConstants.VOXEL_ISO_LEVEL); } int originalContent = voxelMap.GetVoxelContent(ref tempVoxelCoord); if (originalContent > MyVoxelConstants.VOXEL_CONTENT_EMPTY) { int newVal = originalContent - contentToRemove; if (newVal < MyVoxelConstants.VOXEL_CONTENT_EMPTY) newVal = MyVoxelConstants.VOXEL_CONTENT_EMPTY; voxelMap.SetVoxelContent((byte)newVal, ref tempVoxelCoord); } } } } }
// Generates sphere in a voxel map. We modify only voxels inside the sphere - set them to full or partialy full. // Other voxel are untouched. Center is relative to voxel map (not world coordinates). Radius is in metres. public static void CreateSphere(MyVoxelMap voxelMap, BoundingSphere sphere) { MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("MyVoxelGenerator.CreateSphere"); // Get min corner of the explosion MyMwcVector3Int minCorner = voxelMap.GetVoxelCoordinateFromMeters(new Vector3( sphere.Center.X - sphere.Radius - MyVoxelConstants.VOXEL_SIZE_IN_METRES, sphere.Center.Y - sphere.Radius - MyVoxelConstants.VOXEL_SIZE_IN_METRES, sphere.Center.Z - sphere.Radius - MyVoxelConstants.VOXEL_SIZE_IN_METRES)); // Get max corner of the explosion MyMwcVector3Int maxCorner = voxelMap.GetVoxelCoordinateFromMeters(new Vector3( sphere.Center.X + sphere.Radius + MyVoxelConstants.VOXEL_SIZE_IN_METRES, sphere.Center.Y + sphere.Radius + MyVoxelConstants.VOXEL_SIZE_IN_METRES, sphere.Center.Z + sphere.Radius + MyVoxelConstants.VOXEL_SIZE_IN_METRES)); voxelMap.FixVoxelCoord(ref minCorner); voxelMap.FixVoxelCoord(ref maxCorner); MyMwcVector3Int tempVoxelCoord; for (tempVoxelCoord.X = minCorner.X; tempVoxelCoord.X <= maxCorner.X; tempVoxelCoord.X++) { for (tempVoxelCoord.Y = minCorner.Y; tempVoxelCoord.Y <= maxCorner.Y; tempVoxelCoord.Y++) { for (tempVoxelCoord.Z = minCorner.Z; tempVoxelCoord.Z <= maxCorner.Z; tempVoxelCoord.Z++) { Vector3 voxelPosition = voxelMap.GetVoxelCenterPositionAbsolute(ref tempVoxelCoord); float dist = (voxelPosition - sphere.Center).Length(); float diff = dist - sphere.Radius; byte newContent; if (diff > MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { newContent = MyVoxelConstants.VOXEL_CONTENT_EMPTY; } else if (diff < -MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { newContent = MyVoxelConstants.VOXEL_CONTENT_FULL; } else { // This formula will work even if diff is positive or negative newContent = (byte)(MyVoxelConstants.VOXEL_ISO_LEVEL - diff / MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF * MyVoxelConstants.VOXEL_ISO_LEVEL); } byte originalContent = voxelMap.GetVoxelContent(ref tempVoxelCoord); if (newContent > originalContent) { voxelMap.SetVoxelContent(newContent, ref tempVoxelCoord); } } } } MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock(); }
// This is same as normal cut-out box, but cache is invalidated (so that voxel changes be immediately visible) public static void CutOutBoxInvalidateCache(MyVoxelMap voxelMap, BoundingBox box, ref bool changed) { // Get min corner of the box MyMwcVector3Int minCorner = voxelMap.GetVoxelCoordinateFromMeters(box.Min); // Get max corner of the box MyMwcVector3Int maxCorner = voxelMap.GetVoxelCoordinateFromMeters(box.Max); voxelMap.FixVoxelCoord(ref minCorner); voxelMap.FixVoxelCoord(ref maxCorner); // We are tracking which voxels were changed, so we can invalidate only needed cells in the cache MyMwcVector3Int minChanged = maxCorner; MyMwcVector3Int maxChanged = minCorner; int removedVoxelContent = 0; MyMwcVector3Int tempVoxelCoord; for (tempVoxelCoord.X = minCorner.X; tempVoxelCoord.X <= maxCorner.X; tempVoxelCoord.X++) { for (tempVoxelCoord.Y = minCorner.Y; tempVoxelCoord.Y <= maxCorner.Y; tempVoxelCoord.Y++) { for (tempVoxelCoord.Z = minCorner.Z; tempVoxelCoord.Z <= maxCorner.Z; tempVoxelCoord.Z++) { Vector3 voxelPosition = voxelMap.GetVoxelCenterPositionAbsolute(ref tempVoxelCoord); byte contentToRemove = MyVoxelConstants.VOXEL_CONTENT_FULL; int originalContent = voxelMap.GetVoxelContent(ref tempVoxelCoord); if (originalContent > MyVoxelConstants.VOXEL_CONTENT_EMPTY) { changed = true; int newVal = originalContent - contentToRemove; if (newVal < MyVoxelConstants.VOXEL_CONTENT_EMPTY) newVal = MyVoxelConstants.VOXEL_CONTENT_EMPTY; voxelMap.SetVoxelContent((byte)newVal, ref tempVoxelCoord); removedVoxelContent += originalContent - newVal; if (tempVoxelCoord.X < minChanged.X) minChanged.X = tempVoxelCoord.X; if (tempVoxelCoord.Y < minChanged.Y) minChanged.Y = tempVoxelCoord.Y; if (tempVoxelCoord.Z < minChanged.Z) minChanged.Z = tempVoxelCoord.Z; if (tempVoxelCoord.X > maxChanged.X) maxChanged.X = tempVoxelCoord.X; if (tempVoxelCoord.Y > maxChanged.Y) maxChanged.Y = tempVoxelCoord.Y; if (tempVoxelCoord.Z > maxChanged.Z) maxChanged.Z = tempVoxelCoord.Z; } } } } if (removedVoxelContent > 0) { // Extend borders for cleaning, so it's one pixel on both sides minChanged.X -= 1; minChanged.Y -= 1; minChanged.Z -= 1; maxChanged.X += 1; maxChanged.Y += 1; maxChanged.Z += 1; voxelMap.FixVoxelCoord(ref minChanged); voxelMap.FixVoxelCoord(ref maxChanged); voxelMap.InvalidateCache(minChanged, maxChanged); } }
} //Create Cuboid public static void CutOutCuboid(MyVoxelMap voxelMap, MyCuboid cuboid, MyMwcVoxelMaterialsEnum? material) { BoundingBox aabb = cuboid.GetAABB(); MyMwcVector3Int minCorner = voxelMap.GetVoxelCoordinateFromMeters(aabb.Min - new Vector3(MyVoxelConstants.VOXEL_SIZE_IN_METRES)); MyMwcVector3Int maxCorner = voxelMap.GetVoxelCoordinateFromMeters(aabb.Max + new Vector3(MyVoxelConstants.VOXEL_SIZE_IN_METRES)); voxelMap.FixVoxelCoord(ref minCorner); voxelMap.FixVoxelCoord(ref maxCorner); MyMwcVector3Int tempVoxelCoord; for (tempVoxelCoord.X = minCorner.X; tempVoxelCoord.X <= maxCorner.X; tempVoxelCoord.X++) { for (tempVoxelCoord.Y = minCorner.Y; tempVoxelCoord.Y <= maxCorner.Y; tempVoxelCoord.Y++) { for (tempVoxelCoord.Z = minCorner.Z; tempVoxelCoord.Z <= maxCorner.Z; tempVoxelCoord.Z++) { Vector3 position = voxelMap.GetVoxelCenterPositionAbsolute(ref tempVoxelCoord); BoundingBox voxelAABB = new BoundingBox(position - new Vector3(MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF), position + new Vector3(MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF)); bool isInside = true; for (int i = 0; i < cuboid.Sides.Length; i++) { MyPlane side = cuboid.Sides[i].Plane; float distance = MyUtils.GetDistanceFromPointToPlane(ref position, ref side); if (distance > 0) { isInside = false; break; } } float minDistance = float.MaxValue; for (int i = 0; i < cuboid.Sides.Length; i++) { MyQuad quad = new MyQuad(); quad.Point0 = cuboid.Sides[i].Lines[0].From; quad.Point1 = cuboid.Sides[i].Lines[1].From; quad.Point2 = cuboid.Sides[i].Lines[3].From; quad.Point3 = cuboid.Sides[i].Lines[2].From; float distance = MyUtils.GetDistancePointToQuad(ref position, ref quad); if (distance < minDistance) { minDistance = distance; } } byte newContent = 0; if (isInside) { if (minDistance > MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { newContent = MyVoxelConstants.VOXEL_CONTENT_EMPTY; } else { // This formula will work even if diff is positive or negative //newContent = MyVoxelConstants.VOXEL_CONTENT_EMPTY; newContent = (byte)(MyVoxelConstants.VOXEL_ISO_LEVEL - minDistance / MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF * MyVoxelConstants.VOXEL_ISO_LEVEL); //newContent = (byte)(MyVoxelConstants.VOXEL_CONTENT_FULL - (byte)(MyVoxelConstants.VOXEL_ISO_LEVEL - minDistance / MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF * MyVoxelConstants.VOXEL_ISO_LEVEL)); } } else { if (minDistance > MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { newContent = MyVoxelConstants.VOXEL_CONTENT_FULL; } else { //newContent = MyVoxelConstants.VOXEL_CONTENT_FULL; // This formula will work even if diff is positive or negative //newContent = (byte)(MyVoxelConstants.VOXEL_ISO_LEVEL - minDistance / MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF * MyVoxelConstants.VOXEL_ISO_LEVEL); newContent = (byte)(MyVoxelConstants.VOXEL_CONTENT_FULL - (byte)(MyVoxelConstants.VOXEL_ISO_LEVEL - minDistance / MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF * MyVoxelConstants.VOXEL_ISO_LEVEL)); } } byte originalContent = voxelMap.GetVoxelContent(ref tempVoxelCoord); if (newContent < originalContent) { voxelMap.SetVoxelContent(newContent, ref tempVoxelCoord); if (material.HasValue) voxelMap.SetVoxelMaterialAndIndestructibleContent(material.Value, 0, ref tempVoxelCoord); } } } } voxelMap.InvalidateCache(minCorner, maxCorner); voxelMap.CalcAverageDataCellMaterials(); } //CutOut Cuboid
} //CreateCylinder public static void CutOutCylinder(MyVoxelMap voxelMap, float radius1, float radius2, MyOrientedBoundingBox box, MyMwcVoxelMaterialsEnum? material, ref bool changed) { //box.HalfExtent x = radius1 //box.HalfExtent y = length/2 //box.HalfExtent z = radius2 BoundingBox aabb = box.GetAABB(); MyMwcVector3Int minCorner = voxelMap.GetVoxelCoordinateFromMeters(aabb.Min - new Vector3(MyVoxelConstants.VOXEL_SIZE_IN_METRES)); MyMwcVector3Int maxCorner = voxelMap.GetVoxelCoordinateFromMeters(aabb.Max + new Vector3(MyVoxelConstants.VOXEL_SIZE_IN_METRES)); voxelMap.FixVoxelCoord(ref minCorner); voxelMap.FixVoxelCoord(ref maxCorner); MyMwcVector3Int tempVoxelCoord; for (tempVoxelCoord.X = minCorner.X; tempVoxelCoord.X <= maxCorner.X; tempVoxelCoord.X++) { for (tempVoxelCoord.Y = minCorner.Y; tempVoxelCoord.Y <= maxCorner.Y; tempVoxelCoord.Y++) { for (tempVoxelCoord.Z = minCorner.Z; tempVoxelCoord.Z <= maxCorner.Z; tempVoxelCoord.Z++) { Vector3 position = voxelMap.GetVoxelCenterPositionAbsolute(ref tempVoxelCoord); //BoundingBox voxelAABB = new BoundingBox(position - new Vector3(MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF), position + new Vector3(MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF)); byte newContent = 0; // Transform the point into box-local space and check against // our extents. Quaternion qinv = Quaternion.Conjugate(box.Orientation); Vector3 plocal = Vector3.Transform(position - box.Center, qinv); //MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF float distY = 0; int sY = System.Math.Sign(plocal.Y); if (sY > 0) { distY = plocal.Y - box.HalfExtent.Y; } else { distY = plocal.Y + box.HalfExtent.Y; } float distRatio = 1; distRatio = MathHelper.Clamp((plocal.Y + box.HalfExtent.Y) / (2 * box.HalfExtent.Y), 0, 1); int contentY = -1; if (sY > 0) { if (distY > MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { contentY = MyVoxelConstants.VOXEL_CONTENT_FULL; } else if (distY < -MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { contentY = MyVoxelConstants.VOXEL_CONTENT_EMPTY; } else { // This formula will work even if diff is positive or negative //contentY = (byte)(MyVoxelConstants.VOXEL_ISO_LEVEL - distY / MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF * MyVoxelConstants.VOXEL_ISO_LEVEL); contentY = MyVoxelConstants.VOXEL_CONTENT_FULL - (byte)(MyVoxelConstants.VOXEL_ISO_LEVEL - distY / MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF * MyVoxelConstants.VOXEL_ISO_LEVEL); } } else { if (distY < -MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { contentY = MyVoxelConstants.VOXEL_CONTENT_FULL; } else if (distY > MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { contentY = MyVoxelConstants.VOXEL_CONTENT_EMPTY; } else { // This formula will work even if diff is positive or negative //contentY = MyVoxelConstants.VOXEL_CONTENT_FULL - (byte)(MyVoxelConstants.VOXEL_ISO_LEVEL - distY / MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF * MyVoxelConstants.VOXEL_ISO_LEVEL); contentY = (byte)(MyVoxelConstants.VOXEL_ISO_LEVEL - distY / MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF * MyVoxelConstants.VOXEL_ISO_LEVEL); } } Matrix boxMatrix = Matrix.CreateFromQuaternion(box.Orientation); Vector3 hY1 = box.Center + boxMatrix.Up * box.HalfExtent.Y; Vector3 hY2 = box.Center + boxMatrix.Down * box.HalfExtent.Y; float dist = MyUtils.GetPointLineDistance(ref hY1, ref hY2, ref position); float diff = dist - MathHelper.Lerp(radius2, radius1, distRatio); //float diff = dist - box.HalfExtent.Z; byte newContent2; if (diff > MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { newContent2 = MyVoxelConstants.VOXEL_CONTENT_FULL; } else if (diff < -MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { newContent2 = MyVoxelConstants.VOXEL_CONTENT_EMPTY; } else { // This formula will work even if diff is positive or negative //newContent2 = (byte)(MyVoxelConstants.VOXEL_ISO_LEVEL - diff / MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF * MyVoxelConstants.VOXEL_ISO_LEVEL); newContent2 = (byte)(MyVoxelConstants.VOXEL_CONTENT_FULL - (int)(MyVoxelConstants.VOXEL_ISO_LEVEL - diff / MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF * MyVoxelConstants.VOXEL_ISO_LEVEL)); } newContent = (byte)System.Math.Max(contentY, newContent2); //newContent = (byte)contentY; byte originalContent = voxelMap.GetVoxelContent(ref tempVoxelCoord); if (newContent < originalContent) { voxelMap.SetVoxelContent(newContent, ref tempVoxelCoord); if (material.HasValue) voxelMap.SetVoxelMaterialAndIndestructibleContent(material.Value, 0, ref tempVoxelCoord); } } } } voxelMap.InvalidateCache(minCorner, maxCorner); voxelMap.CalcAverageDataCellMaterials(); } //CutoutCylinder
public static void CutOutOrientedBox(MyVoxelMap voxelMap, MyOrientedBoundingBox box, ref bool changed) { BoundingBox aabb = box.GetAABB(); MyMwcVector3Int minCorner = voxelMap.GetVoxelCoordinateFromMeters(aabb.Min - new Vector3(MyVoxelConstants.VOXEL_SIZE_IN_METRES)); MyMwcVector3Int maxCorner = voxelMap.GetVoxelCoordinateFromMeters(aabb.Max + new Vector3(MyVoxelConstants.VOXEL_SIZE_IN_METRES)); voxelMap.FixVoxelCoord(ref minCorner); voxelMap.FixVoxelCoord(ref maxCorner); System.Threading.Tasks.Parallel.For(minCorner.X, maxCorner.X, i => { System.Threading.Tasks.Parallel.For(minCorner.Y, maxCorner.Y, j => { System.Threading.Tasks.Parallel.For(minCorner.Z, maxCorner.Z, k => { MyMwcVector3Int tempVoxelCoord = new MyMwcVector3Int(i, j, k); Vector3 position = voxelMap.GetVoxelCenterPositionAbsolute(ref tempVoxelCoord); BoundingBox voxelAABB = new BoundingBox(position - new Vector3(MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF), position + new Vector3(MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF)); byte newContent = 0; ContainmentType ct = box.Contains(ref voxelAABB); if (ct == ContainmentType.Contains) { newContent = MyVoxelConstants.VOXEL_CONTENT_EMPTY; } else if (ct == ContainmentType.Disjoint) { newContent = MyVoxelConstants.VOXEL_CONTENT_FULL; } else { // Transform the point into box-local space and check against // our extents. Quaternion qinv = Quaternion.Conjugate(box.Orientation); Vector3 plocal = Vector3.Transform(position - box.Center, qinv); //MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF float distX = 0; float distY = 0; float distZ = 0; int sX = System.Math.Sign(plocal.X); int sY = System.Math.Sign(plocal.Y); int sZ = System.Math.Sign(plocal.Z); if (sX > 0) { distX = plocal.X - box.HalfExtent.X; } else { distX = plocal.X + box.HalfExtent.X; } if (sY > 0) { distY = plocal.Y - box.HalfExtent.Y; } else { distY = plocal.Y + box.HalfExtent.Y; } if (sZ > 0) { distZ = plocal.Z - box.HalfExtent.Z; } else { distZ = plocal.Z + box.HalfExtent.Z; } //float diff = (distX + distY + distZ) / 3; //float diff = (sX*sY*sZ) * System.Math.Min(System.Math.Min(distX,distY), distZ); int contentX; int contentY; int contentZ; if (sX < 0) { if (distX > MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { contentX = MyVoxelConstants.VOXEL_CONTENT_EMPTY; } else if (distX < -MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { contentX = MyVoxelConstants.VOXEL_CONTENT_FULL; } else { // This formula will work even if diff is positive or negative contentX = MyVoxelConstants.VOXEL_CONTENT_FULL - (byte)(MyVoxelConstants.VOXEL_ISO_LEVEL - distX / MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF * MyVoxelConstants.VOXEL_ISO_LEVEL); } } else { if (distX < -MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { contentX = MyVoxelConstants.VOXEL_CONTENT_EMPTY; } else if (distX > MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { contentX = MyVoxelConstants.VOXEL_CONTENT_FULL; } else { // This formula will work even if diff is positive or negative contentX = (byte)(MyVoxelConstants.VOXEL_ISO_LEVEL - distX / MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF * MyVoxelConstants.VOXEL_ISO_LEVEL); } } if (sY < 0) { if (distY > MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { contentY = MyVoxelConstants.VOXEL_CONTENT_EMPTY; } else if (distY < -MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { contentY = MyVoxelConstants.VOXEL_CONTENT_FULL; } else { // This formula will work even if diff is positive or negative contentY = MyVoxelConstants.VOXEL_CONTENT_FULL - (byte)(MyVoxelConstants.VOXEL_ISO_LEVEL - distY / MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF * MyVoxelConstants.VOXEL_ISO_LEVEL); } } else { if (distY < -MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { contentY = MyVoxelConstants.VOXEL_CONTENT_EMPTY; } else if (distY > MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { contentY = MyVoxelConstants.VOXEL_CONTENT_FULL; } else { // This formula will work even if diff is positive or negative contentY = (byte)(MyVoxelConstants.VOXEL_ISO_LEVEL - distY / MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF * MyVoxelConstants.VOXEL_ISO_LEVEL); } } if (sZ < 0) { if (distZ > MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { contentZ = MyVoxelConstants.VOXEL_CONTENT_EMPTY; } else if (distZ < -MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { contentZ = MyVoxelConstants.VOXEL_CONTENT_FULL; } else { // This formula will work even if diff is positive or negative contentZ = MyVoxelConstants.VOXEL_CONTENT_FULL - (byte)(MyVoxelConstants.VOXEL_ISO_LEVEL - distZ / MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF * MyVoxelConstants.VOXEL_ISO_LEVEL); } } else { if (distZ < -MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { contentZ = MyVoxelConstants.VOXEL_CONTENT_EMPTY; } else if (distZ > MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF) { contentZ = MyVoxelConstants.VOXEL_CONTENT_FULL; } else { // This formula will work even if diff is positive or negative contentZ = (byte)(MyVoxelConstants.VOXEL_ISO_LEVEL - distZ / MyVoxelConstants.VOXEL_SIZE_IN_METRES_HALF * MyVoxelConstants.VOXEL_ISO_LEVEL); } } //voxelMap.SetVoxelContent( (byte)((contentX + contentY + contentZ)/3.0f), ref tempVoxelCoord); newContent = (byte)(System.Math.Min(System.Math.Min(contentX, contentY), contentZ)); } byte originalContent = voxelMap.GetVoxelContent(ref tempVoxelCoord); if (newContent < originalContent) { voxelMap.SetVoxelContent(newContent, ref tempVoxelCoord, true); //changed = true; } }); }); }); /* for (tempVoxelCoord.X = minCorner.X; tempVoxelCoord.X <= maxCorner.X; tempVoxelCoord.X++) { for (tempVoxelCoord.Y = minCorner.Y; tempVoxelCoord.Y <= maxCorner.Y; tempVoxelCoord.Y++) { for (tempVoxelCoord.Z = minCorner.Z; tempVoxelCoord.Z <= maxCorner.Z; tempVoxelCoord.Z++) { } } }*/ changed = true; voxelMap.InvalidateCache(minCorner, maxCorner); voxelMap.CalcAverageDataCellMaterials(); }