diff --git a/core/src/main/java/art/arcane/iris/generation/mantle/CaveCarveScratch.java b/core/src/main/java/art/arcane/iris/generation/mantle/CaveCarveScratch.java
index 5fd6a127c..ba0e5fd64 100644
--- a/core/src/main/java/art/arcane/iris/generation/mantle/CaveCarveScratch.java
+++ b/core/src/main/java/art/arcane/iris/generation/mantle/CaveCarveScratch.java
@@ -97,14 +97,6 @@ final class CaveCarveScratch {
int adaptiveGeometryStep = -1;
int adaptiveGeometryAxisCells = -1;
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() {
Arrays.fill(sectionMatter, null);
@@ -112,18 +104,4 @@ final class CaveCarveScratch {
Arrays.fill(densityColumns, 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];
- }
}
diff --git a/core/src/main/java/art/arcane/iris/generation/mantle/CaveLatticeBatch.java b/core/src/main/java/art/arcane/iris/generation/mantle/CaveLatticeBatch.java
new file mode 100644
index 000000000..0b7a77023
--- /dev/null
+++ b/core/src/main/java/art/arcane/iris/generation/mantle/CaveLatticeBatch.java
@@ -0,0 +1,81 @@
+/*
+ * 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 .
+ */
+
+package art.arcane.iris.generation.mantle;
+
+import art.arcane.volmlib.util.noise.program.NoiseScratch;
+
+final class CaveLatticeBatch {
+ private static final ThreadLocal CURRENT = ThreadLocal.withInitial(CaveLatticeBatch::new);
+
+ final NoiseScratch noise = new NoiseScratch();
+ final int[] cornerStart = new int[CaveDensityLattice.CORNERS + 1];
+ final int[] snapStart = new int[CaveDensityLattice.CORNERS + 1];
+ int[] y = new int[0];
+ int[] snapY = new int[0];
+ boolean[] snapMissing = new boolean[0];
+ double[] warpA = new double[0];
+ double[] warpB = new double[0];
+ double[] warpedX = new double[0];
+ double[] warpedY = new double[0];
+ double[] warpedZ = new double[0];
+ double[] density = new double[0];
+ double[] values = new double[0];
+ double[] pointX = new double[0];
+ double[] pointY = new double[0];
+ double[] pointZ = new double[0];
+ int[] pointIndex = new int[0];
+ private CaveLatticeBatch nested;
+ private boolean active;
+
+ static CaveLatticeBatch acquire() {
+ CaveLatticeBatch batch = CURRENT.get();
+ while (batch.active) {
+ if (batch.nested == null) {
+ batch.nested = new CaveLatticeBatch();
+ }
+ batch = batch.nested;
+ }
+ batch.active = true;
+ return batch;
+ }
+
+ void release() {
+ active = false;
+ }
+
+ void ensure(int size) {
+ if (y.length >= size) {
+ return;
+ }
+ y = new int[size];
+ snapY = new int[size];
+ snapMissing = new boolean[size];
+ warpA = new double[size];
+ warpB = new double[size];
+ warpedX = new double[size];
+ warpedY = new double[size];
+ warpedZ = new double[size];
+ density = new double[size];
+ values = new double[size * 2];
+ pointX = new double[size * 2];
+ pointY = new double[size * 2];
+ pointZ = new double[size * 2];
+ pointIndex = new int[size * 2];
+ }
+}
diff --git a/core/src/main/java/art/arcane/iris/generation/mantle/IrisCaveCarver3D.java b/core/src/main/java/art/arcane/iris/generation/mantle/IrisCaveCarver3D.java
index fa348442e..30a355965 100644
--- a/core/src/main/java/art/arcane/iris/generation/mantle/IrisCaveCarver3D.java
+++ b/core/src/main/java/art/arcane/iris/generation/mantle/IrisCaveCarver3D.java
@@ -75,7 +75,6 @@ public class IrisCaveCarver3D {
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 NOISE_SCRATCH = ThreadLocal.withInitial(NoiseScratch::new);
private final Engine engine;
private final IrisData data;
@@ -1009,7 +1008,7 @@ public class IrisCaveCarver3D {
if (cellKernel) {
if (classified == null) {
- fillCellLattice(scratch, scratch, x0, z0, minY, passMaxY);
+ fillCellLattice(scratch, x0, z0, minY, passMaxY);
}
return emitCellPass(chunk, scratch, classified, x0, z0, minY, passMaxY, minSection, maxSection,
activeColumnIndices, activeColumnTopY, activeColumnCount, surfaceFluidBoundaries,
@@ -1237,16 +1236,24 @@ public class IrisCaveCarver3D {
}
}
- 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;
+ private void fillCellLattice(CaveCarveScratch plan, int x0, int z0, int fromY, int toY) {
+ CaveLatticeBatch batch = CaveLatticeBatch.acquire();
+ try {
+ fillCellLattice(batch, plan.cellLattice, x0, z0, fromY, toY);
+ } finally {
+ batch.release();
+ }
+ }
+
+ private void fillCellLattice(CaveLatticeBatch batch, CaveDensityLattice lattice, int x0, int z0, int fromY, int toY) {
+ batch.ensure((toY - fromY + 1) * CaveDensityLattice.CORNERS);
+ int[] latticeY = batch.y;
+ int[] cornerStart = batch.cornerStart;
long reads = 0L;
- long samples = 0L;
+ int count = 0;
for (int corner = 0; corner < CaveDensityLattice.CORNERS; corner++) {
+ cornerStart[corner] = count;
CaveDensityGrid.Column column = lattice.densityColumns[corner];
- int count = 0;
for (int y = fromY; y <= toY; y++) {
if (!lattice.needs(corner, y)) {
continue;
@@ -1259,55 +1266,60 @@ public class IrisCaveCarver3D {
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]);
+ }
+ cornerStart[CaveDensityLattice.CORNERS] = count;
+ if (count > 0) {
+ sampleLatticePoints(batch, lattice, x0, z0, count);
+ double[] density = batch.density;
+ for (int corner = 0; corner < CaveDensityLattice.CORNERS; corner++) {
+ CaveDensityGrid.Column column = lattice.densityColumns[corner];
+ for (int index = cornerStart[corner]; index < cornerStart[corner + 1]; 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);
+ GenerationCounter.CAVE_LATTICE_SAMPLES.add(count);
}
}
- 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);
+ private void sampleLatticePoints(CaveLatticeBatch batch, CaveDensityLattice lattice, int x0, int z0, int count) {
+ double[] xs = batch.warpedX;
+ double[] ys = batch.warpedY;
+ double[] zs = batch.warpedZ;
+ if (hasWarp) {
+ resolveLatticeWarp(batch, lattice, x0, z0);
+ } else {
+ int[] latticeY = batch.y;
+ int[] cornerStart = batch.cornerStart;
+ for (int corner = 0; corner < CaveDensityLattice.CORNERS; corner++) {
+ double x = x0 + (corner / 3) * ADAPTIVE_DEEP_SAMPLE_STEP;
+ double z = z0 + (corner % 3) * ADAPTIVE_DEEP_SAMPLE_STEP;
+ for (int index = cornerStart[corner]; index < cornerStart[corner + 1]; index++) {
+ xs[index] = x;
+ ys[index] = latticeY[index];
+ zs[index] = z;
+ }
}
- return;
}
- resolveLatticeWarp(scratch, warpColumn, x, z, count);
- double[] warpedX = scratch.latticeWarpedX;
- double[] warpedY = scratch.latticeWarpedY;
- double[] warpedZ = scratch.latticeWarpedZ;
+ NoiseScratch noise = batch.noise;
+ double[] density = batch.density;
+ double[] values = batch.values;
+ baseDensity.fillPoints3D(noise, xs, ys, zs, count, values);
for (int index = 0; index < count; index++) {
- density[index] = baseDensity.sampleSigned3D(warpedX[index], warpedY[index], warpedZ[index]) * baseWeight;
+ density[index] = values[index] * baseWeight;
}
+ detailDensity.fillPoints3D(noise, xs, ys, zs, count, values);
for (int index = 0; index < count; index++) {
- density[index] += detailDensity.sampleSigned3D(warpedX[index], warpedY[index], warpedZ[index]) * detailWeight;
+ density[index] += values[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;
+ if (hasWarp) {
+ for (CaveFieldModuleState module : modules) {
+ addLatticeModule(batch, module, count);
}
}
for (int index = 0; index < count; index++) {
@@ -1315,78 +1327,186 @@ public class IrisCaveCarver3D {
}
}
- 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;
+ private void addLatticeModule(CaveLatticeBatch batch, CaveFieldModuleState module, int count) {
+ NoiseScratch noise = batch.noise;
+ int[] latticeY = batch.y;
+ double[] xs = batch.warpedX;
+ double[] ys = batch.warpedY;
+ double[] zs = batch.warpedZ;
+ double[] density = batch.density;
+ double[] values = batch.values;
+ int[] band = batch.pointIndex;
+ int inBand = 0;
for (int index = 0; index < count; index++) {
- int snap = snapWarp(latticeY[index]);
- if (snapCount == 0 || snapY[snapCount - 1] != snap) {
- snapY[snapCount++] = snap;
+ int y = latticeY[index];
+ if (y >= module.minY && y <= module.maxY) {
+ band[inBand++] = index;
}
}
+ if (inBand == 0) {
+ return;
+ }
+ if (inBand == count) {
+ module.density.fillPoints3D(noise, xs, ys, zs, count, values);
+ for (int index = 0; index < count; index++) {
+ double moduleDensity = values[index] - module.threshold;
+ if (module.invert) {
+ moduleDensity = -moduleDensity;
+ }
+ density[index] += moduleDensity * module.weight;
+ }
+ return;
+ }
+ double[] bandX = batch.pointX;
+ double[] bandY = batch.pointY;
+ double[] bandZ = batch.pointZ;
+ for (int slot = 0; slot < inBand; slot++) {
+ int index = band[slot];
+ bandX[slot] = xs[index];
+ bandY[slot] = ys[index];
+ bandZ[slot] = zs[index];
+ }
+ module.density.fillPoints3D(noise, bandX, bandY, bandZ, inBand, values);
+ for (int slot = 0; slot < inBand; slot++) {
+ double moduleDensity = values[slot] - module.threshold;
+ if (module.invert) {
+ moduleDensity = -moduleDensity;
+ }
+ density[band[slot]] += moduleDensity * module.weight;
+ }
+ }
+
+ private void resolveLatticeWarp(CaveLatticeBatch batch, CaveDensityLattice lattice, int x0, int z0) {
+ int[] latticeY = batch.y;
+ int[] cornerStart = batch.cornerStart;
+ int[] snapStart = batch.snapStart;
+ int[] snapY = batch.snapY;
+ boolean[] snapMissing = batch.snapMissing;
+ double[] warpA = batch.warpA;
+ double[] warpB = batch.warpB;
+ int snapCount = 0;
+ int pending = 0;
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;
+ for (int corner = 0; corner < CaveDensityLattice.CORNERS; corner++) {
+ int first = snapCount;
+ snapStart[corner] = first;
+ for (int index = cornerStart[corner]; index < cornerStart[corner + 1]; index++) {
+ int snap = snapWarp(latticeY[index]);
+ if (snapCount == first || snapY[snapCount - 1] != snap) {
+ snapY[snapCount++] = snap;
+ }
}
- if (runStart >= 0 && (!missing || snapY[snapIndex] != snapY[snapIndex - 1] + warpResolution)) {
- computeLatticeWarpRun(scratch, warpColumn, x, z, runStart, snapIndex);
- computed += snapIndex - runStart;
- runStart = -1;
+ if (snapCount == first) {
+ continue;
}
- if (missing && runStart < 0) {
- runStart = snapIndex;
+ int x = x0 + (corner / 3) * ADAPTIVE_DEEP_SAMPLE_STEP;
+ int z = z0 + (corner % 3) * ADAPTIVE_DEEP_SAMPLE_STEP;
+ CaveDensityGrid.Column warpColumn = lattice.warpColumns[corner];
+ int runStart = -1;
+ for (int snapIndex = first; 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);
+ snapMissing[snapIndex] = missing;
+ warpA[snapIndex] = cachedA;
+ warpB[snapIndex] = cachedB;
+ }
+ if (runStart >= 0 && (!missing || snapY[snapIndex] != snapY[snapIndex - 1] + warpResolution)) {
+ pending = queueLatticeWarpRun(batch, x, z, runStart, snapIndex, pending);
+ computed += snapIndex - runStart;
+ runStart = -1;
+ }
+ if (missing && runStart < 0) {
+ runStart = snapIndex;
+ }
}
}
+ snapStart[CaveDensityLattice.CORNERS] = snapCount;
+ if (pending > 0) {
+ evaluateLatticeWarpPoints(batch, lattice, pending);
+ }
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[] warpedX = batch.warpedX;
+ double[] warpedY = batch.warpedY;
+ double[] warpedZ = batch.warpedZ;
+ for (int corner = 0; corner < CaveDensityLattice.CORNERS; corner++) {
+ int x = x0 + (corner / 3) * ADAPTIVE_DEEP_SAMPLE_STEP;
+ int z = z0 + (corner % 3) * ADAPTIVE_DEEP_SAMPLE_STEP;
+ int snapIndex = snapStart[corner];
+ for (int index = cornerStart[corner]; index < cornerStart[corner + 1]; 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);
}
- 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;
+ private int queueLatticeWarpRun(CaveLatticeBatch batch, int x, int z, int from, int to, int pending) {
+ int[] snapY = batch.snapY;
+ double[] pointX = batch.pointX;
+ double[] pointY = batch.pointY;
+ double[] pointZ = batch.pointZ;
+ int[] target = batch.pointIndex;
int length = to - from;
+ int queued = pending;
if (length == 1) {
- warpA[from] = warpDensity.sampleSigned3D(x, snapY[from], z);
+ pointX[queued] = x;
+ pointY[queued] = snapY[from];
+ pointZ[queued] = z;
+ target[queued++] = from << 1;
} else {
- double[] column = scratch.latticeWarpedX;
- warpDensity.fillColumn3D(NOISE_SCRATCH.get(), x, z, snapY[from], warpResolution, length, column);
- System.arraycopy(column, 0, warpA, from, length);
+ double[] column = batch.values;
+ warpDensity.fillColumn3D(batch.noise, x, z, snapY[from], warpResolution, length, column);
+ System.arraycopy(column, 0, batch.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]);
+ pointX[queued] = x + WARP_B_OFFSET_X;
+ pointY[queued] = snapY[snapIndex] - WARP_B_OFFSET_Y;
+ pointZ[queued] = z + WARP_B_OFFSET_Z;
+ target[queued++] = (snapIndex << 1) | 1;
+ }
+ return queued;
+ }
+
+ private void evaluateLatticeWarpPoints(CaveLatticeBatch batch, CaveDensityLattice lattice, int pending) {
+ double[] values = batch.values;
+ warpDensity.fillPoints3D(batch.noise, batch.pointX, batch.pointY, batch.pointZ, pending, values);
+ int[] target = batch.pointIndex;
+ double[] warpA = batch.warpA;
+ double[] warpB = batch.warpB;
+ for (int slot = 0; slot < pending; slot++) {
+ int code = target[slot];
+ if ((code & 1) == 0) {
+ warpA[code >> 1] = values[slot];
+ } else {
+ warpB[code >> 1] = values[slot];
+ }
+ }
+ int[] snapStart = batch.snapStart;
+ int[] snapY = batch.snapY;
+ boolean[] snapMissing = batch.snapMissing;
+ for (int corner = 0; corner < CaveDensityLattice.CORNERS; corner++) {
+ CaveDensityGrid.Column warpColumn = lattice.warpColumns[corner];
+ if (warpColumn == null) {
+ continue;
+ }
+ for (int snapIndex = snapStart[corner]; snapIndex < snapStart[corner + 1]; snapIndex++) {
+ if (snapMissing[snapIndex]) {
+ warpColumn.set(snapY[snapIndex], warpA[snapIndex]);
+ warpColumn.set(snapY[snapIndex] + 1, warpB[snapIndex]);
+ }
}
}
}
@@ -1470,7 +1590,7 @@ public class IrisCaveCarver3D {
private void classifyCellRows(DensityPass pass, int minimumY, int maximumY, byte[] classified) {
CaveCarveScratch scratch = new CaveCarveScratch();
CaveCarveScratch plan = pass.sourceScratch();
- fillCellLattice(scratch, plan, pass.x(), pass.z(), minimumY, maximumY);
+ fillCellLattice(plan, pass.x(), pass.z(), minimumY, maximumY);
long[] rowBits = scratch.rowCarveBits;
for (int y = minimumY; y <= maximumY; y++) {
int fadeIndex = y - pass.minimumY();
diff --git a/core/src/test/java/art/arcane/iris/generation/mantle/IrisCaveCarver3DLatticeTest.java b/core/src/test/java/art/arcane/iris/generation/mantle/IrisCaveCarver3DLatticeTest.java
index db789b87f..2f0d6aab3 100644
--- a/core/src/test/java/art/arcane/iris/generation/mantle/IrisCaveCarver3DLatticeTest.java
+++ b/core/src/test/java/art/arcane/iris/generation/mantle/IrisCaveCarver3DLatticeTest.java
@@ -32,7 +32,7 @@ public class IrisCaveCarver3DLatticeTest {
private static final int WORLD_HEIGHT = 384;
@Test
- public void batchedLatticeColumnsMatchScalarPlaneDensityBitForBit() throws Exception {
+ public void batchedLatticePointsMatchScalarPlaneDensityBitForBit() throws Exception {
Engine engine = createEngine();
CaveDensityGrid grid = new CaveDensityGrid(256);
IrisCaveProfile[] profiles = {
@@ -43,9 +43,9 @@ public class IrisCaveCarver3DLatticeTest {
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 sampleLatticePoints = IrisCaveCarver3D.class.getDeclaredMethod("sampleLatticePoints",
+ CaveLatticeBatch.class, CaveDensityLattice.class, int.class, int.class, int.class);
+ sampleLatticePoints.setAccessible(true);
Method samplePlaneDensity = IrisCaveCarver3D.class.getDeclaredMethod("samplePlaneDensity",
CaveCarveScratch.class, int.class, int.class, int.class);
samplePlaneDensity.setAccessible(true);
@@ -55,23 +55,30 @@ public class IrisCaveCarver3DLatticeTest {
for (IrisCaveProfile profile : profiles) {
IrisCaveCarver3D carver = new IrisCaveCarver3D(engine, profile, grid);
CaveDensityGrid.Owner warpOwner = (CaveDensityGrid.Owner) warpOwnerField.get(carver);
- CaveCarveScratch batch = new CaveCarveScratch();
+ CaveLatticeBatch batch = new CaveLatticeBatch();
+ CaveDensityLattice lattice = new CaveDensityLattice();
CaveCarveScratch reference = new CaveCarveScratch();
- for (int trial = 0; trial < 48; trial++) {
+ for (int trial = 0; trial < 24; 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 x0 = latticeX << CaveDensityGrid.LATTICE_SHIFT;
+ int z0 = latticeZ << CaveDensityGrid.LATTICE_SHIFT;
+ for (int corner = 0; corner < CaveDensityLattice.CORNERS; corner++) {
+ lattice.warpColumns[corner] = (trial + corner) % 3 == 0 ? null
+ : grid.warpColumn(warpOwner, WORLD_HEIGHT + 1, latticeX + corner / 3, latticeZ + corner % 3);
+ }
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]));
+ sampleLatticePoints.invoke(carver, batch, lattice, x0, z0, count);
+ for (int corner = 0; corner < CaveDensityLattice.CORNERS; corner++) {
+ int x = x0 + (corner / 3) * 8;
+ int z = z0 + (corner % 3) * 8;
+ for (int index = batch.cornerStart[corner]; index < batch.cornerStart[corner + 1]; index++) {
+ int y = batch.y[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.density[index]));
+ }
}
}
}
@@ -107,16 +114,23 @@ public class IrisCaveCarver3DLatticeTest {
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);
+ private static int selectRows(Random random, CaveLatticeBatch batch) {
+ batch.ensure(WORLD_HEIGHT * CaveDensityLattice.CORNERS);
int count = 0;
- for (int y = start; y <= end; y++) {
- if (random.nextInt(5) != 0) {
- scratch.latticeY[count++] = y;
+ for (int corner = 0; corner < CaveDensityLattice.CORNERS; corner++) {
+ batch.cornerStart[corner] = count;
+ if (random.nextInt(4) == 0) {
+ continue;
+ }
+ int start = random.nextInt(40);
+ int end = start + random.nextInt(WORLD_HEIGHT - start);
+ for (int y = start; y <= end; y++) {
+ if (random.nextInt(5) != 0) {
+ batch.y[count++] = y;
+ }
}
}
+ batch.cornerStart[CaveDensityLattice.CORNERS] = count;
return count;
}