/// <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}"); }
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); } } } } } }