This commit is contained in:
Brian Neumann-Fopiano
2026-04-15 16:35:49 -04:00
parent 6a5616abae
commit 8c05f1bf1d
5 changed files with 153 additions and 24 deletions
@@ -20,12 +20,14 @@ package art.arcane.iris.engine.actuator;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.framework.EngineAssignedActuator;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.engine.IrisComplex;
import art.arcane.iris.engine.object.IrisBiome;
import art.arcane.iris.engine.object.IrisDimension;
import art.arcane.iris.engine.object.IrisRegion;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.iris.util.project.context.ChunkedDataCache;
import art.arcane.iris.util.project.context.ChunkContext;
import art.arcane.iris.util.project.context.ChunkedDoubleDataCache;
import art.arcane.volmlib.util.documentation.BlockCoordinates;
import art.arcane.iris.util.project.hunk.Hunk;
import art.arcane.volmlib.util.math.RNG;
@@ -171,11 +173,14 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<BlockData>
private void terrainSliverOptimized(int x, int z, int xf, Hunk<BlockData> h, ChunkContext context) {
int chunkHeight = h.getHeight();
int chunkDepth = h.getDepth();
int fluidHeight = getDimension().getFluidHeight();
boolean bedrockEnabled = getDimension().isBedrock();
IrisDimension dimension = getDimension();
IrisData data = getData();
IrisComplex complex = getComplex();
RNG localRng = rng;
int fluidHeight = dimension.getFluidHeight();
boolean bedrockEnabled = dimension.isBedrock();
ChunkedDataCache<IrisBiome> biomeCache = context.getBiome();
ChunkedDataCache<IrisRegion> regionCache = context.getRegion();
ChunkedDoubleDataCache heightCache = context.getHeight();
ChunkedDataCache<BlockData> fluidCache = context.getFluid();
ChunkedDataCache<BlockData> rockCache = context.getRock();
int realX = xf + x;
@@ -193,6 +198,8 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<BlockData>
int topY = Math.min(hf, chunkHeight - 1);
BlockData fluid = fluidCache.get(xf, zf);
BlockData rock = rockCache.get(xf, zf);
boolean hasSurfaceOres = biome.hasSurfaceOres() || region.hasSurfaceOres() || dimension.hasSurfaceOres();
boolean hasUndergroundOres = biome.hasUndergroundOres() || region.hasUndergroundOres() || dimension.hasUndergroundOres();
KList<BlockData> blocks = null;
KList<BlockData> fblocks = null;
@@ -203,9 +210,12 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<BlockData>
continue;
}
BlockData ore = biome.generateOres(realX, i, realZ, rng, getData(), true);
ore = ore == null ? region.generateOres(realX, i, realZ, rng, getData(), true) : ore;
ore = ore == null ? getDimension().generateOres(realX, i, realZ, rng, getData(), true) : ore;
BlockData ore = null;
if (hasSurfaceOres) {
ore = biome.generateSurfaceOres(realX, i, realZ, localRng, data);
ore = ore == null ? region.generateSurfaceOres(realX, i, realZ, localRng, data) : ore;
ore = ore == null ? dimension.generateSurfaceOres(realX, i, realZ, localRng, data) : ore;
}
if (ore != null) {
h.set(xf, i, zf, ore);
continue;
@@ -214,7 +224,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<BlockData>
if (i > he && i <= hf) {
int fdepth = hf - i;
if (fblocks == null) {
fblocks = biome.generateSeaLayers(realX, realZ, rng, hf - he, getData());
fblocks = biome.generateSeaLayers(realX, realZ, localRng, hf - he, data);
}
if (fblocks.hasIndex(fdepth)) {
@@ -228,7 +238,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<BlockData>
if (i <= he) {
int depth = he - i;
if (blocks == null) {
blocks = biome.generateLayers(getDimension(), realX, realZ, rng, he, he, getData(), getComplex());
blocks = biome.generateLayers(dimension, realX, realZ, localRng, he, he, data, complex);
}
if (blocks.hasIndex(depth)) {
@@ -236,9 +246,11 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<BlockData>
continue;
}
ore = biome.generateOres(realX, i, realZ, rng, getData(), false);
ore = ore == null ? region.generateOres(realX, i, realZ, rng, getData(), false) : ore;
ore = ore == null ? getDimension().generateOres(realX, i, realZ, rng, getData(), false) : ore;
if (hasUndergroundOres) {
ore = biome.generateUndergroundOres(realX, i, realZ, localRng, data);
ore = ore == null ? region.generateUndergroundOres(realX, i, realZ, localRng, data) : ore;
ore = ore == null ? dimension.generateUndergroundOres(realX, i, realZ, localRng, data) : ore;
}
if (ore != null) {
h.set(xf, i, zf, ore);
@@ -32,6 +32,7 @@ import art.arcane.volmlib.util.collection.KMap;
import art.arcane.volmlib.util.collection.KSet;
import art.arcane.iris.util.project.context.ChunkContext;
import art.arcane.iris.util.common.data.B;
import art.arcane.iris.util.project.stream.ProceduralStream;
import art.arcane.volmlib.util.documentation.BlockCoordinates;
import art.arcane.volmlib.util.documentation.ChunkCoordinates;
import art.arcane.volmlib.util.format.Form;
@@ -71,6 +72,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
IrisBiome surfaceBiome = complex.getTrueBiomeStream().get(xxx, zzz);
int surfaceY = getEngineMantle().getEngine().getHeight(xxx, zzz, true);
IrisBiome caveBiome = resolveCaveObjectBiome(xxx, zzz, surfaceY, surfaceBiome);
SurfaceHeightLookup surfaceHeightLookup = new SurfaceHeightLookup(context);
if (traceRegen) {
Iris.info("Regen object layer start: chunk=" + x + "," + z
+ " surfaceBiome=" + surfaceBiome.getLoadKey()
@@ -81,7 +83,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
+ " regionSurfacePlacers=" + region.getSurfaceObjects().size()
+ " regionCavePlacers=" + region.getCarvingObjects().size());
}
ObjectPlacementSummary summary = placeObjects(writer, rng, x, z, surfaceBiome, caveBiome, region, complex, traceRegen);
ObjectPlacementSummary summary = placeObjects(writer, rng, x, z, surfaceBiome, caveBiome, region, complex, traceRegen, surfaceHeightLookup);
if (traceRegen) {
Iris.info("Regen object layer done: chunk=" + x + "," + z
+ " biomeSurfacePlacersChecked=" + summary.biomeSurfacePlacersChecked()
@@ -141,7 +143,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
}
@ChunkCoordinates
private ObjectPlacementSummary placeObjects(MantleWriter writer, RNG rng, int x, int z, IrisBiome surfaceBiome, IrisBiome caveBiome, IrisRegion region, IrisComplex complex, boolean traceRegen) {
private ObjectPlacementSummary placeObjects(MantleWriter writer, RNG rng, int x, int z, IrisBiome surfaceBiome, IrisBiome caveBiome, IrisRegion region, IrisComplex complex, boolean traceRegen, SurfaceHeightLookup surfaceHeightLookup) {
int biomeSurfaceChecked = 0;
int biomeSurfaceTriggered = 0;
int biomeCaveChecked = 0;
@@ -174,7 +176,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
if (chance) {
biomeSurfaceTriggered++;
try {
ObjectPlacementResult result = placeObject(writer, rng, x << 4, z << 4, i, biomeSurfaceExclusionDepth, complex, traceRegen, x, z, "biome-surface");
ObjectPlacementResult result = placeObject(writer, rng, x << 4, z << 4, i, biomeSurfaceExclusionDepth, complex, traceRegen, x, z, "biome-surface", surfaceHeightLookup);
attempts += result.attempts();
placed += result.placed();
rejected += result.rejected();
@@ -239,7 +241,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
if (chance) {
regionSurfaceTriggered++;
try {
ObjectPlacementResult result = placeObject(writer, rng, x << 4, z << 4, i, regionSurfaceExclusionDepth, complex, traceRegen, x, z, "region-surface");
ObjectPlacementResult result = placeObject(writer, rng, x << 4, z << 4, i, regionSurfaceExclusionDepth, complex, traceRegen, x, z, "region-surface", surfaceHeightLookup);
attempts += result.attempts();
placed += result.placed();
rejected += result.rejected();
@@ -319,7 +321,8 @@ public class MantleObjectComponent extends IrisMantleComponent {
boolean traceRegen,
int chunkX,
int chunkZ,
String scope
String scope,
SurfaceHeightLookup surfaceHeightLookup
) {
int attempts = 0;
int placed = 0;
@@ -345,7 +348,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
int xx = rng.i(x, x + 15);
int zz = rng.i(z, z + 15);
int surfaceObjectExclusionRadius = resolveSurfaceObjectExclusionRadius(v);
if (surfaceObjectExclusionDepth > 0 && hasSurfaceCarveExposure(writer, xx, zz, surfaceObjectExclusionDepth, surfaceObjectExclusionRadius)) {
if (surfaceObjectExclusionDepth > 0 && hasSurfaceCarveExposure(writer, surfaceHeightLookup, xx, zz, surfaceObjectExclusionDepth, surfaceObjectExclusionRadius)) {
rejected++;
continue;
}
@@ -817,15 +820,16 @@ public class MantleObjectComponent extends IrisMantleComponent {
return Math.max(1, caveProfile.getAnchorSearchAttempts());
}
private boolean hasSurfaceCarveExposure(MantleWriter writer, int x, int z, int depth, int radius) {
private boolean hasSurfaceCarveExposure(MantleWriter writer, SurfaceHeightLookup surfaceHeightLookup, int x, int z, int depth, int radius) {
int horizontalRadius = Math.max(0, radius);
int maxY = getEngineMantle().getEngine().getHeight() - 1;
for (int dx = -horizontalRadius; dx <= horizontalRadius; dx++) {
for (int dz = -horizontalRadius; dz <= horizontalRadius; dz++) {
int columnX = x + dx;
int columnZ = z + dz;
int surfaceY = getEngineMantle().getEngine().getHeight(columnX, columnZ, true);
int surfaceY = surfaceHeightLookup.getRoundedHeight(columnX, columnZ);
int fromY = Math.max(1, surfaceY - Math.max(0, depth));
int toY = Math.min(getEngineMantle().getEngine().getHeight() - 1, surfaceY + 1);
int toY = Math.min(maxY, surfaceY + 1);
for (int y = fromY; y <= toY; y++) {
if (writer.isCarved(columnX, y, columnZ)) {
return true;
@@ -985,4 +989,33 @@ public class MantleObjectComponent extends IrisMantleComponent {
return null;
});
}
private static final class SurfaceHeightLookup {
private final ChunkContext context;
private final ProceduralStream<Double> heightStream;
private final KMap<Long, Integer> columnHeights;
private SurfaceHeightLookup(ChunkContext context) {
this.context = context;
this.heightStream = context.getComplex().getHeightStream();
this.columnHeights = new KMap<>();
}
private int getRoundedHeight(int worldX, int worldZ) {
int chunkBlockX = worldX & ~15;
int chunkBlockZ = worldZ & ~15;
if (chunkBlockX == context.getX() && chunkBlockZ == context.getZ()) {
return context.getRoundedHeight(worldX & 15, worldZ & 15);
}
long columnKey = Cache.key(worldX, worldZ);
Integer columnHeight = columnHeights.get(columnKey);
if (columnHeight == null) {
columnHeight = (int) Math.round(heightStream.getDouble(worldX, worldZ));
columnHeights.put(columnKey, columnHeight);
}
return columnHeight;
}
}
}
@@ -178,7 +178,27 @@ public class IrisBiome extends IrisRegistrant implements IRare {
private KList<IrisOreGenerator> ores = new KList<>();
public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) {
KList<IrisOreGenerator> localOres = getOres(surface);
KList<IrisOreGenerator> localOres = surface ? getSurfaceOres() : getUndergroundOres();
return generateOres(localOres, x, y, z, rng, data);
}
public BlockData generateSurfaceOres(int x, int y, int z, RNG rng, IrisData data) {
return generateOres(getSurfaceOres(), x, y, z, rng, data);
}
public BlockData generateUndergroundOres(int x, int y, int z, RNG rng, IrisData data) {
return generateOres(getUndergroundOres(), x, y, z, rng, data);
}
public boolean hasSurfaceOres() {
return !getSurfaceOres().isEmpty();
}
public boolean hasUndergroundOres() {
return !getUndergroundOres().isEmpty();
}
private BlockData generateOres(KList<IrisOreGenerator> localOres, int x, int y, int z, RNG rng, IrisData data) {
if (localOres.isEmpty()) {
return null;
}
@@ -200,6 +220,14 @@ public class IrisBiome extends IrisRegistrant implements IRare {
undergroundOreCache.reset();
}
private KList<IrisOreGenerator> getSurfaceOres() {
return getOres(true);
}
private KList<IrisOreGenerator> getUndergroundOres() {
return getOres(false);
}
private KList<IrisOreGenerator> getOres(boolean surface) {
AtomicCache<KList<IrisOreGenerator>> oreCache = surface ? surfaceOreCache : undergroundOreCache;
return oreCache.aquire(() -> {
@@ -293,7 +293,27 @@ public class IrisDimension extends IrisRegistrant {
}
public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) {
KList<IrisOreGenerator> localOres = getOres(surface);
KList<IrisOreGenerator> localOres = surface ? getSurfaceOres() : getUndergroundOres();
return generateOres(localOres, x, y, z, rng, data);
}
public BlockData generateSurfaceOres(int x, int y, int z, RNG rng, IrisData data) {
return generateOres(getSurfaceOres(), x, y, z, rng, data);
}
public BlockData generateUndergroundOres(int x, int y, int z, RNG rng, IrisData data) {
return generateOres(getUndergroundOres(), x, y, z, rng, data);
}
public boolean hasSurfaceOres() {
return !getSurfaceOres().isEmpty();
}
public boolean hasUndergroundOres() {
return !getUndergroundOres().isEmpty();
}
private BlockData generateOres(KList<IrisOreGenerator> localOres, int x, int y, int z, RNG rng, IrisData data) {
if (localOres.isEmpty()) {
return null;
}
@@ -315,6 +335,14 @@ public class IrisDimension extends IrisRegistrant {
undergroundOreCache.reset();
}
private KList<IrisOreGenerator> getSurfaceOres() {
return getOres(true);
}
private KList<IrisOreGenerator> getUndergroundOres() {
return getOres(false);
}
private KList<IrisOreGenerator> getOres(boolean surface) {
AtomicCache<KList<IrisOreGenerator>> oreCache = surface ? surfaceOreCache : undergroundOreCache;
return oreCache.aquire(() -> {
@@ -154,7 +154,27 @@ public class IrisRegion extends IrisRegistrant implements IRare {
private KList<IrisOreGenerator> ores = new KList<>();
public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) {
KList<IrisOreGenerator> localOres = getOres(surface);
KList<IrisOreGenerator> localOres = surface ? getSurfaceOres() : getUndergroundOres();
return generateOres(localOres, x, y, z, rng, data);
}
public BlockData generateSurfaceOres(int x, int y, int z, RNG rng, IrisData data) {
return generateOres(getSurfaceOres(), x, y, z, rng, data);
}
public BlockData generateUndergroundOres(int x, int y, int z, RNG rng, IrisData data) {
return generateOres(getUndergroundOres(), x, y, z, rng, data);
}
public boolean hasSurfaceOres() {
return !getSurfaceOres().isEmpty();
}
public boolean hasUndergroundOres() {
return !getUndergroundOres().isEmpty();
}
private BlockData generateOres(KList<IrisOreGenerator> localOres, int x, int y, int z, RNG rng, IrisData data) {
if (localOres.isEmpty()) {
return null;
}
@@ -176,6 +196,14 @@ public class IrisRegion extends IrisRegistrant implements IRare {
undergroundOreCache.reset();
}
private KList<IrisOreGenerator> getSurfaceOres() {
return getOres(true);
}
private KList<IrisOreGenerator> getUndergroundOres() {
return getOres(false);
}
private KList<IrisOreGenerator> getOres(boolean surface) {
AtomicCache<KList<IrisOreGenerator>> oreCache = surface ? surfaceOreCache : undergroundOreCache;
return oreCache.aquire(() -> {