Merged object placement improvements

- Native structure protection uses transformed object bounds
- Object copies share the volume mutation lock
- Upscaling writes expanded voxels without temporary lists
This commit is contained in:
Brian Neumann-Fopiano committed 2026-09-06 03:10:04 -04:00
commit 80155cef93
6 files changed
+150 -53

No files matched your search

@@ -110,7 +110,7 @@ public class IrisObject extends IrisRegistrant {
this.d = d;
center = new Vector3i(w / 2, h / 2, d / 2);
shrinkOffset = new Vector3i(0, 0, 0);
var lock = new ReentrantReadWriteLock();
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
readLock = lock.readLock();
writeLock = lock.writeLock();
}
@@ -135,17 +135,22 @@ public class IrisObject extends IrisRegistrant {
return aabb.aquire(() -> getAABBFor(new IrisBlockVector(w, h, d)));
}
public synchronized IrisObject copy() {
IrisObject o = new IrisObject(w, h, d);
o.setLoadKey(getLoadKey());
o.setLoader(getLoader());
o.setLoadFile(getLoadFile());
o.setCenter(getCenter().clone());
public IrisObject copy() {
readLock.lock();
try {
IrisObject copy = new IrisObject(w, h, d);
copy.setLoadKey(getLoadKey());
copy.setLoader(getLoader());
copy.setLoadFile(getLoadFile());
copy.setCenter(getCenter().clone());
blocks.forEach((i, v) -> o.blocks.put(i.clone(), v));
states.forEach((i, v) -> o.states.put(i.clone(), v.clone()));
blocks.forEach(copy.blocks::put);
states.forEach((position, tile) -> copy.states.put(position, tile.clone()));
return o;
return copy;
} finally {
readLock.unlock();
}
}
public void readLegacy(InputStream in) throws IOException {
@@ -350,7 +350,7 @@ final class IrisObjectPlacementRunner {
return -1;
}
if (!rawStructurePiece && nativeStructureVetoes(placer, config, spin, translating, translateOffset, ceilingHang,
if (!rawStructurePiece && nativeStructureVetoes(placer, config, spin, placementBounds, translating, translateOffset, ceilingHang,
yv < 0 && config.getMode() == ObjectPlaceMode.PAINT, warpMargin, x, y + yrand, z)) {
return -1;
}
@@ -955,7 +955,7 @@ final class IrisObjectPlacementRunner {
* near this placement means no per block work at all. Only when the placement envelope meets a piece does the
* precise pass run, and the first solid block inside a piece rejects the whole object before any write.
*/
private boolean nativeStructureVetoes(IObjectPlacer placer, IrisObjectPlacement config, SpinKernel spin,
private boolean nativeStructureVetoes(IObjectPlacer placer, IrisObjectPlacement config, SpinKernel spin, TransformedBounds bounds,
boolean translating, IrisBlockVector translateOffset, boolean ceilingHang,
boolean paint, int warpMargin, int x, int y, int z) {
Engine engine = placer.getEngine();
@@ -963,23 +963,21 @@ final class IrisObjectPlacementRunner {
return false;
}
int margin = (Math.max(self.getW(), Math.max(self.getH(), self.getD())) / 2) + 1 + warpMargin;
if (translating) {
margin += Math.max(Math.abs(translateOffset.getBlockX()),
Math.max(Math.abs(translateOffset.getBlockY()), Math.abs(translateOffset.getBlockZ())));
}
KList<NativeStructureVolume> volumes = engine.getNativeStructureVolumes(x - margin, z - margin, x + margin, z + margin);
int minX = x + bounds.minX();
int maxX = x + bounds.maxX();
int minZ = z + bounds.minZ();
int maxZ = z + bounds.maxZ();
KList<NativeStructureVolume> volumes = engine.getNativeStructureVolumes(minX, minZ, maxX, maxZ);
if (volumes == null || volumes.isEmpty()) {
return false;
}
int worldY = y + engine.getMinHeight();
int envelopeMinY = paint ? Integer.MIN_VALUE : worldY - margin;
int envelopeMaxY = paint ? Integer.MAX_VALUE : worldY + margin;
int envelopeMinY = paint ? Integer.MIN_VALUE : worldY + bounds.minY() - warpMargin;
int envelopeMaxY = paint ? Integer.MAX_VALUE : worldY + bounds.maxY() + warpMargin;
boolean envelopeMeetsPiece = false;
for (NativeStructureVolume volume : volumes) {
if (volume.intersects(x - margin, envelopeMinY, z - margin, x + margin, envelopeMaxY, z + margin)) {
if (volume.intersects(minX, envelopeMinY, minZ, maxX, envelopeMaxY, maxZ)) {
envelopeMeetsPiece = true;
break;
}
@@ -22,15 +22,12 @@ import art.arcane.iris.spi.IrisLogging;
import art.arcane.iris.spi.PlatformBlockState;
import art.arcane.iris.util.common.data.VectorMap;
import art.arcane.iris.util.common.math.IrisBlockVector;
import art.arcane.iris.util.common.math.IrisVector;
import art.arcane.iris.util.common.math.Vector3i;
import art.arcane.iris.util.common.parallel.BurstExecutor;
import art.arcane.iris.util.common.parallel.MultiBurst;
import art.arcane.volmlib.util.format.Form;
import art.arcane.volmlib.util.scheduling.PrecisionStopwatch;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -289,23 +286,4 @@ final class IrisObjectShaping {
static String materialKey(PlatformBlockState state) {
return IrisProceduralBlocks.materialKey(state);
}
static List<IrisBlockVector> blocksBetweenTwoPoints(IrisVector loc1, IrisVector loc2) {
List<IrisBlockVector> locations = new ArrayList<>();
int topBlockX = Math.max(loc1.getBlockX(), loc2.getBlockX());
int bottomBlockX = Math.min(loc1.getBlockX(), loc2.getBlockX());
int topBlockY = Math.max(loc1.getBlockY(), loc2.getBlockY());
int bottomBlockY = Math.min(loc1.getBlockY(), loc2.getBlockY());
int topBlockZ = Math.max(loc1.getBlockZ(), loc2.getBlockZ());
int bottomBlockZ = Math.min(loc1.getBlockZ(), loc2.getBlockZ());
for (int x = bottomBlockX; x <= topBlockX; x++) {
for (int z = bottomBlockZ; z <= topBlockZ; z++) {
for (int y = bottomBlockY; y <= topBlockY; y++) {
locations.add(new IrisBlockVector(x, y, z));
}
}
}
return locations;
}
}
@@ -96,8 +96,6 @@ final class IrisObjectTransforms {
scale = scale - 0.0001;
}
IrisPosition l1 = self.getAABB().max();
IrisPosition l2 = self.getAABB().min();
VectorMap<PlatformBlockState> placeBlock = new VectorMap<>();
VectorMap<TileData> placeTile = new VectorMap<>();
VectorMap<IrisBlockVector> placeMax = savedOrigin && scale > 1 ? new VectorMap<>() : null;
@@ -153,13 +151,27 @@ final class IrisObjectTransforms {
if (scale > 1) {
IrisVector minimum = savedOrigin ? v : v.clone().add(center);
IrisVector maximum = savedOrigin ? placeMax.get(v) : v.clone().add(center).add(sm1);
for (IrisBlockVector vec : IrisObjectShaping.blocksBetweenTwoPoints(minimum, maximum)) {
oo.blocks.put(vec, entry.getValue());
if (hasTiles) {
if (tile == null) {
oo.states.remove(vec);
} else {
oo.states.put(vec, tile.clone());
int minX = Math.min(minimum.getBlockX(), maximum.getBlockX());
int maxX = Math.max(minimum.getBlockX(), maximum.getBlockX());
int minY = Math.min(minimum.getBlockY(), maximum.getBlockY());
int maxY = Math.max(minimum.getBlockY(), maximum.getBlockY());
int minZ = Math.min(minimum.getBlockZ(), maximum.getBlockZ());
int maxZ = Math.max(minimum.getBlockZ(), maximum.getBlockZ());
IrisBlockVector position = new IrisBlockVector(minX, minY, minZ);
for (int x = minX; x <= maxX; x++) {
position.setX(x);
for (int z = minZ; z <= maxZ; z++) {
position.setZ(z);
for (int y = minY; y <= maxY; y++) {
position.setY(y);
oo.blocks.put(position, entry.getValue());
if (hasTiles) {
if (tile == null) {
oo.states.remove(position);
} else {
oo.states.put(position, tile.clone());
}
}
}
}
}