mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 22:10:18 +00:00
Compiled scalar group heights
- Layer and generator programs sample single points through NoiseProgram.sample2D - Scalar interpolator group means use the compiled programs when the seed matches - Single-point program heights are property-tested against the generator path
This commit is contained in:
1 parent
fa996a2e24
commit
a25233edbc
5 files changed
+180
-2
No files matched your search
@@ -34,6 +34,41 @@ public final class TerrainProgram {
|
|||||||
return new TerrainProgram(generators, programs, heightSeed);
|
return new TerrainProgram(generators, programs, heightSeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long heightSeed() {
|
||||||
|
return heightSeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double average(int group, TerrainBatchScratch scratch, double low, double high, double x, double z) {
|
||||||
|
IrisGenerator[] members = generators[group];
|
||||||
|
if (members.length == 0) {
|
||||||
|
return 0D;
|
||||||
|
}
|
||||||
|
if (!Double.isFinite(low) || !Double.isFinite(high)) {
|
||||||
|
throw new IllegalStateException("Terrain generator bounds were not finite at " + x + "," + z
|
||||||
|
+ " (generator=" + members[0].getLoadKey() + ", bounds=" + low + ".." + high + ").");
|
||||||
|
}
|
||||||
|
double height = 0D;
|
||||||
|
if (low == high) {
|
||||||
|
for (int member = 0; member < members.length; member++) {
|
||||||
|
height += low;
|
||||||
|
}
|
||||||
|
return height / members.length;
|
||||||
|
}
|
||||||
|
GeneratorEvaluations.scalar(members.length);
|
||||||
|
GeneratorProgram[] compiled = programs[group];
|
||||||
|
for (int member = 0; member < members.length; member++) {
|
||||||
|
GeneratorProgram program = compiled[member];
|
||||||
|
double noise = program != null ? program.height(scratch, x, z) : members[member].getHeight(x, z, heightSeed);
|
||||||
|
if (!Double.isFinite(noise)) {
|
||||||
|
throw new IllegalStateException("Terrain generator noise was not finite at " + x + "," + z
|
||||||
|
+ " (generator=" + members[member].getLoadKey() + ", noise=" + noise
|
||||||
|
+ ", bounds=" + low + ".." + high + ").");
|
||||||
|
}
|
||||||
|
height += M.lerp(low, high, noise);
|
||||||
|
}
|
||||||
|
return height / members.length;
|
||||||
|
}
|
||||||
|
|
||||||
public int groups() {
|
public int groups() {
|
||||||
return generators.length;
|
return generators.length;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,70 @@ public final class GeneratorProgram {
|
|||||||
surfaceHeights(scratch, xs, zs, count, out);
|
surfaceHeights(scratch, xs, zs, count, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double height(TerrainBatchScratch scratch, double x, double z) {
|
||||||
|
if (layers.length == 0) {
|
||||||
|
return 0D;
|
||||||
|
}
|
||||||
|
if (surfaceDetail == 1D) {
|
||||||
|
return sample(scratch, x, z);
|
||||||
|
}
|
||||||
|
double lowerX = Math.floor(x / SURFACE_GRID) * SURFACE_GRID;
|
||||||
|
double lowerZ = Math.floor(z / SURFACE_GRID) * SURFACE_GRID;
|
||||||
|
double dx = (x - lowerX) / SURFACE_GRID;
|
||||||
|
double dz = (z - lowerZ) / SURFACE_GRID;
|
||||||
|
IrisGenerator.SurfaceCacheStripe[] stripes = generator.surfaceStripes();
|
||||||
|
double northWest = lattice(scratch, stripes, lowerX, lowerZ);
|
||||||
|
if (dx == 0D && dz == 0D) {
|
||||||
|
return northWest;
|
||||||
|
}
|
||||||
|
double north = dx == 0D ? northWest
|
||||||
|
: IrisInterpolation.lerp(northWest, lattice(scratch, stripes, lowerX + SURFACE_GRID, lowerZ), dx);
|
||||||
|
double smooth = north;
|
||||||
|
if (dz != 0D) {
|
||||||
|
double southWest = lattice(scratch, stripes, lowerX, lowerZ + SURFACE_GRID);
|
||||||
|
double south = dx == 0D ? southWest
|
||||||
|
: IrisInterpolation.lerp(southWest, lattice(scratch, stripes, lowerX + SURFACE_GRID, lowerZ + SURFACE_GRID), dx);
|
||||||
|
smooth = IrisInterpolation.lerp(north, south, dz);
|
||||||
|
}
|
||||||
|
return surfaceDetail == 0D ? smooth : smooth + (sample(scratch, x, z) - smooth) * surfaceDetail;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double lattice(TerrainBatchScratch scratch, IrisGenerator.SurfaceCacheStripe[] stripes, double x, double z) {
|
||||||
|
if (x != (int) x || z != (int) z) {
|
||||||
|
return sample(scratch, x, z);
|
||||||
|
}
|
||||||
|
int mark = scratch.mark();
|
||||||
|
double[] cached = scratch.buffer(1);
|
||||||
|
boolean hit = IrisGenerator.cachedSurface(stripes, x, z, superSeed, cached, 0);
|
||||||
|
double value = cached[0];
|
||||||
|
scratch.reset(mark);
|
||||||
|
if (hit) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
double height = sample(scratch, x, z);
|
||||||
|
IrisGenerator.storeSurface(stripes, x, z, superSeed, height);
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double sample(TerrainBatchScratch scratch, double x, double z) {
|
||||||
|
double sampleX = (x + offsetX) / zoom;
|
||||||
|
double sampleZ = (z + offsetZ) / zoom;
|
||||||
|
double value = multiplicitive ? 1D : 0D;
|
||||||
|
for (NoiseLayerProgram layer : layers) {
|
||||||
|
double noise = layer.sample(scratch, sampleX, sampleZ);
|
||||||
|
value = multiplicitive ? value * noise : value + noise;
|
||||||
|
}
|
||||||
|
value = multiplicitive ? value * opacity : (value / totalOpacity) * opacity;
|
||||||
|
if (Double.isNaN(value)) {
|
||||||
|
value = 0D;
|
||||||
|
}
|
||||||
|
if (cliffs != null) {
|
||||||
|
double height = IrisInterpolation.lerp(cliffHeightMin, cliffHeightMax, cliffs.sample(scratch, sampleX, sampleZ));
|
||||||
|
value = (Math.round((value * 255D) / height) * height) / 255D;
|
||||||
|
}
|
||||||
|
return cellCracks ? generator.cell(x, z, value, 0D) : value;
|
||||||
|
}
|
||||||
|
|
||||||
private void surfaceHeights(TerrainBatchScratch scratch, double[] xs, double[] zs, int count, double[] out) {
|
private void surfaceHeights(TerrainBatchScratch scratch, double[] xs, double[] zs, int count, double[] out) {
|
||||||
int mark = scratch.mark();
|
int mark = scratch.mark();
|
||||||
int indexMark = scratch.indexMark();
|
int indexMark = scratch.indexMark();
|
||||||
|
|||||||
@@ -125,6 +125,53 @@ public final class NoiseLayerProgram {
|
|||||||
scratch.reset(mark);
|
scratch.reset(mark);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double sample(TerrainBatchScratch scratch, double x, double z) {
|
||||||
|
if (!enabled) {
|
||||||
|
return offsetY;
|
||||||
|
}
|
||||||
|
double sampleX = x;
|
||||||
|
double sampleZ = z;
|
||||||
|
if (children.length > 0) {
|
||||||
|
int mark = scratch.mark();
|
||||||
|
double[] pair = scratch.buffer(2);
|
||||||
|
for (int child = 0; child < children.length; child++) {
|
||||||
|
children[child].samplePair(scratch, x, z, pair);
|
||||||
|
double offset = childOffsets[child];
|
||||||
|
sampleX += pair[0] - offset;
|
||||||
|
sampleZ += pair[1] - offset;
|
||||||
|
}
|
||||||
|
scratch.reset(mark);
|
||||||
|
}
|
||||||
|
return post(program.sample2D((sampleX / zoom) + offsetX, (sampleZ / zoom) + offsetZ));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void samplePair(TerrainBatchScratch scratch, double x, double z, double[] out) {
|
||||||
|
if (!enabled) {
|
||||||
|
out[0] = offsetY;
|
||||||
|
out[1] = offsetY;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double forwardX = x;
|
||||||
|
double forwardZ = z;
|
||||||
|
double reverseX = z;
|
||||||
|
double reverseZ = x;
|
||||||
|
if (children.length > 0) {
|
||||||
|
int mark = scratch.mark();
|
||||||
|
double[] pair = scratch.buffer(2);
|
||||||
|
for (int child = 0; child < children.length; child++) {
|
||||||
|
children[child].samplePair(scratch, x, z, pair);
|
||||||
|
double offset = childOffsets[child];
|
||||||
|
forwardX += pair[0] - offset;
|
||||||
|
forwardZ += pair[1] - offset;
|
||||||
|
reverseX += pair[1] - offset;
|
||||||
|
reverseZ += pair[0] - offset;
|
||||||
|
}
|
||||||
|
scratch.reset(mark);
|
||||||
|
}
|
||||||
|
out[0] = post(program.sample2D((forwardX / zoom) + offsetX, (forwardZ / zoom) + offsetZ));
|
||||||
|
out[1] = post(program.sample2D((reverseX / zoom) + offsetX, (reverseZ / zoom) + offsetZ));
|
||||||
|
}
|
||||||
|
|
||||||
private double post(double raw) {
|
private double post(double raw) {
|
||||||
double n = raw * opacity;
|
double n = raw * opacity;
|
||||||
n = negative ? (-n + opacity) : n;
|
n = negative ? (-n + opacity) : n;
|
||||||
|
|||||||
@@ -2149,7 +2149,19 @@ public class IrisComplex implements DataProvider {
|
|||||||
double hi = sampledBounds.max();
|
double hi = sampledBounds.max();
|
||||||
double lo = sampledBounds.min();
|
double lo = sampledBounds.min();
|
||||||
|
|
||||||
return averageGeneratorHeights(generators, lo, hi, x, z, seed + 239945);
|
return groupHeight(interpolatorIndex, generators, lo, hi, x, z, seed + 239945);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double groupHeight(int group, IrisGenerator[] generators, double low, double high, double x, double z, long heightSeed) {
|
||||||
|
TerrainProgram program = terrainProgram;
|
||||||
|
if (program == null && terrainEngine != null) {
|
||||||
|
program = terrainProgram();
|
||||||
|
}
|
||||||
|
if (program == null || program.heightSeed() != heightSeed || group >= program.groups()
|
||||||
|
|| program.groupSize(group) != generators.length || generators != frozenGenerators[group]) {
|
||||||
|
return averageGeneratorHeights(generators, low, high, x, z, heightSeed);
|
||||||
|
}
|
||||||
|
return program.average(group, BATCH_SCRATCH.get(), low, high, x, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
private NoiseBounds gridSampleBounds(Engine engine, IrisInterpolator interpolator, int interpolatorIndex, IrisGenerator[] generators, double x, double z) {
|
private NoiseBounds gridSampleBounds(Engine engine, IrisInterpolator interpolator, int interpolatorIndex, IrisGenerator[] generators, double x, double z) {
|
||||||
@@ -2349,7 +2361,7 @@ public class IrisComplex implements DataProvider {
|
|||||||
lo = biLerp(boundsLow(b00), boundsLow(b10), boundsLow(b01), boundsLow(b11), fx, fz);
|
lo = biLerp(boundsLow(b00), boundsLow(b10), boundsLow(b01), boundsLow(b11), fx, fz);
|
||||||
hi = biLerp(boundsHigh(b00), boundsHigh(b10), boundsHigh(b01), boundsHigh(b11), fx, fz);
|
hi = biLerp(boundsHigh(b00), boundsHigh(b10), boundsHigh(b01), boundsHigh(b11), fx, fz);
|
||||||
}
|
}
|
||||||
h += averageGeneratorHeights(generators, lo, hi, x, z, heightSeed);
|
h += groupHeight(interpolatorIndex, generators, lo, hi, x, z, heightSeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
return h;
|
return h;
|
||||||
|
|||||||
@@ -39,6 +39,26 @@ public class GeneratorProgramParityTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void singlePointProgramHeightsMatchScalarHeightsBitForBit() {
|
||||||
|
SplittableRandom random = new SplittableRandom(0xFEEDL);
|
||||||
|
TerrainBatchScratch scratch = new TerrainBatchScratch();
|
||||||
|
for (int generatorIndex = 0; generatorIndex < 160; generatorIndex++) {
|
||||||
|
long shapeSeed = random.nextLong();
|
||||||
|
IrisGenerator scalar = generator(new SplittableRandom(shapeSeed));
|
||||||
|
IrisGenerator compiled = generator(new SplittableRandom(shapeSeed));
|
||||||
|
long superSeed = random.nextLong();
|
||||||
|
GeneratorProgram program = compiled.compileProgram(superSeed);
|
||||||
|
for (int sample = 0; sample < 400; sample++) {
|
||||||
|
double x = coordinate(random);
|
||||||
|
double z = random.nextInt(5) == 0 ? x : coordinate(random);
|
||||||
|
assertEquals("generator " + generatorIndex + " point " + x + "," + z,
|
||||||
|
Double.doubleToRawLongBits(scalar.getHeight(x, z, superSeed)),
|
||||||
|
Double.doubleToRawLongBits(program.height(scratch, x, z)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void chunkGridsMatchScalarHeightsBitForBit() {
|
public void chunkGridsMatchScalarHeightsBitForBit() {
|
||||||
SplittableRandom random = new SplittableRandom(0xC0FFEEL);
|
SplittableRandom random = new SplittableRandom(0xC0FFEEL);
|
||||||
|
|||||||
Reference in new issue
Block a user