This commit is contained in:
Brian Neumann-Fopiano committed 2026-07-18 18:12:04 -04:00
1 parent 0193f75daa
commit ad41b6795c
48 files changed
+1193 -141

No files matched your search

@@ -163,7 +163,6 @@ final class DecoratorCore {
}
data.set(x, height, z, fixFacesForHunk(
decorator.getForceBlock().getBlockData(irisData), data, x, z, realX, height, realZ, mantle));
return;
}
if (!decorator.isForcePlace()) {
@@ -135,36 +135,42 @@ public class IrisStructureComponent extends IrisMantleComponent {
ObjectPlaceMode mode = structure.getPlaceMode();
int failedPieces = 0;
Long2IntOpenHashMap foundationColumns = placement.getStilt() == null
IrisStructureStiltSettings stilt = placement.getStilt();
Long2IntOpenHashMap foundationColumns = stilt == null
? null : new Long2IntOpenHashMap();
boolean supportNonOccluding = stilt != null && stilt.isSupportNonOccluding();
if (placement.isUnderground()) {
ObjectPlaceMode undergroundMode = (mode == ObjectPlaceMode.ORGANIC_STILT || mode == ObjectPlaceMode.CEILING_HANG)
? mode : ObjectPlaceMode.STRUCTURE_PIECE;
for (PlacedStructurePiece p : pieces) {
if (placeObject(writer, structure, p, undergroundMode, p.getY(), rng, foundationColumns) == -1) {
if (placeObject(writer, structure, p, undergroundMode, p.getY(), rng,
foundationColumns, supportNonOccluding) == -1) {
failedPieces++;
}
}
} else if (mode == ObjectPlaceMode.STRUCTURE_PIECE || mode == ObjectPlaceMode.FLOATING) {
for (PlacedStructurePiece p : pieces) {
if (placeObject(writer, structure, p, ObjectPlaceMode.STRUCTURE_PIECE, p.getY(), rng, foundationColumns) == -1) {
if (placeObject(writer, structure, p, ObjectPlaceMode.STRUCTURE_PIECE, p.getY(), rng,
foundationColumns, supportNonOccluding) == -1) {
failedPieces++;
}
}
} else if (pieces.size() == 1) {
if (placeObject(writer, structure, pieces.getFirst(), mode, -1, rng, foundationColumns) == -1) {
if (placeObject(writer, structure, pieces.getFirst(), mode, -1, rng,
foundationColumns, supportNonOccluding) == -1) {
failedPieces++;
}
} else {
for (PlacedStructurePiece p : pieces) {
if (placeObject(writer, structure, p, ObjectPlaceMode.STRUCTURE_PIECE, p.getY(), rng, foundationColumns) == -1) {
if (placeObject(writer, structure, p, ObjectPlaceMode.STRUCTURE_PIECE, p.getY(), rng,
foundationColumns, supportNonOccluding) == -1) {
failedPieces++;
}
}
}
requireAppliedPieces(resolved, cx, cz, failedPieces);
if (failedPieces == 0 && placement.getStilt() != null) {
placeFoundation(writer, foundationColumns, placement.getStilt(), rng, !placement.isUnderground());
if (failedPieces == 0 && stilt != null) {
placeFoundation(writer, foundationColumns, stilt, rng, !placement.isUnderground());
}
}
@@ -533,7 +539,8 @@ public class IrisStructureComponent extends IrisMantleComponent {
}
private int placeObject(MantleWriter writer, IrisStructure structure, PlacedStructurePiece p,
ObjectPlaceMode mode, int y, RNG rng, Long2IntOpenHashMap foundationColumns) {
ObjectPlaceMode mode, int y, RNG rng, Long2IntOpenHashMap foundationColumns,
boolean supportNonOccluding) {
IrisObject object = p.getObject();
String objectKey = object.getLoadKey();
IrisObjectPlacement config = structure.createLootPlacement(objectKey);
@@ -549,7 +556,8 @@ public class IrisStructureComponent extends IrisMantleComponent {
String marker = structurePlacementMarker(structure, p, objectKey);
return object.place(p.getX(), placeY, p.getZ(), writer, config, rng, (position, state) -> {
StructureFoundationPlanner.recordBaseCell(
foundationColumns, position.getX(), position.getY(), position.getZ(), state);
foundationColumns, position.getX(), position.getY(), position.getZ(), state,
supportNonOccluding);
if (marker != null && shouldWriteStructureMarker(state)) {
writer.setData(position.getX(), position.getY(), position.getZ(), marker);
}
@@ -13,8 +13,9 @@ final class StructureFoundationPlanner {
}
static void recordBaseCell(Long2IntOpenHashMap columns, int x, int y, int z,
PlatformBlockState state) {
if (columns == null || state == null || !state.isOccluding()) {
PlatformBlockState state, boolean supportNonOccluding) {
if (columns == null || state == null
|| !(supportNonOccluding ? state.isSolid() : state.isOccluding())) {
return;
}
long columnKey = pack(x, z);
@@ -24,6 +24,7 @@ import art.arcane.iris.engine.object.IrisBiome;
import art.arcane.iris.engine.object.IrisDepositGenerator;
import art.arcane.iris.engine.object.IrisDepositVariant;
import art.arcane.iris.engine.object.IrisDimension;
import art.arcane.iris.engine.object.IrisDimensionCarvingResolver;
import art.arcane.iris.engine.object.IrisObject;
import art.arcane.iris.engine.object.IrisProceduralBlocks;
import art.arcane.iris.engine.object.IrisRegion;
@@ -87,6 +88,7 @@ public class IrisDepositModifier extends EngineAssignedModifier<PlatformBlockSta
}
public void generate(IrisDepositGenerator k, MantleChunk chunk, Hunk<PlatformBlockState> data, RNG rng, int cx, int cz, boolean safe, HeightMap he, ChunkContext context) {
IrisDimensionCarvingResolver.State carvingState = new IrisDimensionCarvingResolver.State();
if (k.getSpawnChance() < rng.d())
return;
@@ -123,6 +125,29 @@ public class IrisDepositModifier extends EngineAssignedModifier<PlatformBlockSta
if (y > k.getMaxHeight() || y < k.getMinHeight() || y > height - 2)
continue;
if (k.isOre(getData())) {
IrisBiome depositBiome = getEngine().getCaveBiome(
(cx << 4) + x, y, (cz << 4) + z, carvingState);
if (depositBiome != null) {
double frequencyMultiplier = depositBiome.getOreDepositFrequencyMultiplier();
if (frequencyMultiplier < 1D
&& !passesOreFrequency(frequencyMultiplier, rng.d())) {
continue;
}
double sizeMultiplier = depositBiome.getOreDepositSizeMultiplier();
if (sizeMultiplier != 1D) {
IrisObject scaledClump = k.getClump(getEngine(), rng, getData(), sizeMultiplier);
int scaledDimension = scaledClump.getW();
x = clampDepositCenter(x, scaledDimension, 16);
y = clampDepositCenter(y, scaledDimension, getEngine().getHeight());
z = clampDepositCenter(z, scaledDimension, 16);
clump = scaledClump;
}
}
}
IrisDimension dimension = getDimension();
for (art.arcane.iris.util.common.math.IrisBlockVector j : clump.getBlocks().keys()) {
@@ -148,7 +173,8 @@ public class IrisDepositModifier extends EngineAssignedModifier<PlatformBlockSta
if (chunk.get(nx, ny, nz, MatterCavern.class) == null) {
PlatformBlockState ore = clump.getBlocks().get(j);
PlatformBlockState remapped = resolveDepositVariant(cx, cz, nx, ny, nz, ore, dimension, context);
PlatformBlockState remapped = resolveDepositVariant(
cx, cz, nx, ny, nz, ore, dimension, context, carvingState);
PlatformBlockState finalBlock = remapped != null
? remapped
: B.toDeepSlateOre(current, ore);
@@ -177,12 +203,22 @@ public class IrisDepositModifier extends EngineAssignedModifier<PlatformBlockSta
return state != null && !state.isAir() && !state.isFluid();
}
private PlatformBlockState resolveDepositVariant(int cx, int cz, int nx, int localY, int nz, PlatformBlockState ore, IrisDimension dimension, ChunkContext context) {
static boolean passesOreFrequency(double multiplier, double sample) {
return multiplier >= 1D || sample < Math.max(0D, multiplier);
}
static int clampDepositCenter(int center, int dimension, int limit) {
int minimum = dimension / 2;
int maximum = (int) (limit - dimension / 2D);
return Math.max(minimum, Math.min(center, maximum));
}
private PlatformBlockState resolveDepositVariant(int cx, int cz, int nx, int localY, int nz, PlatformBlockState ore, IrisDimension dimension, ChunkContext context, IrisDimensionCarvingResolver.State carvingState) {
int worldX = (cx << 4) + nx;
int worldZ = (cz << 4) + nz;
int worldY = absoluteWorldY(getEngine().getMinHeight(), localY);
IrisBiome biome = getEngine().getBiome(worldX, localY, worldZ);
IrisBiome biome = getEngine().getCaveBiome(worldX, localY, worldZ, carvingState);
if (biome != null) {
PlatformBlockState match = matchDepositVariant(biome.getDepositVariants(), ore, worldY);
if (match != null) {
@@ -205,6 +205,14 @@ public class IrisBiome extends IrisRegistrant implements IRare {
@ArrayType(min = 1, type = IrisDepositGenerator.class)
@Desc("Define biome deposit generators that add onto the existing regional and global deposit generators")
private KList<IrisDepositGenerator> deposits = new KList<>();
@MinNumber(0)
@MaxNumber(1)
@Desc("Multiplier applied to the frequency of every ore deposit whose center is inside this biome. A value of 0.4 keeps 40% of ore veins while leaving non-ore deposits unchanged.")
private double oreDepositFrequencyMultiplier = 1D;
@MinNumber(0.01)
@MaxNumber(16)
@Desc("Multiplier applied to the block count of every ore deposit whose center is inside this biome. Non-ore deposits are unchanged.")
private double oreDepositSizeMultiplier = 1D;
@ArrayType(min = 1, type = IrisDepositVariant.class)
@Desc("Deposit ore remap rules scoped to this biome. Each entry declares a vertical band and a source->replacement block id map. Applied before regional and dimension rules; first matching biome rule wins.")
private KList<IrisDepositVariant> depositVariants = new KList<>();
@@ -27,16 +27,18 @@ import art.arcane.iris.engine.object.annotations.MaxNumber;
import art.arcane.iris.engine.object.annotations.MinNumber;
import art.arcane.iris.engine.object.annotations.Required;
import art.arcane.iris.engine.object.annotations.Snippet;
import art.arcane.iris.spi.PlatformBlockState;
import art.arcane.iris.util.common.math.IrisBlockVector;
import art.arcane.iris.util.common.math.Vector3i;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.collection.KSet;
import art.arcane.volmlib.util.math.BlockPosition;
import art.arcane.volmlib.util.math.RNG;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import art.arcane.iris.spi.PlatformBlockState;
import lombok.experimental.Accessors;
@Snippet("deposit")
@@ -46,8 +48,10 @@ import lombok.experimental.Accessors;
@Desc("Creates ore & other block deposits underground")
@Data
public class IrisDepositGenerator {
private final transient AtomicCache<KList<IrisObject>> objects = new AtomicCache<>();
private final transient ConcurrentMap<ClumpCacheKey, KList<IrisObject>> objects = new ConcurrentHashMap<>();
private final transient AtomicCache<KList<PlatformBlockState>> blockData = new AtomicCache<>();
private final transient AtomicCache<Boolean> ore = new AtomicCache<>();
private final transient ConcurrentMap<ClumpCacheKey, KList<IrisObject>> scaledObjects = new ConcurrentHashMap<>();
@Required
@MinNumber(0)
@MaxNumber(8192) // TODO: WARNING HEIGHT
@@ -98,13 +102,14 @@ public class IrisDepositGenerator {
private boolean replaceBedrock = false;
public IrisObject getClump(Engine engine, RNG rng, IrisData rdata) {
KList<IrisObject> objects = this.objects.aquire(() ->
{
RNG rngv = new RNG(engine.getSeedManager().getDeposit() + hashCode());
ClumpCacheKey cacheKey = new ClumpCacheKey(engine.getSeedManager().getDeposit(), minSize, maxSize);
KList<IrisObject> objects = this.objects.computeIfAbsent(cacheKey, key -> {
RNG rngv = new RNG(key.depositSeed() + hashCode());
KList<IrisObject> objectsf = new KList<>();
for (int i = 0; i < varience; i++) {
objectsf.add(generateClumpObject(rngv.nextParallelRNG(2349 * i + 3598), rdata));
objectsf.add(generateClumpObject(
rngv.nextParallelRNG(2349 * i + 3598), rdata, key.minSize(), key.maxSize()));
}
return objectsf;
@@ -112,12 +117,40 @@ public class IrisDepositGenerator {
return objects.get(rng.i(0, objects.size()));
}
public IrisObject getClump(Engine engine, RNG rng, IrisData rdata, double sizeMultiplier) {
if (sizeMultiplier == 1D) {
return getClump(engine, rng, rdata);
}
int scaledMinSize = scaledDepositSize(minSize, sizeMultiplier);
int scaledMaxSize = scaledDepositSize(maxSize, sizeMultiplier);
ClumpCacheKey cacheKey = new ClumpCacheKey(
engine.getSeedManager().getDeposit(), scaledMinSize, scaledMaxSize);
KList<IrisObject> objects = scaledObjects.computeIfAbsent(cacheKey, key -> {
long sizeSeed = ((long) key.minSize() << 32) ^ (key.maxSize() & 0xffffffffL);
RNG rngv = new RNG(key.depositSeed() + hashCode() + sizeSeed);
KList<IrisObject> generated = new KList<>();
for (int i = 0; i < varience; i++) {
generated.add(generateClumpObject(
rngv.nextParallelRNG(2349 * i + 3598), rdata, key.minSize(), key.maxSize()));
}
return generated;
});
return objects.get(rng.i(0, objects.size()));
}
public int getMaxDimension() {
return Math.min(11, (int) Math.ceil(Math.cbrt(maxSize)));
}
private IrisObject generateClumpObject(RNG rngv, IrisData rdata) {
int s = rngv.i(minSize, maxSize + 1);
static int scaledDepositSize(int size, double multiplier) {
return Math.max(0, Math.min(8192, (int) Math.round(size * multiplier)));
}
private IrisObject generateClumpObject(RNG rngv, IrisData rdata, int clumpMinSize, int clumpMaxSize) {
int s = rngv.i(clumpMinSize, clumpMaxSize + 1);
if (s == 1) {
IrisObject o = new IrisObject(1, 1, 1);
Vector3i center = o.getCenter();
@@ -184,4 +217,19 @@ public class IrisDepositGenerator {
return blockData;
});
}
public boolean isOre(IrisData rdata) {
return ore.aquire(() -> {
for (PlatformBlockState block : getBlockData(rdata)) {
if (block.isOre()) {
return true;
}
}
return false;
});
}
record ClumpCacheKey(long depositSeed, int minSize, int maxSize) {
}
}
@@ -69,6 +69,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
@@ -100,7 +101,7 @@ public class IrisObject extends IrisRegistrant {
protected transient volatile boolean smartBored = false;
@Setter
protected transient AtomicCache<AxisAlignedBB> aabb = new AtomicCache<>();
private transient final AtomicCache<Boolean> treeBlockPresence = new AtomicCache<>();
private transient final AtomicCache<KList<IrisBlockVector>> surfaceSupportOffsets = new AtomicCache<>();
@Getter
private VectorMap<PlatformBlockState> blocks;
@Getter
@@ -326,7 +327,7 @@ public class IrisObject extends IrisRegistrant {
}
public void readLegacy(InputStream in) throws IOException {
treeBlockPresence.reset();
surfaceSupportOffsets.reset();
DataInputStream din = new DataInputStream(in);
this.w = din.readInt();
this.h = din.readInt();
@@ -358,7 +359,7 @@ public class IrisObject extends IrisRegistrant {
}
public void read(InputStream in) throws Throwable {
treeBlockPresence.reset();
surfaceSupportOffsets.reset();
DataInputStream din = new DataInputStream(in);
this.w = din.readInt();
this.h = din.readInt();
@@ -602,6 +603,7 @@ public class IrisObject extends IrisRegistrant {
shrinkOffset = offset;
blocks = b;
states = s;
surfaceSupportOffsets.reset();
}
public void clean() {
@@ -613,6 +615,7 @@ public class IrisObject extends IrisRegistrant {
blocks = d;
states = dx;
surfaceSupportOffsets.reset();
}
public IrisBlockVector getSigned(int x, int y, int z) {
@@ -624,7 +627,7 @@ public class IrisObject extends IrisRegistrant {
}
public void setUnsigned(int x, int y, int z, PlatformBlockState block) {
treeBlockPresence.reset();
surfaceSupportOffsets.reset();
IrisBlockVector v = getSigned(x, y, z);
if (block == null) {
@@ -646,7 +649,7 @@ public class IrisObject extends IrisRegistrant {
}
public void setUnsigned(int x, int y, int z, Block block, boolean legacy) {
treeBlockPresence.reset();
surfaceSupportOffsets.reset();
IrisBlockVector v = getSigned(x, y, z);
if (block == null) {
@@ -932,9 +935,8 @@ public class IrisObject extends IrisRegistrant {
&& !config.isUnderwater()
&& !config.isOnwater()
&& config.getCarvingSupport().supportsSurface()
&& hasTreeBlocks()
&& IrisSurfaceOpening.isOpen(oplacer, getLoader(), x, z, config.getTranslate(),
config.getRotation(), spinx, spiny, spinz)) {
config.getRotation(), spinx, spiny, spinz, getSurfaceSupportOffsets())) {
return -1;
}
@@ -1511,16 +1513,32 @@ public class IrisObject extends IrisRegistrant {
};
}
boolean hasTreeBlocks() {
Boolean present = treeBlockPresence.aquire(() -> {
KList<IrisBlockVector> getSurfaceSupportOffsets() {
return surfaceSupportOffsets.aquire(() -> {
readLock.lock();
try {
return IrisSurfaceOpening.containsTreeBlocks(blocks.values());
int lowestY = Integer.MAX_VALUE;
KList<IrisBlockVector> offsets = new KList<>();
for (Map.Entry<IrisBlockVector, PlatformBlockState> entry : blocks) {
PlatformBlockState state = entry.getValue();
if (state == null || !state.isSolid() || state.isFoliage()) {
continue;
}
IrisBlockVector position = entry.getKey();
int blockY = position.getBlockY();
if (blockY < lowestY) {
lowestY = blockY;
offsets.clear();
}
if (blockY == lowestY) {
offsets.add(position.clone());
}
}
return offsets;
} finally {
readLock.unlock();
}
});
return Boolean.TRUE.equals(present);
}
private boolean isCarvedCaveAnchor(IObjectPlacer placer, int x, int y, int z) {
@@ -1584,6 +1602,7 @@ public class IrisObject extends IrisRegistrant {
blocks = d;
states = dx;
surfaceSupportOffsets.reset();
shrinkwrap();
writeLock.unlock();
}
@@ -1713,6 +1732,7 @@ public class IrisObject extends IrisRegistrant {
}
blocks = b;
surfaceSupportOffsets.reset();
writeLock.unlock();
}
@@ -1744,6 +1764,7 @@ public class IrisObject extends IrisRegistrant {
}
blocks = b;
surfaceSupportOffsets.reset();
writeLock.unlock();
}
@@ -1779,6 +1800,7 @@ public class IrisObject extends IrisRegistrant {
}
blocks = b;
surfaceSupportOffsets.reset();
writeLock.unlock();
}
@@ -21,4 +21,7 @@ public class IrisStructureStiltSettings {
@Desc("Block palette used for foundation columns.")
private IrisMaterialPalette palette = new IrisMaterialPalette().qclear().qadd("minecraft:cobblestone");
@Desc("For Iris-authored structures, whether solid partial blocks such as slabs, stairs, and walls may seed foundation columns instead of requiring a fully occluding base block.")
private boolean supportNonOccluding = false;
}
@@ -1,9 +1,10 @@
package art.arcane.iris.engine.object;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.spi.PlatformBlockState;
import art.arcane.iris.util.common.math.IrisBlockVector;
import java.util.List;
final class IrisSurfaceOpening {
private static final int SUPPORT_RADIUS = 1;
@@ -11,15 +12,37 @@ final class IrisSurfaceOpening {
}
static boolean isOpen(IObjectPlacer placer, IrisData data, int x, int z, IrisObjectTranslate translate,
IrisObjectRotation rotation, int spinX, int spinY, int spinZ) {
IrisBlockVector offset = new IrisBlockVector(0, 0, 0);
if (translate != null) {
offset = rotation == null
? translate.translate(offset)
: translate.translate(offset, rotation, spinX, spinY, spinZ);
IrisObjectRotation rotation, int spinX, int spinY, int spinZ,
List<IrisBlockVector> supportOffsets) {
if (supportOffsets.isEmpty()) {
IrisBlockVector origin = transform(new IrisBlockVector(0, 0, 0), translate, rotation, spinX, spinY, spinZ);
return isOpenAround(placer, data, x + origin.getBlockX(), z + origin.getBlockZ());
}
int centerX = x + offset.getBlockX();
int centerZ = z + offset.getBlockZ();
for (IrisBlockVector supportOffset : supportOffsets) {
IrisBlockVector transformed = transform(supportOffset, translate, rotation, spinX, spinY, spinZ);
if (isOpenAround(placer, data, x + transformed.getBlockX(), z + transformed.getBlockZ())) {
return true;
}
}
return false;
}
private static IrisBlockVector transform(IrisBlockVector offset, IrisObjectTranslate translate,
IrisObjectRotation rotation, int spinX, int spinY, int spinZ) {
IrisBlockVector transformed = offset.clone();
if (rotation != null) {
transformed = rotation.rotate(transformed, spinX, spinY, spinZ);
}
if (translate == null) {
return transformed;
}
return rotation == null
? translate.translate(transformed)
: translate.translate(transformed, rotation, spinX, spinY, spinZ);
}
private static boolean isOpenAround(IObjectPlacer placer, IrisData data, int centerX, int centerZ) {
for (int dx = -SUPPORT_RADIUS; dx <= SUPPORT_RADIUS; dx++) {
for (int dz = -SUPPORT_RADIUS; dz <= SUPPORT_RADIUS; dz++) {
int sampleX = centerX + dx;
@@ -32,13 +55,4 @@ final class IrisSurfaceOpening {
}
return false;
}
static boolean containsTreeBlocks(Iterable<PlatformBlockState> states) {
for (PlatformBlockState state : states) {
if (state != null && state.isTreeBlock()) {
return true;
}
}
return false;
}
}