mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 05:50:39 +00:00
Merge W3-polish
This commit is contained in:
commit
e4f20a7f7f
7 files changed
+215
-11
No files matched your search
+33
-10
@@ -304,7 +304,12 @@ public class IrisDepositGenerator {
|
|||||||
return new ClumpFootprint(cells.extentX * 2 + 1, cells.minY, false);
|
return new ClumpFootprint(cells.extentX * 2 + 1, cells.minY, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void vanillaEllipsoidCells(RNG rng, int size, CellSink sink) {
|
/**
|
||||||
|
* Emits the clump's cells sphere by sphere in x, y, z order, duplicates included. The squared y and z
|
||||||
|
* offsets are computed once per sphere row; the sums keep the per-cell evaluation order, so every cell
|
||||||
|
* test is bit-identical to evaluating {@code nx * nx + ny * ny + nz * nz} in place.
|
||||||
|
*/
|
||||||
|
static void vanillaEllipsoidCells(RNG rng, int size, CellSink sink) {
|
||||||
float angle = rng.nextFloat() * (float) Math.PI;
|
float angle = rng.nextFloat() * (float) Math.PI;
|
||||||
float reach = size / 8F;
|
float reach = size / 8F;
|
||||||
double startX = Math.sin(angle) * reach;
|
double startX = Math.sin(angle) * reach;
|
||||||
@@ -347,6 +352,8 @@ public class IrisDepositGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double[] squaredY = new double[16];
|
||||||
|
double[] squaredZ = new double[16];
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
double radius = nodes[i * 4 + 3];
|
double radius = nodes[i * 4 + 3];
|
||||||
if (radius < 0D) {
|
if (radius < 0D) {
|
||||||
@@ -361,21 +368,37 @@ public class IrisDepositGenerator {
|
|||||||
int maxY = Math.max((int) Math.floor(centerY + radius), minY);
|
int maxY = Math.max((int) Math.floor(centerY + radius), minY);
|
||||||
int minZ = (int) Math.floor(centerZ - radius);
|
int minZ = (int) Math.floor(centerZ - radius);
|
||||||
int maxZ = Math.max((int) Math.floor(centerZ + radius), minZ);
|
int maxZ = Math.max((int) Math.floor(centerZ + radius), minZ);
|
||||||
|
int spanY = maxY - minY + 1;
|
||||||
|
int spanZ = maxZ - minZ + 1;
|
||||||
|
if (spanY > squaredY.length) {
|
||||||
|
squaredY = new double[spanY];
|
||||||
|
}
|
||||||
|
if (spanZ > squaredZ.length) {
|
||||||
|
squaredZ = new double[spanZ];
|
||||||
|
}
|
||||||
|
for (int y = 0; y < spanY; y++) {
|
||||||
|
double ny = (minY + y + 0.5D - centerY) / radius;
|
||||||
|
squaredY[y] = ny * ny;
|
||||||
|
}
|
||||||
|
for (int z = 0; z < spanZ; z++) {
|
||||||
|
double nz = (minZ + z + 0.5D - centerZ) / radius;
|
||||||
|
squaredZ[z] = nz * nz;
|
||||||
|
}
|
||||||
|
|
||||||
for (int x = minX; x <= maxX; x++) {
|
for (int x = minX; x <= maxX; x++) {
|
||||||
double nx = (x + 0.5D - centerX) / radius;
|
double nx = (x + 0.5D - centerX) / radius;
|
||||||
if (nx * nx >= 1D) {
|
double squaredX = nx * nx;
|
||||||
|
if (squaredX >= 1D) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (int y = minY; y <= maxY; y++) {
|
for (int y = 0; y < spanY; y++) {
|
||||||
double ny = (y + 0.5D - centerY) / radius;
|
double squaredXY = squaredX + squaredY[y];
|
||||||
if (nx * nx + ny * ny >= 1D) {
|
if (squaredXY >= 1D) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (int z = minZ; z <= maxZ; z++) {
|
for (int z = 0; z < spanZ; z++) {
|
||||||
double nz = (z + 0.5D - centerZ) / radius;
|
if (squaredXY + squaredZ[z] < 1D) {
|
||||||
if (nx * nx + ny * ny + nz * nz < 1D) {
|
sink.add(x, minY + y, minZ + z);
|
||||||
sink.add(x, y, z);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -601,7 +624,7 @@ public class IrisDepositGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
private interface CellSink {
|
interface CellSink {
|
||||||
void add(int x, int y, int z);
|
void add(int x, int y, int z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -537,6 +537,14 @@ public final class HydrologyTileCache implements AutoCloseable {
|
|||||||
chunkColumns(blockX, blockZ);
|
chunkColumns(blockX, blockZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Every column of the block's chunk, indexed {@code localZ * 16 + localX}; a column without hydrology
|
||||||
|
* is null. The array is shared with the cache and must not be modified.
|
||||||
|
*/
|
||||||
|
public HydrologyColumnSample[] chunkSamples(int blockX, int blockZ) {
|
||||||
|
return chunkColumns(blockX, blockZ).columns();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the column's chunk can be answered without planning: every tile it composes from is
|
* Whether the column's chunk can be answered without planning: every tile it composes from is
|
||||||
* already planned. A caller that must not wait (the server thread answering a height or biome
|
* already planned. A caller that must not wait (the server thread answering a height or biome
|
||||||
|
|||||||
+5
@@ -195,6 +195,11 @@ public final class IrisHydrologyRuntime implements AutoCloseable {
|
|||||||
cache.prepareChunkColumns(blockX, blockZ);
|
cache.prepareChunkColumns(blockX, blockZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The block's chunk columns indexed {@code localZ * 16 + localX}; shared, never modified by callers. */
|
||||||
|
public HydrologyColumnSample[] chunkSamples(int blockX, int blockZ) {
|
||||||
|
return cache.chunkSamples(blockX, blockZ);
|
||||||
|
}
|
||||||
|
|
||||||
/** Whether the chunk or one of its eight neighbours holds a planned footprint column. */
|
/** Whether the chunk or one of its eight neighbours holds a planned footprint column. */
|
||||||
public boolean hasColumnsAround(int chunkX, int chunkZ) {
|
public boolean hasColumnsAround(int chunkX, int chunkZ) {
|
||||||
return cache.hasColumnsAround(chunkX, chunkZ);
|
return cache.hasColumnsAround(chunkX, chunkZ);
|
||||||
|
|||||||
@@ -1062,6 +1062,31 @@ public class IrisComplex implements DataProvider {
|
|||||||
return transitionHydrologySample(sample, hydrologyWeight, transitionDisplacement);
|
return transitionHydrologySample(sample, hydrologyWeight, transitionDisplacement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The raw hydrology columns of the chunk holding the block, for a caller that samples every column of
|
||||||
|
* one chunk through {@link #sampleHydrologyColumn(HydrologyColumnSample[], int, int)}; null without
|
||||||
|
* hydrology.
|
||||||
|
*/
|
||||||
|
public HydrologyColumnSample[] hydrologyChunkSamples(int x, int z) {
|
||||||
|
return hydrologyRuntime == null ? null : hydrologyRuntime.chunkSamples(x, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@link #sampleHydrologyColumn(double, double)} for a column of the chunk {@code chunkSamples} came from. */
|
||||||
|
public HydrologyColumnSample sampleHydrologyColumn(HydrologyColumnSample[] chunkSamples, int x, int z) {
|
||||||
|
if (chunkSamples == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
HydrologyColumnSample sample = chunkSamples[(z & 15) << 4 | (x & 15)];
|
||||||
|
if (transitionGenerationPlan == null) {
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
double hydrologyWeight = transitionHydrologyWeight(x, z);
|
||||||
|
if (sample == null) {
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
return transitionHydrologySample(sample, hydrologyWeight, transitionDisplacement);
|
||||||
|
}
|
||||||
|
|
||||||
private HydrologyColumnSnapshot sampleHydrologySnapshot(int x, int z) {
|
private HydrologyColumnSnapshot sampleHydrologySnapshot(int x, int z) {
|
||||||
if (hydrologyRuntime == null) {
|
if (hydrologyRuntime == null) {
|
||||||
return HydrologyColumnSnapshot.ready(null);
|
return HydrologyColumnSnapshot.ready(null);
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<NativeBloc
|
|||||||
private final IrisData data;
|
private final IrisData data;
|
||||||
private final IrisComplex complex;
|
private final IrisComplex complex;
|
||||||
private final ProceduralStream<Double> riverWaterSurfaceStream;
|
private final ProceduralStream<Double> riverWaterSurfaceStream;
|
||||||
|
private final HydrologyColumnSample[] hydrologySamples;
|
||||||
private final boolean bedrockEnabled;
|
private final boolean bedrockEnabled;
|
||||||
private final boolean hideOres;
|
private final boolean hideOres;
|
||||||
private final boolean transitionDisplaced;
|
private final boolean transitionDisplaced;
|
||||||
@@ -186,6 +187,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<NativeBloc
|
|||||||
data = getData();
|
data = getData();
|
||||||
complex = getComplex();
|
complex = getComplex();
|
||||||
riverWaterSurfaceStream = complex.getRiverWaterSurfaceStream();
|
riverWaterSurfaceStream = complex.getRiverWaterSurfaceStream();
|
||||||
|
hydrologySamples = complex.hydrologyChunkSamples(x, z);
|
||||||
bedrockEnabled = dimension.isBedrock();
|
bedrockEnabled = dimension.isBedrock();
|
||||||
hideOres = dimension.isHideOresForHiddenOre();
|
hideOres = dimension.isHideOresForHiddenOre();
|
||||||
transitionDisplaced = complex.getTransitionDisplacement() != null;
|
transitionDisplaced = complex.getTransitionDisplacement() != null;
|
||||||
@@ -226,7 +228,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<NativeBloc
|
|||||||
}
|
}
|
||||||
|
|
||||||
int topY = Math.min(hf, chunkHeight - 1);
|
int topY = Math.min(hf, chunkHeight - 1);
|
||||||
HydrologyColumnSample hydrology = complex.sampleHydrologyColumn(realX, realZ);
|
HydrologyColumnSample hydrology = complex.sampleHydrologyColumn(hydrologySamples, realX, realZ);
|
||||||
HydrologyColumnLayer hydrologyFluid = hydrology == null
|
HydrologyColumnLayer hydrologyFluid = hydrology == null
|
||||||
? null
|
? null
|
||||||
: hydrology.primarySurfaceFluidLayerOrNull();
|
: hydrology.primarySurfaceFluidLayerOrNull();
|
||||||
|
|||||||
+113
@@ -0,0 +1,113 @@
|
|||||||
|
package art.arcane.iris.generation.decoration;
|
||||||
|
|
||||||
|
import art.arcane.volmlib.util.math.RNG;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class IrisDepositEllipsoidCellsTest {
|
||||||
|
@Test
|
||||||
|
public void cellsAndDrawsMatchThePerCellEvaluation() {
|
||||||
|
int[] sizes = {1, 2, 3, 4, 5, 7, 8, 9, 12, 16, 17, 24, 32, 33, 48, 64, 96, 200};
|
||||||
|
for (int size : sizes) {
|
||||||
|
long seeds = size <= 64 ? 300L : 8L;
|
||||||
|
for (long seed = 0L; seed < seeds; seed++) {
|
||||||
|
long mixedSeed = seed * 0x9E3779B97F4A7C15L + size;
|
||||||
|
RNG expectedRng = new RNG(mixedSeed);
|
||||||
|
RNG actualRng = new RNG(mixedSeed);
|
||||||
|
List<Long> expected = new ArrayList<>();
|
||||||
|
List<Long> actual = new ArrayList<>();
|
||||||
|
referenceCells(expectedRng, size, (x, y, z) -> expected.add(pack(x, y, z)));
|
||||||
|
IrisDepositGenerator.vanillaEllipsoidCells(actualRng, size, (x, y, z) -> actual.add(pack(x, y, z)));
|
||||||
|
assertEquals("size " + size + " seed " + seed, expected, actual);
|
||||||
|
assertEquals("size " + size + " seed " + seed, expectedRng.nextLong(), actualRng.nextLong());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long pack(int x, int y, int z) {
|
||||||
|
return ((long) (x & 0x1FFFFF) << 42) | ((long) (y & 0x1FFFFF) << 21) | (z & 0x1FFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void referenceCells(RNG rng, int size, IrisDepositGenerator.CellSink sink) {
|
||||||
|
float angle = rng.nextFloat() * (float) Math.PI;
|
||||||
|
float reach = size / 8F;
|
||||||
|
double startX = Math.sin(angle) * reach;
|
||||||
|
double endX = -Math.sin(angle) * reach;
|
||||||
|
double startZ = Math.cos(angle) * reach;
|
||||||
|
double endZ = -Math.cos(angle) * reach;
|
||||||
|
double startY = rng.nextInt(3) - 2;
|
||||||
|
double endY = rng.nextInt(3) - 2;
|
||||||
|
double[] nodes = new double[size * 4];
|
||||||
|
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
float progress = (float) i / size;
|
||||||
|
double radiusNoise = rng.nextDouble() * size / 16D;
|
||||||
|
nodes[i * 4] = startX + (endX - startX) * progress;
|
||||||
|
nodes[i * 4 + 1] = startY + (endY - startY) * progress;
|
||||||
|
nodes[i * 4 + 2] = startZ + (endZ - startZ) * progress;
|
||||||
|
nodes[i * 4 + 3] = ((Math.sin(Math.PI * progress) + 1D) * radiusNoise + 1D) / 2D;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < size - 1; i++) {
|
||||||
|
if (nodes[i * 4 + 3] <= 0D) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int j = i + 1; j < size; j++) {
|
||||||
|
if (nodes[j * 4 + 3] <= 0D) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
double dx = nodes[i * 4] - nodes[j * 4];
|
||||||
|
double dy = nodes[i * 4 + 1] - nodes[j * 4 + 1];
|
||||||
|
double dz = nodes[i * 4 + 2] - nodes[j * 4 + 2];
|
||||||
|
double dr = nodes[i * 4 + 3] - nodes[j * 4 + 3];
|
||||||
|
if (dr * dr <= dx * dx + dy * dy + dz * dz) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (dr > 0D) {
|
||||||
|
nodes[j * 4 + 3] = -1D;
|
||||||
|
} else {
|
||||||
|
nodes[i * 4 + 3] = -1D;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
double radius = nodes[i * 4 + 3];
|
||||||
|
if (radius < 0D) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
double centerX = nodes[i * 4];
|
||||||
|
double centerY = nodes[i * 4 + 1];
|
||||||
|
double centerZ = nodes[i * 4 + 2];
|
||||||
|
int minX = (int) Math.floor(centerX - radius);
|
||||||
|
int maxX = Math.max((int) Math.floor(centerX + radius), minX);
|
||||||
|
int minY = (int) Math.floor(centerY - radius);
|
||||||
|
int maxY = Math.max((int) Math.floor(centerY + radius), minY);
|
||||||
|
int minZ = (int) Math.floor(centerZ - radius);
|
||||||
|
int maxZ = Math.max((int) Math.floor(centerZ + radius), minZ);
|
||||||
|
|
||||||
|
for (int x = minX; x <= maxX; x++) {
|
||||||
|
double nx = (x + 0.5D - centerX) / radius;
|
||||||
|
if (nx * nx >= 1D) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int y = minY; y <= maxY; y++) {
|
||||||
|
double ny = (y + 0.5D - centerY) / radius;
|
||||||
|
if (nx * nx + ny * ny >= 1D) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int z = minZ; z <= maxZ; z++) {
|
||||||
|
double nz = (z + 0.5D - centerZ) / radius;
|
||||||
|
if (nx * nx + ny * ny + nz * nz < 1D) {
|
||||||
|
sink.add(x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1125,6 +1125,34 @@ public class HydrologyTileCacheTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void chunkSamplesMatchPerColumnLookups() {
|
||||||
|
HydrologyTileCache perColumn = new HydrologyTileCache(new HydrologyPlanner(811L, featureSettings(), this::featureTerrain), 8);
|
||||||
|
HydrologyTileCache perChunk = new HydrologyTileCache(new HydrologyPlanner(811L, featureSettings(), this::featureTerrain), 8);
|
||||||
|
HydrologyColumnSample plannedColumn = perColumn.get(new HydrologyTileKey(0, 0)).footprint().columns().values().stream()
|
||||||
|
.filter(HydrologyColumnSample::present)
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow();
|
||||||
|
int centreChunkX = Math.floorDiv(plannedColumn.x(), 16);
|
||||||
|
int centreChunkZ = Math.floorDiv(plannedColumn.z(), 16);
|
||||||
|
int present = 0;
|
||||||
|
for (int chunkZ = centreChunkZ - 2; chunkZ <= centreChunkZ + 2; chunkZ++) {
|
||||||
|
for (int chunkX = centreChunkX - 2; chunkX <= centreChunkX + 2; chunkX++) {
|
||||||
|
HydrologyColumnSample[] samples = perChunk.chunkSamples(chunkX * 16 + 5, chunkZ * 16 + 11);
|
||||||
|
assertEquals(256, samples.length);
|
||||||
|
for (int localZ = 0; localZ < 16; localZ++) {
|
||||||
|
for (int localX = 0; localX < 16; localX++) {
|
||||||
|
HydrologyColumnSample expected = perColumn
|
||||||
|
.columnAt(chunkX * 16 + localX, chunkZ * 16 + localZ).orElse(null);
|
||||||
|
assertEquals(expected, samples[localZ * 16 + localX]);
|
||||||
|
present += expected == null ? 0 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(present > 0);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void singleColumnContributionReusesItsImmutableSample() {
|
public void singleColumnContributionReusesItsImmutableSample() {
|
||||||
HydrologyColumnSample sample = compositionSample(90, List.of(compositionLayer(11L, 80)));
|
HydrologyColumnSample sample = compositionSample(90, List.of(compositionLayer(11L, 80)));
|
||||||
|
|||||||
Reference in new issue
Block a user