Example #1
0
        /// <summary>
        /// Processes an asteroid Voxel using function callbacks.
        /// This allows for muti-threading, and generating content via algorithims.
        /// </summary>
        public static void ProcessAsteroid(MyVoxelMap voxelMap, bool multiThread, byte materialIndex, Action <MyVoxelBuilderArgs> func, bool readWrite = true)
        {
            long    counterTotal = (long)voxelMap.Size.X * voxelMap.Size.Y * voxelMap.Size.Z;
            long    counter      = 0;
            decimal progress     = 0;
            var     timer        = new Stopwatch();

            Debug.Write($"Building Asteroid : {progress:000},");
            Console.Write($"Building Asteroid : {progress:000},");
            Exception threadException = null;

            timer.Start();

            if (!multiThread)
            {
                #region single thread processing

                Vector3I  block;
                const int cellSize  = 64;
                var       cacheSize = Vector3I.Min(new Vector3I(cellSize), voxelMap.Storage.Size);
                var       oldCache  = new MyStorageData();

                // read the asteroid in chunks of 64 to avoid the Arithmetic overflow issue.
                for (block.Z = 0; block.Z < voxelMap.Storage.Size.Z; block.Z += cellSize)
                {
                    for (block.Y = 0; block.Y < voxelMap.Storage.Size.Y; block.Y += cellSize)
                    {
                        for (block.X = 0; block.X < voxelMap.Storage.Size.X; block.X += cellSize)
                        {
                            oldCache.Resize(cacheSize);
                            // LOD0 is required to read if you intend to write back to the voxel storage.
                            Vector3I maxRange = block + cacheSize - 1;
                            voxelMap.Storage.ReadRange(oldCache, MyStorageDataTypeFlags.ContentAndMaterial, 0, block, maxRange);

                            Vector3I p;
                            for (p.Z = 0; p.Z < cacheSize.Z; ++p.Z)
                            {
                                for (p.Y = 0; p.Y < cacheSize.Y; ++p.Y)
                                {
                                    for (p.X = 0; p.X < cacheSize.X; ++p.X)
                                    {
                                        var coords = block + p;

                                        byte volume       = 0x0;
                                        byte cellMaterial = materialIndex;

                                        if (readWrite)
                                        {
                                            volume       = oldCache.Content(ref p);
                                            cellMaterial = oldCache.Material(ref p);
                                        }

                                        var args = new MyVoxelBuilderArgs(voxelMap.Size, coords, cellMaterial, volume);

                                        try
                                        {
                                            func(args);
                                        }
                                        catch (Exception ex)
                                        {
                                            threadException = ex;
                                            break;
                                        }

                                        if (args.Volume != volume)
                                        {
                                            oldCache.Set(MyStorageDataTypeEnum.Content, ref p, args.Volume);
                                        }

                                        if (args.MaterialIndex != cellMaterial)
                                        {
                                            oldCache.Set(MyStorageDataTypeEnum.Material, ref p, args.MaterialIndex);
                                        }

                                        counter++;
                                        var prog = Math.Floor(counter / (decimal)counterTotal * 100);
                                        if (prog != progress)
                                        {
                                            progress = prog;
                                            Debug.Write($"{progress:000},");
                                        }
                                    }
                                }
                            }

                            voxelMap.Storage.WriteRange(oldCache, MyStorageDataTypeFlags.ContentAndMaterial, block, maxRange);
                        }
                    }
                }

                #endregion
            }
            else
            {
                #region multi thread processing

                // TODO: re-write the multi thread processing to be more stable.
                // But still try and max out the processors.

                Vector3I  block;
                const int cellSize      = 64;
                var       cacheSize     = Vector3I.Min(new Vector3I(cellSize), voxelMap.Storage.Size);
                long      threadCounter = counterTotal / cellSize / cellSize / cellSize;

                for (block.Z = 0; block.Z < voxelMap.Storage.Size.Z; block.Z += cellSize)
                {
                    for (block.Y = 0; block.Y < voxelMap.Storage.Size.Y; block.Y += cellSize)
                    {
                        for (block.X = 0; block.X < voxelMap.Storage.Size.X; block.X += cellSize)
                        {
                            var oldCache = new MyStorageData();
                            oldCache.Resize(cacheSize);
                            // LOD1 is not detailed enough for content information on asteroids.
                            Vector3I maxRange = block + cacheSize - 1;
                            voxelMap.Storage.ReadRange(oldCache, MyStorageDataTypeFlags.ContentAndMaterial, 0, block, maxRange);

                            var task = new Task(obj =>
                            {
                                var bgw = (MyVoxelTaskWorker)obj;

                                Vector3I p;
                                for (p.Z = 0; p.Z < cacheSize.Z; ++p.Z)
                                {
                                    for (p.Y = 0; p.Y < cacheSize.Y; ++p.Y)
                                    {
                                        for (p.X = 0; p.X < cacheSize.X; ++p.X)
                                        {
                                            var coords = bgw.BaseCoords + p;

                                            byte volume       = 0x0;
                                            byte cellMaterial = materialIndex;

                                            if (readWrite)
                                            {
                                                // read the existing material and volume into the arguments before passing the to args for processing.
                                                volume       = bgw.VoxelCache.Content(ref p);
                                                cellMaterial = bgw.VoxelCache.Material(ref p);
                                            }

                                            var args = new MyVoxelBuilderArgs(voxelMap.Size, coords, cellMaterial, volume);

                                            try
                                            {
                                                func(args);
                                            }
                                            catch (Exception ex)
                                            {
                                                threadException = ex;
                                                threadCounter   = 0;
                                                break;
                                            }

                                            if (args.Volume != volume)
                                            {
                                                bgw.VoxelCache.Set(MyStorageDataTypeEnum.Content, ref p, args.Volume);
                                            }

                                            if (args.MaterialIndex != cellMaterial)
                                            {
                                                bgw.VoxelCache.Set(MyStorageDataTypeEnum.Material, ref p, args.MaterialIndex);
                                            }

                                            counter++;
                                            var prog = Math.Floor(counter / (decimal)counterTotal * 100);
                                            if (prog != progress)
                                            {
                                                progress = prog;
                                                Debug.Write($"{progress:000},");
                                            }
                                        }
                                    }
                                }

                                lock (Locker)
                                {
                                    var b       = bgw.BaseCoords;
                                    Vector3I mr = bgw.BaseCoords + cacheSize - 1;
                                    voxelMap.Storage.WriteRange(bgw.VoxelCache, MyStorageDataTypeFlags.ContentAndMaterial, b, mr);

                                    counter += (long)cellSize * cellSize * cellSize;
                                    var prog = Math.Floor(counter / (decimal)counterTotal * 100);
                                    if (prog != progress)
                                    {
                                        progress = prog;
                                        Debug.Write($"{progress:000},");
                                        Console.Write($"{progress:000},");
                                        GC.Collect();
                                    }
                                    threadCounter--;
                                }
                            }, new MyVoxelTaskWorker(block, oldCache));

                            task.Start();
                        }
                    }
                }



                GC.Collect();

                while (threadCounter > 0)
                {
                    System.Windows.Forms.Application.DoEvents();
                }

                System.Threading.Thread.Sleep(100);
                System.Windows.Forms.Application.DoEvents();

                #endregion
            }

            timer.Stop();

            if (threadException != null)
            {
                throw threadException;
            }

            voxelMap.RefreshAssets();

            //voxelMap.UpdateContentBounds();

            Debug.WriteLine($" Done. | {timer.Elapsed}  | VoxCells {voxelMap.VoxCells:#,##0}");
            Console.WriteLine($" Done. | {timer.Elapsed}  | VoxCells {voxelMap.VoxCells:#,##0}");
        }
Example #2
0
        internal unsafe void ReadRange(MyStorageData target, MyStorageDataTypeEnum type, ref Vector3I writeOffset, int lodIndex, ref Vector3I minInLod, ref Vector3I maxInLod)
        {
            ProfilerShort.Begin("MySparseOctree2.ReadRangeToContent"); try
            {
                int          stackIdx  = 0;
                int          stackSize = MySparseOctree.EstimateStackSize(m_treeHeight);
                MyCellCoord *stack     = stackalloc MyCellCoord[stackSize];
                MyCellCoord  data      = new MyCellCoord(m_treeHeight - 1, ref Vector3I.Zero);
                stack[stackIdx++] = data;

                MyOctreeNode node;
                Vector3I     childPosRelative, min, max, nodePositionInChild;
                int          lodDiff;
                while (stackIdx > 0)
                {
                    Debug.Assert(stackIdx <= stackSize);
                    data = stack[--stackIdx];
                    node = m_nodes[data.PackId32()];

                    lodDiff             = data.Lod - lodIndex;
                    min                 = minInLod >> lodDiff;
                    max                 = maxInLod >> lodDiff;
                    nodePositionInChild = data.CoordInLod << 1;
                    min                -= nodePositionInChild;
                    max                -= nodePositionInChild;
                    for (int i = 0; i < MyOctreeNode.CHILD_COUNT; ++i)
                    {
                        ComputeChildCoord(i, out childPosRelative);
                        if (!childPosRelative.IsInsideInclusive(ref min, ref max))
                        {
                            continue;
                        }
                        if (lodIndex < data.Lod && node.HasChild(i))
                        {
                            Debug.Assert(stackIdx < stackSize);
                            stack[stackIdx++] = new MyCellCoord(data.Lod - 1, nodePositionInChild + childPosRelative);
                        }
                        else
                        {
                            var nodeData = node.Data[i];
                            var childMin = nodePositionInChild + childPosRelative;
                            if (lodDiff == 0)
                            {
                                var write = writeOffset + childMin - minInLod;
                                target.Set(type, ref write, nodeData);
                            }
                            else
                            {
                                childMin <<= lodDiff;
                                var childMax = childMin + (1 << lodDiff) - 1;
                                Vector3I.Max(ref childMin, ref minInLod, out childMin);
                                Vector3I.Min(ref childMax, ref maxInLod, out childMax);
                                for (int z = childMin.Z; z <= childMax.Z; ++z)
                                {
                                    for (int y = childMin.Y; y <= childMax.Y; ++y)
                                    {
                                        for (int x = childMin.X; x <= childMax.X; ++x)
                                        {
                                            var write = writeOffset;
                                            write.X += x - minInLod.X;
                                            write.Y += y - minInLod.Y;
                                            write.Z += z - minInLod.Z;
                                            target.Set(type, ref write, nodeData);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            finally { ProfilerShort.End(); }
        }
        public static bool DoGrading(
            IReadOnlyList <IRailGradeComponent> components, Vector3D target, float radius, uint availableForDeposit,
            uint availableForExcavate,
            uint[] excavatedByMaterial, byte materialToDeposit, out uint totalDeposited, out uint totalExcavated,
            bool testDynamic, out bool triedToChange, out bool intersectedDynamic)
        {
            try
            {
                var voxelRadius = (int)Math.Ceiling(radius);
                {
                    _dynamicEntities.Clear();
                    _workingVoxels.Clear();
                    var sphere = new BoundingSphereD(target, voxelRadius + 2);
                    var tmp    = MyEntities.GetEntitiesInSphere(ref sphere);
                    using (tmp.GetClearToken())
                        foreach (var e in tmp)
                        {
                            if (e is MyVoxelBase vox)
                            {
                                _workingVoxels.Add(vox);
                            }
                            if (e.Physics != null && !e.Physics.IsStatic)
                            {
                                _dynamicEntities.Add(e);
                            }
                        }
                }


                totalDeposited     = 0;
                totalExcavated     = 0;
                triedToChange      = false;
                intersectedDynamic = false;

                var fill     = new IGradeShape[components.Count];
                var excavate = new IGradeShape[components.Count];
                for (var i = 0; i < components.Count; i++)
                {
                    components[i].Unblit(out fill[i], out excavate[i]);
                }

                var voxel = MyGamePruningStructureSandbox.GetClosestPlanet(target)?.RootVoxel;
                if (voxel == null)
                {
                    return(false);
                }
                Vector3I center;
                MyVoxelCoordSystems.WorldPositionToVoxelCoord(voxel.PositionLeftBottomCorner, ref target, out center);
                var voxMin = center - voxelRadius - 1;
                var voxMax = center + voxelRadius + 1;
                _storage.Resize(voxMin, voxMax);
                voxel.Storage.ReadRange(_storage, MyStorageDataTypeFlags.ContentAndMaterial, 0, voxMin, voxMax);

                var changed = false;

                #region Mutate

                for (var i = 0;
                     i <= voxelRadius && (!triedToChange || availableForExcavate > 0 || availableForDeposit > 0);
                     i++)
                {
                    for (var e = new ShellEnumerator(center - i, center + i);
                         e.MoveNext() && (!triedToChange || availableForExcavate > 0 || availableForDeposit > 0);)
                    {
                        var      vCoord    = e.Current;
                        var      dataCoord = e.Current - voxMin;
                        Vector3D worldCoord;
                        MyVoxelCoordSystems.VoxelCoordToWorldPosition(voxel.PositionLeftBottomCorner, ref vCoord,
                                                                      out worldCoord);
                        var cval = _storage.Get(MyStorageDataTypeEnum.Content, ref dataCoord);

                        byte?excavationDensity = null;
                        if (cval > 0 && (!triedToChange || availableForExcavate > 0))
                        {
                            float density = 0;
                            foreach (var c in excavate.Where(x => x != null))
                            {
                                density = Math.Max(density, c.GetDensity(ref worldCoord));
                            }
                            if (density > 0)
                            {
                                excavationDensity = (byte)((1 - density) * byte.MaxValue);
                            }
                        }

                        byte?fillDensity = null;
                        if (cval < byte.MaxValue && (!triedToChange || availableForDeposit > 0))
                        {
                            float density = 0;
                            foreach (var c in fill.Where(x => x != null))
                            {
                                density = Math.Max(density, c.GetDensity(ref worldCoord));
                            }
                            if (density > 0)
                            {
                                fillDensity = (byte)(density * byte.MaxValue);
                            }
                        }

                        if ((!fillDensity.HasValue || cval >= fillDensity.Value) &&
                            (!excavationDensity.HasValue || cval <= excavationDensity.Value))
                        {
                            continue;
                        }

                        if (excavationDensity.HasValue && excavationDensity.Value < cval)
                        {
                            triedToChange = true;
                            var toExtract = (uint)Math.Min(availableForExcavate, cval - excavationDensity.Value);
                            if (toExtract > 0)
                            {
                                var mid = _storage.Get(MyStorageDataTypeEnum.Material, ref dataCoord);
                                if (excavatedByMaterial != null && mid < excavatedByMaterial.Length)
                                {
                                    excavatedByMaterial[mid] += toExtract;
                                }
                                DisableFarming(worldCoord);
                                _storage.Set(MyStorageDataTypeEnum.Content, ref dataCoord, (byte)(cval - toExtract));
                                totalExcavated       += toExtract;
                                availableForExcavate -= toExtract;
                                changed = true;
                            }

                            continue;
                        }

                        if (!fillDensity.HasValue || fillDensity.Value <= cval)
                        {
                            continue;
                        }
                        triedToChange = true;
                        var toFill = Math.Min(availableForDeposit, fillDensity.Value - cval);
                        if (toFill <= 0)
                        {
                            continue;
                        }

                        // would this deposit in midair?
                        {
                            var test = worldCoord;
                            test += 2 * Vector3D.Normalize(
                                MyGravityProviderSystem.CalculateNaturalGravityInPoint(worldCoord));
                            Vector3I vtest;
                            MyVoxelCoordSystems.WorldPositionToVoxelCoord(voxel.PositionLeftBottomCorner, ref test,
                                                                          out vtest);
                            vtest = Vector3I.Clamp(vtest, voxMin, voxMax) - voxMin;
                            if (vtest != vCoord && _storage.Get(MyStorageDataTypeEnum.Content, ref vtest) == 0)
                            {
                                continue;
                            }
                        }

                        // would it touch something dynamic?
                        if (testDynamic)
                        {
                            var box = new BoundingBoxD(worldCoord - 0.25, worldCoord + 0.25);
                            var bad = false;
                            foreach (var k in _dynamicEntities)
                            {
                                if (k.PositionComp.WorldAABB.Contains(box) == ContainmentType.Disjoint)
                                {
                                    continue;
                                }
                                var obb = new OrientedBoundingBoxD(k.PositionComp.LocalAABB, k.WorldMatrix);
                                if (!obb.Intersects(ref box))
                                {
                                    continue;
                                }
                                bad = true;
                                break;
                            }

                            if (bad)
                            {
                                intersectedDynamic = true;
                                continue;
                            }
                        }

                        changed = true;
                        DisableFarming(worldCoord);
                        availableForDeposit = (uint)(availableForDeposit - toFill);
                        totalDeposited     += (uint)toFill;
                        _storage.Set(MyStorageDataTypeEnum.Content, ref dataCoord, (byte)(cval + toFill));
                        if (fillDensity.Value <= cval * 1.25f)
                        {
                            continue;
                        }
                        var t = -Vector3I.One;
                        for (var itrContent = new Vector3I_RangeIterator(ref t, ref Vector3I.One);
                             itrContent.IsValid();
                             itrContent.MoveNext())
                        {
                            var tpos = dataCoord + itrContent.Current;
//                    var state = _storage.Get(MyStorageDataTypeEnum.Content, ref tpos);
//                    if (itrContent.Current == Vector3I.Zero || state == 0)
                            _storage.Set(MyStorageDataTypeEnum.Material, ref tpos, materialToDeposit);
                        }
                    }
                }

                #endregion Mutate

                if (changed)
                {
                    voxel.Storage.WriteRange(_storage, MyStorageDataTypeFlags.ContentAndMaterial, voxMin, voxMax);
                }

                return(changed);
            }
            finally
            {
                _dynamicEntities.Clear();
                _workingVoxels.Clear();
            }
        }
        private unsafe void ReadRange(
            MyStorageData target,
            ref Vector3I targetWriteOffset,
            MyStorageDataTypeFlags types,
            int treeHeight,
            Dictionary<UInt64, MyOctreeNode> nodes,
            Dictionary<UInt64, IMyOctreeLeafNode> leaves,
            int lodIndex,
            ref Vector3I minInLod,
            ref Vector3I maxInLod,
            ref MyVoxelRequestFlags flags)
        {
            int stackIdx = 0;
            int stackSize = MySparseOctree.EstimateStackSize(treeHeight);
            MyCellCoord* stack = stackalloc MyCellCoord[stackSize];
            MyCellCoord data = new MyCellCoord(treeHeight + LeafLodCount, ref Vector3I.Zero);
            stack[stackIdx++] = data;
            MyCellCoord cell = new MyCellCoord();

            var octreeType = types.Requests(MyStorageDataTypeEnum.Content) ? MyStorageDataTypeEnum.Content : MyStorageDataTypeEnum.Material;

            FillOutOfBounds(target, octreeType, ref targetWriteOffset, lodIndex, minInLod, maxInLod);

            while (stackIdx > 0)
            {
                Debug.Assert(stackIdx <= stackSize);
                data = stack[--stackIdx];

                cell.Lod = Math.Max(data.Lod - LeafLodCount, 0);
                cell.CoordInLod = data.CoordInLod;

                int lodDiff;
                IMyOctreeLeafNode leaf;
                if (leaves.TryGetValue(cell.PackId64(), out leaf))
                {
                    lodDiff = data.Lod - lodIndex;
                    var rangeMinInDataLod = minInLod >> lodDiff;
                    var rangeMaxInDataLod = maxInLod >> lodDiff;
                    if (data.CoordInLod.IsInsideInclusive(ref rangeMinInDataLod, ref rangeMaxInDataLod))
                    {
                        var nodePosInLod = data.CoordInLod << lodDiff;
                        var writeOffset = nodePosInLod - minInLod;
                        Vector3I.Max(ref writeOffset, ref Vector3I.Zero, out writeOffset);
                        writeOffset += targetWriteOffset;
                        var lodSizeMinusOne = new Vector3I((1 << lodDiff) - 1);

                        Vector3I minInLeaf = minInLod - nodePosInLod;
                        Vector3I maxInLeaf = maxInLod - nodePosInLod;
                        if (!minInLeaf.IsInsideInclusive(Vector3I.Zero, lodSizeMinusOne) || !maxInLeaf.IsInsideInclusive(Vector3I.Zero, lodSizeMinusOne))
                        {
                            minInLeaf = Vector3I.Clamp(minInLod - nodePosInLod, Vector3I.Zero, lodSizeMinusOne);
                            maxInLeaf = Vector3I.Clamp(maxInLod - nodePosInLod, Vector3I.Zero, lodSizeMinusOne);
                            // No Optimizations when not reading entirelly from the provider because the octree can't take it.
                            flags &= MyVoxelRequestFlags.SurfaceMaterial;
                        }


                        leaf.ReadRange(target, types, ref writeOffset, lodIndex, ref minInLeaf, ref maxInLeaf, ref flags);
                        // Occlusion is not stored in tree, we just read from provider
                        if (!leaf.ReadOnly && types.Requests(MyStorageDataTypeEnum.Occlusion))
                        {
                            minInLeaf += nodePosInLod;
                            maxInLeaf += nodePosInLod;
                            ReadFromProvider(target, MyStorageDataTypeFlags.Occlusion, ref writeOffset, lodIndex, ref minInLeaf, ref maxInLeaf, ref flags);
                        }
                    }
                    continue;
                }

                cell.Lod -= 1;
                lodDiff = data.Lod - 1 - lodIndex;

                MyOctreeNode node;
                if (nodes.TryGetValue(cell.PackId64(), out node) == false)
                {
                    Debug.Fail("invalid querry for node");
                    continue;
                }
                var min = minInLod >> lodDiff;
                var max = maxInLod >> lodDiff;
                var nodePositionInChild = data.CoordInLod << 1;
                min -= nodePositionInChild;
                max -= nodePositionInChild;
                for (int i = 0; i < MyOctreeNode.CHILD_COUNT; ++i)
                {
                    Vector3I childPosRelative;
                    ComputeChildCoord(i, out childPosRelative);
                    if (!childPosRelative.IsInsideInclusive(ref min, ref max))
                        continue;
                    if (lodIndex < data.Lod && node.HasChild(i))
                    {
                        Debug.Assert(stackIdx < stackSize);
                        stack[stackIdx++] = new MyCellCoord(data.Lod - 1, nodePositionInChild + childPosRelative);
                    }
                    else
                    {
                        // No Optimizations when not reading entirelly from the provider because the octree can't take it.
                        flags &= MyVoxelRequestFlags.SurfaceMaterial;

                        var childMin = nodePositionInChild + childPosRelative;
                        childMin <<= lodDiff;
                        var writeOffset = childMin - minInLod;
                        Vector3I.Max(ref writeOffset, ref Vector3I.Zero, out writeOffset);
                        writeOffset += targetWriteOffset;
                        var nodeData = node.GetData(i);
                        if (lodDiff == 0)
                        {
                            target.Set(octreeType, ref writeOffset, nodeData);
                        }
                        else
                        {
                            var childMax = childMin + ((1 << lodDiff) - 1);
                            Vector3I.Max(ref childMin, ref minInLod, out childMin);
                            Vector3I.Min(ref childMax, ref maxInLod, out childMax);
                            for (int z = childMin.Z; z <= childMax.Z; ++z)
                            for (int y = childMin.Y; y <= childMax.Y; ++y)
                            for (int x = childMin.X; x <= childMax.X; ++x)
                            {
                                Vector3I write = writeOffset;
                                write.X += x - childMin.X;
                                write.Y += y - childMin.Y;
                                write.Z += z - childMin.Z;
                                target.Set(octreeType, ref write, nodeData);
                            }
                        }
                    }
                }
            }
        }