mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 14:00:26 +00:00
Basin hydrology
- The route-and-reject hydrology system and its probes are removed - Hydrology is configured under dimension hydrology and per biome, region and dimension hydrologyPolicy - riverPolicy and the old hydrology config types are gone - Grid kernels cover priority flood, D8 routing, flow accumulation, basin extraction, breaching and stream burning - A basin spills at its pour point and an interior bump can never be the spill - Region plans cover 4096-block regions on a 64-block grid for major rivers and lakes - Window plans cover 512-block windows on an 8-block grid for reaches and ponds - Plans are cached in memory, persisted per world under iris/hydrology and keyed by pack, seed, world height and settings - Only the main runtime of an engine persists plans and stores swap on hotload and default runtime change - Demand rasters outrank prefetch and stopping pregen withdraws its queued prefetch - Ponds come from natural basins, seeded cups and chained cups with strictly falling levels - A pond level never exceeds its rim plus maxFill or the end grade of any inflow - Pond outlet loops are opened at their lowest pond - Reaches split at junctions, pond outlets and 256-block grid lines - Reaches meander, fit the valley, grade to a running minimum and smooth with Chaikin passes - Drops of two blocks or more get a lip and one-block corner steps become dry rim ledges - Cascades and waterfalls write falling fluid from the mantle at level eight - Inflows into region lakes end at the lake level - River mouths and water beside the sea sit at the sea surface - The per-chunk raster gives terrain height, water head, role, tier, fluid, cave ceiling and shore distance - WaterRole codes are stable and exposed to packs through the WATER_ROLE engine stream - WATER_HEAD replaces the old river engine streams and an unknown stream name fails the pack load - Banks take SMOOTH, LINEAR, CONCAVE, TERRACED or CLIFF profiles - Containment berms grow as tall as the water needs - Columns within two blocks of water stay solid and keep no Terrain3D overhang - Seam dams hold water where new chunks meet chunks generated earlier - The transition blend skips hydrology-owned columns - Caves stay at least two blocks below river and pond beds - The painter is the only writer of still water - Objects cannot open water, falls or bank ground and solid objects may stand in water - On-water objects need fluid at their anchor column - Floating child islands stay out of channels - Sea inlets run inland from cliff coasts at sea level and end in a chamber pool - Inlet passages are sealed against carvers, subterrain features and the post pass - Cave pools settle into carved lows, keep one fluid and stay out of inlet space - Pool and inlet cells use their own cave actions in mantle data - Water vegetation lists sit on tiers, ponds and hydrologyPolicy with SEA_FLOOR, SEA_SURFACE and SHORE_LINE parts - Pack validation compiles hydrology settings and rejects empty or water-holding palettes - Chunk generation fails closed when its 3x3 water rasters are not planned - /iris find covers stream, river, major river, cascade, waterfall, mouth, pond, lake, dam, sea inlet and cave pool - Generation history records the new feature types and drops old hydrology entries - The studio map draws water from rasters up close and from plans when zoomed out - Pregen prepares the current region and one region ahead - hydrologyProbe, riverTransectProbe, hydrologyBlockProbe, seaInletProbe and cavePoolProbe report containment, rises, seams, tick conversion and missing surface - Test forks run with a 2 GB heap, a 60-minute timeout and the Mockito agent loaded explicitly - Generation with hydrology off is unchanged
This commit is contained in:
1 parent
2d211cf106
commit
db0f98d103
710 files changed
+41791
-91883
No files matched your search
+30
-12
@@ -2,8 +2,9 @@ package art.arcane.iris.probe;
|
||||
|
||||
import art.arcane.iris.configuration.IrisSettings;
|
||||
import art.arcane.iris.generation.concurrent.MultiBurst;
|
||||
import art.arcane.iris.generation.hydrology.HydrologyTileCache;
|
||||
import art.arcane.iris.generation.hydrology.runtime.IrisHydrologyRuntime;
|
||||
import art.arcane.iris.generation.hydrology.engine.HydrologyPregeneration;
|
||||
import art.arcane.iris.generation.hydrology.plan.WindowKey;
|
||||
import art.arcane.iris.world.pregen.PregenHydrology;
|
||||
import art.arcane.iris.world.pregen.PregenPerformanceProfile;
|
||||
|
||||
import net.minecraft.core.registries.Registries;
|
||||
@@ -31,11 +32,16 @@ import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.HexFormat;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
public final class OfflineRegionGenerator {
|
||||
private static final int CHUNKS_PER_HYDROLOGY_REGION = WindowKey.SIZE >> 4;
|
||||
|
||||
private OfflineRegionGenerator() {
|
||||
}
|
||||
|
||||
@@ -97,14 +103,13 @@ public final class OfflineRegionGenerator {
|
||||
PregenPerformanceProfile.apply(session.engine());
|
||||
System.out.println("generationProfile=pregen noiseCacheSize=" + IrisSettings.get().getPerformance().getNoiseCacheSize()
|
||||
+ " burstParallelism=" + MultiBurst.burst.parallelism() + " multicore=true");
|
||||
IrisHydrologyRuntime hydrology = session.engine().getComplex().getHydrologyRuntime();
|
||||
try (HydrologyTileCache.PregenerationScope hydrologyScope = hydrology == null ? null
|
||||
: hydrology.preparePregeneration(configuration.hydrologyArea());
|
||||
try (HydrologyPregeneration pregeneration = PregenHydrology.open(session.engine());
|
||||
HeadlessTerrainContext context = HeadlessTerrainContext.create(new HeadlessTerrainContext.Options(
|
||||
session.engine(), runtime, staged.resolve("templates"), configuration.levelKey(),
|
||||
configuration.noiseSettings(), spigot));
|
||||
RegionGenerationWindow workers = new RegionGenerationWindow(configuration.parallelism(),
|
||||
RegionGenerationWindow.Policy.STANDALONE)) {
|
||||
PregenHydrology.prepareAll(pregeneration, configuration.hydrologyRegions());
|
||||
HeadlessRegionTerrain.Session rolling = new HeadlessRegionTerrain.Session(workers);
|
||||
Path regionDirectory = session.engine().getTarget().getWorld().worldFolder().toPath().resolve("region");
|
||||
Files.createDirectory(regionDirectory);
|
||||
@@ -239,14 +244,27 @@ public final class OfflineRegionGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
HydrologyTileCache.PregenerationArea hydrologyArea() {
|
||||
List<long[]> hydrologyRegions() {
|
||||
int halo = NativeRegionPlan.terrainHalo();
|
||||
long minimumX = ((long) chunkX - halo) * 16L;
|
||||
long minimumZ = ((long) chunkZ - halo) * 16L;
|
||||
long maximumX = ((long) chunkX + (long) width * regionsX + halo) * 16L - 1L;
|
||||
long maximumZ = ((long) chunkZ + (long) width * regionsZ + halo) * 16L - 1L;
|
||||
return new HydrologyTileCache.PregenerationArea(Math.toIntExact((minimumX + maximumX) / 2L),
|
||||
Math.toIntExact((minimumZ + maximumZ) / 2L), minimumX, minimumZ, maximumX, maximumZ);
|
||||
Set<Long> seen = new HashSet<>();
|
||||
List<long[]> regions = new ArrayList<>();
|
||||
for (int row = 0; row < regionsZ; row++) {
|
||||
for (int column = 0; column < regionsX; column++) {
|
||||
int regionX = row % 2 == 0 ? column : regionsX - column - 1;
|
||||
int minimumX = Math.floorDiv(chunkX + regionX * width - halo, CHUNKS_PER_HYDROLOGY_REGION);
|
||||
int maximumX = Math.floorDiv(chunkX + (regionX + 1) * width - 1 + halo, CHUNKS_PER_HYDROLOGY_REGION);
|
||||
int minimumZ = Math.floorDiv(chunkZ + row * width - halo, CHUNKS_PER_HYDROLOGY_REGION);
|
||||
int maximumZ = Math.floorDiv(chunkZ + (row + 1) * width - 1 + halo, CHUNKS_PER_HYDROLOGY_REGION);
|
||||
for (int hydrologyZ = minimumZ; hydrologyZ <= maximumZ; hydrologyZ++) {
|
||||
for (int hydrologyX = minimumX; hydrologyX <= maximumX; hydrologyX++) {
|
||||
if (seen.add(((long) hydrologyX << 32) | (hydrologyZ & 0xFFFFFFFFL))) {
|
||||
regions.add(new long[]{hydrologyX, hydrologyZ});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return regions;
|
||||
}
|
||||
|
||||
static Configuration parse(String[] arguments) throws IOException {
|
||||
|
||||
+1
-1
@@ -137,7 +137,7 @@ public class HeadlessChunkGeneratorTest {
|
||||
}
|
||||
Files.writeString(pack.resolve("dimensions/main.json"), """
|
||||
{"name":"Native columns","focus":"swamp","regions":["owned"],"fluidHeight":40,"logicalHeight":256,
|
||||
"landChance":1,"carvingEnabled":false,"hydrology":{"rivers":{"enabled":false}}}
|
||||
"landChance":1,"carvingEnabled":false}
|
||||
""");
|
||||
Files.writeString(pack.resolve("regions/owned.json"), "{\"landBiomes\":[\"swamp\"]}");
|
||||
Files.writeString(pack.resolve("generators/flat.json"), "{\"composite\":[]}");
|
||||
|
||||
+5
-7
@@ -1,7 +1,6 @@
|
||||
package art.arcane.iris.probe;
|
||||
|
||||
import art.arcane.iris.structure.nativegen.NativeStructureVolumeIndex;
|
||||
import art.arcane.iris.generation.hydrology.HydrologyTileCache;
|
||||
import art.arcane.volmlib.nativelib.terrain.structure.NativeStructureVolume;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
@@ -68,11 +67,10 @@ public final class HeadlessRegionTerrainTest {
|
||||
OfflineRegionGenerator.Configuration export = new OfflineRegionGenerator.Configuration(
|
||||
workspace.pack(), "main", 69420L, -3, 2, 1, 2, 3, 4, configuration,
|
||||
"probe", Level.OVERWORLD, NoiseGeneratorSettings.OVERWORLD, root.resolve("export"));
|
||||
HydrologyTileCache.PregenerationArea bounds = export.hydrologyArea();
|
||||
assertEquals(-80L, bounds.minimumBlockX());
|
||||
assertEquals(0L, bounds.minimumBlockZ());
|
||||
assertEquals(15L, bounds.maximumBlockX());
|
||||
assertEquals(111L, bounds.maximumBlockZ());
|
||||
List<long[]> regions = export.hydrologyRegions();
|
||||
assertEquals(2, regions.size());
|
||||
assertArrayEquals(new long[]{-1L, 0L}, regions.get(0));
|
||||
assertArrayEquals(new long[]{0L, 0L}, regions.get(1));
|
||||
assertEquals(1, result.targetChunks());
|
||||
assertEquals(529, result.chunks().size());
|
||||
long terrain = result.chunks().stream().filter(chunk -> chunk.getPersistedStatus() == ChunkStatus.TERRAIN).count();
|
||||
@@ -189,7 +187,7 @@ public final class HeadlessRegionTerrainTest {
|
||||
}
|
||||
Files.writeString(pack.resolve("dimensions/main.json"), """
|
||||
{"name":"Native region","focus":"swamp","regions":["owned"],"fluidHeight":40,"logicalHeight":256,
|
||||
"landChance":1,"carvingEnabled":false,"hydrology":{"rivers":{"enabled":false}}}
|
||||
"landChance":1,"carvingEnabled":false}
|
||||
""");
|
||||
Files.writeString(pack.resolve("regions/owned.json"), "{\"landBiomes\":[\"swamp\"]}");
|
||||
Files.writeString(pack.resolve("generators/flat.json"), "{\"composite\":[]}");
|
||||
|
||||
Reference in new issue
Block a user