Perfection and ore lookups

- Perfection memoizes solid and spike flags per block state identity
- Terrain painting resolves the ore chance engine once per chunk
This commit is contained in:
Brian Neumann-Fopiano committed 2026-10-22 11:05:58 -04:00
1 parent 9cd470c7c8
commit d9c5d00be3
5 files changed
+127 -80

No files matched your search

@@ -1,5 +1,6 @@
package art.arcane.iris.generation.decoration;
import art.arcane.iris.generation.runtime.Engine;
import art.arcane.iris.pack.loading.IrisData;
import art.arcane.iris.pack.value.IrisRange;
import art.arcane.volmlib.nativelib.terrain.NativeBlockState;
@@ -94,10 +95,10 @@ public final class IrisOreBands {
/**
* The ore placed at a Y inside {@link #contains(int)}, or null when no generator places one.
*/
public NativeBlockState generate(int x, int y, int z, RNG rng, IrisData data) {
public NativeBlockState generate(int x, int y, int z, RNG rng, IrisData data, Engine engine) {
IrisOreGenerator[] candidates = byY == null ? generators : byY[y - minY];
for (IrisOreGenerator candidate : candidates) {
NativeBlockState ore = candidate.generate(x, y, z, rng, data);
NativeBlockState ore = candidate.generate(x, y, z, rng, data, engine);
if (ore != null) {
return ore;
}
@@ -65,6 +65,10 @@ public class IrisOreGenerator {
}
public NativeBlockState generate(int x, int y, int z, RNG rng, IrisData data) {
return generate(x, y, z, rng, data, data == null ? null : data.getEngine());
}
public NativeBlockState generate(int x, int y, int z, RNG rng, IrisData data, Engine engine) {
if (threshold <= 0 || !range.contains(y)) {
return null;
}
@@ -73,7 +77,7 @@ public class IrisOreGenerator {
return null;
}
if (chance(rng, data).sample(x, y, z) > threshold) {
if (chance(rng, data, engine).sample(x, y, z) > threshold) {
return null;
}
@@ -81,8 +85,11 @@ public class IrisOreGenerator {
}
private OreChance chance(RNG rng, IrisData data) {
return chance(rng, data, data == null ? null : data.getEngine());
}
private OreChance chance(RNG rng, IrisData data, Engine engine) {
long seed = rng.getSeed();
Engine engine = data == null ? null : data.getEngine();
CachedChance recent = recentChance;
if (recent != null && recent.key.seed == seed && recent.key.data == data && recent.key.engine == engine) {
return recent.chance;
@@ -38,6 +38,7 @@ import it.unimi.dsi.fastutil.ints.IntArrayList;
public class IrisPerfectionModifier extends EngineAssignedModifier<NativeBlockState> {
private static final int SOLID = 1;
private static final int SPIKE = 2;
private static final int SLICE_TASKS = 16;
private static final BoundBlockState AIR = BoundBlockState.of("AIR");
private static final BoundBlockState WATER = BoundBlockState.of("WATER");
private static final Map<String, BoundBlockState> ORE_BASES = buildOreBases();
@@ -87,70 +88,94 @@ public class IrisPerfectionModifier extends EngineAssignedModifier<NativeBlockSt
if (getDimension().isHideOresForHiddenOre()) {
hideOres(output, multicore);
}
int tasks = multicore ? SLICE_TASKS : 1;
int slicesPerTask = 16 / tasks;
BurstExecutor burst = burst().burst(multicore);
for (int index = 0; index < 16; index++) {
int columnX = index;
burst.queue(() -> perfectSlice(output, columnX));
for (int task = 0; task < tasks; task++) {
int firstSlice = task * slicesPerTask;
burst.queue(() -> perfectSlices(output, firstSlice, firstSlice + slicesPerTask));
}
burst.complete();
getEngine().getMetrics().getPerfection().put(p.getMilliseconds());
}
private void perfectSlice(Hunk<NativeBlockState> output, int x) {
private void perfectSlices(Hunk<NativeBlockState> output, int firstSlice, int lastSlice) {
IntArrayList surfaces = new IntArrayList();
StateClasses classes = new StateClasses();
for (int z = 0; z < 16; z++) {
boolean changed;
do {
surfaces.clear();
int top = getHeight(output, x, z, classes);
boolean inside = true;
surfaces.add(top);
for (int y = top; y >= 0; y--) {
NativeBlockState block = output.get(x, y, z);
int flags = classes.of(block);
if ((flags & SPIKE) != 0) {
block = normalizeSpike(block, output, x, z, y, AIR.get(), WATER.get());
flags = classes.of(block);
}
boolean now = (flags & SOLID) != 0;
if (now != inside) {
inside = now;
if (inside) {
surfaces.add(y);
}
}
}
changed = false;
for (int index = 0; index < surfaces.size(); index++) {
int y = surfaces.getInt(index);
NativeBlockState tip = output.get(x, y, z);
if (!B.isDecorant(tip)) {
continue;
}
NativeBlockState below = output.get(x, y - 1, z);
boolean remove = below == null || !B.canPlaceOnto(tip, below);
boolean removeBelow = false;
if (!remove && IrisProceduralBlocks.hasProperty(below, "half")) {
NativeBlockState support = output.get(x, y - 2, z);
if (support == null || !B.canPlaceOnto(below, support)) {
remove = true;
removeBelow = true;
}
}
if (remove) {
changed = true;
output.set(x, y, z, AIR.get());
if (removeBelow) {
output.set(x, y - 1, z, AIR.get());
}
}
}
} while (changed);
StateFlags states = new StateFlags();
NativeBlockState air = AIR.get();
NativeBlockState water = WATER.get();
for (int x = firstSlice; x < lastSlice; x++) {
for (int z = 0; z < 16; z++) {
perfectColumn(output, x, z, surfaces, states, air, water);
}
}
}
private static void perfectColumn(Hunk<NativeBlockState> output, int x, int z, IntArrayList surfaces,
StateFlags states, NativeBlockState air, NativeBlockState water) {
do {
surfaces.clear();
int top = getHeight(output, x, z, states);
boolean inside = true;
surfaces.add(top);
NativeBlockState classified = null;
int classifiedFlags = 0;
for (int y = top; y >= 0; y--) {
NativeBlockState block = output.get(x, y, z);
int flags = 0;
if (block != null) {
if (block != classified) {
classified = block;
classifiedFlags = states.of(block);
}
flags = classifiedFlags;
if ((flags & SPIKE) != 0) {
block = normalizeSpike(block, output, x, z, y, air, water);
flags = block == null ? 0 : states.of(block);
}
}
boolean now = (flags & SOLID) != 0;
if (now != inside) {
inside = now;
if (inside) {
surfaces.add(y);
}
}
}
} while (removeUnsupportedDecorants(output, x, z, surfaces, air));
}
private static boolean removeUnsupportedDecorants(Hunk<NativeBlockState> output, int x, int z,
IntArrayList surfaces, NativeBlockState air) {
boolean changed = false;
for (int index = 0; index < surfaces.size(); index++) {
int y = surfaces.getInt(index);
NativeBlockState tip = output.get(x, y, z);
if (!B.isDecorant(tip)) {
continue;
}
NativeBlockState below = output.get(x, y - 1, z);
boolean remove = below == null || !B.canPlaceOnto(tip, below);
boolean removeBelow = false;
if (!remove && IrisProceduralBlocks.hasProperty(below, "half")) {
NativeBlockState support = output.get(x, y - 2, z);
if (support == null || !B.canPlaceOnto(below, support)) {
remove = true;
removeBelow = true;
}
}
if (remove) {
changed = true;
output.set(x, y, z, air);
if (removeBelow) {
output.set(x, y - 1, z, air);
}
}
}
return changed;
}
static NativeBlockState normalizeSpike(NativeBlockState state, Hunk<NativeBlockState> output,
int x, int z, int y, NativeBlockState air, NativeBlockState water) {
if (IrisSpeleothems.isSupported(state, output, x, z, y)) {
@@ -208,9 +233,19 @@ public class IrisPerfectionModifier extends EngineAssignedModifier<NativeBlockSt
burst.complete();
}
private int getHeight(Hunk<NativeBlockState> output, int x, int z, StateClasses classes) {
private static int getHeight(Hunk<NativeBlockState> output, int x, int z, StateFlags states) {
NativeBlockState classified = null;
int classifiedFlags = 0;
for (int i = Math.min(output.getHeight() - 1, ColumnExtent.highestStoredY(output, x, z)); i >= 0; i--) {
if ((classes.of(output.get(x, i, z)) & SOLID) != 0) {
NativeBlockState state = output.get(x, i, z);
if (state == null) {
continue;
}
if (state != classified) {
classified = state;
classifiedFlags = states.of(state);
}
if ((classifiedFlags & SOLID) != 0) {
return i;
}
}
@@ -218,19 +253,21 @@ public class IrisPerfectionModifier extends EngineAssignedModifier<NativeBlockSt
return 0;
}
private static final class StateClasses {
private NativeBlockState last;
private int lastFlags;
private static final class StateFlags {
private static final int SLOTS = 64;
private final NativeBlockState[] states = new NativeBlockState[SLOTS];
private final byte[] flags = new byte[SLOTS];
private int of(NativeBlockState state) {
if (state == null) {
return 0;
int slot = System.identityHashCode(state) & (SLOTS - 1);
if (states[slot] == state) {
return flags[slot];
}
if (state != last) {
last = state;
lastFlags = (state.isAir() || state.isFluid() ? 0 : SOLID) | (IrisSpeleothems.isSpike(state) ? SPIKE : 0);
}
return lastFlags;
int computed = (state.isAir() || state.isFluid() ? 0 : SOLID) | (IrisSpeleothems.isSpike(state) ? SPIKE : 0);
states[slot] = state;
flags[slot] = (byte) computed;
return computed;
}
}
}
@@ -153,6 +153,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<NativeBloc
private final int chunkHeight;
private final IrisDimension dimension;
private final IrisData data;
private final Engine oreEngine;
private final IrisComplex complex;
private final ProceduralStream<Double> riverWaterSurfaceStream;
private final boolean bedrockEnabled;
@@ -195,6 +196,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<NativeBloc
chunkHeight = h.getHeight();
dimension = getDimension();
data = getData();
oreEngine = data == null ? null : data.getEngine();
complex = getComplex();
riverWaterSurfaceStream = complex.getRiverWaterSurfaceStream();
bedrockEnabled = dimension.isBedrock();
@@ -459,13 +461,13 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<NativeBloc
private NativeBlockState surfaceOre(int realX, int y, int realZ) {
NativeBlockState ore = null;
if (biomeSurfaceOres.contains(y)) {
ore = biomeSurfaceOres.generate(realX, y, realZ, rng, data);
ore = biomeSurfaceOres.generate(realX, y, realZ, rng, data, oreEngine);
}
if (ore == null && regionSurfaceOres.contains(y)) {
ore = regionSurfaceOres.generate(realX, y, realZ, rng, data);
ore = regionSurfaceOres.generate(realX, y, realZ, rng, data, oreEngine);
}
if (ore == null && dimensionSurfaceOres.contains(y)) {
ore = dimensionSurfaceOres.generate(realX, y, realZ, rng, data);
ore = dimensionSurfaceOres.generate(realX, y, realZ, rng, data, oreEngine);
}
return ore;
}
@@ -473,13 +475,13 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<NativeBloc
private NativeBlockState undergroundOre(int realX, int y, int realZ) {
NativeBlockState ore = null;
if (biomeUndergroundOres.contains(y)) {
ore = biomeUndergroundOres.generate(realX, y, realZ, rng, data);
ore = biomeUndergroundOres.generate(realX, y, realZ, rng, data, oreEngine);
}
if (ore == null && regionUndergroundOres.contains(y)) {
ore = regionUndergroundOres.generate(realX, y, realZ, rng, data);
ore = regionUndergroundOres.generate(realX, y, realZ, rng, data, oreEngine);
}
if (ore == null && dimensionUndergroundOres.contains(y)) {
ore = dimensionUndergroundOres.generate(realX, y, realZ, rng, data);
ore = dimensionUndergroundOres.generate(realX, y, realZ, rng, data, oreEngine);
}
return ore;
}
@@ -48,7 +48,7 @@ public class IrisOreBandsTest {
}
for (int x = -3; x <= 3; x++) {
assertSame("trial " + trial + " y " + y + " x " + x,
scan(ores, x, y, 5 - x), bands.generate(x, y, 5 - x, RNG_SOURCE, null));
scan(ores, x, y, 5 - x), bands.generate(x, y, 5 - x, RNG_SOURCE, null, null));
}
}
}
@@ -110,10 +110,10 @@ public class IrisOreBandsTest {
ores.add(generator(1D, 57D, 1D, 2L));
ores.add(generator(58D, 58D, 1D, 3L));
IrisOreBands bands = IrisOreBands.of(ores);
assertSame(scan(ores, 0, 0, 0), bands.generate(0, 0, 0, RNG_SOURCE, null));
assertSame(scan(ores, 0, 30, 0), bands.generate(0, 30, 0, RNG_SOURCE, null));
assertSame(scan(ores, 0, 58, 0), bands.generate(0, 58, 0, RNG_SOURCE, null));
assertNull(bands.contains(59) ? bands.generate(0, 59, 0, RNG_SOURCE, null) : null);
assertSame(scan(ores, 0, 0, 0), bands.generate(0, 0, 0, RNG_SOURCE, null, null));
assertSame(scan(ores, 0, 30, 0), bands.generate(0, 30, 0, RNG_SOURCE, null, null));
assertSame(scan(ores, 0, 58, 0), bands.generate(0, 58, 0, RNG_SOURCE, null, null));
assertNull(bands.contains(59) ? bands.generate(0, 59, 0, RNG_SOURCE, null, null) : null);
}
private static final class FixedPalette extends IrisMaterialPalette {
private final NativeBlockState state;