//  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;
        }
Beispiel #2
0
        bool PerformChangeMaterialVoxels(MyVoxelMap voxelMap, MyMwcVector3Int voxelCoord, MyMwcVoxelMaterialsEnum? voxelMaterial, ref bool changed)
        {
            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().StartProfilingBlock("PerformChangeMaterialVoxels");
            if (voxelMaterial != null)
            {
                byte originalContent = voxelMap.GetVoxelContent(ref voxelCoord);
                if (originalContent == MyVoxelConstants.VOXEL_CONTENT_EMPTY)
                {
                    // if there are no voxel content then do nothing
                    MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock();
                    return false;
                }

                MyMwcVoxelMaterialsEnum originalMaterial;
                byte originalIndestructibleContent;
                voxelMap.GetMaterialAndIndestructibleContent(ref voxelCoord, out originalMaterial,
                                                             out originalIndestructibleContent);
                if (originalMaterial == voxelMaterial.Value)
                {
                    // if original material is same as new material then do nothing
                    MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock();
                    return false;
                }

                byte indestructibleContent = MyVoxelConstants.VOXEL_CONTENT_EMPTY;

                voxelMap.SetVoxelMaterialAndIndestructibleContent(voxelMaterial.Value, indestructibleContent, ref voxelCoord);
                changed = true;
                MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock();
                return true;
            }
            MinerWars.AppCode.Game.Render.MyRender.GetRenderProfiler().EndProfilingBlock();
            return false;
        }
        // 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();
        }