mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 05:50:39 +00:00
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:
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;
|
||||
|
||||
Reference in new issue
Block a user