public unsafe World DownSample(int extraLods, out int voxelCount) { World subWorld = new World(dimensions, lod + extraLods); World thisWorld = this; int step = 1 << subWorld.lod; int totalVoxels = 0; // parallelize downsampling on the X-axis System.Threading.Tasks.Parallel.For(0, dimensions.x / step, (int i) => { int yVoxels = subWorld.dimensions.y >> subWorld.lod; RLEElement[] elementBuffer = new RLEElement[yVoxels]; WorldBuilder.RLEColumnBuilder builder = new WorldBuilder.RLEColumnBuilder(); int x = i * step; for (int z = 0; z < subWorld.dimensions.z; z += step) { // downsample a {step, step} grid of columns into one RLEColumn downSampled = thisWorld.DownSampleColumn(x, z, elementBuffer, extraLods, ref builder, ref totalVoxels, ref subWorld.Storage); *subWorld.Storage.GetColumnPointer(subWorld.GetIndexKnownInBounds(int2(x, z))) = downSampled; } }); voxelCount = totalVoxels; return(subWorld); }
/// <summary> /// Output a column of data into the columnbuilder; after doing this with all columns the builder will be resolved to a new, merged column /// </summary> unsafe void DownSamplePartial(int x, int z, int extraLods, ref WorldBuilder.RLEColumnBuilder columnBuilder) { RLEColumn column = *Storage.GetColumnPointer(GetIndexKnownInBounds(int2(x, z))); if (column.RunCount <= 0) { return; } int2 elementBounds = dimensions.y >> lod; int nextLod = lod + extraLods; ColorARGB32 *colorPointer = column.ColorPointer(ref Storage); for (int run = 0; run < column.RunCount; run++) { RLEElement element = column.GetIndex(ref Storage, run); elementBounds = int2(elementBounds.x - element.Length, elementBounds.x); if (element.IsAir) { continue; } for (int i = 0; i < element.Length; i++) { int Y = elementBounds.x + i; int colorIdx = element.ColorsIndex + element.Length - i - 1; columnBuilder.SetVoxel(Y >> nextLod, colorPointer[colorIdx]); } } }
public RLEColumn(RLEElement[] buffer, int runCount, int solidCount, int voxelScale, ref WorldAllocator allocator) { this = default; if (runCount <= 0) { throw new ArgumentOutOfRangeException(); } this.runCount = (ushort)runCount; int allocationElementCount = runCount + solidCount + 2; storagePointer = allocator.AllocateElements(allocationElementCount); RLEElement *startPointer = ElementGuardStart(ref allocator); // initialize element guards startPointer[0] = new RLEElement(0, 0); for (int i = 0; i < runCount; i++) { startPointer[i + 1] = buffer[i]; } startPointer[runCount + 1] = new RLEElement(0, 0); int worldMin = int.MaxValue; int worldMax = int.MinValue; int elementBoundsMin = 0; int elementBoundsMax = 0; for (int i = runCount - 1; i >= 0; i--) { RLEElement element = buffer[i]; elementBoundsMin = elementBoundsMax; elementBoundsMax = elementBoundsMin + element.Length; if (element.IsAir) { continue; } worldMin = Mathf.Min(worldMin, elementBoundsMin); worldMax = Mathf.Max(worldMax, elementBoundsMax); } if (worldMin == int.MaxValue) { throw new InvalidOperationException("only air elements in the RLE"); } this.worldMin = (ushort)(worldMin * voxelScale); this.worldMax = (ushort)(worldMax * voxelScale); }