mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 14:00:26 +00:00
Batched warped lattice noise
- Cell lattice passes gather every missing corner sample of the pass and evaluate base, detail and modules through NoiseProgram.fillPoints3D - Warp B samples and single-row warp A samples are evaluated as one point batch per pass; longer warp A runs keep fillColumn3D - Module bands compact their points before the batch call and scatter back in the original accumulation order - Lattice batch buffers and their NoiseScratch live in a per-thread CaveLatticeBatch chain that nested carves acquire separately - Lattice test compares pass-wide point batches against scalar plane density bit for bit
This commit is contained in:
1 parent
024214d7f0
commit
a2e4ec7070
4 files changed
+335
-142
No files matched your search
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package art.arcane.iris.generation.mantle;
|
||||
|
||||
import art.arcane.volmlib.util.noise.program.NoiseScratch;
|
||||
|
||||
final class CaveLatticeBatch {
|
||||
private static final ThreadLocal<CaveLatticeBatch> 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];
|
||||
}
|
||||
}
|
||||
@@ -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<NoiseScratch> 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();
|
||||
|
||||
+37
-23
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user