mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 14:00:26 +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;
|
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.loading.IrisData;
|
||||||
import art.arcane.iris.pack.value.IrisRange;
|
import art.arcane.iris.pack.value.IrisRange;
|
||||||
import art.arcane.volmlib.nativelib.terrain.NativeBlockState;
|
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.
|
* 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];
|
IrisOreGenerator[] candidates = byY == null ? generators : byY[y - minY];
|
||||||
for (IrisOreGenerator candidate : candidates) {
|
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) {
|
if (ore != null) {
|
||||||
return ore;
|
return ore;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,10 @@ public class IrisOreGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
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)) {
|
if (threshold <= 0 || !range.contains(y)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -73,7 +77,7 @@ public class IrisOreGenerator {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chance(rng, data).sample(x, y, z) > threshold) {
|
if (chance(rng, data, engine).sample(x, y, z) > threshold) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,8 +85,11 @@ public class IrisOreGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private OreChance chance(RNG rng, IrisData data) {
|
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();
|
long seed = rng.getSeed();
|
||||||
Engine engine = data == null ? null : data.getEngine();
|
|
||||||
CachedChance recent = recentChance;
|
CachedChance recent = recentChance;
|
||||||
if (recent != null && recent.key.seed == seed && recent.key.data == data && recent.key.engine == engine) {
|
if (recent != null && recent.key.seed == seed && recent.key.data == data && recent.key.engine == engine) {
|
||||||
return recent.chance;
|
return recent.chance;
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import it.unimi.dsi.fastutil.ints.IntArrayList;
|
|||||||
public class IrisPerfectionModifier extends EngineAssignedModifier<NativeBlockState> {
|
public class IrisPerfectionModifier extends EngineAssignedModifier<NativeBlockState> {
|
||||||
private static final int SOLID = 1;
|
private static final int SOLID = 1;
|
||||||
private static final int SPIKE = 2;
|
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 AIR = BoundBlockState.of("AIR");
|
||||||
private static final BoundBlockState WATER = BoundBlockState.of("WATER");
|
private static final BoundBlockState WATER = BoundBlockState.of("WATER");
|
||||||
private static final Map<String, BoundBlockState> ORE_BASES = buildOreBases();
|
private static final Map<String, BoundBlockState> ORE_BASES = buildOreBases();
|
||||||
@@ -87,70 +88,94 @@ public class IrisPerfectionModifier extends EngineAssignedModifier<NativeBlockSt
|
|||||||
if (getDimension().isHideOresForHiddenOre()) {
|
if (getDimension().isHideOresForHiddenOre()) {
|
||||||
hideOres(output, multicore);
|
hideOres(output, multicore);
|
||||||
}
|
}
|
||||||
|
int tasks = multicore ? SLICE_TASKS : 1;
|
||||||
|
int slicesPerTask = 16 / tasks;
|
||||||
BurstExecutor burst = burst().burst(multicore);
|
BurstExecutor burst = burst().burst(multicore);
|
||||||
for (int index = 0; index < 16; index++) {
|
for (int task = 0; task < tasks; task++) {
|
||||||
int columnX = index;
|
int firstSlice = task * slicesPerTask;
|
||||||
burst.queue(() -> perfectSlice(output, columnX));
|
burst.queue(() -> perfectSlices(output, firstSlice, firstSlice + slicesPerTask));
|
||||||
}
|
}
|
||||||
burst.complete();
|
burst.complete();
|
||||||
|
|
||||||
getEngine().getMetrics().getPerfection().put(p.getMilliseconds());
|
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();
|
IntArrayList surfaces = new IntArrayList();
|
||||||
StateClasses classes = new StateClasses();
|
StateFlags states = new StateFlags();
|
||||||
for (int z = 0; z < 16; z++) {
|
NativeBlockState air = AIR.get();
|
||||||
boolean changed;
|
NativeBlockState water = WATER.get();
|
||||||
do {
|
for (int x = firstSlice; x < lastSlice; x++) {
|
||||||
surfaces.clear();
|
for (int z = 0; z < 16; z++) {
|
||||||
int top = getHeight(output, x, z, classes);
|
perfectColumn(output, x, z, surfaces, states, air, water);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
static NativeBlockState normalizeSpike(NativeBlockState state, Hunk<NativeBlockState> output,
|
||||||
int x, int z, int y, NativeBlockState air, NativeBlockState water) {
|
int x, int z, int y, NativeBlockState air, NativeBlockState water) {
|
||||||
if (IrisSpeleothems.isSupported(state, output, x, z, y)) {
|
if (IrisSpeleothems.isSupported(state, output, x, z, y)) {
|
||||||
@@ -208,9 +233,19 @@ public class IrisPerfectionModifier extends EngineAssignedModifier<NativeBlockSt
|
|||||||
burst.complete();
|
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--) {
|
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;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -218,19 +253,21 @@ public class IrisPerfectionModifier extends EngineAssignedModifier<NativeBlockSt
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class StateClasses {
|
private static final class StateFlags {
|
||||||
private NativeBlockState last;
|
private static final int SLOTS = 64;
|
||||||
private int lastFlags;
|
|
||||||
|
private final NativeBlockState[] states = new NativeBlockState[SLOTS];
|
||||||
|
private final byte[] flags = new byte[SLOTS];
|
||||||
|
|
||||||
private int of(NativeBlockState state) {
|
private int of(NativeBlockState state) {
|
||||||
if (state == null) {
|
int slot = System.identityHashCode(state) & (SLOTS - 1);
|
||||||
return 0;
|
if (states[slot] == state) {
|
||||||
|
return flags[slot];
|
||||||
}
|
}
|
||||||
if (state != last) {
|
int computed = (state.isAir() || state.isFluid() ? 0 : SOLID) | (IrisSpeleothems.isSpike(state) ? SPIKE : 0);
|
||||||
last = state;
|
states[slot] = state;
|
||||||
lastFlags = (state.isAir() || state.isFluid() ? 0 : SOLID) | (IrisSpeleothems.isSpike(state) ? SPIKE : 0);
|
flags[slot] = (byte) computed;
|
||||||
}
|
return computed;
|
||||||
return lastFlags;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,6 +153,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<NativeBloc
|
|||||||
private final int chunkHeight;
|
private final int chunkHeight;
|
||||||
private final IrisDimension dimension;
|
private final IrisDimension dimension;
|
||||||
private final IrisData data;
|
private final IrisData data;
|
||||||
|
private final Engine oreEngine;
|
||||||
private final IrisComplex complex;
|
private final IrisComplex complex;
|
||||||
private final ProceduralStream<Double> riverWaterSurfaceStream;
|
private final ProceduralStream<Double> riverWaterSurfaceStream;
|
||||||
private final boolean bedrockEnabled;
|
private final boolean bedrockEnabled;
|
||||||
@@ -195,6 +196,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<NativeBloc
|
|||||||
chunkHeight = h.getHeight();
|
chunkHeight = h.getHeight();
|
||||||
dimension = getDimension();
|
dimension = getDimension();
|
||||||
data = getData();
|
data = getData();
|
||||||
|
oreEngine = data == null ? null : data.getEngine();
|
||||||
complex = getComplex();
|
complex = getComplex();
|
||||||
riverWaterSurfaceStream = complex.getRiverWaterSurfaceStream();
|
riverWaterSurfaceStream = complex.getRiverWaterSurfaceStream();
|
||||||
bedrockEnabled = dimension.isBedrock();
|
bedrockEnabled = dimension.isBedrock();
|
||||||
@@ -459,13 +461,13 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<NativeBloc
|
|||||||
private NativeBlockState surfaceOre(int realX, int y, int realZ) {
|
private NativeBlockState surfaceOre(int realX, int y, int realZ) {
|
||||||
NativeBlockState ore = null;
|
NativeBlockState ore = null;
|
||||||
if (biomeSurfaceOres.contains(y)) {
|
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)) {
|
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)) {
|
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;
|
return ore;
|
||||||
}
|
}
|
||||||
@@ -473,13 +475,13 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<NativeBloc
|
|||||||
private NativeBlockState undergroundOre(int realX, int y, int realZ) {
|
private NativeBlockState undergroundOre(int realX, int y, int realZ) {
|
||||||
NativeBlockState ore = null;
|
NativeBlockState ore = null;
|
||||||
if (biomeUndergroundOres.contains(y)) {
|
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)) {
|
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)) {
|
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;
|
return ore;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public class IrisOreBandsTest {
|
|||||||
}
|
}
|
||||||
for (int x = -3; x <= 3; x++) {
|
for (int x = -3; x <= 3; x++) {
|
||||||
assertSame("trial " + trial + " y " + y + " x " + 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(1D, 57D, 1D, 2L));
|
||||||
ores.add(generator(58D, 58D, 1D, 3L));
|
ores.add(generator(58D, 58D, 1D, 3L));
|
||||||
IrisOreBands bands = IrisOreBands.of(ores);
|
IrisOreBands bands = IrisOreBands.of(ores);
|
||||||
assertSame(scan(ores, 0, 0, 0), bands.generate(0, 0, 0, RNG_SOURCE, 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));
|
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));
|
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);
|
assertNull(bands.contains(59) ? bands.generate(0, 59, 0, RNG_SOURCE, null, null) : null);
|
||||||
}
|
}
|
||||||
private static final class FixedPalette extends IrisMaterialPalette {
|
private static final class FixedPalette extends IrisMaterialPalette {
|
||||||
private final NativeBlockState state;
|
private final NativeBlockState state;
|
||||||
|
|||||||
Reference in new issue
Block a user