Merged carve noise batching

- Compiled cave density fields with batched per-pass lattices and shared warp fields
This commit is contained in:
Brian Neumann-Fopiano committed 2026-10-22 01:18:30 -04:00
commit 024214d7f0
10 files changed
+685 -114

No files matched your search

@@ -68,8 +68,7 @@ final class CaveCarveScratch {
final int[] tileTopY = new int[4]; final int[] tileTopY = new int[4];
final long[] rowCarveBits = new long[4]; final long[] rowCarveBits = new long[4];
final long[] cellPassBits = new long[4]; final long[] cellPassBits = new long[4];
final double[] rowLatticeDensity = new double[9]; final CaveDensityLattice cellLattice = new CaveDensityLattice();
final CaveDensityGrid.Column[] cellCornerColumns = new CaveDensityGrid.Column[9];
final double[] cellThresholdMin = new double[4]; final double[] cellThresholdMin = new double[4];
final double[] cellThresholdMax = new double[4]; final double[] cellThresholdMax = new double[4];
final double[] cellThresholdMaxBoost = new double[4]; final double[] cellThresholdMaxBoost = new double[4];
@@ -98,11 +97,33 @@ final class CaveCarveScratch {
int adaptiveGeometryStep = -1; int adaptiveGeometryStep = -1;
int adaptiveGeometryAxisCells = -1; int adaptiveGeometryAxisCells = -1;
boolean fullWeightsInitialized; boolean fullWeightsInitialized;
int[] latticeY = new int[0];
int[] latticeSnapY = new int[0];
double[] latticeWarpA = new double[0];
double[] latticeWarpB = new double[0];
double[] latticeWarpedX = new double[0];
double[] latticeWarpedY = new double[0];
double[] latticeWarpedZ = new double[0];
double[] latticeDensity = new double[0];
void releaseReferences() { void releaseReferences() {
Arrays.fill(sectionMatter, null); Arrays.fill(sectionMatter, null);
Arrays.fill(sectionSlices, null); Arrays.fill(sectionSlices, null);
Arrays.fill(densityColumns, null); Arrays.fill(densityColumns, null);
Arrays.fill(cellCornerColumns, null); cellLattice.releaseReferences();
}
void ensureLatticeBatch(int size) {
if (latticeY.length >= size) {
return;
}
latticeY = new int[size];
latticeSnapY = new int[size];
latticeWarpA = new double[size];
latticeWarpB = new double[size];
latticeWarpedX = new double[size];
latticeWarpedY = new double[size];
latticeWarpedZ = new double[size];
latticeDensity = new double[size];
} }
} }
@@ -5,6 +5,7 @@ import art.arcane.iris.generation.cache.ConcurrentClockCache;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle; import java.lang.invoke.VarHandle;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReferenceArray; import java.util.concurrent.atomic.AtomicReferenceArray;
@@ -22,42 +23,63 @@ final class CaveDensityGrid {
private static final AtomicInteger OWNERS = new AtomicInteger(); private static final AtomicInteger OWNERS = new AtomicInteger();
private final ConcurrentClockCache<Column> columns; private final ConcurrentClockCache<Column> columns;
private final ConcurrentClockCache<Column> warpColumns;
private final ConcurrentHashMap<Object, Owner> owners = new ConcurrentHashMap<>();
CaveDensityGrid(int capacity) { CaveDensityGrid(int capacity) {
columns = new ConcurrentClockCache<>(capacity); columns = new ConcurrentClockCache<>(capacity);
warpColumns = new ConcurrentClockCache<>(Math.max(1, capacity >> 2));
} }
static int nextOwner() { Owner owner(Object fieldKey) {
return OWNERS.getAndIncrement(); return owners.computeIfAbsent(fieldKey, ignored -> new Owner(OWNERS.getAndIncrement()));
} }
Column column(Object owner, int ownerId, int height, int latticeX, int latticeZ) { Column column(Owner owner, int height, int latticeX, int latticeZ) {
long key = (long) (ownerId & 0xFFFF) << 48 | (latticeX & COORDINATE_MASK) << 24 | (latticeZ & COORDINATE_MASK); return column(columns, owner, height, latticeX, latticeZ);
Column cached = columns.get(key); }
if (cached == null) {
cached = columns.putIfAbsent(key, new Column(owner, latticeX, latticeZ, height)); Column warpColumn(Owner owner, int height, int latticeX, int latticeZ) {
} return column(warpColumns, owner, height, latticeX, latticeZ);
return cached.matches(owner, latticeX, latticeZ) ? cached : null;
} }
void clear() { void clear() {
columns.clear(); columns.clear();
warpColumns.clear();
owners.clear();
}
private static Column column(ConcurrentClockCache<Column> cache, Owner owner, int height, int latticeX, int latticeZ) {
long key = (long) (owner.id & 0xFFFF) << 48 | (latticeX & COORDINATE_MASK) << 24 | (latticeZ & COORDINATE_MASK);
Column cached = cache.get(key);
if (cached == null) {
cached = cache.putIfAbsent(key, new Column(owner, latticeX, latticeZ, height));
}
return cached.matches(owner, latticeX, latticeZ) ? cached : null;
}
static final class Owner {
private final int id;
private Owner(int id) {
this.id = id;
}
} }
static final class Column { static final class Column {
private final Object owner; private final Owner owner;
private final int latticeX; private final int latticeX;
private final int latticeZ; private final int latticeZ;
private final AtomicReferenceArray<double[]> segments; private final AtomicReferenceArray<double[]> segments;
private Column(Object owner, int latticeX, int latticeZ, int height) { private Column(Owner owner, int latticeX, int latticeZ, int height) {
this.owner = owner; this.owner = owner;
this.latticeX = latticeX; this.latticeX = latticeX;
this.latticeZ = latticeZ; this.latticeZ = latticeZ;
segments = new AtomicReferenceArray<>(Math.ceilDiv(Math.max(1, height), SEGMENT_SIZE)); segments = new AtomicReferenceArray<>(Math.ceilDiv(Math.max(1, height), SEGMENT_SIZE));
} }
boolean matches(Object owner, int latticeX, int latticeZ) { boolean matches(Owner owner, int latticeX, int latticeZ) {
return this.owner == owner && this.latticeX == latticeX && this.latticeZ == latticeZ; return this.owner == owner && this.latticeX == latticeX && this.latticeZ == latticeZ;
} }
@@ -0,0 +1,74 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.generation.mantle;
import java.util.Arrays;
final class CaveDensityLattice {
static final int CORNERS = 9;
final CaveDensityGrid.Column[] densityColumns = new CaveDensityGrid.Column[CORNERS];
final CaveDensityGrid.Column[] warpColumns = new CaveDensityGrid.Column[CORNERS];
private double[] values = new double[0];
private byte[] rowCells = new byte[0];
private short[] rowCorners = new short[0];
private int minimumY;
private int rows;
void reset(int minimumY, int rows) {
this.minimumY = minimumY;
this.rows = rows;
if (rowCells.length < rows) {
rowCells = new byte[rows];
rowCorners = new short[rows];
}
if (values.length < rows * CORNERS) {
values = new double[rows * CORNERS];
}
Arrays.fill(rowCells, 0, rows, (byte) 0);
Arrays.fill(rowCorners, 0, rows, (short) 0);
}
void markRow(int y, int cells, int corners) {
int row = y - minimumY;
rowCells[row] = (byte) cells;
rowCorners[row] = (short) corners;
}
int cells(int y) {
return rowCells[y - minimumY];
}
boolean needs(int corner, int y) {
return (rowCorners[y - minimumY] & (1 << corner)) != 0;
}
double density(int corner, int y) {
return values[corner * rows + (y - minimumY)];
}
void store(int corner, int y, double density) {
values[corner * rows + (y - minimumY)] = density;
}
void releaseReferences() {
Arrays.fill(densityColumns, null);
Arrays.fill(warpColumns, null);
}
}
@@ -0,0 +1,51 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.generation.mantle;
import art.arcane.volmlib.util.noise.program.NoiseProgram;
import art.arcane.volmlib.util.noise.program.PrimitiveKind;
record CaveFieldKey(long signature, long probe, NoiseProgram identity) {
private static final double[][] PROBE_POINTS = {
{0.5D, 3.25D, -7.75D},
{-1031.125D, 77.5D, 2049.875D},
{16D, 255D, -16D},
{123_456.375D, -40.25D, -98_765.625D}
};
static CaveFieldKey of(NoiseProgram program) {
if (!shareable(program)) {
return new CaveFieldKey(program.structuralSignature(), 0L, program);
}
long probe = 0xCBF29CE484222325L;
for (double[] point : PROBE_POINTS) {
probe = (probe ^ Double.doubleToRawLongBits(program.sampleSigned3D(point[0], point[1], point[2]))) * 0x100000001B3L;
}
return new CaveFieldKey(program.structuralSignature(), probe, null);
}
private static boolean shareable(NoiseProgram program) {
for (int node = 0; node < program.nodeCount(); node++) {
if (program.nodeOpaque(node) || program.nodeKernel(node).kind() == PrimitiveKind.GENERIC) {
return false;
}
}
return true;
}
}
@@ -20,10 +20,10 @@ package art.arcane.iris.generation.mantle;
import art.arcane.iris.generation.cave.IrisCaveFieldModule; import art.arcane.iris.generation.cave.IrisCaveFieldModule;
import art.arcane.iris.pack.value.IrisRange; import art.arcane.iris.pack.value.IrisRange;
import art.arcane.volmlib.util.noise.CNG; import art.arcane.volmlib.util.noise.program.NoiseProgram;
final class CaveFieldModuleState { final class CaveFieldModuleState {
final CNG density; final NoiseProgram density;
final int minY; final int minY;
final int maxY; final int maxY;
final double weight; final double weight;
@@ -32,7 +32,7 @@ final class CaveFieldModuleState {
final double minContribution; final double minContribution;
final double maxContribution; final double maxContribution;
CaveFieldModuleState(IrisCaveFieldModule module, CNG density) { CaveFieldModuleState(IrisCaveFieldModule module, NoiseProgram density) {
IrisRange range = module.getVerticalRange(); IrisRange range = module.getVerticalRange();
this.density = density; this.density = density;
this.minY = (int) Math.floor(range.getMin()); this.minY = (int) Math.floor(range.getMin());
@@ -47,12 +47,12 @@ final class CaveFieldModuleState {
} }
double sample(double x, int y, double z) { double sample(double x, int y, double z) {
double sampled = density.noiseFastSigned3D(x, y, z); double sampled = density.sampleSigned3D(x, y, z);
return invert ? (threshold - sampled) * weight : (sampled - threshold) * weight; return invert ? (threshold - sampled) * weight : (sampled - threshold) * weight;
} }
double sample(double x, double y, double z) { double sample(double x, double y, double z) {
double sampled = density.noiseFastSigned3D(x, y, z); double sampled = density.sampleSigned3D(x, y, z);
return invert ? (threshold - sampled) * weight : (sampled - threshold) * weight; return invert ? (threshold - sampled) * weight : (sampled - threshold) * weight;
} }
} }
@@ -20,12 +20,15 @@ package art.arcane.iris.generation.mantle;
import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.pack.loading.IrisData;
import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.runtime.Engine;
import art.arcane.iris.generation.runtime.GenerationCounter;
import art.arcane.iris.generation.cave.IrisCaveFieldModule; import art.arcane.iris.generation.cave.IrisCaveFieldModule;
import art.arcane.iris.generation.cave.IrisCaveProfile; import art.arcane.iris.generation.cave.IrisCaveProfile;
import art.arcane.iris.pack.value.IrisRange; import art.arcane.iris.pack.value.IrisRange;
import art.arcane.iris.generation.noise.IrisGeneratorStyle; import art.arcane.iris.generation.noise.IrisGeneratorStyle;
import art.arcane.iris.generation.noise.IrisStyledRange; import art.arcane.iris.generation.noise.IrisStyledRange;
import art.arcane.volmlib.util.noise.CNG; import art.arcane.volmlib.util.noise.CNG;
import art.arcane.volmlib.util.noise.program.NoiseProgram;
import art.arcane.volmlib.util.noise.program.NoiseScratch;
import art.arcane.volmlib.util.mantle.runtime.MantleChunk; import art.arcane.volmlib.util.mantle.runtime.MantleChunk;
import art.arcane.volmlib.util.math.M; import art.arcane.volmlib.util.math.M;
import art.arcane.volmlib.util.math.PowerOfTwoCoordinates; import art.arcane.volmlib.util.math.PowerOfTwoCoordinates;
@@ -69,13 +72,17 @@ public class IrisCaveCarver3D {
private static final LongAdder OPEN_CELLS = new LongAdder(); private static final LongAdder OPEN_CELLS = new LongAdder();
private static final LongAdder MIXED_CELLS = new LongAdder(); private static final LongAdder MIXED_CELLS = new LongAdder();
private static final LongAdder EXACT_ROWS = new LongAdder(); private static final LongAdder EXACT_ROWS = new LongAdder();
private static final double WARP_B_OFFSET_X = 31.37D;
private static final double WARP_B_OFFSET_Y = 17.21D;
private static final double WARP_B_OFFSET_Z = 23.91D;
private static final ThreadLocal<NoiseScratch> NOISE_SCRATCH = ThreadLocal.withInitial(NoiseScratch::new);
private final Engine engine; private final Engine engine;
private final IrisData data; private final IrisData data;
private final IrisCaveProfile profile; private final IrisCaveProfile profile;
private final CNG baseDensity; private final NoiseProgram baseDensity;
private final CNG detailDensity; private final NoiseProgram detailDensity;
private final CNG warpDensity; private final NoiseProgram warpDensity;
private final CNG surfaceBreakDensity; private final CNG surfaceBreakDensity;
private final RNG thresholdRng; private final RNG thresholdRng;
private final CaveFieldModuleState[] modules; private final CaveFieldModuleState[] modules;
@@ -105,7 +112,8 @@ public class IrisCaveCarver3D {
private final ThreadLocal<Boolean> carving = ThreadLocal.withInitial(() -> false); private final ThreadLocal<Boolean> carving = ThreadLocal.withInitial(() -> false);
private final ThreadLocal<CaveCarveScratch> scratchCache = ThreadLocal.withInitial(CaveCarveScratch::new); private final ThreadLocal<CaveCarveScratch> scratchCache = ThreadLocal.withInitial(CaveCarveScratch::new);
private final CaveDensityGrid densityGrid; private final CaveDensityGrid densityGrid;
private final int densityGridOwner = CaveDensityGrid.nextOwner(); private final CaveDensityGrid.Owner densityOwner;
private final CaveDensityGrid.Owner warpOwner;
public IrisCaveCarver3D(Engine engine, IrisCaveProfile profile) { public IrisCaveCarver3D(Engine engine, IrisCaveProfile profile) {
this(engine, profile, new CaveDensityGrid(STANDALONE_DENSITY_GRID_COLUMNS)); this(engine, profile, new CaveDensityGrid(STANDALONE_DENSITY_GRID_COLUMNS));
@@ -123,9 +131,9 @@ public class IrisCaveCarver3D {
List<CaveFieldModuleState> moduleStates = new ArrayList<>(); List<CaveFieldModuleState> moduleStates = new ArrayList<>();
RNG baseRng = new RNG(engine.getSeedManager().getCarve()); RNG baseRng = new RNG(engine.getSeedManager().getCarve());
this.baseDensity = profile.getBaseDensityStyle().create(baseRng.nextParallelRNG(934_447), data); this.baseDensity = NoiseProgram.compileSigned3D(profile.getBaseDensityStyle().create(baseRng.nextParallelRNG(934_447), data));
this.detailDensity = profile.getDetailDensityStyle().create(baseRng.nextParallelRNG(612_991), data); this.detailDensity = NoiseProgram.compileSigned3D(profile.getDetailDensityStyle().create(baseRng.nextParallelRNG(612_991), data));
this.warpDensity = profile.getWarpStyle().create(baseRng.nextParallelRNG(770_713), data); this.warpDensity = NoiseProgram.compileSigned3D(profile.getWarpStyle().create(baseRng.nextParallelRNG(770_713), data));
this.surfaceBreakDensity = profile.getSurfaceBreakStyle().create(baseRng.nextParallelRNG(341_219), data); this.surfaceBreakDensity = profile.getSurfaceBreakStyle().create(baseRng.nextParallelRNG(341_219), data);
this.thresholdRng = baseRng.nextParallelRNG(489_112); this.thresholdRng = baseRng.nextParallelRNG(489_112);
this.baseWeight = profile.getBaseWeight(); this.baseWeight = profile.getBaseWeight();
@@ -143,7 +151,7 @@ public class IrisCaveCarver3D {
int index = 0; int index = 0;
for (IrisCaveFieldModule module : profile.getModules()) { for (IrisCaveFieldModule module : profile.getModules()) {
CNG moduleDensity = module.getStyle().create(baseRng.nextParallelRNG(1_000_003L + (index * 65_537L)), data); CNG moduleDensity = module.getStyle().create(baseRng.nextParallelRNG(1_000_003L + (index * 65_537L)), data);
CaveFieldModuleState state = new CaveFieldModuleState(module, moduleDensity); CaveFieldModuleState state = new CaveFieldModuleState(module, NoiseProgram.compileSigned3D(moduleDensity));
moduleStates.add(state); moduleStates.add(state);
weight += Math.abs(state.weight); weight += Math.abs(state.weight);
index++; index++;
@@ -160,6 +168,21 @@ public class IrisCaveCarver3D {
verticalDensityStep = parallelDensity ? Math.max(1, engine.getDimension().getCaveDensitySamplingStep()) : 1; verticalDensityStep = parallelDensity ? Math.max(1, engine.getDimension().getCaveDensitySamplingStep()) : 1;
worldMinimumY = engine.getWorld().minHeight(); worldMinimumY = engine.getWorld().minHeight();
densityMaximumY = engine.getWorld().getHeight() - 1; densityMaximumY = engine.getWorld().getHeight() - 1;
CaveFieldKey warpKey = CaveFieldKey.of(warpDensity);
warpOwner = densityGrid.owner(new WarpFieldKey(warpKey));
densityOwner = densityGrid.owner(new DensityFieldKey(hasWarp, warpKey, Double.doubleToRawLongBits(warpStrength),
CaveFieldKey.of(baseDensity), Double.doubleToRawLongBits(baseWeight), CaveFieldKey.of(detailDensity),
Double.doubleToRawLongBits(detailWeight), Double.doubleToRawLongBits(inverseNormalization),
moduleFieldKeys(modules)));
}
private static List<ModuleFieldKey> moduleFieldKeys(CaveFieldModuleState[] modules) {
List<ModuleFieldKey> keys = new ArrayList<>(modules.length);
for (CaveFieldModuleState module : modules) {
keys.add(new ModuleFieldKey(CaveFieldKey.of(module.density), module.minY, module.maxY,
Double.doubleToRawLongBits(module.weight), Double.doubleToRawLongBits(module.threshold), module.invert));
}
return List.copyOf(keys);
} }
public static CellCounters cellCounters() { public static CellCounters cellCounters() {
@@ -985,6 +1008,9 @@ public class IrisCaveCarver3D {
fluidMaxY, matterByY, cellKernel)); fluidMaxY, matterByY, cellKernel));
if (cellKernel) { if (cellKernel) {
if (classified == null) {
fillCellLattice(scratch, scratch, x0, z0, minY, passMaxY);
}
return emitCellPass(chunk, scratch, classified, x0, z0, minY, passMaxY, minSection, maxSection, return emitCellPass(chunk, scratch, classified, x0, z0, minY, passMaxY, minSection, maxSection,
activeColumnIndices, activeColumnTopY, activeColumnCount, surfaceFluidBoundaries, activeColumnIndices, activeColumnTopY, activeColumnCount, surfaceFluidBoundaries,
surfaceBreakThresholdBoost, verticalEdgeFade, fluidMaxY, matterByY, fluidSupportPlan); surfaceBreakThresholdBoost, verticalEdgeFade, fluidMaxY, matterByY, fluidSupportPlan);
@@ -1174,18 +1200,194 @@ public class IrisCaveCarver3D {
for (int row = 1; row < rows; row++) { for (int row = 1; row < rows; row++) {
rowActiveCount[row] += rowActiveCount[row - 1]; rowActiveCount[row] += rowActiveCount[row - 1];
} }
int latticeX = x0 >> CaveDensityGrid.LATTICE_SHIFT;
int latticeZ = z0 >> CaveDensityGrid.LATTICE_SHIFT;
int usedCorners = 0; int usedCorners = 0;
for (int cell = 0; cell < 4; cell++) { for (int cell = 0; cell < 4; cell++) {
if (plan.cellTopY[cell] >= minY) { if (plan.cellTopY[cell] >= minY) {
usedCorners |= CELL_CORNER_MASKS[cell]; usedCorners |= CELL_CORNER_MASKS[cell];
} }
} }
for (int corner = 0; corner < 9; corner++) { prepareCellLattice(plan, x0 >> CaveDensityGrid.LATTICE_SHIFT, z0 >> CaveDensityGrid.LATTICE_SHIFT, minY, passMaxY,
plan.cellCornerColumns[corner] = (usedCorners & (1 << corner)) == 0 ? null usedCorners);
: densityGrid.column(this, densityGridOwner, densityMaximumY + 1, }
latticeX + (corner / 3), latticeZ + (corner % 3));
private void prepareCellLattice(CaveCarveScratch plan, int latticeX, int latticeZ, int minY, int passMaxY, int usedCorners) {
CaveDensityLattice lattice = plan.cellLattice;
lattice.reset(minY, passMaxY - minY + 1);
for (int corner = 0; corner < CaveDensityLattice.CORNERS; corner++) {
boolean used = (usedCorners & (1 << corner)) != 0;
int cornerX = latticeX + (corner / 3);
int cornerZ = latticeZ + (corner % 3);
lattice.densityColumns[corner] = used ? densityGrid.column(densityOwner, densityMaximumY + 1, cornerX, cornerZ) : null;
lattice.warpColumns[corner] = used && hasWarp
? densityGrid.warpColumn(warpOwner, densityMaximumY + 2, cornerX, cornerZ) : null;
}
for (int y = minY; y <= passMaxY; y++) {
if (plan.rowActiveCount[y - minY] < ADAPTIVE_MIN_PLANE_COLUMNS) {
continue;
}
int cells = 0;
int corners = 0;
for (int cell = 0; cell < 4; cell++) {
if (isCellActive(plan, cell, y)) {
cells |= 1 << cell;
corners |= CELL_CORNER_MASKS[cell];
}
}
lattice.markRow(y, cells, corners);
}
}
private void fillCellLattice(CaveCarveScratch scratch, CaveCarveScratch plan, int x0, int z0, int fromY, int toY) {
CaveDensityLattice lattice = plan.cellLattice;
scratch.ensureLatticeBatch(toY - fromY + 1);
int[] latticeY = scratch.latticeY;
double[] density = scratch.latticeDensity;
long reads = 0L;
long samples = 0L;
for (int corner = 0; corner < CaveDensityLattice.CORNERS; corner++) {
CaveDensityGrid.Column column = lattice.densityColumns[corner];
int count = 0;
for (int y = fromY; y <= toY; y++) {
if (!lattice.needs(corner, y)) {
continue;
}
reads++;
double cached = column == null ? Double.NaN : column.get(y);
if (Double.isNaN(cached)) {
latticeY[count++] = y;
} else {
lattice.store(corner, y, cached);
}
}
if (count == 0) {
continue;
}
samples += count;
sampleLatticeColumn(scratch, lattice.warpColumns[corner], x0 + (corner / 3) * ADAPTIVE_DEEP_SAMPLE_STEP,
z0 + (corner % 3) * ADAPTIVE_DEEP_SAMPLE_STEP, count);
for (int index = 0; index < count; index++) {
lattice.store(corner, latticeY[index], density[index]);
if (column != null) {
column.set(latticeY[index], density[index]);
}
}
}
if (GenerationCounter.ENABLED) {
GenerationCounter.CAVE_LATTICE_READS.add(reads);
GenerationCounter.CAVE_LATTICE_SAMPLES.add(samples);
}
}
private void sampleLatticeColumn(CaveCarveScratch scratch, CaveDensityGrid.Column warpColumn, int x, int z, int count) {
int[] latticeY = scratch.latticeY;
double[] density = scratch.latticeDensity;
if (!hasWarp) {
for (int index = 0; index < count; index++) {
density[index] = sampleDensityNoWarpNoModules(x, latticeY[index], z);
}
return;
}
resolveLatticeWarp(scratch, warpColumn, x, z, count);
double[] warpedX = scratch.latticeWarpedX;
double[] warpedY = scratch.latticeWarpedY;
double[] warpedZ = scratch.latticeWarpedZ;
for (int index = 0; index < count; index++) {
density[index] = baseDensity.sampleSigned3D(warpedX[index], warpedY[index], warpedZ[index]) * baseWeight;
}
for (int index = 0; index < count; index++) {
density[index] += detailDensity.sampleSigned3D(warpedX[index], warpedY[index], warpedZ[index]) * detailWeight;
}
for (CaveFieldModuleState module : modules) {
for (int index = 0; index < count; index++) {
int y = latticeY[index];
if (y < module.minY || y > module.maxY) {
continue;
}
double moduleDensity = module.density.sampleSigned3D(warpedX[index], warpedY[index], warpedZ[index]) - module.threshold;
if (module.invert) {
moduleDensity = -moduleDensity;
}
density[index] += moduleDensity * module.weight;
}
}
for (int index = 0; index < count; index++) {
density[index] = density[index] * inverseNormalization;
}
}
private void resolveLatticeWarp(CaveCarveScratch scratch, CaveDensityGrid.Column warpColumn, int x, int z, int count) {
int[] latticeY = scratch.latticeY;
int[] snapY = scratch.latticeSnapY;
double[] warpA = scratch.latticeWarpA;
double[] warpB = scratch.latticeWarpB;
int snapCount = 0;
for (int index = 0; index < count; index++) {
int snap = snapWarp(latticeY[index]);
if (snapCount == 0 || snapY[snapCount - 1] != snap) {
snapY[snapCount++] = snap;
}
}
long computed = 0L;
int runStart = -1;
for (int snapIndex = 0; snapIndex <= snapCount; snapIndex++) {
boolean missing = false;
if (snapIndex < snapCount) {
int snap = snapY[snapIndex];
double cachedA = warpColumn == null ? Double.NaN : warpColumn.get(snap);
double cachedB = warpColumn == null ? Double.NaN : warpColumn.get(snap + 1);
missing = Double.isNaN(cachedA) || Double.isNaN(cachedB);
warpA[snapIndex] = cachedA;
warpB[snapIndex] = cachedB;
}
if (runStart >= 0 && (!missing || snapY[snapIndex] != snapY[snapIndex - 1] + warpResolution)) {
computeLatticeWarpRun(scratch, warpColumn, x, z, runStart, snapIndex);
computed += snapIndex - runStart;
runStart = -1;
}
if (missing && runStart < 0) {
runStart = snapIndex;
}
}
if (GenerationCounter.ENABLED) {
GenerationCounter.CAVE_WARP_SAMPLES.add(computed);
}
double[] warpedX = scratch.latticeWarpedX;
double[] warpedY = scratch.latticeWarpedY;
double[] warpedZ = scratch.latticeWarpedZ;
int snapIndex = 0;
for (int index = 0; index < count; index++) {
int y = latticeY[index];
int snap = snapWarp(y);
while (snapY[snapIndex] != snap) {
snapIndex++;
}
double a = warpA[snapIndex];
double b = warpB[snapIndex];
warpedX[index] = x + (a * warpStrength);
warpedY[index] = y + (b * warpStrength);
warpedZ[index] = z + ((a - b) * 0.5D * warpStrength);
}
}
private void computeLatticeWarpRun(CaveCarveScratch scratch, CaveDensityGrid.Column warpColumn, int x, int z, int from, int to) {
int[] snapY = scratch.latticeSnapY;
double[] warpA = scratch.latticeWarpA;
double[] warpB = scratch.latticeWarpB;
int length = to - from;
if (length == 1) {
warpA[from] = warpDensity.sampleSigned3D(x, snapY[from], z);
} else {
double[] column = scratch.latticeWarpedX;
warpDensity.fillColumn3D(NOISE_SCRATCH.get(), x, z, snapY[from], warpResolution, length, column);
System.arraycopy(column, 0, warpA, from, length);
}
for (int snapIndex = from; snapIndex < to; snapIndex++) {
int snap = snapY[snapIndex];
warpB[snapIndex] = warpDensity.sampleSigned3D(x + WARP_B_OFFSET_X, snap - WARP_B_OFFSET_Y, z + WARP_B_OFFSET_Z);
if (warpColumn != null) {
warpColumn.set(snap, warpA[snapIndex]);
warpColumn.set(snap + 1, warpB[snapIndex]);
}
} }
} }
@@ -1268,6 +1470,7 @@ public class IrisCaveCarver3D {
private void classifyCellRows(DensityPass pass, int minimumY, int maximumY, byte[] classified) { private void classifyCellRows(DensityPass pass, int minimumY, int maximumY, byte[] classified) {
CaveCarveScratch scratch = new CaveCarveScratch(); CaveCarveScratch scratch = new CaveCarveScratch();
CaveCarveScratch plan = pass.sourceScratch(); CaveCarveScratch plan = pass.sourceScratch();
fillCellLattice(scratch, plan, pass.x(), pass.z(), minimumY, maximumY);
long[] rowBits = scratch.rowCarveBits; long[] rowBits = scratch.rowCarveBits;
for (int y = minimumY; y <= maximumY; y++) { for (int y = minimumY; y <= maximumY; y++) {
int fadeIndex = y - pass.minimumY(); int fadeIndex = y - pass.minimumY();
@@ -1313,7 +1516,7 @@ public class IrisCaveCarver3D {
) { ) {
Arrays.fill(rowBits, 0L); Arrays.fill(rowBits, 0L);
if (plan.rowActiveCount[y - minY] >= ADAPTIVE_MIN_PLANE_COLUMNS) { if (plan.rowActiveCount[y - minY] >= ADAPTIVE_MIN_PLANE_COLUMNS) {
classifyCellRow(scratch, plan, x0, z0, y, minY, surfaceBreakThresholdBoost, verticalEdgeFade, rowBits); classifyCellRow(scratch, plan, y, minY, surfaceBreakThresholdBoost, verticalEdgeFade, rowBits);
return; return;
} }
int[] planeColumnIndices = scratch.planeColumnIndices; int[] planeColumnIndices = scratch.planeColumnIndices;
@@ -1346,32 +1549,17 @@ public class IrisCaveCarver3D {
private void classifyCellRow( private void classifyCellRow(
CaveCarveScratch scratch, CaveCarveScratch scratch,
CaveCarveScratch plan, CaveCarveScratch plan,
int x0,
int z0,
int y, int y,
int minY, int minY,
double surfaceBreakThresholdBoost, double surfaceBreakThresholdBoost,
double[] verticalEdgeFade, double[] verticalEdgeFade,
long[] rowBits long[] rowBits
) { ) {
int activeCells = 0; CaveDensityLattice lattice = plan.cellLattice;
int corners = 0; int activeCells = lattice.cells(y);
for (int cell = 0; cell < 4; cell++) {
if (isCellActive(plan, cell, y)) {
activeCells |= 1 << cell;
corners |= CELL_CORNER_MASKS[cell];
}
}
if (activeCells == 0) { if (activeCells == 0) {
return; return;
} }
double[] density = scratch.rowLatticeDensity;
for (int corner = 0; corner < 9; corner++) {
if ((corners & (1 << corner)) != 0) {
density[corner] = cornerDensity(scratch, plan.cellCornerColumns[corner],
x0 + (corner / 3) * ADAPTIVE_DEEP_SAMPLE_STEP, y, z0 + (corner % 3) * ADAPTIVE_DEEP_SAMPLE_STEP);
}
}
int fadeIndex = y - minY; int fadeIndex = y - minY;
double fade = verticalEdgeFade[fadeIndex]; double fade = verticalEdgeFade[fadeIndex];
double closure = plan.surfaceClosureThreshold[fadeIndex]; double closure = plan.surfaceClosureThreshold[fadeIndex];
@@ -1380,10 +1568,10 @@ public class IrisCaveCarver3D {
continue; continue;
} }
int corner = ((cell >> 1) * 3) + (cell & 1); int corner = ((cell >> 1) * 3) + (cell & 1);
double d00 = density[corner]; double d00 = lattice.density(corner, y);
double d01 = density[corner + 1]; double d01 = lattice.density(corner + 1, y);
double d10 = density[corner + 3]; double d10 = lattice.density(corner + 3, y);
double d11 = density[corner + 4]; double d11 = lattice.density(corner + 4, y);
double low = plan.cellThresholdMin[cell] - fade; double low = plan.cellThresholdMin[cell] - fade;
if (y >= plan.cellCeilingFadeStart[cell]) { if (y >= plan.cellCeilingFadeStart[cell]) {
low = Math.min(low, closure); low = Math.min(low, closure);
@@ -1524,19 +1712,6 @@ public class IrisCaveCarver3D {
&& (y < plan.columnProtectStartY[column] || y > plan.columnProtectEndY[column]); && (y < plan.columnProtectStartY[column] || y > plan.columnProtectEndY[column]);
} }
private double cornerDensity(CaveCarveScratch scratch, CaveDensityGrid.Column column, int x, int y, int z) {
if (column == null) {
return samplePlaneDensity(scratch, x, y, z);
}
double cached = column.get(y);
if (!Double.isNaN(cached)) {
return cached;
}
double density = samplePlaneDensity(scratch, x, y, z);
column.set(y, density);
return density;
}
private double cellThreshold(CaveCarveScratch plan, int column, int y, int minY, double surfaceBreakThresholdBoost, private double cellThreshold(CaveCarveScratch plan, int column, int y, int minY, double surfaceBreakThresholdBoost,
double[] verticalEdgeFade) { double[] verticalEdgeFade) {
return CaveCellKernel.threshold(cellLocalThreshold(plan, column, y, minY, surfaceBreakThresholdBoost, verticalEdgeFade), return CaveCellKernel.threshold(cellLocalThreshold(plan, column, y, minY, surfaceBreakThresholdBoost, verticalEdgeFade),
@@ -2644,7 +2819,7 @@ public class IrisCaveCarver3D {
} }
private boolean classifyDensityPointNoWarpNoModules(int x, int y, int z, double thresholdLimit) { private boolean classifyDensityPointNoWarpNoModules(int x, int y, int z, double thresholdLimit) {
double density = baseDensity.noiseFastSigned3D(x, y, z) * baseWeight; double density = baseDensity.sampleSigned3D(x, y, z) * baseWeight;
if ((density + detailMinContribution) > thresholdLimit) { if ((density + detailMinContribution) > thresholdLimit) {
return false; return false;
} }
@@ -2652,7 +2827,7 @@ public class IrisCaveCarver3D {
return true; return true;
} }
density += detailDensity.noiseFastSigned3D(x, y, z) * detailWeight; density += detailDensity.sampleSigned3D(x, y, z) * detailWeight;
return density <= thresholdLimit; return density <= thresholdLimit;
} }
@@ -2670,7 +2845,7 @@ public class IrisCaveCarver3D {
return classifyDensityPointNoWarpNoModules(x, y, z, thresholdLimit); return classifyDensityPointNoWarpNoModules(x, y, z, thresholdLimit);
} }
double density = baseDensity.noiseFastSigned3D(x, y, z) * baseWeight; double density = baseDensity.sampleSigned3D(x, y, z) * baseWeight;
if ((density + detailMinContribution + remainingMin[0]) > thresholdLimit) { if ((density + detailMinContribution + remainingMin[0]) > thresholdLimit) {
return false; return false;
} }
@@ -2678,7 +2853,7 @@ public class IrisCaveCarver3D {
return true; return true;
} }
density += detailDensity.noiseFastSigned3D(x, y, z) * detailWeight; density += detailDensity.sampleSigned3D(x, y, z) * detailWeight;
if ((density + remainingMin[0]) > thresholdLimit) { if ((density + remainingMin[0]) > thresholdLimit) {
return false; return false;
} }
@@ -2720,7 +2895,7 @@ public class IrisCaveCarver3D {
double warpedX = x + (warpA * warpStrength); double warpedX = x + (warpA * warpStrength);
double warpedY = y + (warpB * warpStrength); double warpedY = y + (warpB * warpStrength);
double warpedZ = z + ((warpA - warpB) * 0.5D * warpStrength); double warpedZ = z + ((warpA - warpB) * 0.5D * warpStrength);
double density = baseDensity.noiseFastSigned3D(warpedX, warpedY, warpedZ) * baseWeight; double density = baseDensity.sampleSigned3D(warpedX, warpedY, warpedZ) * baseWeight;
if ((density + detailMinContribution) > thresholdLimit) { if ((density + detailMinContribution) > thresholdLimit) {
return false; return false;
} }
@@ -2728,7 +2903,7 @@ public class IrisCaveCarver3D {
return true; return true;
} }
density += detailDensity.noiseFastSigned3D(warpedX, warpedY, warpedZ) * detailWeight; density += detailDensity.sampleSigned3D(warpedX, warpedY, warpedZ) * detailWeight;
return density <= thresholdLimit; return density <= thresholdLimit;
} }
@@ -2756,7 +2931,7 @@ public class IrisCaveCarver3D {
double warpedX = x + (warpA * warpStrength); double warpedX = x + (warpA * warpStrength);
double warpedY = y + (warpB * warpStrength); double warpedY = y + (warpB * warpStrength);
double warpedZ = z + ((warpA - warpB) * 0.5D * warpStrength); double warpedZ = z + ((warpA - warpB) * 0.5D * warpStrength);
double density = baseDensity.noiseFastSigned3D(warpedX, warpedY, warpedZ) * baseWeight; double density = baseDensity.sampleSigned3D(warpedX, warpedY, warpedZ) * baseWeight;
if ((density + detailMinContribution + remainingMin[0]) > thresholdLimit) { if ((density + detailMinContribution + remainingMin[0]) > thresholdLimit) {
return false; return false;
} }
@@ -2764,7 +2939,7 @@ public class IrisCaveCarver3D {
return true; return true;
} }
density += detailDensity.noiseFastSigned3D(warpedX, warpedY, warpedZ) * detailWeight; density += detailDensity.sampleSigned3D(warpedX, warpedY, warpedZ) * detailWeight;
if ((density + remainingMin[0]) > thresholdLimit) { if ((density + remainingMin[0]) > thresholdLimit) {
return false; return false;
} }
@@ -2990,8 +3165,8 @@ public class IrisCaveCarver3D {
int latticeZ = z >> CaveDensityGrid.LATTICE_SHIFT; int latticeZ = z >> CaveDensityGrid.LATTICE_SHIFT;
int slot = ((latticeX & 3) << 2) | (latticeZ & 3); int slot = ((latticeX & 3) << 2) | (latticeZ & 3);
CaveDensityGrid.Column column = parallelDensity ? scratch.densityColumns[slot] : null; CaveDensityGrid.Column column = parallelDensity ? scratch.densityColumns[slot] : null;
if (column == null || !column.matches(this, latticeX, latticeZ)) { if (column == null || !column.matches(densityOwner, latticeX, latticeZ)) {
column = densityGrid.column(this, densityGridOwner, densityMaximumY + 1, latticeX, latticeZ); column = densityGrid.column(densityOwner, densityMaximumY + 1, latticeX, latticeZ);
if (parallelDensity) { if (parallelDensity) {
scratch.densityColumns[slot] = column; scratch.densityColumns[slot] = column;
} }
@@ -3020,8 +3195,8 @@ public class IrisCaveCarver3D {
} }
private double sampleDensityNoWarpNoModules(int x, int y, int z) { private double sampleDensityNoWarpNoModules(int x, int y, int z) {
double density = baseDensity.noiseFastSigned3D(x, y, z) * baseWeight; double density = baseDensity.sampleSigned3D(x, y, z) * baseWeight;
density += detailDensity.noiseFastSigned3D(x, y, z) * detailWeight; density += detailDensity.sampleSigned3D(x, y, z) * detailWeight;
return density * inverseNormalization; return density * inverseNormalization;
} }
@@ -3032,11 +3207,11 @@ public class IrisCaveCarver3D {
} }
CaveFieldModuleState[] localModules = scratch.activeModules; CaveFieldModuleState[] localModules = scratch.activeModules;
double density = baseDensity.noiseFastSigned3D(x, y, z) * baseWeight; double density = baseDensity.sampleSigned3D(x, y, z) * baseWeight;
density += detailDensity.noiseFastSigned3D(x, y, z) * detailWeight; density += detailDensity.sampleSigned3D(x, y, z) * detailWeight;
for (int moduleIndex = 0; moduleIndex < activeModuleCount; moduleIndex++) { for (int moduleIndex = 0; moduleIndex < activeModuleCount; moduleIndex++) {
CaveFieldModuleState module = localModules[moduleIndex]; CaveFieldModuleState module = localModules[moduleIndex];
double moduleDensity = module.density.noiseFastSigned3D(x, y, z) - module.threshold; double moduleDensity = module.density.sampleSigned3D(x, y, z) - module.threshold;
if (module.invert) { if (module.invert) {
moduleDensity = -moduleDensity; moduleDensity = -moduleDensity;
} }
@@ -3057,8 +3232,8 @@ public class IrisCaveCarver3D {
double warpedX = x + (warpA * warpStrength); double warpedX = x + (warpA * warpStrength);
double warpedY = y + (warpB * warpStrength); double warpedY = y + (warpB * warpStrength);
double warpedZ = z + ((warpA - warpB) * 0.5D * warpStrength); double warpedZ = z + ((warpA - warpB) * 0.5D * warpStrength);
double density = baseDensity.noiseFastSigned3D(warpedX, warpedY, warpedZ) * baseWeight; double density = baseDensity.sampleSigned3D(warpedX, warpedY, warpedZ) * baseWeight;
density += detailDensity.noiseFastSigned3D(warpedX, warpedY, warpedZ) * detailWeight; density += detailDensity.sampleSigned3D(warpedX, warpedY, warpedZ) * detailWeight;
return density * inverseNormalization; return density * inverseNormalization;
} }
@@ -3082,11 +3257,11 @@ public class IrisCaveCarver3D {
double warpedX = x + (warpA * warpStrength); double warpedX = x + (warpA * warpStrength);
double warpedY = y + (warpB * warpStrength); double warpedY = y + (warpB * warpStrength);
double warpedZ = z + ((warpA - warpB) * 0.5D * warpStrength); double warpedZ = z + ((warpA - warpB) * 0.5D * warpStrength);
double density = baseDensity.noiseFastSigned3D(warpedX, warpedY, warpedZ) * baseWeight; double density = baseDensity.sampleSigned3D(warpedX, warpedY, warpedZ) * baseWeight;
density += detailDensity.noiseFastSigned3D(warpedX, warpedY, warpedZ) * detailWeight; density += detailDensity.sampleSigned3D(warpedX, warpedY, warpedZ) * detailWeight;
for (int moduleIndex = 0; moduleIndex < activeModuleCount; moduleIndex++) { for (int moduleIndex = 0; moduleIndex < activeModuleCount; moduleIndex++) {
CaveFieldModuleState module = localModules[moduleIndex]; CaveFieldModuleState module = localModules[moduleIndex];
double moduleDensity = module.density.noiseFastSigned3D(warpedX, warpedY, warpedZ) - module.threshold; double moduleDensity = module.density.sampleSigned3D(warpedX, warpedY, warpedZ) - module.threshold;
if (module.invert) { if (module.invert) {
moduleDensity = -moduleDensity; moduleDensity = -moduleDensity;
} }
@@ -3110,8 +3285,8 @@ public class IrisCaveCarver3D {
scratch.warpCacheX[slot] = sx; scratch.warpCacheX[slot] = sx;
scratch.warpCacheY[slot] = sy; scratch.warpCacheY[slot] = sy;
scratch.warpCacheZ[slot] = sz; scratch.warpCacheZ[slot] = sz;
scratch.warpCacheA[slot] = warpDensity.noiseFastSigned3D(sx, sy, sz); scratch.warpCacheA[slot] = warpDensity.sampleSigned3D(sx, sy, sz);
scratch.warpCacheB[slot] = warpDensity.noiseFastSigned3D(sx + 31.37D, sy - 17.21D, sz + 23.91D); scratch.warpCacheB[slot] = warpDensity.sampleSigned3D(sx + 31.37D, sy - 17.21D, sz + 23.91D);
return slot; return slot;
} }
@@ -3227,7 +3402,7 @@ public class IrisCaveCarver3D {
private boolean isAquiferCandidate(CaveCarveScratch scratch, int x, int y, int z, double localThreshold) { private boolean isAquiferCandidate(CaveCarveScratch scratch, int x, int y, int z, double localThreshold) {
double depthFactor = Math.max(0D, Math.min(1.5D, (fluidHeight - y) / 48D)); double depthFactor = Math.max(0D, Math.min(1.5D, (fluidHeight - y) / 48D));
double cutoff = 0.35D + (depthFactor * 0.2D); double cutoff = 0.35D + (depthFactor * 0.2D);
if (detailDensity.noiseFastSigned3D(x, y * 0.5D, z) <= cutoff) { if (detailDensity.sampleSigned3D(x, y * 0.5D, z) <= cutoff) {
return false; return false;
} }
return !fluidRequiresFloor || hasAquiferCupSupport(scratch, x, y, z, localThreshold); return !fluidRequiresFloor || hasAquiferCupSupport(scratch, x, y, z, localThreshold);
@@ -3400,4 +3575,15 @@ public class IrisCaveCarver3D {
return verticalEdgeFade; return verticalEdgeFade;
} }
private record WarpFieldKey(CaveFieldKey field) {
}
private record ModuleFieldKey(CaveFieldKey field, int minY, int maxY, long weight, long threshold, boolean invert) {
}
private record DensityFieldKey(boolean warp, CaveFieldKey warpField, long warpStrength, CaveFieldKey base, long baseWeight,
CaveFieldKey detail, long detailWeight, long inverseNormalization,
List<ModuleFieldKey> modules) {
}
} }
@@ -5,7 +5,10 @@ import java.util.concurrent.atomic.LongAdder;
public enum GenerationCounter { public enum GenerationCounter {
GENERATOR_EVALUATIONS("generator_evaluations"), GENERATOR_EVALUATIONS("generator_evaluations"),
CORNER_STARCASTS("corner_starcasts"), CORNER_STARCASTS("corner_starcasts"),
HYDROLOGY_LOOKUPS("hydrology_lookups"); HYDROLOGY_LOOKUPS("hydrology_lookups"),
CAVE_LATTICE_READS("cave_lattice_reads"),
CAVE_LATTICE_SAMPLES("cave_lattice_samples"),
CAVE_WARP_SAMPLES("cave_warp_samples");
public static final boolean ENABLED = Boolean.getBoolean("iris.generation.counters"); public static final boolean ENABLED = Boolean.getBoolean("iris.generation.counters");
@@ -83,6 +83,11 @@ public class IrisCaveCarver3DCellKernelTest {
assertScenario(Scenario.FLOOR_FLUID_LAYERED, 0xb4c0fb23e071617dL, 68747); assertScenario(Scenario.FLOOR_FLUID_LAYERED, 0xb4c0fb23e071617dL, 68747);
} }
@Test
public void packStyleProfilesSharingFieldsMatchGoldenWriteSequence() throws Exception {
assertScenario(Scenario.PACK_SHARED, 0xdbe20a1ca87f9cc7L, 269035);
}
@Test @Test
public void cellKernelDecidesCellsWithoutColumnEvaluation() throws Exception { public void cellKernelDecidesCellsWithoutColumnEvaluation() throws Exception {
IrisCaveCarver3D.CellCounters before = IrisCaveCarver3D.cellCounters(); IrisCaveCarver3D.CellCounters before = IrisCaveCarver3D.cellCounters();
@@ -196,7 +201,31 @@ public class IrisCaveCarver3DCellKernelTest {
return profile; return profile;
} }
private static IrisCaveProfile packProfile(NoiseStyle warp, double warpZoom, double warpStrength, NoiseStyle base,
double baseZoom, double threshold, IrisCaveFieldModule... modules) {
IrisCaveProfile profile = baseProfile(true, false);
profile.setWarpStyle(new IrisGeneratorStyle(warp).zoomed(warpZoom));
profile.setWarpStrength(warpStrength);
profile.setBaseDensityStyle(new IrisGeneratorStyle(base).zoomed(baseZoom));
profile.setDetailDensityStyle(new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.5D));
profile.setBaseWeight(0.82D);
profile.setDetailWeight(0.15D);
profile.setDensityThreshold(new IrisStyledRange(threshold - 0.06D, threshold + 0.05D,
new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.78D)));
profile.setThresholdBias(0.12D);
profile.setSampleStep(2);
profile.setModules(new KList<>(modules));
return profile;
}
private static IrisCaveFieldModule packModule(NoiseStyle style, double zoom, double weight, double threshold,
double minY, double maxY, boolean invert) {
return new IrisCaveFieldModule(new IrisGeneratorStyle(style).zoomed(zoom), weight, threshold,
new IrisRange(minY, maxY), invert);
}
private enum Scenario { private enum Scenario {
PACK_SHARED,
WARP_MODULES, WARP_MODULES,
WARP_TERRAIN, WARP_TERRAIN,
STYLED_BLEND, STYLED_BLEND,
@@ -207,6 +236,20 @@ public class IrisCaveCarver3DCellKernelTest {
FLOOR_FLUID_LAYERED; FLOOR_FLUID_LAYERED;
IrisCaveProfile[] profiles() { IrisCaveProfile[] profiles() {
if (this == PACK_SHARED) {
return new IrisCaveProfile[]{
packProfile(NoiseStyle.FRACTAL_WATER, 0.44D, 3.2D, NoiseStyle.PERLIN_IRIS, 0.72D, -0.02D,
packModule(NoiseStyle.FRACTAL_FBM_SIMPLEX, 0.24D, 0.08D, 0.03D, 10D, 140D, false),
packModule(NoiseStyle.SIMPLEX_VASCULAR, 1.02D, 0.05D, 0.07D, 4D, 90D, true)),
packProfile(NoiseStyle.FRACTAL_WATER, 0.44D, 3.2D, NoiseStyle.FRACTAL_BILLOW_SIMPLEX, 0.656D, 0.01D,
packModule(NoiseStyle.CELLULAR_BILINEAR, 1.32D, 0.06D, 0.02D, 20D, 150D, false)),
packProfile(NoiseStyle.FRACTAL_WATER, 0.44D, 3.2D, NoiseStyle.PERLIN_IRIS, 0.72D, 0.04D,
packModule(NoiseStyle.FRACTAL_FBM_SIMPLEX, 0.24D, 0.08D, 0.03D, 10D, 140D, false),
packModule(NoiseStyle.SIMPLEX_VASCULAR, 1.02D, 0.05D, 0.07D, 4D, 90D, true)),
packProfile(NoiseStyle.PERLIN_IRIS, 0.35D, 2.4D, NoiseStyle.IRIS_DOUBLE, 0.68D, -0.04D,
packModule(NoiseStyle.VASCULAR_THIN, 1.28D, 0.04D, 0.05D, 8D, 120D, false))
};
}
if (this == FLOOR_FLUID_LAYERED) { if (this == FLOOR_FLUID_LAYERED) {
return new IrisCaveProfile[]{ return new IrisCaveProfile[]{
baseProfile(true, true).setAllowFluid(true).setFluidRequiresFloor(true).setFluidMinDepthBelowSurface(4), baseProfile(true, true).setAllowFluid(true).setFluidRequiresFloor(true).setFluidMinDepthBelowSurface(4),
@@ -228,7 +271,7 @@ public class IrisCaveCarver3DCellKernelTest {
.setFluidMinDepthBelowSurface(6).setAllowLava(false); .setFluidMinDepthBelowSurface(6).setAllowLava(false);
case NO_WARP_MODULES -> baseProfile(false, true); case NO_WARP_MODULES -> baseProfile(false, true);
case LATTICE -> baseProfile(true, true).setSampleStep(3); case LATTICE -> baseProfile(true, true).setSampleStep(3);
case FLOOR_FLUID_LAYERED -> baseProfile(true, true); case FLOOR_FLUID_LAYERED, PACK_SHARED -> baseProfile(true, true);
}; };
} }
@@ -239,6 +282,7 @@ public class IrisCaveCarver3DCellKernelTest {
int worldZ = (chunkZ << 4) + (index & 15); int worldZ = (chunkZ << 4) + (index & 15);
double blend = 0.5D + 0.6D * Math.sin(worldX * 0.11D) * Math.cos(worldZ * 0.07D); double blend = 0.5D + 0.6D * Math.sin(worldX * 0.11D) * Math.cos(worldZ * 0.07D);
weights[index] = switch (this) { weights[index] = switch (this) {
case PACK_SHARED -> 0.5D + 0.6D * Math.sin(worldX * 0.13D + layer * 1.7D) * Math.cos(worldZ * 0.09D - layer);
case STYLED_BLEND, FLUID_BOUNDARY -> blend; case STYLED_BLEND, FLUID_BOUNDARY -> blend;
case FLOOR_FLUID_LAYERED -> layer == 0 ? blend : 1D - blend; case FLOOR_FLUID_LAYERED -> layer == 0 ? blend : 1D - blend;
default -> 1D; default -> 1D;
@@ -0,0 +1,163 @@
package art.arcane.iris.generation.mantle;
import art.arcane.iris.generation.cave.IrisCaveFieldModule;
import art.arcane.iris.generation.cave.IrisCaveProfile;
import art.arcane.iris.generation.noise.IrisGeneratorStyle;
import art.arcane.iris.generation.noise.IrisStyledRange;
import art.arcane.iris.generation.noise.NoiseStyle;
import art.arcane.iris.generation.runtime.Engine;
import art.arcane.iris.generation.runtime.EngineMetrics;
import art.arcane.iris.generation.runtime.SeedManager;
import art.arcane.iris.generation.terrain.IrisDimension;
import art.arcane.iris.pack.loading.IrisData;
import art.arcane.iris.pack.value.IrisRange;
import art.arcane.iris.world.IrisWorld;
import art.arcane.volmlib.util.collection.KList;
import org.junit.Test;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Random;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.withSettings;
public class IrisCaveCarver3DLatticeTest {
private static final long CARVE_SEED = 7_340_119_802_557L;
private static final int WORLD_HEIGHT = 384;
@Test
public void batchedLatticeColumnsMatchScalarPlaneDensityBitForBit() throws Exception {
Engine engine = createEngine();
CaveDensityGrid grid = new CaveDensityGrid(256);
IrisCaveProfile[] profiles = {
profile(NoiseStyle.FRACTAL_WATER, 0.44D, 3.2D, NoiseStyle.PERLIN_IRIS, 0.72D,
module(NoiseStyle.FRACTAL_FBM_SIMPLEX, 0.24D, 10D, 140D, false),
module(NoiseStyle.CELLULAR_BILINEAR, 1.32D, 4D, 300D, true)),
profile(NoiseStyle.PERLIN_IRIS, 0.35D, 2.4D, NoiseStyle.IRIS_DOUBLE, 0.68D,
module(NoiseStyle.VASCULAR_THIN, 1.28D, 8D, 220D, false)),
profile(NoiseStyle.SIMPLEX, 0.5D, 0D, NoiseStyle.FRACTAL_BILLOW_SIMPLEX, 0.656D)
};
Method sampleLatticeColumn = IrisCaveCarver3D.class.getDeclaredMethod("sampleLatticeColumn",
CaveCarveScratch.class, CaveDensityGrid.Column.class, int.class, int.class, int.class);
sampleLatticeColumn.setAccessible(true);
Method samplePlaneDensity = IrisCaveCarver3D.class.getDeclaredMethod("samplePlaneDensity",
CaveCarveScratch.class, int.class, int.class, int.class);
samplePlaneDensity.setAccessible(true);
Field warpOwnerField = IrisCaveCarver3D.class.getDeclaredField("warpOwner");
warpOwnerField.setAccessible(true);
Random random = new Random(91_337L);
for (IrisCaveProfile profile : profiles) {
IrisCaveCarver3D carver = new IrisCaveCarver3D(engine, profile, grid);
CaveDensityGrid.Owner warpOwner = (CaveDensityGrid.Owner) warpOwnerField.get(carver);
CaveCarveScratch batch = new CaveCarveScratch();
CaveCarveScratch reference = new CaveCarveScratch();
for (int trial = 0; trial < 48; trial++) {
int latticeX = random.nextInt(4001) - 2000;
int latticeZ = random.nextInt(4001) - 2000;
int x = latticeX << CaveDensityGrid.LATTICE_SHIFT;
int z = latticeZ << CaveDensityGrid.LATTICE_SHIFT;
int count = selectRows(random, batch);
CaveDensityGrid.Column warpColumn = trial % 3 == 0 ? null
: grid.warpColumn(warpOwner, WORLD_HEIGHT + 1, latticeX, latticeZ);
for (int pass = 0; pass < 2; pass++) {
sampleLatticeColumn.invoke(carver, batch, warpColumn, x, z, count);
for (int index = 0; index < count; index++) {
int y = batch.latticeY[index];
double expected = (double) samplePlaneDensity.invoke(carver, reference, x, y, z);
assertEquals("x=" + x + " y=" + y + " z=" + z + " pass=" + pass,
Double.doubleToRawLongBits(expected), Double.doubleToRawLongBits(batch.latticeDensity[index]));
}
}
}
}
}
@Test
public void identicalFieldsShareGridOwnersAcrossProfiles() throws Exception {
Engine engine = createEngine();
CaveDensityGrid grid = new CaveDensityGrid(256);
Field densityOwnerField = IrisCaveCarver3D.class.getDeclaredField("densityOwner");
densityOwnerField.setAccessible(true);
Field warpOwnerField = IrisCaveCarver3D.class.getDeclaredField("warpOwner");
warpOwnerField.setAccessible(true);
IrisCaveCarver3D first = new IrisCaveCarver3D(engine, profile(NoiseStyle.FRACTAL_WATER, 0.44D, 3.2D,
NoiseStyle.PERLIN_IRIS, 0.72D, module(NoiseStyle.SIMPLEX_VASCULAR, 1.02D, 4D, 90D, true)), grid);
IrisCaveCarver3D sameDensity = new IrisCaveCarver3D(engine, profile(NoiseStyle.FRACTAL_WATER, 0.44D, 3.2D,
NoiseStyle.PERLIN_IRIS, 0.72D, module(NoiseStyle.SIMPLEX_VASCULAR, 1.02D, 4D, 90D, true))
.setDensityThreshold(new IrisStyledRange(0.2D, 0.2D, new IrisGeneratorStyle(NoiseStyle.FLAT))), grid);
IrisCaveCarver3D sameWarp = new IrisCaveCarver3D(engine, profile(NoiseStyle.FRACTAL_WATER, 0.44D, 1.7D,
NoiseStyle.FRACTAL_BILLOW_SIMPLEX, 0.656D), grid);
IrisCaveCarver3D otherWarp = new IrisCaveCarver3D(engine, profile(NoiseStyle.FRACTAL_WATER, 0.46D, 3.2D,
NoiseStyle.PERLIN_IRIS, 0.72D, module(NoiseStyle.SIMPLEX_VASCULAR, 1.02D, 4D, 90D, true)), grid);
IrisCaveCarver3D otherModule = new IrisCaveCarver3D(engine, profile(NoiseStyle.FRACTAL_WATER, 0.44D, 3.2D,
NoiseStyle.PERLIN_IRIS, 0.72D, module(NoiseStyle.SIMPLEX_VASCULAR, 1.02D, 4D, 91D, true)), grid);
assertSame(densityOwnerField.get(first), densityOwnerField.get(sameDensity));
assertSame(warpOwnerField.get(first), warpOwnerField.get(sameDensity));
assertSame(warpOwnerField.get(first), warpOwnerField.get(sameWarp));
assertSame(warpOwnerField.get(first), warpOwnerField.get(otherModule));
assertNotSame(densityOwnerField.get(first), densityOwnerField.get(sameWarp));
assertNotSame(densityOwnerField.get(first), densityOwnerField.get(otherModule));
assertNotSame(densityOwnerField.get(first), densityOwnerField.get(otherWarp));
assertNotSame(warpOwnerField.get(first), warpOwnerField.get(otherWarp));
}
private static int selectRows(Random random, CaveCarveScratch scratch) {
int start = random.nextInt(40);
int end = start + 20 + random.nextInt(WORLD_HEIGHT - start - 20);
scratch.ensureLatticeBatch(end - start + 1);
int count = 0;
for (int y = start; y <= end; y++) {
if (random.nextInt(5) != 0) {
scratch.latticeY[count++] = y;
}
}
return count;
}
private static IrisCaveProfile profile(NoiseStyle warp, double warpZoom, double warpStrength, NoiseStyle base,
double baseZoom, IrisCaveFieldModule... modules) {
IrisCaveProfile profile = new IrisCaveProfile();
profile.setEnabled(true);
profile.setVerticalRange(new IrisRange(4D, 360D));
profile.setWarpStyle(new IrisGeneratorStyle(warp).zoomed(warpZoom));
profile.setWarpStrength(warpStrength);
profile.setBaseDensityStyle(new IrisGeneratorStyle(base).zoomed(baseZoom));
profile.setDetailDensityStyle(new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.5D));
profile.setSurfaceBreakStyle(new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.82D));
profile.setBaseWeight(0.82D);
profile.setDetailWeight(0.15D);
profile.setDensityThreshold(new IrisStyledRange(-0.1D, 0.02D, new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.78D)));
profile.setSampleStep(2);
profile.setModules(new KList<>(modules));
return profile;
}
private static IrisCaveFieldModule module(NoiseStyle style, double zoom, double minY, double maxY, boolean invert) {
return new IrisCaveFieldModule(new IrisGeneratorStyle(style).zoomed(zoom), 0.07D, 0.04D, new IrisRange(minY, maxY), invert);
}
private static Engine createEngine() {
Engine engine = mock(Engine.class, withSettings().stubOnly());
IrisData data = mock(IrisData.class, withSettings().stubOnly());
IrisDimension dimension = mock(IrisDimension.class, withSettings().stubOnly());
SeedManager seedManager = mock(SeedManager.class, withSettings().stubOnly());
doReturn(data).when(engine).getData();
doReturn(dimension).when(engine).getDimension();
doReturn(seedManager).when(engine).getSeedManager();
doReturn(CARVE_SEED).when(seedManager).getCarve();
doReturn(new EngineMetrics(16)).when(engine).getMetrics();
doReturn(IrisWorld.builder().minHeight(-64).maxHeight(WORLD_HEIGHT - 64).build()).when(engine).getWorld();
doReturn(WORLD_HEIGHT).when(engine).getHeight();
doReturn(200).when(engine).getHeight(anyInt(), anyInt());
doReturn(20).when(dimension).getCaveLavaHeight();
doReturn(64).when(dimension).getFluidHeight();
doReturn(1).when(dimension).getCaveDensitySamplingStep();
return engine;
}
}
@@ -13,6 +13,7 @@ import art.arcane.iris.generation.noise.IrisStyledRange;
import art.arcane.iris.world.IrisWorld; import art.arcane.iris.world.IrisWorld;
import art.arcane.iris.generation.noise.NoiseStyle; import art.arcane.iris.generation.noise.NoiseStyle;
import art.arcane.volmlib.util.noise.CNG; import art.arcane.volmlib.util.noise.CNG;
import art.arcane.volmlib.util.noise.program.NoiseProgram;
import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.mantle.runtime.Mantle; import art.arcane.volmlib.util.mantle.runtime.Mantle;
import art.arcane.volmlib.util.mantle.runtime.MantleChunk; import art.arcane.volmlib.util.mantle.runtime.MantleChunk;
@@ -148,7 +149,7 @@ public class IrisCaveCarver3DNearParityTest {
sampledY.add((int) y); sampledY.add((int) y);
return x * 0.0001D + y * 0.01D + z * 0.0002D; return x * 0.0001D + y * 0.01D + z * 0.0002D;
}).when(density).noiseFastSigned3D(anyDouble(), anyDouble(), anyDouble()); }).when(density).noiseFastSigned3D(anyDouble(), anyDouble(), anyDouble());
baseDensityField.set(carver, density); baseDensityField.set(carver, NoiseProgram.compileSigned3D(density));
Method prepare = IrisCaveCarver3D.class.getDeclaredMethod("prepareVerticalDensityPlane", Method prepare = IrisCaveCarver3D.class.getDeclaredMethod("prepareVerticalDensityPlane",
CaveCarveScratch.class, int.class, int.class, int.class); CaveCarveScratch.class, int.class, int.class, int.class);
prepare.setAccessible(true); prepare.setAccessible(true);
@@ -168,7 +169,7 @@ public class IrisCaveCarver3DNearParityTest {
doReturn(1).when(dimension).getCaveDensitySamplingStep(); doReturn(1).when(dimension).getCaveDensitySamplingStep();
IrisCaveCarver3D scalar = new IrisCaveCarver3D(engine, IrisCaveCarver3D scalar = new IrisCaveCarver3D(engine,
createProfile(false, false).setDetailWeight(0D).setAllowFluid(false)); createProfile(false, false).setDetailWeight(0D).setAllowFluid(false));
baseDensityField.set(scalar, density); baseDensityField.set(scalar, NoiseProgram.compileSigned3D(density));
CaveCarveScratch scalarScratch = new CaveCarveScratch(); CaveCarveScratch scalarScratch = new CaveCarveScratch();
for (int index = 0; index < 256; index++) { for (int index = 0; index < 256; index++) {
scalarScratch.planeColumnIndices[index] = index; scalarScratch.planeColumnIndices[index] = index;
@@ -292,10 +293,10 @@ public class IrisCaveCarver3DNearParityTest {
IrisCaveCarver3D carver = new IrisCaveCarver3D(engine, profile); IrisCaveCarver3D carver = new IrisCaveCarver3D(engine, profile);
CNG base = mock(CNG.class, withSettings().stubOnly()); CNG base = mock(CNG.class, withSettings().stubOnly());
doReturn(1D).when(base).noiseFastSigned3D(anyDouble(), anyDouble(), anyDouble()); doReturn(1D).when(base).noiseFastSigned3D(anyDouble(), anyDouble(), anyDouble());
baseDensityField.set(carver, base); baseDensityField.set(carver, NoiseProgram.compileSigned3D(base));
CNG module = mock(CNG.class, withSettings().stubOnly()); CNG module = mock(CNG.class, withSettings().stubOnly());
doReturn(-1D).when(module).noiseFastSigned3D(anyDouble(), anyDouble(), anyDouble()); doReturn(-1D).when(module).noiseFastSigned3D(anyDouble(), anyDouble(), anyDouble());
moduleDensityField.set(((CaveFieldModuleState[]) modulesField.get(carver))[0], module); moduleDensityField.set(((CaveFieldModuleState[]) modulesField.get(carver))[0], NoiseProgram.compileSigned3D(module));
WriterCapture capture = createWriterCapture(128); WriterCapture capture = createWriterCapture(128);
carver.carve(capture.writer, -1, 2, fullWeights(), 0D, 0D, null, filledHeights(110)); carver.carve(capture.writer, -1, 2, fullWeights(), 0D, 0D, null, filledHeights(110));
assertEquals(512, capture.carvedCells.size()); assertEquals(512, capture.carvedCells.size());
@@ -364,13 +365,13 @@ public class IrisCaveCarver3DNearParityTest {
} }
return -1D; return -1D;
}).when(density).noiseFastSigned3D(anyDouble(), anyDouble(), anyDouble()); }).when(density).noiseFastSigned3D(anyDouble(), anyDouble(), anyDouble());
baseDensityField.set(carver, density); baseDensityField.set(carver, NoiseProgram.compileSigned3D(density));
assertThrows(IllegalStateException.class, () -> carver.carve(capture.writer, 0, 0, assertThrows(IllegalStateException.class, () -> carver.carve(capture.writer, 0, 0,
fullWeights(), 0D, 0D, null, filledHeights(110))); fullWeights(), 0D, 0D, null, filledHeights(110)));
} else { } else {
carver.carve(capture.writer, 0, 0, fullWeights(), 0D, 0D, null, filledHeights(110)); carver.carve(capture.writer, 0, 0, fullWeights(), 0D, 0D, null, filledHeights(110));
assertFalse(capture.carvedCells.isEmpty());
} }
assertFalse(capture.carvedCells.isEmpty());
Field scratchField = IrisCaveCarver3D.class.getDeclaredField("scratchCache"); Field scratchField = IrisCaveCarver3D.class.getDeclaredField("scratchCache");
scratchField.setAccessible(true); scratchField.setAccessible(true);
ThreadLocal<?> scratchCache = (ThreadLocal<?>) scratchField.get(carver); ThreadLocal<?> scratchCache = (ThreadLocal<?>) scratchField.get(carver);
@@ -385,6 +386,12 @@ public class IrisCaveCarver3DNearParityTest {
for (CaveDensityGrid.Column column : scratch.densityColumns) { for (CaveDensityGrid.Column column : scratch.densityColumns) {
assertNull(column); assertNull(column);
} }
for (CaveDensityGrid.Column column : scratch.cellLattice.densityColumns) {
assertNull(column);
}
for (CaveDensityGrid.Column column : scratch.cellLattice.warpColumns) {
assertNull(column);
}
} }
} }
@@ -439,7 +446,7 @@ public class IrisCaveCarver3DNearParityTest {
}).when(detail).noiseFastSigned3D(anyDouble(), anyDouble(), anyDouble()); }).when(detail).noiseFastSigned3D(anyDouble(), anyDouble(), anyDouble());
Field detailField = IrisCaveCarver3D.class.getDeclaredField("detailDensity"); Field detailField = IrisCaveCarver3D.class.getDeclaredField("detailDensity");
detailField.setAccessible(true); detailField.setAccessible(true);
detailField.set(carver, detail); detailField.set(carver, NoiseProgram.compileSigned3D(detail));
WriterCapture capture = createWriterCapture(128); WriterCapture capture = createWriterCapture(128);
try (ForkJoinPool pool = new ForkJoinPool(4)) { try (ForkJoinPool pool = new ForkJoinPool(4)) {
assertEquals(0, pool.submit(() -> carver.carve(capture.writer, 0, 0, fullWeights(), assertEquals(0, pool.submit(() -> carver.carve(capture.writer, 0, 0, fullWeights(),
@@ -772,7 +779,7 @@ public class IrisCaveCarver3DNearParityTest {
rejectedDensity.put("11:10:10", -1D); rejectedDensity.put("11:10:10", -1D);
rejectedDensity.put("9:10:10", -1D); rejectedDensity.put("9:10:10", -1D);
CoordinateDensityCNG rejectedNoise = new CoordinateDensityCNG(rejectedDensity); CoordinateDensityCNG rejectedNoise = new CoordinateDensityCNG(rejectedDensity);
baseDensityField.set(carver, rejectedNoise); baseDensityField.set(carver, NoiseProgram.compileSigned3D(rejectedNoise));
boolean rejected = (boolean) aquiferCupSupportMethod.invoke( boolean rejected = (boolean) aquiferCupSupportMethod.invoke(
carver, new CaveCarveScratch(), 10, 10, 10, 0D); carver, new CaveCarveScratch(), 10, 10, 10, 0D);
@@ -781,7 +788,7 @@ public class IrisCaveCarver3DNearParityTest {
assertEquals(List.of("10:9:10", "10:8:10", "11:10:10", "9:10:10"), rejectedNoise.sampledCoordinates); assertEquals(List.of("10:9:10", "10:8:10", "11:10:10", "9:10:10"), rejectedNoise.sampledCoordinates);
CoordinateDensityCNG acceptedNoise = new CoordinateDensityCNG(Map.of()); CoordinateDensityCNG acceptedNoise = new CoordinateDensityCNG(Map.of());
baseDensityField.set(carver, acceptedNoise); baseDensityField.set(carver, NoiseProgram.compileSigned3D(acceptedNoise));
boolean accepted = (boolean) aquiferCupSupportMethod.invoke( boolean accepted = (boolean) aquiferCupSupportMethod.invoke(
carver, new CaveCarveScratch(), 10, 10, 10, 0D); carver, new CaveCarveScratch(), 10, 10, 10, 0D);
@@ -1100,7 +1107,7 @@ public class IrisCaveCarver3DNearParityTest {
AtomicLong isolatedSamples = new AtomicLong(); AtomicLong isolatedSamples = new AtomicLong();
IrisCaveCarver3D shared = new IrisCaveCarver3D(engine, IrisCaveCarver3D shared = new IrisCaveCarver3D(engine,
createProfile(warp, modules).setAdaptiveSampling(true), new CaveDensityGrid(4096)); createProfile(warp, modules).setAdaptiveSampling(true), new CaveDensityGrid(4096));
baseDensityField.set(shared, new CountingCNG((CNG) baseDensityField.get(shared), sharedSamples)); baseDensityField.set(shared, NoiseProgram.compileSigned3D(new CountingCNG((NoiseProgram) baseDensityField.get(shared), sharedSamples)));
for (int chunkX = -1; chunkX <= 1; chunkX++) { for (int chunkX = -1; chunkX <= 1; chunkX++) {
for (int chunkZ = -1; chunkZ <= 1; chunkZ++) { for (int chunkZ = -1; chunkZ <= 1; chunkZ++) {
WriterCapture sharedCapture = createWriterCapture(96); WriterCapture sharedCapture = createWriterCapture(96);
@@ -1108,7 +1115,7 @@ public class IrisCaveCarver3DNearParityTest {
null, filledHeights(90)); null, filledHeights(90));
IrisCaveCarver3D isolated = new IrisCaveCarver3D(engine, IrisCaveCarver3D isolated = new IrisCaveCarver3D(engine,
createProfile(warp, modules).setAdaptiveSampling(true)); createProfile(warp, modules).setAdaptiveSampling(true));
baseDensityField.set(isolated, new CountingCNG((CNG) baseDensityField.get(isolated), isolatedSamples)); baseDensityField.set(isolated, NoiseProgram.compileSigned3D(new CountingCNG((NoiseProgram) baseDensityField.get(isolated), isolatedSamples)));
WriterCapture isolatedCapture = createWriterCapture(96); WriterCapture isolatedCapture = createWriterCapture(96);
int isolatedCarved = isolated.carve(isolatedCapture.writer, chunkX, chunkZ, fullWeights(), 0D, 0D, int isolatedCarved = isolated.carve(isolatedCapture.writer, chunkX, chunkZ, fullWeights(), 0D, 0D,
null, filledHeights(90)); null, filledHeights(90));
@@ -1241,7 +1248,7 @@ public class IrisCaveCarver3DNearParityTest {
new IrisCaveCarver3D(engine, profile).carve(expected.writer, -1, 2, new IrisCaveCarver3D(engine, profile).carve(expected.writer, -1, 2,
fullWeights(), 0D, 0D, null, filledHeights(110)); fullWeights(), 0D, 0D, null, filledHeights(110));
IrisCaveCarver3D carver = new IrisCaveCarver3D(engine, profile); IrisCaveCarver3D carver = new IrisCaveCarver3D(engine, profile);
CNG original = (CNG) baseDensityField.get(carver); NoiseProgram original = (NoiseProgram) baseDensityField.get(carver);
CNG density = mock(CNG.class, withSettings().stubOnly()); CNG density = mock(CNG.class, withSettings().stubOnly());
AtomicBoolean entered = new AtomicBoolean(); AtomicBoolean entered = new AtomicBoolean();
WriterCapture nested = createWriterCapture(128); WriterCapture nested = createWriterCapture(128);
@@ -1252,9 +1259,9 @@ public class IrisCaveCarver3DNearParityTest {
double x = invocation.getArgument(0); double x = invocation.getArgument(0);
double y = invocation.getArgument(1); double y = invocation.getArgument(1);
double z = invocation.getArgument(2); double z = invocation.getArgument(2);
return original.noiseFastSigned3D(x, y, z); return original.sampleSigned3D(x, y, z);
}).when(density).noiseFastSigned3D(anyDouble(), anyDouble(), anyDouble()); }).when(density).noiseFastSigned3D(anyDouble(), anyDouble(), anyDouble());
baseDensityField.set(carver, density); baseDensityField.set(carver, NoiseProgram.compileSigned3D(density));
WriterCapture actual = createWriterCapture(128); WriterCapture actual = createWriterCapture(128);
carver.carve(actual.writer, -1, 2, fullWeights(), 0D, 0D, null, filledHeights(110)); carver.carve(actual.writer, -1, 2, fullWeights(), 0D, 0D, null, filledHeights(110));
assertTrue(entered.get()); assertTrue(entered.get());
@@ -1268,7 +1275,7 @@ public class IrisCaveCarver3DNearParityTest {
doAnswer(invocation -> { doAnswer(invocation -> {
lookups.incrementAndGet(); lookups.incrementAndGet();
return invocation.callRealMethod(); return invocation.callRealMethod();
}).when(grid).column(any(), anyInt(), anyInt(), anyInt(), anyInt()); }).when(grid).column(any(), anyInt(), anyInt(), anyInt());
return grid; return grid;
} }
@@ -1292,10 +1299,10 @@ public class IrisCaveCarver3DNearParityTest {
} }
private void countSkippableSamples(IrisCaveCarver3D carver, AtomicLong samples) throws Exception { private void countSkippableSamples(IrisCaveCarver3D carver, AtomicLong samples) throws Exception {
detailDensityField.set(carver, new CountingCNG((CNG) detailDensityField.get(carver), samples)); detailDensityField.set(carver, NoiseProgram.compileSigned3D(new CountingCNG((NoiseProgram) detailDensityField.get(carver), samples)));
CaveFieldModuleState[] modules = (CaveFieldModuleState[]) modulesField.get(carver); CaveFieldModuleState[] modules = (CaveFieldModuleState[]) modulesField.get(carver);
for (CaveFieldModuleState module : modules) { for (CaveFieldModuleState module : modules) {
moduleDensityField.set(module, new CountingCNG((CNG) moduleDensityField.get(module), samples)); moduleDensityField.set(module, NoiseProgram.compileSigned3D(new CountingCNG((NoiseProgram) moduleDensityField.get(module), samples)));
} }
} }
@@ -1893,10 +1900,10 @@ public class IrisCaveCarver3DNearParityTest {
} }
private static final class CountingCNG extends CNG { private static final class CountingCNG extends CNG {
private final CNG delegate; private final NoiseProgram delegate;
private final AtomicLong samples; private final AtomicLong samples;
private CountingCNG(CNG delegate, AtomicLong samples) { private CountingCNG(NoiseProgram delegate, AtomicLong samples) {
super(new RNG(41_117L)); super(new RNG(41_117L));
this.delegate = delegate; this.delegate = delegate;
this.samples = samples; this.samples = samples;
@@ -1905,7 +1912,7 @@ public class IrisCaveCarver3DNearParityTest {
@Override @Override
public double noiseFastSigned3D(double x, double y, double z) { public double noiseFastSigned3D(double x, double y, double z) {
samples.incrementAndGet(); samples.incrementAndGet();
return delegate.noiseFastSigned3D(x, y, z); return delegate.sampleSigned3D(x, y, z);
} }
} }