mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-11-01 08:50:06 +00:00
Merge branch 'perf-plan' into perf-review
This commit is contained in:
commit
8e625d4547
20 files changed
+457
-296
No files matched your search
+5
@@ -348,6 +348,11 @@ public final class ModdedPregenMethod implements PregeneratorMethod {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRegionBounds(int minRegionX, int minRegionZ, int maxRegionX, int maxRegionZ) {
|
||||||
|
hydrology.bound(minRegionX, minRegionZ, maxRegionX, maxRegionZ);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRegionsAhead(List<long[]> regions) {
|
public void onRegionsAhead(List<long[]> regions) {
|
||||||
if (!closing.get()) {
|
if (!closing.get()) {
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
package art.arcane.iris.generation.hydrology.engine;
|
|
||||||
|
|
||||||
import art.arcane.iris.generation.hydrology.grid.HeightLattice;
|
|
||||||
import art.arcane.iris.generation.hydrology.grid.HydrologySampleBuffer;
|
|
||||||
import art.arcane.iris.generation.hydrology.grid.HydrologyTerrainSource;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
final class FineLatticeSource implements HydrologyTerrainSource {
|
|
||||||
private final HeightLattice fine;
|
|
||||||
private final HydrologyTerrainSource terrain;
|
|
||||||
|
|
||||||
FineLatticeSource(HeightLattice fine, HydrologyTerrainSource terrain) {
|
|
||||||
this.fine = Objects.requireNonNull(fine, "fine");
|
|
||||||
this.terrain = Objects.requireNonNull(terrain, "terrain");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sample(int[] xs, int[] zs, HydrologySampleBuffer out, boolean baseOnly) {
|
|
||||||
if (baseOnly) {
|
|
||||||
throw new IllegalArgumentException("The fine lattice holds natural heights only");
|
|
||||||
}
|
|
||||||
if (xs.length != zs.length) {
|
|
||||||
throw new IllegalArgumentException("Fine lattice read has " + xs.length + " x and " + zs.length + " z coordinates");
|
|
||||||
}
|
|
||||||
int cell = fine.cell();
|
|
||||||
int[] cellXs = new int[xs.length];
|
|
||||||
int[] cellZs = new int[zs.length];
|
|
||||||
for (int index = 0; index < xs.length; index++) {
|
|
||||||
if (Math.floorMod(xs[index], cell) != 0 || Math.floorMod(zs[index], cell) != 0) {
|
|
||||||
throw new IllegalArgumentException("Column " + xs[index] + "," + zs[index] + " is not a node of the " + cell + "-block fine lattice");
|
|
||||||
}
|
|
||||||
cellXs[index] = Math.floorDiv(xs[index], cell);
|
|
||||||
cellZs[index] = Math.floorDiv(zs[index], cell);
|
|
||||||
}
|
|
||||||
fine.fill(cellXs, cellZs, out);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int seaLevel() {
|
|
||||||
return terrain.seaLevel();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int minY() {
|
|
||||||
return terrain.minY();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int maxY() {
|
|
||||||
return terrain.maxY();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+3
-5
@@ -11,23 +11,21 @@ import art.arcane.iris.generation.hydrology.water.ReachShaper;
|
|||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public record HydrologyPlanners(RegionPlanner regions, WindowPlanner windows, HeightLattice windowLattice) {
|
public record HydrologyPlanners(RegionPlanner regions, WindowPlanner windows, HeightLattice fineLattice) {
|
||||||
public HydrologyPlanners {
|
public HydrologyPlanners {
|
||||||
Objects.requireNonNull(regions, "regions");
|
Objects.requireNonNull(regions, "regions");
|
||||||
Objects.requireNonNull(windows, "windows");
|
Objects.requireNonNull(windows, "windows");
|
||||||
Objects.requireNonNull(windowLattice, "windowLattice");
|
Objects.requireNonNull(fineLattice, "fineLattice");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HydrologyPlanners create(HydrologySettings settings, HydrologyTerrainSource source) {
|
public static HydrologyPlanners create(HydrologySettings settings, HydrologyTerrainSource source) {
|
||||||
Objects.requireNonNull(settings, "settings");
|
Objects.requireNonNull(settings, "settings");
|
||||||
Objects.requireNonNull(source, "source");
|
Objects.requireNonNull(source, "source");
|
||||||
HeightLattice fine = new HeightLattice(source, WindowPlanner.FINE_CELL, false, HydrologyPlanCache.FINE_LATTICE_CELLS);
|
HeightLattice fine = new HeightLattice(source, WindowPlanner.FINE_CELL, false, HydrologyPlanCache.FINE_LATTICE_CELLS);
|
||||||
HeightLattice window = new HeightLattice(new FineLatticeSource(fine, source), WindowPlanner.CELL, false,
|
|
||||||
HydrologyPlanCache.WINDOW_LATTICE_CELLS);
|
|
||||||
PondPlanner ponds = new PondPlanner(settings);
|
PondPlanner ponds = new PondPlanner(settings);
|
||||||
ReachShaper shaper = new ReachShaper(settings);
|
ReachShaper shaper = new ReachShaper(settings);
|
||||||
RegionPlanner regions = new RegionPlanner(settings,
|
RegionPlanner regions = new RegionPlanner(settings,
|
||||||
new HeightLattice(source, RegionPlanner.CELL, true, HydrologyPlanCache.REGION_LATTICE_CELLS), fine, ponds, shaper);
|
new HeightLattice(source, RegionPlanner.CELL, true, HydrologyPlanCache.REGION_LATTICE_CELLS), fine, ponds, shaper);
|
||||||
return new HydrologyPlanners(regions, new WindowPlanner(settings, window, fine, ponds, shaper), window);
|
return new HydrologyPlanners(regions, new WindowPlanner(settings, fine, ponds, shaper), fine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+6
@@ -18,6 +18,12 @@ public final class HydrologyPregeneration implements AutoCloseable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized void bound(int minRegionX, int minRegionZ, int maxRegionX, int maxRegionZ) {
|
||||||
|
if (!closed) {
|
||||||
|
runtime.boundPregeneration(epoch, minRegionX, minRegionZ, maxRegionX, maxRegionZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void close() {
|
public synchronized void close() {
|
||||||
if (!closed) {
|
if (!closed) {
|
||||||
|
|||||||
+53
-1
@@ -36,6 +36,7 @@ import java.io.UncheckedIOException;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -77,6 +78,7 @@ public final class HydrologyRuntime {
|
|||||||
private final RasterFailures regionPreviewFailures;
|
private final RasterFailures regionPreviewFailures;
|
||||||
private final Set<Object> previews;
|
private final Set<Object> previews;
|
||||||
private final LinkedHashMap<WindowKey, WindowPrefetch> prefetches;
|
private final LinkedHashMap<WindowKey, WindowPrefetch> prefetches;
|
||||||
|
private final Map<Object, PregenArea> pregenAreas;
|
||||||
private final HydrologyBinding binding;
|
private final HydrologyBinding binding;
|
||||||
private final Function<PreparedHydrologyCacheIdentity, HydrologyPlanStore> stores;
|
private final Function<PreparedHydrologyCacheIdentity, HydrologyPlanStore> stores;
|
||||||
private final boolean caveFluidPolicies;
|
private final boolean caveFluidPolicies;
|
||||||
@@ -99,6 +101,7 @@ public final class HydrologyRuntime {
|
|||||||
this.regionPreviewFailures = null;
|
this.regionPreviewFailures = null;
|
||||||
this.previews = null;
|
this.previews = null;
|
||||||
this.prefetches = null;
|
this.prefetches = null;
|
||||||
|
this.pregenAreas = null;
|
||||||
this.binding = HydrologyBinding.UNBOUND;
|
this.binding = HydrologyBinding.UNBOUND;
|
||||||
this.stores = identity -> null;
|
this.stores = identity -> null;
|
||||||
this.caveFluidPolicies = false;
|
this.caveFluidPolicies = false;
|
||||||
@@ -125,6 +128,7 @@ public final class HydrologyRuntime {
|
|||||||
this.regionPreviewFailures = new RasterFailures(ticker);
|
this.regionPreviewFailures = new RasterFailures(ticker);
|
||||||
this.previews = new HashSet<>();
|
this.previews = new HashSet<>();
|
||||||
this.prefetches = new LinkedHashMap<>(PREFETCH_CAPACITY * 2, 0.75f, true);
|
this.prefetches = new LinkedHashMap<>(PREFETCH_CAPACITY * 2, 0.75f, true);
|
||||||
|
this.pregenAreas = new HashMap<>();
|
||||||
this.binding = Objects.requireNonNull(binding, "binding");
|
this.binding = Objects.requireNonNull(binding, "binding");
|
||||||
this.stores = Objects.requireNonNull(stores, "stores");
|
this.stores = Objects.requireNonNull(stores, "stores");
|
||||||
this.caveFluidPolicies = caveFluidPolicies;
|
this.caveFluidPolicies = caveFluidPolicies;
|
||||||
@@ -354,8 +358,28 @@ public final class HydrologyRuntime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void boundPregeneration(Object epoch, int minRegionX, int minRegionZ, int maxRegionX, int maxRegionZ) {
|
||||||
|
Objects.requireNonNull(epoch, "epoch");
|
||||||
|
if (maxRegionX < minRegionX || maxRegionZ < minRegionZ) {
|
||||||
|
throw new IllegalArgumentException("Invalid pregeneration bounds " + minRegionX + "," + minRegionZ + " to " + maxRegionX + ","
|
||||||
|
+ maxRegionZ);
|
||||||
|
}
|
||||||
|
if (cache == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
synchronized (prefetches) {
|
||||||
|
pregenAreas.put(epoch, new PregenArea(minRegionX - 1, minRegionZ - 1, maxRegionX + 1, maxRegionZ + 1));
|
||||||
|
prefetches.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void cancelPregeneration(Object epoch) {
|
void cancelPregeneration(Object epoch) {
|
||||||
if (cache != null) {
|
if (cache != null) {
|
||||||
|
synchronized (prefetches) {
|
||||||
|
if (pregenAreas.remove(epoch) != null) {
|
||||||
|
prefetches.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
cache.cancel(epoch);
|
cache.cancel(epoch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -667,7 +691,7 @@ public final class HydrologyRuntime {
|
|||||||
&& (!known.prepared().isDone() || known.evictions() == evictions)) {
|
&& (!known.prepared().isDone() || known.evictions() == evictions)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CompletableFuture<Void> prepared = cache.prepareWindowsAround(chunkX, chunkZ, chunkX, chunkZ, 1 + settings.prefetchRings());
|
CompletableFuture<Void> prepared = prepareAround(chunkX, chunkZ, owner, 1 + settings.prefetchRings());
|
||||||
prefetches.put(owner, new WindowPrefetch(prepared, evictions));
|
prefetches.put(owner, new WindowPrefetch(prepared, evictions));
|
||||||
if (prefetches.size() > PREFETCH_CAPACITY) {
|
if (prefetches.size() > PREFETCH_CAPACITY) {
|
||||||
prefetches.pollFirstEntry();
|
prefetches.pollFirstEntry();
|
||||||
@@ -675,6 +699,24 @@ public final class HydrologyRuntime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CompletableFuture<Void> prepareAround(int chunkX, int chunkZ, WindowKey owner, int rings) {
|
||||||
|
PregenArea covering = null;
|
||||||
|
for (PregenArea area : pregenAreas.values()) {
|
||||||
|
if (area.contains(owner)) {
|
||||||
|
covering = covering == null ? area : covering.union(area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (covering == null) {
|
||||||
|
return cache.prepareWindowsAround(chunkX, chunkZ, chunkX, chunkZ, rings);
|
||||||
|
}
|
||||||
|
int minX = Math.max(owner.wx() - rings, covering.minX());
|
||||||
|
int minZ = Math.max(owner.wz() - rings, covering.minZ());
|
||||||
|
int maxX = Math.min(owner.wx() + rings, covering.maxX());
|
||||||
|
int maxZ = Math.min(owner.wz() + rings, covering.maxZ());
|
||||||
|
return cache.prepareWindowsAround(minX * CHUNKS_PER_WINDOW, minZ * CHUNKS_PER_WINDOW, (maxX + 1) * CHUNKS_PER_WINDOW - 1,
|
||||||
|
(maxZ + 1) * CHUNKS_PER_WINDOW - 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
private static List<WindowKey> neighbourhood(int chunkX, int chunkZ) {
|
private static List<WindowKey> neighbourhood(int chunkX, int chunkZ) {
|
||||||
WindowKey own = WindowKey.containing(chunkX << 4, chunkZ << 4);
|
WindowKey own = WindowKey.containing(chunkX << 4, chunkZ << 4);
|
||||||
List<WindowKey> keys = new ArrayList<>(9);
|
List<WindowKey> keys = new ArrayList<>(9);
|
||||||
@@ -754,6 +796,16 @@ public final class HydrologyRuntime {
|
|||||||
private record WindowPrefetch(CompletableFuture<Void> prepared, long evictions) {
|
private record WindowPrefetch(CompletableFuture<Void> prepared, long evictions) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private record PregenArea(int minX, int minZ, int maxX, int maxZ) {
|
||||||
|
private boolean contains(WindowKey key) {
|
||||||
|
return key.wx() > minX && key.wx() < maxX && key.wz() > minZ && key.wz() < maxZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PregenArea union(PregenArea other) {
|
||||||
|
return new PregenArea(Math.min(minX, other.minX), Math.min(minZ, other.minZ), Math.max(maxX, other.maxX), Math.max(maxZ, other.maxZ));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final class LastRaster {
|
private static final class LastRaster {
|
||||||
private long key;
|
private long key;
|
||||||
private ChunkWaterRaster raster;
|
private ChunkWaterRaster raster;
|
||||||
|
|||||||
@@ -1,34 +1,59 @@
|
|||||||
package art.arcane.iris.generation.hydrology.grid;
|
package art.arcane.iris.generation.hydrology.grid;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
import java.util.concurrent.atomic.AtomicReferenceArray;
|
||||||
|
|
||||||
public final class HeightLattice {
|
public final class HeightLattice {
|
||||||
|
private static final int TILE_SHIFT = 5;
|
||||||
|
private static final int COLUMN_TILE_SHIFT = 3;
|
||||||
|
private static final int COLUMN_CELLS = 1 << 20;
|
||||||
|
private static final CompletableFuture<Void> READY = CompletableFuture.completedFuture(null);
|
||||||
|
private static final int[] NO_NODES = new int[0];
|
||||||
|
private static final Tile[] NO_TILES = new Tile[0];
|
||||||
|
|
||||||
private final HydrologyTerrainSource source;
|
private final HydrologyTerrainSource source;
|
||||||
private final int cell;
|
private final int cell;
|
||||||
private final boolean baseOnly;
|
private final boolean baseOnly;
|
||||||
private final int capacityCells;
|
private final int tileShift;
|
||||||
private final ConcurrentHashMap<Long, Slot> cells;
|
private final int tileMask;
|
||||||
private final ConcurrentLinkedQueue<Long> insertionOrder;
|
private final int tileCells;
|
||||||
|
private final int capacityTiles;
|
||||||
|
private final ConcurrentHashMap<Long, Tile> tiles;
|
||||||
|
private final AtomicLong clock;
|
||||||
private final AtomicBoolean evicting;
|
private final AtomicBoolean evicting;
|
||||||
|
private final HeightLattice columns;
|
||||||
|
|
||||||
public HeightLattice(HydrologyTerrainSource source, int cell, boolean baseOnly, int capacityCells) {
|
public HeightLattice(HydrologyTerrainSource source, int cell, boolean baseOnly, int capacityCells) {
|
||||||
|
this(source, cell, baseOnly, capacityCells, TILE_SHIFT, new HeightLattice(source, 1, baseOnly, COLUMN_CELLS, COLUMN_TILE_SHIFT, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private HeightLattice(HydrologyTerrainSource source, int cell, boolean baseOnly, int capacityCells, int tileShift, HeightLattice columns) {
|
||||||
if (cell <= 0 || capacityCells <= 0) {
|
if (cell <= 0 || capacityCells <= 0) {
|
||||||
throw new IllegalArgumentException("Invalid lattice cell=" + cell + " capacityCells=" + capacityCells);
|
throw new IllegalArgumentException("Invalid lattice cell=" + cell + " capacityCells=" + capacityCells);
|
||||||
}
|
}
|
||||||
this.source = Objects.requireNonNull(source, "source");
|
this.source = Objects.requireNonNull(source, "source");
|
||||||
this.cell = cell;
|
this.cell = cell;
|
||||||
this.baseOnly = baseOnly;
|
this.baseOnly = baseOnly;
|
||||||
this.capacityCells = capacityCells;
|
this.tileShift = tileShift;
|
||||||
this.cells = new ConcurrentHashMap<>();
|
this.tileMask = (1 << tileShift) - 1;
|
||||||
this.insertionOrder = new ConcurrentLinkedQueue<>();
|
this.tileCells = 1 << (2 * tileShift);
|
||||||
|
this.capacityTiles = Math.ceilDiv(capacityCells, tileCells);
|
||||||
|
this.tiles = new ConcurrentHashMap<>();
|
||||||
|
this.clock = new AtomicLong();
|
||||||
this.evicting = new AtomicBoolean();
|
this.evicting = new AtomicBoolean();
|
||||||
|
this.columns = columns;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int cell() {
|
public int cell() {
|
||||||
@@ -67,19 +92,24 @@ public final class HeightLattice {
|
|||||||
throw new IllegalArgumentException("Column sample has " + xs.length + " x and " + zs.length + " z coordinates");
|
throw new IllegalArgumentException("Column sample has " + xs.length + " x and " + zs.length + " z coordinates");
|
||||||
}
|
}
|
||||||
requireCapacity(out, xs.length);
|
requireCapacity(out, xs.length);
|
||||||
if (xs.length > 0) {
|
if (xs.length == 0) {
|
||||||
source.sample(xs, zs, out, baseOnly);
|
return;
|
||||||
}
|
}
|
||||||
|
if (columns == null) {
|
||||||
|
source.sample(xs, zs, out, baseOnly);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
columns.resolve(new Scatter(xs, zs), xs.length, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resolve(Nodes nodes, int count, HydrologySampleBuffer out) {
|
private void resolve(Nodes nodes, int count, HydrologySampleBuffer out) {
|
||||||
Slot[] slots = new Slot[count];
|
Pass pass = new Pass(nodes, out, clock.incrementAndGet());
|
||||||
int unresolved = count;
|
int[] open = null;
|
||||||
while (unresolved > 0) {
|
int remaining = count;
|
||||||
CompletableFuture<HydrologySampleBuffer> batch = claimAndSample(nodes, slots);
|
while (remaining > 0) {
|
||||||
unresolved = awaitForeign(batch, slots);
|
open = pass.run(open, remaining);
|
||||||
|
remaining = open == null ? 0 : open.length;
|
||||||
}
|
}
|
||||||
copy(slots, out);
|
|
||||||
evictOverflow();
|
evictOverflow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,130 +124,33 @@ public final class HeightLattice {
|
|||||||
Math.multiplyExact(Math.addExact(cell0, Math.multiplyExact(extent - 1, stride)), cell);
|
Math.multiplyExact(Math.addExact(cell0, Math.multiplyExact(extent - 1, stride)), cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CompletableFuture<HydrologySampleBuffer> claimAndSample(Nodes nodes, Slot[] slots) {
|
private Tile tile(int tileX, int tileZ) {
|
||||||
CompletableFuture<HydrologySampleBuffer> batch = new CompletableFuture<>();
|
Long key = key(tileX, tileZ);
|
||||||
try {
|
Tile known = tiles.get(key);
|
||||||
int[] xs = new int[slots.length];
|
if (known != null) {
|
||||||
int[] zs = new int[slots.length];
|
return known;
|
||||||
int claimed = claim(batch, nodes, slots, xs, zs);
|
|
||||||
if (claimed > 0) {
|
|
||||||
HydrologySampleBuffer samples = new HydrologySampleBuffer(claimed);
|
|
||||||
source.sample(Arrays.copyOf(xs, claimed), Arrays.copyOf(zs, claimed), samples, baseOnly);
|
|
||||||
batch.complete(samples);
|
|
||||||
}
|
|
||||||
} catch (Throwable failure) {
|
|
||||||
try {
|
|
||||||
release(batch, nodes, slots);
|
|
||||||
} finally {
|
|
||||||
batch.completeExceptionally(failure);
|
|
||||||
}
|
|
||||||
throw failure;
|
|
||||||
}
|
|
||||||
return batch;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int claim(CompletableFuture<HydrologySampleBuffer> batch, Nodes nodes, Slot[] slots, int[] xs, int[] zs) {
|
|
||||||
int claimed = 0;
|
|
||||||
for (int index = 0; index < slots.length; index++) {
|
|
||||||
if (slots[index] != null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int cellX = nodes.cellX(index);
|
|
||||||
int cellZ = nodes.cellZ(index);
|
|
||||||
Long key = key(cellX, cellZ);
|
|
||||||
Slot existing = cells.get(key);
|
|
||||||
if (existing == null) {
|
|
||||||
Slot fresh = new Slot(batch, claimed);
|
|
||||||
existing = cells.putIfAbsent(key, fresh);
|
|
||||||
if (existing == null) {
|
|
||||||
slots[index] = fresh;
|
|
||||||
insertionOrder.offer(key);
|
|
||||||
xs[claimed] = cellX * cell;
|
|
||||||
zs[claimed] = cellZ * cell;
|
|
||||||
claimed++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
slots[index] = existing;
|
|
||||||
}
|
|
||||||
return claimed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void release(CompletableFuture<HydrologySampleBuffer> batch, Nodes nodes, Slot[] slots) {
|
|
||||||
for (int index = 0; index < slots.length; index++) {
|
|
||||||
Slot slot = slots[index];
|
|
||||||
if (slot != null && slot.batch() == batch) {
|
|
||||||
cells.remove(key(nodes.cellX(index), nodes.cellZ(index)), slot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int awaitForeign(CompletableFuture<HydrologySampleBuffer> own, Slot[] slots) {
|
|
||||||
int unresolved = 0;
|
|
||||||
for (int index = 0; index < slots.length; index++) {
|
|
||||||
CompletableFuture<HydrologySampleBuffer> batch = slots[index].batch();
|
|
||||||
if (batch != own && !await(batch)) {
|
|
||||||
slots[index] = null;
|
|
||||||
unresolved++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return unresolved;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean await(CompletableFuture<HydrologySampleBuffer> batch) {
|
|
||||||
if (!batch.isDone()) {
|
|
||||||
try {
|
|
||||||
batch.get();
|
|
||||||
} catch (ExecutionException | CancellationException failure) {
|
|
||||||
return false;
|
|
||||||
} catch (InterruptedException interruption) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
throw new CancellationException("Hydrology lattice wait interrupted.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return !batch.isCompletedExceptionally();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void copy(Slot[] slots, HydrologySampleBuffer out) {
|
|
||||||
float[] height = out.height();
|
|
||||||
boolean[] ocean = out.ocean();
|
|
||||||
float[] rainfall = out.rainfall();
|
|
||||||
float[] pondDensity = out.pondDensity();
|
|
||||||
byte[] permission = out.permission();
|
|
||||||
String[] fluidProfile = out.fluidProfile();
|
|
||||||
for (int index = 0; index < slots.length; index++) {
|
|
||||||
Slot slot = slots[index];
|
|
||||||
HydrologySampleBuffer samples = slot.batch().join();
|
|
||||||
int sample = slot.index();
|
|
||||||
height[index] = samples.height()[sample];
|
|
||||||
ocean[index] = samples.ocean()[sample];
|
|
||||||
rainfall[index] = samples.rainfall()[sample];
|
|
||||||
pondDensity[index] = samples.pondDensity()[sample];
|
|
||||||
permission[index] = samples.permission()[sample];
|
|
||||||
fluidProfile[index] = samples.fluidProfile()[sample];
|
|
||||||
}
|
}
|
||||||
|
return tiles.computeIfAbsent(key, ignored -> new Tile(tileCells));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void evictOverflow() {
|
private void evictOverflow() {
|
||||||
if (cells.size() <= capacityCells || !evicting.compareAndSet(false, true)) {
|
if (tiles.size() <= capacityTiles || !evicting.compareAndSet(false, true)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
int excess = cells.size() - capacityCells;
|
List<Map.Entry<Long, Tile>> idle = new ArrayList<>(tiles.size());
|
||||||
while (excess > 0) {
|
for (Map.Entry<Long, Tile> entry : tiles.entrySet()) {
|
||||||
Long key = insertionOrder.poll();
|
if (entry.getValue().pending.get() == 0) {
|
||||||
if (key == null) {
|
idle.add(entry);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
Slot slot = cells.get(key);
|
}
|
||||||
if (slot == null) {
|
idle.sort(Comparator.comparingLong(entry -> entry.getValue().used));
|
||||||
continue;
|
int target = capacityTiles - (capacityTiles >> 3);
|
||||||
}
|
for (int index = 0; index < idle.size() && tiles.size() > target; index++) {
|
||||||
if (!slot.batch().isDone()) {
|
Map.Entry<Long, Tile> entry = idle.get(index);
|
||||||
insertionOrder.offer(key);
|
Tile tile = entry.getValue();
|
||||||
excess--;
|
if (tile.pending.get() == 0) {
|
||||||
} else if (cells.remove(key, slot)) {
|
tiles.remove(entry.getKey(), tile);
|
||||||
excess--;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@@ -232,7 +165,219 @@ public final class HeightLattice {
|
|||||||
return mixed ^ (mixed >>> 31);
|
return mixed ^ (mixed >>> 31);
|
||||||
}
|
}
|
||||||
|
|
||||||
private record Slot(CompletableFuture<HydrologySampleBuffer> batch, int index) {
|
private static boolean await(CompletableFuture<Void> batch) {
|
||||||
|
if (!batch.isDone()) {
|
||||||
|
try {
|
||||||
|
batch.get();
|
||||||
|
} catch (ExecutionException | CancellationException failure) {
|
||||||
|
return false;
|
||||||
|
} catch (InterruptedException interruption) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new CancellationException("Hydrology lattice wait interrupted.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !batch.isCompletedExceptionally();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void copy(HydrologySampleBuffer samples, int sample, HydrologySampleBuffer out, int index) {
|
||||||
|
out.height()[index] = samples.height()[sample];
|
||||||
|
out.ocean()[index] = samples.ocean()[sample];
|
||||||
|
out.rainfall()[index] = samples.rainfall()[sample];
|
||||||
|
out.pondDensity()[index] = samples.pondDensity()[sample];
|
||||||
|
out.permission()[index] = samples.permission()[sample];
|
||||||
|
out.fluidProfile()[index] = samples.fluidProfile()[sample];
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class Pass {
|
||||||
|
private final Nodes nodes;
|
||||||
|
private final HydrologySampleBuffer out;
|
||||||
|
private final long stamp;
|
||||||
|
private int[] claimedIndex = NO_NODES;
|
||||||
|
private Tile[] claimedTile = NO_TILES;
|
||||||
|
private int[] claimedLocal = NO_NODES;
|
||||||
|
private int claimed;
|
||||||
|
private int[] waitingIndex = NO_NODES;
|
||||||
|
private Tile[] waitingTile = NO_TILES;
|
||||||
|
private int[] waitingLocal = NO_NODES;
|
||||||
|
private int waiting;
|
||||||
|
private Tile current;
|
||||||
|
private int currentX;
|
||||||
|
private int currentZ;
|
||||||
|
|
||||||
|
private Pass(Nodes nodes, HydrologySampleBuffer out, long stamp) {
|
||||||
|
this.nodes = nodes;
|
||||||
|
this.out = out;
|
||||||
|
this.stamp = stamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[] run(int[] open, int remaining) {
|
||||||
|
claimed = 0;
|
||||||
|
waiting = 0;
|
||||||
|
CompletableFuture<Void> batch = new CompletableFuture<>();
|
||||||
|
for (int position = 0; position < remaining; position++) {
|
||||||
|
visit(open == null ? position : open[position], batch);
|
||||||
|
}
|
||||||
|
if (claimed > 0) {
|
||||||
|
sample(batch);
|
||||||
|
}
|
||||||
|
return awaitWaiting();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void visit(int index, CompletableFuture<Void> batch) {
|
||||||
|
int cellX = nodes.cellX(index);
|
||||||
|
int cellZ = nodes.cellZ(index);
|
||||||
|
Tile tile = tileOf(cellX >> tileShift, cellZ >> tileShift);
|
||||||
|
int local = ((cellZ & tileMask) << tileShift) | (cellX & tileMask);
|
||||||
|
while (true) {
|
||||||
|
CompletableFuture<Void> mark = tile.marks.get(local);
|
||||||
|
if (mark == READY) {
|
||||||
|
tile.copy(local, out, index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mark != null) {
|
||||||
|
addWaiting(index, tile, local);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (tile.marks.compareAndSet(local, null, batch)) {
|
||||||
|
tile.pending.incrementAndGet();
|
||||||
|
addClaimed(index, tile, local);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tile tileOf(int tileX, int tileZ) {
|
||||||
|
if (current == null || currentX != tileX || currentZ != tileZ) {
|
||||||
|
current = tile(tileX, tileZ);
|
||||||
|
currentX = tileX;
|
||||||
|
currentZ = tileZ;
|
||||||
|
current.used = stamp;
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sample(CompletableFuture<Void> batch) {
|
||||||
|
try {
|
||||||
|
int[] xs = new int[claimed];
|
||||||
|
int[] zs = new int[claimed];
|
||||||
|
for (int position = 0; position < claimed; position++) {
|
||||||
|
xs[position] = nodes.cellX(claimedIndex[position]) * cell;
|
||||||
|
zs[position] = nodes.cellZ(claimedIndex[position]) * cell;
|
||||||
|
}
|
||||||
|
HydrologySampleBuffer samples = new HydrologySampleBuffer(claimed);
|
||||||
|
source.sample(xs, zs, samples, baseOnly);
|
||||||
|
for (int position = 0; position < claimed; position++) {
|
||||||
|
Tile tile = claimedTile[position];
|
||||||
|
int local = claimedLocal[position];
|
||||||
|
tile.store(local, samples, position);
|
||||||
|
tile.marks.set(local, READY);
|
||||||
|
tile.pending.decrementAndGet();
|
||||||
|
copy(samples, position, out, claimedIndex[position]);
|
||||||
|
}
|
||||||
|
batch.complete(null);
|
||||||
|
} catch (Throwable failure) {
|
||||||
|
try {
|
||||||
|
release(batch);
|
||||||
|
} finally {
|
||||||
|
batch.completeExceptionally(failure);
|
||||||
|
}
|
||||||
|
throw failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void release(CompletableFuture<Void> batch) {
|
||||||
|
for (int position = 0; position < claimed; position++) {
|
||||||
|
Tile tile = claimedTile[position];
|
||||||
|
if (tile.marks.compareAndSet(claimedLocal[position], batch, null)) {
|
||||||
|
tile.pending.decrementAndGet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[] awaitWaiting() {
|
||||||
|
int[] open = null;
|
||||||
|
int unresolved = 0;
|
||||||
|
for (int position = 0; position < waiting; position++) {
|
||||||
|
Tile tile = waitingTile[position];
|
||||||
|
int local = waitingLocal[position];
|
||||||
|
CompletableFuture<Void> mark = tile.marks.get(local);
|
||||||
|
if (mark != READY && (mark == null || !await(mark) || tile.marks.get(local) != READY)) {
|
||||||
|
if (open == null) {
|
||||||
|
open = new int[waiting - position];
|
||||||
|
}
|
||||||
|
open[unresolved++] = waitingIndex[position];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tile.copy(local, out, waitingIndex[position]);
|
||||||
|
}
|
||||||
|
return open == null ? null : Arrays.copyOf(open, unresolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addClaimed(int index, Tile tile, int local) {
|
||||||
|
if (claimed == claimedIndex.length) {
|
||||||
|
int grown = Math.max(16, claimed * 2);
|
||||||
|
claimedIndex = Arrays.copyOf(claimedIndex, grown);
|
||||||
|
claimedTile = Arrays.copyOf(claimedTile, grown);
|
||||||
|
claimedLocal = Arrays.copyOf(claimedLocal, grown);
|
||||||
|
}
|
||||||
|
claimedIndex[claimed] = index;
|
||||||
|
claimedTile[claimed] = tile;
|
||||||
|
claimedLocal[claimed] = local;
|
||||||
|
claimed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addWaiting(int index, Tile tile, int local) {
|
||||||
|
if (waiting == waitingIndex.length) {
|
||||||
|
int grown = Math.max(16, waiting * 2);
|
||||||
|
waitingIndex = Arrays.copyOf(waitingIndex, grown);
|
||||||
|
waitingTile = Arrays.copyOf(waitingTile, grown);
|
||||||
|
waitingLocal = Arrays.copyOf(waitingLocal, grown);
|
||||||
|
}
|
||||||
|
waitingIndex[waiting] = index;
|
||||||
|
waitingTile[waiting] = tile;
|
||||||
|
waitingLocal[waiting] = local;
|
||||||
|
waiting++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class Tile {
|
||||||
|
private final float[] height;
|
||||||
|
private final boolean[] ocean;
|
||||||
|
private final float[] rainfall;
|
||||||
|
private final float[] pondDensity;
|
||||||
|
private final byte[] permission;
|
||||||
|
private final String[] fluidProfile;
|
||||||
|
private final AtomicReferenceArray<CompletableFuture<Void>> marks;
|
||||||
|
private final AtomicInteger pending = new AtomicInteger();
|
||||||
|
private volatile long used;
|
||||||
|
|
||||||
|
private Tile(int cells) {
|
||||||
|
this.height = new float[cells];
|
||||||
|
this.ocean = new boolean[cells];
|
||||||
|
this.rainfall = new float[cells];
|
||||||
|
this.pondDensity = new float[cells];
|
||||||
|
this.permission = new byte[cells];
|
||||||
|
this.fluidProfile = new String[cells];
|
||||||
|
this.marks = new AtomicReferenceArray<>(cells);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void store(int local, HydrologySampleBuffer samples, int sample) {
|
||||||
|
height[local] = samples.height()[sample];
|
||||||
|
ocean[local] = samples.ocean()[sample];
|
||||||
|
rainfall[local] = samples.rainfall()[sample];
|
||||||
|
pondDensity[local] = samples.pondDensity()[sample];
|
||||||
|
permission[local] = samples.permission()[sample];
|
||||||
|
fluidProfile[local] = samples.fluidProfile()[sample];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void copy(int local, HydrologySampleBuffer out, int index) {
|
||||||
|
out.height()[index] = height[local];
|
||||||
|
out.ocean()[index] = ocean[local];
|
||||||
|
out.rainfall()[index] = rainfall[local];
|
||||||
|
out.pondDensity()[index] = pondDensity[local];
|
||||||
|
out.permission()[index] = permission[local];
|
||||||
|
out.fluidProfile()[index] = fluidProfile[local];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface Nodes {
|
private interface Nodes {
|
||||||
|
|||||||
+6
-6
@@ -32,10 +32,10 @@ import java.util.function.Consumer;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public final class HydrologyPlanCache {
|
public final class HydrologyPlanCache {
|
||||||
private static final int FRONT = 3;
|
private static final int FINE_FRONT = 9;
|
||||||
public static final int WINDOW_LATTICE_CELLS = frontCells(WindowKey.SIZE, WindowPlanner.CELL);
|
private static final int REGION_FRONT = 5;
|
||||||
public static final int FINE_LATTICE_CELLS = frontCells(WindowKey.SIZE, WindowPlanner.FINE_CELL);
|
public static final int FINE_LATTICE_CELLS = frontCells(FINE_FRONT, WindowKey.SIZE, WindowPlanner.FINE_CELL);
|
||||||
public static final int REGION_LATTICE_CELLS = frontCells(RegionKey.SIZE, RegionPlanner.CELL);
|
public static final int REGION_LATTICE_CELLS = frontCells(REGION_FRONT, RegionKey.SIZE, RegionPlanner.CELL);
|
||||||
private static final int WINDOW_CAPACITY = 128;
|
private static final int WINDOW_CAPACITY = 128;
|
||||||
private static final int REGION_CAPACITY = 64;
|
private static final int REGION_CAPACITY = 64;
|
||||||
private static final long DEMAND = Long.MIN_VALUE;
|
private static final long DEMAND = Long.MIN_VALUE;
|
||||||
@@ -312,8 +312,8 @@ public final class HydrologyPlanCache {
|
|||||||
return Math.max(1, parallelism / 2);
|
return Math.max(1, parallelism / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int frontCells(int unit, int cell) {
|
private static int frontCells(int front, int unit, int cell) {
|
||||||
int cells = (FRONT + 2) * unit / cell;
|
int cells = (front + 2) * unit / cell;
|
||||||
return cells * cells;
|
return cells * cells;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ public final class WindowPlanner {
|
|||||||
public static final int CELL = 8;
|
public static final int CELL = 8;
|
||||||
public static final int FINE_CELL = 4;
|
public static final int FINE_CELL = 4;
|
||||||
private static final int WINDOW_CELLS = WindowKey.SIZE / CELL;
|
private static final int WINDOW_CELLS = WindowKey.SIZE / CELL;
|
||||||
|
private static final int FINE_STRIDE = CELL / FINE_CELL;
|
||||||
private static final int SPAN = 3 * WINDOW_CELLS;
|
private static final int SPAN = 3 * WINDOW_CELLS;
|
||||||
private static final int REACH_OVERHANG = 256;
|
private static final int REACH_OVERHANG = 256;
|
||||||
private static final float BASIN_DEPTH = 0.5f;
|
private static final float BASIN_DEPTH = 0.5f;
|
||||||
@@ -43,22 +44,17 @@ public final class WindowPlanner {
|
|||||||
private static final long SALT_HEAD = 0x5354524541484dL;
|
private static final long SALT_HEAD = 0x5354524541484dL;
|
||||||
|
|
||||||
private final HydrologySettings settings;
|
private final HydrologySettings settings;
|
||||||
private final HeightLattice lattice;
|
|
||||||
private final HeightLattice fine;
|
private final HeightLattice fine;
|
||||||
private final PondPlanner ponds;
|
private final PondPlanner ponds;
|
||||||
private final ReachShaper shaper;
|
private final ReachShaper shaper;
|
||||||
private final SeaInletPlanner inlets;
|
private final SeaInletPlanner inlets;
|
||||||
|
|
||||||
public WindowPlanner(HydrologySettings settings, HeightLattice lattice, HeightLattice fine, PondPlanner ponds, ReachShaper shaper) {
|
public WindowPlanner(HydrologySettings settings, HeightLattice fine, PondPlanner ponds, ReachShaper shaper) {
|
||||||
this.settings = Objects.requireNonNull(settings, "settings");
|
this.settings = Objects.requireNonNull(settings, "settings");
|
||||||
this.lattice = Objects.requireNonNull(lattice, "lattice");
|
|
||||||
this.fine = Objects.requireNonNull(fine, "fine");
|
this.fine = Objects.requireNonNull(fine, "fine");
|
||||||
this.ponds = Objects.requireNonNull(ponds, "ponds");
|
this.ponds = Objects.requireNonNull(ponds, "ponds");
|
||||||
this.shaper = Objects.requireNonNull(shaper, "shaper");
|
this.shaper = Objects.requireNonNull(shaper, "shaper");
|
||||||
this.inlets = new SeaInletPlanner(settings);
|
this.inlets = new SeaInletPlanner(settings);
|
||||||
if (lattice.cell() != CELL) {
|
|
||||||
throw new IllegalArgumentException("Window lattice cell " + lattice.cell() + " must be " + CELL);
|
|
||||||
}
|
|
||||||
if (fine.cell() != FINE_CELL) {
|
if (fine.cell() != FINE_CELL) {
|
||||||
throw new IllegalArgumentException("Window fine lattice cell " + fine.cell() + " must be " + FINE_CELL);
|
throw new IllegalArgumentException("Window fine lattice cell " + fine.cell() + " must be " + FINE_CELL);
|
||||||
}
|
}
|
||||||
@@ -87,7 +83,7 @@ public final class WindowPlanner {
|
|||||||
int cellX = Math.multiplyExact(key.wx() - 1, WINDOW_CELLS);
|
int cellX = Math.multiplyExact(key.wx() - 1, WINDOW_CELLS);
|
||||||
int cellZ = Math.multiplyExact(key.wz() - 1, WINDOW_CELLS);
|
int cellZ = Math.multiplyExact(key.wz() - 1, WINDOW_CELLS);
|
||||||
HydrologySampleBuffer samples = new HydrologySampleBuffer(SPAN * SPAN);
|
HydrologySampleBuffer samples = new HydrologySampleBuffer(SPAN * SPAN);
|
||||||
lattice.fill(cellX, cellZ, SPAN, SPAN, samples);
|
fine.fill(cellX * FINE_STRIDE, cellZ * FINE_STRIDE, SPAN, SPAN, FINE_STRIDE, samples);
|
||||||
DemGrid raw = new DemGrid(cellX * CELL, cellZ * CELL, CELL, SPAN, SPAN);
|
DemGrid raw = new DemGrid(cellX * CELL, cellZ * CELL, CELL, SPAN, SPAN);
|
||||||
System.arraycopy(samples.height(), 0, raw.values(), 0, SPAN * SPAN);
|
System.arraycopy(samples.height(), 0, raw.values(), 0, SPAN * SPAN);
|
||||||
List<Pond> lakes = regionLakes(plans.values());
|
List<Pond> lakes = regionLakes(plans.values());
|
||||||
|
|||||||
+18
-3
@@ -33,6 +33,7 @@ public final class SeaInletPlanner {
|
|||||||
private static final long SALT_HEADROOM = 0x53454148454144L;
|
private static final long SALT_HEADROOM = 0x53454148454144L;
|
||||||
private static final long SALT_DEPTH = 0x53454144455054L;
|
private static final long SALT_DEPTH = 0x53454144455054L;
|
||||||
private static final long SALT_DENSITY = 0x53454144454e53L;
|
private static final long SALT_DENSITY = 0x53454144454e53L;
|
||||||
|
private static final double SQUARED_MARGIN = 1e-9D;
|
||||||
|
|
||||||
private final HydrologySettings settings;
|
private final HydrologySettings settings;
|
||||||
|
|
||||||
@@ -403,22 +404,36 @@ public final class SeaInletPlanner {
|
|||||||
double dx = bx - ax;
|
double dx = bx - ax;
|
||||||
double dz = bz - az;
|
double dz = bz - az;
|
||||||
double span = dx * dx + dz * dz;
|
double span = dx * dx + dz * dz;
|
||||||
|
double outside = radius > 0f ? squaredAbove(radius) : Double.POSITIVE_INFINITY;
|
||||||
|
double inside = radius > 0f ? squaredBelow(radius) : Double.NEGATIVE_INFINITY;
|
||||||
|
double wetOutside = wetRadius > 0f ? squaredAbove(wetRadius) : Double.POSITIVE_INFINITY;
|
||||||
|
double wetInside = wetRadius > 0f ? squaredBelow(wetRadius) : Double.NEGATIVE_INFINITY;
|
||||||
for (int j = minJ; j <= maxJ; j++) {
|
for (int j = minJ; j <= maxJ; j++) {
|
||||||
double pz = grid.originZ() + (double) j * cell;
|
double pz = grid.originZ() + (double) j * cell;
|
||||||
for (int i = minI; i <= maxI; i++) {
|
for (int i = minI; i <= maxI; i++) {
|
||||||
double px = grid.originX() + (double) i * cell;
|
double px = grid.originX() + (double) i * cell;
|
||||||
double t = span <= 0D ? 0D : Math.clamp(((px - ax) * dx + (pz - az) * dz) / span, 0D, 1D);
|
double t = span <= 0D ? 0D : Math.clamp(((px - ax) * dx + (pz - az) * dz) / span, 0D, 1D);
|
||||||
double distance = Math.hypot(px - (ax + t * dx), pz - (az + t * dz));
|
double ex = px - (ax + t * dx);
|
||||||
if (distance > radius) {
|
double ez = pz - (az + t * dz);
|
||||||
|
double squared = ex * ex + ez * ez;
|
||||||
|
if (squared > outside || !(squared < inside) && Math.hypot(ex, ez) > radius) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int index = j * grid.width() + i;
|
int index = j * grid.width() + i;
|
||||||
cover[index] = Math.min(cover[index], bed);
|
cover[index] = Math.min(cover[index], bed);
|
||||||
wet[index] |= distance <= wetRadius;
|
wet[index] |= squared < wetInside || !(squared > wetOutside) && Math.hypot(ex, ez) <= wetRadius;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static double squaredAbove(float radius) {
|
||||||
|
return (double) radius * radius * (1D + SQUARED_MARGIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double squaredBelow(float radius) {
|
||||||
|
return (double) radius * radius * (1D - SQUARED_MARGIN);
|
||||||
|
}
|
||||||
|
|
||||||
private static float pathLength(DemGrid grid, List<Integer> path) {
|
private static float pathLength(DemGrid grid, List<Integer> path) {
|
||||||
float length = 0f;
|
float length = 0f;
|
||||||
for (int station = 1; station < path.size(); station++) {
|
for (int station = 1; station < path.size(); station++) {
|
||||||
|
|||||||
@@ -268,6 +268,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
@Override
|
@Override
|
||||||
public void onRegionBounds(int minRegionX, int minRegionZ, int maxRegionX, int maxRegionZ) {
|
public void onRegionBounds(int minRegionX, int minRegionZ, int maxRegionX, int maxRegionZ) {
|
||||||
retention = createRetention(minRegionX, minRegionZ, maxRegionX, maxRegionZ);
|
retention = createRetention(minRegionX, minRegionZ, maxRegionX, maxRegionZ);
|
||||||
|
hydrology.bound(minRegionX, minRegionZ, maxRegionX, maxRegionZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PregenChunkRetention createRetention(int minRegionX, int minRegionZ, int maxRegionX, int maxRegionZ) {
|
private PregenChunkRetention createRetention(int minRegionX, int minRegionZ, int maxRegionX, int maxRegionZ) {
|
||||||
|
|||||||
@@ -219,6 +219,11 @@ public class MedievalPregenMethod implements PregeneratorMethod {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRegionBounds(int minRegionX, int minRegionZ, int maxRegionX, int maxRegionZ) {
|
||||||
|
hydrology.bound(minRegionX, minRegionZ, maxRegionX, maxRegionZ);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRegionsAhead(List<long[]> regions) {
|
public void onRegionsAhead(List<long[]> regions) {
|
||||||
if (!closing.get()) {
|
if (!closing.get()) {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ public final class PregenHydrology implements AutoCloseable {
|
|||||||
private final Supplier<Engine> engine;
|
private final Supplier<Engine> engine;
|
||||||
private HydrologyRuntime owner;
|
private HydrologyRuntime owner;
|
||||||
private HydrologyPregeneration epoch;
|
private HydrologyPregeneration epoch;
|
||||||
|
private int[] bounds;
|
||||||
private boolean closed;
|
private boolean closed;
|
||||||
|
|
||||||
public PregenHydrology(Supplier<Engine> engine) {
|
public PregenHydrology(Supplier<Engine> engine) {
|
||||||
@@ -28,12 +29,21 @@ public final class PregenHydrology implements AutoCloseable {
|
|||||||
withdraw();
|
withdraw();
|
||||||
owner = runtime;
|
owner = runtime;
|
||||||
epoch = runtime == null ? null : runtime.openPregeneration();
|
epoch = runtime == null ? null : runtime.openPregeneration();
|
||||||
|
applyBounds();
|
||||||
}
|
}
|
||||||
if (epoch != null) {
|
if (epoch != null) {
|
||||||
epoch.prepare(regions);
|
epoch.prepare(regions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized void bound(int minRegionX, int minRegionZ, int maxRegionX, int maxRegionZ) {
|
||||||
|
if (closed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bounds = new int[]{minRegionX, minRegionZ, maxRegionX, maxRegionZ};
|
||||||
|
applyBounds();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void close() {
|
public synchronized void close() {
|
||||||
closed = true;
|
closed = true;
|
||||||
@@ -41,6 +51,12 @@ public final class PregenHydrology implements AutoCloseable {
|
|||||||
owner = null;
|
owner = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applyBounds() {
|
||||||
|
if (epoch != null && bounds != null) {
|
||||||
|
epoch.bound(bounds[0], bounds[1], bounds[2], bounds[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void withdraw() {
|
private void withdraw() {
|
||||||
if (epoch != null) {
|
if (epoch != null) {
|
||||||
epoch.close();
|
epoch.close();
|
||||||
|
|||||||
-80
@@ -1,80 +0,0 @@
|
|||||||
package art.arcane.iris.generation.hydrology.engine;
|
|
||||||
|
|
||||||
import art.arcane.iris.generation.hydrology.grid.HeightLattice;
|
|
||||||
import art.arcane.iris.generation.hydrology.grid.HydrologySampleBuffer;
|
|
||||||
import art.arcane.iris.generation.hydrology.grid.HydrologyTerrainSource;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertThrows;
|
|
||||||
|
|
||||||
public class FineLatticeSourceTest {
|
|
||||||
@Test
|
|
||||||
public void windowLatticeReadsItsNodesFromTheFineLattice() {
|
|
||||||
CountingTerrain terrain = new CountingTerrain();
|
|
||||||
HeightLattice fine = new HeightLattice(terrain, 4, false, 1 << 16);
|
|
||||||
HeightLattice window = new HeightLattice(new FineLatticeSource(fine, terrain), 8, false, 1 << 16);
|
|
||||||
|
|
||||||
fine.fill(0, 0, 64, 64, new HydrologySampleBuffer(64 * 64));
|
|
||||||
assertEquals(4096L, terrain.samples.get());
|
|
||||||
HydrologySampleBuffer held = new HydrologySampleBuffer(32 * 32);
|
|
||||||
window.fill(0, 0, 32, 32, held);
|
|
||||||
assertEquals(4096L, terrain.samples.get());
|
|
||||||
assertHeights(held, 0, 0, 32);
|
|
||||||
|
|
||||||
HydrologySampleBuffer fresh = new HydrologySampleBuffer(16 * 16);
|
|
||||||
window.fill(32, 0, 16, 16, fresh);
|
|
||||||
assertEquals(4096L + 256L, terrain.samples.get());
|
|
||||||
assertHeights(fresh, 32, 0, 16);
|
|
||||||
fine.fill(64, 0, 32, 32, new HydrologySampleBuffer(32 * 32));
|
|
||||||
assertEquals(4096L + 1024L, terrain.samples.get());
|
|
||||||
|
|
||||||
FineLatticeSource source = new FineLatticeSource(fine, terrain);
|
|
||||||
assertThrows(IllegalArgumentException.class, () -> source.sample(new int[]{8}, new int[]{8}, new HydrologySampleBuffer(1), true));
|
|
||||||
assertThrows(IllegalArgumentException.class, () -> source.sample(new int[]{6}, new int[]{8}, new HydrologySampleBuffer(1), false));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void assertHeights(HydrologySampleBuffer samples, int cellX0, int cellZ0, int side) {
|
|
||||||
for (int index = 0; index < side * side; index++) {
|
|
||||||
int x = (cellX0 + index % side) * 8;
|
|
||||||
int z = (cellZ0 + index / side) * 8;
|
|
||||||
assertEquals(x + "," + z, Float.floatToRawIntBits(CountingTerrain.height(x, z)), Float.floatToRawIntBits(samples.height()[index]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class CountingTerrain implements HydrologyTerrainSource {
|
|
||||||
private final AtomicLong samples = new AtomicLong();
|
|
||||||
|
|
||||||
private static float height(int x, int z) {
|
|
||||||
return 64f + (float) Math.sin(x * 0.013D) * 9f + (float) Math.cos(z * 0.021D) * 7f;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sample(int[] xs, int[] zs, HydrologySampleBuffer out, boolean baseOnly) {
|
|
||||||
samples.addAndGet(xs.length);
|
|
||||||
for (int index = 0; index < xs.length; index++) {
|
|
||||||
out.height()[index] = height(xs[index], zs[index]);
|
|
||||||
out.permission()[index] = HydrologySampleBuffer.ALLOW_STREAMS | HydrologySampleBuffer.ALLOW_PONDS;
|
|
||||||
out.rainfall()[index] = 1f;
|
|
||||||
out.pondDensity()[index] = 1f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int seaLevel() {
|
|
||||||
return 40;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int minY() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int maxY() {
|
|
||||||
return 384;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+30
@@ -67,6 +67,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
@@ -414,6 +415,35 @@ public class HydrologyRuntimeTest {
|
|||||||
assertEquals(3, prefetches.get());
|
assertEquals(3, prefetches.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(timeout = 180_000L)
|
||||||
|
public void boundedPregenerationPrefetchesOnlyWindowsItsChunksNeed() {
|
||||||
|
FakePlanners planners = new FakePlanners(settings());
|
||||||
|
HydrologyPlanCache cache = spy(new HydrologyPlanCache(planners.settings, planners::region, planners::window,
|
||||||
|
pool(planners, 2), () -> false));
|
||||||
|
List<int[]> prepared = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
doAnswer(invocation -> {
|
||||||
|
prepared.add(new int[]{invocation.getArgument(0), invocation.getArgument(1), invocation.getArgument(2), invocation.getArgument(3),
|
||||||
|
invocation.getArgument(4)});
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
|
}).when(cache).prepareWindowsAround(anyInt(), anyInt(), anyInt(), anyInt(), anyInt());
|
||||||
|
HydrologyRuntime runtime = new HydrologyRuntime(planners.settings, cache, planners::natural, () -> false, planners.ticker::get);
|
||||||
|
runtimes.add(runtime);
|
||||||
|
int chunks = WindowKey.SIZE >> 4;
|
||||||
|
HydrologyPregeneration pregeneration = runtime.openPregeneration();
|
||||||
|
pregeneration.bound(0, 0, 1, 1);
|
||||||
|
|
||||||
|
runtime.raster(0, 0);
|
||||||
|
assertArrayEquals(new int[]{-chunks, -chunks, 3 * chunks - 1, 3 * chunks - 1, 0}, prepared.getLast());
|
||||||
|
runtime.raster(chunks + 5, chunks + 5);
|
||||||
|
assertArrayEquals(new int[]{-chunks, -chunks, 3 * chunks - 1, 3 * chunks - 1, 0}, prepared.getLast());
|
||||||
|
runtime.raster(-3 * chunks, 0);
|
||||||
|
assertArrayEquals(new int[]{-3 * chunks, 0, -3 * chunks, 0, 2}, prepared.getLast());
|
||||||
|
|
||||||
|
pregeneration.close();
|
||||||
|
runtime.raster(1, 1);
|
||||||
|
assertArrayEquals(new int[]{1, 1, 1, 1, 2}, prepared.getLast());
|
||||||
|
}
|
||||||
|
|
||||||
@Test(timeout = 180_000L)
|
@Test(timeout = 180_000L)
|
||||||
public void preparePregenerationPlansRegionsInOrder() throws Exception {
|
public void preparePregenerationPlansRegionsInOrder() throws Exception {
|
||||||
FakePlanners planners = new FakePlanners(settings());
|
FakePlanners planners = new FakePlanners(settings());
|
||||||
|
|||||||
+2
-3
@@ -154,10 +154,9 @@ public class IrisHydrologyTerrainSourceTest {
|
|||||||
HydrologySampleBuffer rect = new HydrologySampleBuffer(32 * 32);
|
HydrologySampleBuffer rect = new HydrologySampleBuffer(32 * 32);
|
||||||
fine.fill(-600, 400, 32, 32, rect);
|
fine.fill(-600, 400, 32, 32, rect);
|
||||||
assertMatchesScalar(reference, "fine rect", rect, nodes(-600, 400, 32, 1, 4), false);
|
assertMatchesScalar(reference, "fine rect", rect, nodes(-600, 400, 32, 1, 4), false);
|
||||||
HeightLattice window = new HeightLattice(new FineLatticeSource(fine, source), 8, false, 1 << 16);
|
|
||||||
HydrologySampleBuffer strided = new HydrologySampleBuffer(16 * 16);
|
HydrologySampleBuffer strided = new HydrologySampleBuffer(16 * 16);
|
||||||
window.fill(-150, 300, 16, 16, 3, strided);
|
fine.fill(-300, 600, 16, 16, 6, strided);
|
||||||
assertMatchesScalar(reference, "window strided", strided, nodes(-150, 300, 16, 3, 8), false);
|
assertMatchesScalar(reference, "fine strided", strided, nodes(-300, 600, 16, 6, 4), false);
|
||||||
HeightLattice coarse = new HeightLattice(source, 64, true, 1 << 12);
|
HeightLattice coarse = new HeightLattice(source, 64, true, 1 << 12);
|
||||||
HydrologySampleBuffer coarseBase = new HydrologySampleBuffer(16 * 16);
|
HydrologySampleBuffer coarseBase = new HydrologySampleBuffer(16 * 16);
|
||||||
coarse.fill(-8, -8, 16, 16, coarseBase);
|
coarse.fill(-8, -8, 16, 16, coarseBase);
|
||||||
|
|||||||
@@ -287,6 +287,31 @@ public class HeightLatticeTest {
|
|||||||
assertThrows(IllegalArgumentException.class, () -> lattice.fill(cellXs, cellZs, new HydrologySampleBuffer(5)));
|
assertThrows(IllegalArgumentException.class, () -> lattice.fill(cellXs, cellZs, new HydrologySampleBuffer(5)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sampledColumnsAreReadOnceAndMatchTheSource() {
|
||||||
|
CountingSource source = new CountingSource();
|
||||||
|
HeightLattice lattice = new HeightLattice(source, 4, false, 1 << 16);
|
||||||
|
int[] xs = {5, -13, 5, 1027, -13, 6};
|
||||||
|
int[] zs = {-2, 77, -2, 3, 77, -2};
|
||||||
|
HydrologySampleBuffer first = new HydrologySampleBuffer(xs.length);
|
||||||
|
lattice.sampleColumns(xs, zs, first);
|
||||||
|
assertEquals(4, source.samples());
|
||||||
|
HydrologySampleBuffer second = new HydrologySampleBuffer(xs.length);
|
||||||
|
lattice.sampleColumns(xs, zs, second);
|
||||||
|
assertEquals(4, source.samples());
|
||||||
|
HydrologySampleBuffer expected = new HydrologySampleBuffer(xs.length);
|
||||||
|
new CountingSource().sample(xs, zs, expected, false);
|
||||||
|
for (int index = 0; index < xs.length; index++) {
|
||||||
|
assertEquals(Float.floatToIntBits(expected.height()[index]), Float.floatToIntBits(first.height()[index]));
|
||||||
|
assertEquals(Float.floatToIntBits(expected.height()[index]), Float.floatToIntBits(second.height()[index]));
|
||||||
|
assertEquals(expected.ocean()[index], second.ocean()[index]);
|
||||||
|
assertEquals(expected.permission()[index], second.permission()[index]);
|
||||||
|
assertEquals(expected.fluidProfile()[index], second.fluidProfile()[index]);
|
||||||
|
}
|
||||||
|
lattice.sampleColumns(new int[0], new int[0], new HydrologySampleBuffer(0));
|
||||||
|
assertEquals(1, source.calls());
|
||||||
|
}
|
||||||
|
|
||||||
private static void fillWindow(HeightLattice lattice, int wx, int wz, int cell, boolean baseOnly) {
|
private static void fillWindow(HeightLattice lattice, int wx, int wz, int cell, boolean baseOnly) {
|
||||||
assertFill(lattice, cell, baseOnly, (wx - 1) * WINDOW, (wz - 1) * WINDOW, AREA, AREA);
|
assertFill(lattice, cell, baseOnly, (wx - 1) * WINDOW, (wz - 1) * WINDOW, AREA, AREA);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1045,7 +1045,7 @@ public class HydrologyPlanCacheTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static WindowPlanner windowPlanner(HydrologySettings settings, SyntheticTerrain terrain) {
|
private static WindowPlanner windowPlanner(HydrologySettings settings, SyntheticTerrain terrain) {
|
||||||
return new WindowPlanner(settings, new HeightLattice(terrain, WindowPlanner.CELL, false, HydrologyPlanCache.WINDOW_LATTICE_CELLS),
|
return new WindowPlanner(settings,
|
||||||
new HeightLattice(terrain, WindowPlanner.FINE_CELL, false, HydrologyPlanCache.FINE_LATTICE_CELLS), new PondPlanner(settings),
|
new HeightLattice(terrain, WindowPlanner.FINE_CELL, false, HydrologyPlanCache.FINE_LATTICE_CELLS), new PondPlanner(settings),
|
||||||
new ReachShaper(settings));
|
new ReachShaper(settings));
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -81,7 +81,7 @@ public class HydrologyPlanCodecTest {
|
|||||||
assertEquals(planned, HydrologyPlanCodec.decodeRegion(settings, HydrologyPlanCodec.encode(settings, planned)).orElseThrow());
|
assertEquals(planned, HydrologyPlanCodec.decodeRegion(settings, HydrologyPlanCodec.encode(settings, planned)).orElseThrow());
|
||||||
regions.add(planned);
|
regions.add(planned);
|
||||||
}
|
}
|
||||||
WindowPlan planned = new WindowPlanner(settings, new HeightLattice(terrain, WindowPlanner.CELL, false, 1 << 18),
|
WindowPlan planned = new WindowPlanner(settings,
|
||||||
new HeightLattice(terrain, WindowPlanner.FINE_CELL, false, 1 << 21), new PondPlanner(settings), new ReachShaper(settings))
|
new HeightLattice(terrain, WindowPlanner.FINE_CELL, false, 1 << 21), new PondPlanner(settings), new ReachShaper(settings))
|
||||||
.plan(LAND, regions);
|
.plan(LAND, regions);
|
||||||
assertFalse(planned.reaches().isEmpty());
|
assertFalse(planned.reaches().isEmpty());
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public class WindowPlannerTest {
|
|||||||
new int[]{head, head}, new float[]{2f, 2f}, new float[]{2f, 2f}, new ReachStyle[]{ReachStyle.RUN, ReachStyle.RUN}, 1000f);
|
new int[]{head, head}, new float[]{2f, 2f}, new float[]{2f, 2f}, new ReachStyle[]{ReachStyle.RUN, ReachStyle.RUN}, 1000f);
|
||||||
return new ShapedNetwork(List.of(outlet), List.of(pond.withOutletReachId(outlet.id())));
|
return new ShapedNetwork(List.of(outlet), List.of(pond.withOutletReachId(outlet.id())));
|
||||||
}).when(shaper).shape(any(), any(), any(), any(), any(), any(), anyLong());
|
}).when(shaper).shape(any(), any(), any(), any(), any(), any(), anyLong());
|
||||||
WindowPlanner planner = new WindowPlanner(settings, new HeightLattice(terrain, WindowPlanner.CELL, false, 1 << 18),
|
WindowPlanner planner = new WindowPlanner(settings,
|
||||||
new HeightLattice(terrain, WindowPlanner.FINE_CELL, false, 1 << 18), ponds, shaper);
|
new HeightLattice(terrain, WindowPlanner.FINE_CELL, false, 1 << 18), ponds, shaper);
|
||||||
|
|
||||||
WindowComputation settled = planner.compute(ORIGIN, noRegions(ORIGIN));
|
WindowComputation settled = planner.compute(ORIGIN, noRegions(ORIGIN));
|
||||||
@@ -587,7 +587,7 @@ public class WindowPlannerTest {
|
|||||||
}
|
}
|
||||||
return network;
|
return network;
|
||||||
}).when(shaper).shape(any(), any(), any(), any(), any(), any(), anyLong());
|
}).when(shaper).shape(any(), any(), any(), any(), any(), any(), anyLong());
|
||||||
return new WindowPlanner(settings, new HeightLattice(terrain, WindowPlanner.CELL, false, 1 << 18),
|
return new WindowPlanner(settings,
|
||||||
new HeightLattice(terrain, WindowPlanner.FINE_CELL, false, 1 << 21), new PondPlanner(settings), shaper);
|
new HeightLattice(terrain, WindowPlanner.FINE_CELL, false, 1 << 21), new PondPlanner(settings), shaper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -715,7 +715,7 @@ public class WindowPlannerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static WindowPlanner planner(HydrologySettings settings, SyntheticTerrain terrain) {
|
private static WindowPlanner planner(HydrologySettings settings, SyntheticTerrain terrain) {
|
||||||
return new WindowPlanner(settings, new HeightLattice(terrain, WindowPlanner.CELL, false, 1 << 18),
|
return new WindowPlanner(settings,
|
||||||
new HeightLattice(terrain, WindowPlanner.FINE_CELL, false, 1 << 21), new PondPlanner(settings), new ReachShaper(settings));
|
new HeightLattice(terrain, WindowPlanner.FINE_CELL, false, 1 << 21), new PondPlanner(settings), new ReachShaper(settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -195,7 +195,8 @@ final class HydrologyPlanHarness {
|
|||||||
double landTiles(WindowKey key) {
|
double landTiles(WindowKey key) {
|
||||||
int cells = WindowKey.SIZE / WindowPlanner.CELL;
|
int cells = WindowKey.SIZE / WindowPlanner.CELL;
|
||||||
HydrologySampleBuffer samples = new HydrologySampleBuffer(cells * cells);
|
HydrologySampleBuffer samples = new HydrologySampleBuffer(cells * cells);
|
||||||
planners.windowLattice().fill(key.wx() * cells, key.wz() * cells, cells, cells, samples);
|
int stride = WindowPlanner.CELL / WindowPlanner.FINE_CELL;
|
||||||
|
planners.fineLattice().fill(key.wx() * cells * stride, key.wz() * cells * stride, cells, cells, stride, samples);
|
||||||
int land = 0;
|
int land = 0;
|
||||||
for (int cell = 0; cell < cells * cells; cell++) {
|
for (int cell = 0; cell < cells * cells; cell++) {
|
||||||
if (!samples.ocean()[cell]) {
|
if (!samples.ocean()[cell]) {
|
||||||
|
|||||||
Reference in new issue
Block a user