mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-27 06:20:02 +00:00
Delete the dead v1 hydrology settings surface
- Surface inset, terrain blend and ridge tunnel settings gone; Hydraulics keeps only waterfallMinimumDrop - Branching folded into Routing; refinementSpacing derived from the sample lattice - Contained surface routing branch, surface incision helpers and 13 unreachable compiler members deleted
This commit is contained in:
1 parent
639786537b
commit
896e58e212
24 files changed
+305
-1411
No files matched your search
@@ -8,13 +8,11 @@ public enum HydrologyCandidateRejection {
|
||||
SOURCE_QUOTA,
|
||||
SOURCE_SPACING,
|
||||
OUTLET_LIMIT,
|
||||
RIDGE_CONTAINMENT,
|
||||
OUTLET_LEVEL,
|
||||
CAVE_CONTAINMENT,
|
||||
VOLUME_LIMIT,
|
||||
SURFACE_HEAD_RANGE,
|
||||
SURFACE_HEAD_DISTRIBUTION,
|
||||
SURFACE_RIDGE_TUNNEL_LIMIT,
|
||||
SURFACE_EXPOSURE,
|
||||
SURFACE_DROP_UNSUPPORTED,
|
||||
SURFACE_CORRIDOR_UNSUPPORTED,
|
||||
|
||||
@@ -5,6 +5,8 @@ public enum HydrologyFeatureType {
|
||||
RIFFLE,
|
||||
CASCADE,
|
||||
WATERFALL,
|
||||
// No planner produces a ridge bore any more. The constant stays because its ordinal seeds the
|
||||
// segment, outlet and feature ids of every constant after it on the cave and deep-fluid paths.
|
||||
RIDGE_BORE,
|
||||
UNDERGROUND_POOL,
|
||||
UNDERGROUND_DROP,
|
||||
|
||||
+7
-418
@@ -12,19 +12,15 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
final class HydrologyFootprintCompiler {
|
||||
private static final int FEATURE_ROLE_COUNT = 7;
|
||||
private static final int COURSE_FOOTPRINT_CACHE_SIZE = 8;
|
||||
private static final int VALIDATION_RASTER_CACHE_SIZE = 8;
|
||||
private static final int MINIMUM_ENDPOINT_PROFILE_LENGTH = 48;
|
||||
private static final double MINIMUM_ENDPOINT_RAMP_WIDTHS = 2D;
|
||||
private static final int[][] HORIZONTAL_NEIGHBORS = {
|
||||
{1, 0}, {-1, 0}, {0, 1}, {0, -1}
|
||||
};
|
||||
@@ -53,7 +49,7 @@ final class HydrologyFootprintCompiler {
|
||||
HydrologyPlannerSettings settings,
|
||||
HydrologyTerrainSampler sampler
|
||||
) {
|
||||
this(settings, sampler, HydrologyGeometrySampler.deterministic(0L, sampler));
|
||||
this(settings, sampler, HydrologyGeometrySampler.deterministic(sampler));
|
||||
}
|
||||
|
||||
HydrologyFootprintCompiler(
|
||||
@@ -172,404 +168,6 @@ final class HydrologyFootprintCompiler {
|
||||
return fullMaterializationCount;
|
||||
}
|
||||
|
||||
boolean surfaceHeadwaterRampSupported(RiverCourse course) {
|
||||
Objects.requireNonNull(course, "course");
|
||||
if (course.type() != RiverCourseType.SURFACE) {
|
||||
return true;
|
||||
}
|
||||
if (settings.routing().branching().minimumSurfaceCourseLength()
|
||||
< MINIMUM_ENDPOINT_PROFILE_LENGTH) {
|
||||
return true;
|
||||
}
|
||||
RiverFootprint footprint = compileCourse(course);
|
||||
List<HydrologyPoint> raster = surfaceCourseCenterline(course);
|
||||
if (raster.size() < 2) {
|
||||
return false;
|
||||
}
|
||||
HashSet<Long> ownedCells = new HashSet<>();
|
||||
for (HydrologyColumnSample sample : footprint.columns().values()) {
|
||||
HydrologyColumnLayer fluid = sample.primarySurfaceFluidLayer().orElse(null);
|
||||
if (fluid != null
|
||||
&& fluid.feature().courseId() == course.id()
|
||||
&& fluid.feature().type().isSurface()
|
||||
&& fluid.channel()
|
||||
&& fluid.connectedFluid()
|
||||
&& fluid.fluidOwned()
|
||||
&& !fluid.oceanApron()) {
|
||||
ownedCells.add(RiverFootprint.pack(sample.x(), sample.z()));
|
||||
}
|
||||
}
|
||||
int maximumWidth = 1;
|
||||
for (HydraulicSegment segment : course.segments()) {
|
||||
if (segment.type().isSurface()) {
|
||||
maximumWidth = Math.max(maximumWidth, segment.width());
|
||||
}
|
||||
}
|
||||
HydrologyPoint source = raster.getFirst();
|
||||
if (!ownedNear(ownedCells, source.x(), source.z(), maximumWidth)) {
|
||||
return false;
|
||||
}
|
||||
int cruiseWidth = cruiseWidth(ownedCells, raster, maximumWidth);
|
||||
double transitionLength = endpointTransitionLength(
|
||||
ownedCells,
|
||||
raster,
|
||||
maximumWidth,
|
||||
cruiseWidth
|
||||
);
|
||||
return transitionLength >= cruiseWidth * MINIMUM_ENDPOINT_RAMP_WIDTHS;
|
||||
}
|
||||
|
||||
boolean surfaceIncisionContained(RiverCourse course) {
|
||||
Objects.requireNonNull(course, "course");
|
||||
if (course.type() != RiverCourseType.SURFACE) {
|
||||
return true;
|
||||
}
|
||||
RiverFootprint footprint = compileCourse(course);
|
||||
for (HydrologyColumnSample sample : footprint.columns().values()) {
|
||||
HydrologyColumnLayer fluid = sample.primarySurfaceFluidLayer().orElse(null);
|
||||
if (fluid == null
|
||||
|| fluid.feature().courseId() != course.id()
|
||||
|| fluid.feature().type() == HydrologyFeatureType.MOUTH
|
||||
|| fluid.oceanApron()
|
||||
|| fluid.fallingFluid()
|
||||
|| !fluid.channel()
|
||||
|| !fluid.fluidOwned()) {
|
||||
continue;
|
||||
}
|
||||
HydrologyTerrainSample basis = sampleTerrainBasis(sample.x(), sample.z());
|
||||
HydrologyTerrainSample terrain = sampleTerrain(sample.x(), sample.z());
|
||||
if (terrain == null
|
||||
|| basis == null
|
||||
|| (terrain.naturalHeight() - fluid.fluidHeadY() > permittedSurfaceIncision(terrain)
|
||||
&& basis.naturalHeight() - fluid.fluidHeadY() <= permittedSurfaceIncision(basis))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean surfaceIncisionContained(RiverCourse course, RiverFootprint footprint) {
|
||||
Objects.requireNonNull(course, "course");
|
||||
Objects.requireNonNull(footprint, "footprint");
|
||||
if (course.type() != RiverCourseType.SURFACE) {
|
||||
return true;
|
||||
}
|
||||
for (HydrologyColumnSample sample : footprint.columns().values()) {
|
||||
for (HydrologyColumnLayer fluid : sample.layers()) {
|
||||
if (fluid.feature().courseId() != course.id()
|
||||
|| fluid.feature().type() == HydrologyFeatureType.MOUTH
|
||||
|| fluid.oceanApron()
|
||||
|| fluid.fallingFluid()
|
||||
|| !fluid.channel()
|
||||
|| !fluid.fluidOwned()) {
|
||||
continue;
|
||||
}
|
||||
HydrologyTerrainSample basis = sampleTerrainBasis(sample.x(), sample.z());
|
||||
HydrologyTerrainSample terrain = sampleTerrain(sample.x(), sample.z());
|
||||
if (basis == null
|
||||
|| terrain == null
|
||||
|| (terrain.naturalHeight() - fluid.fluidHeadY()
|
||||
> settings.surface().maximumIncision()
|
||||
&& basis.naturalHeight() - fluid.fluidHeadY()
|
||||
<= permittedSurfaceIncision(basis))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean surfaceBanksContained(RiverCourse course) {
|
||||
Objects.requireNonNull(course, "course");
|
||||
if (course.type() != RiverCourseType.SURFACE) {
|
||||
return true;
|
||||
}
|
||||
RiverFootprint footprint = compileCourse(course);
|
||||
if (!surfaceCenterlinePublished(course, footprint)
|
||||
|| !surfaceTransitionCenterlinePublished(course, footprint)) {
|
||||
return false;
|
||||
}
|
||||
for (HydrologyColumnSample sample : footprint.columns().values()) {
|
||||
HydrologyColumnLayer fluid = sample.primarySurfaceFluidLayer().orElse(null);
|
||||
if (fluid == null || fluid.oceanApron() || fluid.fallingFluid()) {
|
||||
continue;
|
||||
}
|
||||
for (int[] offset : HORIZONTAL_NEIGHBORS) {
|
||||
HydrologyColumnSample neighbor = footprint.sample(
|
||||
sample.x() + offset[0],
|
||||
sample.z() + offset[1]
|
||||
).orElse(null);
|
||||
if (ownsSurfaceFluid(neighbor, course.id())) {
|
||||
continue;
|
||||
}
|
||||
if (neighbor != null
|
||||
&& fluid.fluidHeadY() <= neighbor.seaLevel()
|
||||
&& (neighbor.ocean() || neighbor.naturalHeight() <= neighbor.seaLevel())) {
|
||||
continue;
|
||||
}
|
||||
if (neighbor == null || neighbor.terrainHeight() <= fluid.fluidHeadY()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean surfaceCenterlinePublished(RiverCourse course, RiverFootprint footprint) {
|
||||
for (HydraulicSegment segment : course.segments()) {
|
||||
if (!segment.type().isSurface()) {
|
||||
continue;
|
||||
}
|
||||
for (HydrologyPoint point : segment.centerline()) {
|
||||
HydrologyColumnSample sample = footprint.sample(point.x(), point.z()).orElse(null);
|
||||
if (ownsSurfaceFluid(sample, course.id())
|
||||
|| segment.type() == HydrologyFeatureType.MOUTH
|
||||
&& ownsOceanApron(sample, course.id())) {
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean surfaceTransitionCenterlinePublished(RiverCourse course, RiverFootprint footprint) {
|
||||
for (int segmentIndex = 0; segmentIndex < course.segments().size(); segmentIndex++) {
|
||||
HydraulicSegment segment = course.segments().get(segmentIndex);
|
||||
if (!terrainRoofedSurfaceTransition(course, segment)) {
|
||||
continue;
|
||||
}
|
||||
List<HydrologyPoint> centerline = continuousCenterline(segment);
|
||||
for (int pointIndex = 0; pointIndex < centerline.size(); pointIndex++) {
|
||||
HydrologyPoint point = centerline.get(pointIndex);
|
||||
HydrologyColumnSample sample = footprint.sample(point.x(), point.z()).orElse(null);
|
||||
if (ownsTransitionFluid(sample, course.id(), segment.id(), point.y())) {
|
||||
continue;
|
||||
}
|
||||
boolean joinedStart = pointIndex == 0
|
||||
&& segmentIndex > 0
|
||||
&& segmentsJoin(course.segments().get(segmentIndex - 1), segment);
|
||||
boolean joinedEnd = pointIndex == centerline.size() - 1
|
||||
&& segmentIndex + 1 < course.segments().size()
|
||||
&& segmentsJoin(segment, course.segments().get(segmentIndex + 1));
|
||||
if ((joinedStart || joinedEnd) && ownsCourseFluid(sample, course.id(), point.y())) {
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean ownsTransitionFluid(
|
||||
HydrologyColumnSample sample,
|
||||
long courseId,
|
||||
long segmentId,
|
||||
int fluidHead
|
||||
) {
|
||||
if (sample == null) {
|
||||
return false;
|
||||
}
|
||||
for (HydrologyColumnLayer layer : sample.layers()) {
|
||||
if (layer.feature().courseId() == courseId
|
||||
&& layer.feature().segmentId() == segmentId
|
||||
&& layer.fluidHeadY() == fluidHead
|
||||
&& layer.channel()
|
||||
&& layer.connectedFluid()
|
||||
&& layer.fluidOwned()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean ownsCourseFluid(HydrologyColumnSample sample, long courseId, int fluidHead) {
|
||||
if (sample == null) {
|
||||
return false;
|
||||
}
|
||||
for (HydrologyColumnLayer layer : sample.layers()) {
|
||||
if (layer.feature().courseId() == courseId
|
||||
&& layer.fluidHeadY() == fluidHead
|
||||
&& layer.channel()
|
||||
&& layer.connectedFluid()
|
||||
&& layer.fluidOwned()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private int permittedSurfaceIncision(HydrologyTerrainSample terrain) {
|
||||
int maximumIncision = settings.surface().maximumIncision();
|
||||
return Math.min(
|
||||
maximumIncision,
|
||||
(int) StrictMath.floor(maximumIncision * terrain.incisionMultiplier())
|
||||
);
|
||||
}
|
||||
|
||||
private boolean ownsSurfaceFluid(HydrologyColumnSample sample, long courseId) {
|
||||
if (sample == null) {
|
||||
return false;
|
||||
}
|
||||
HydrologyColumnLayer fluid = sample.primarySurfaceFluidLayer().orElse(null);
|
||||
return fluid != null
|
||||
&& fluid.fluidOwned()
|
||||
&& fluid.feature().courseId() == courseId;
|
||||
}
|
||||
|
||||
private boolean ownsOceanApron(HydrologyColumnSample sample, long courseId) {
|
||||
if (sample == null) {
|
||||
return false;
|
||||
}
|
||||
for (HydrologyColumnLayer layer : sample.layers()) {
|
||||
if (layer.oceanApron() && layer.feature().courseId() == courseId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<HydrologyPoint> surfaceCourseCenterline(RiverCourse course) {
|
||||
ArrayList<HydrologyPoint> points = new ArrayList<>();
|
||||
for (HydraulicSegment segment : course.segments()) {
|
||||
if (!segment.type().isSurface() && segment.type() != HydrologyFeatureType.RIDGE_BORE) {
|
||||
break;
|
||||
}
|
||||
for (HydrologyPoint point : segment.centerline()) {
|
||||
if (points.isEmpty()
|
||||
|| point.x() != points.getLast().x()
|
||||
|| point.z() != points.getLast().z()) {
|
||||
points.add(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rasterCenterline(points);
|
||||
}
|
||||
|
||||
private List<HydrologyPoint> rasterCenterline(List<HydrologyPoint> points) {
|
||||
ArrayList<HydrologyPoint> raster = new ArrayList<>();
|
||||
for (int pairIndex = 0; pairIndex < points.size() - 1; pairIndex++) {
|
||||
HydrologyPoint start = points.get(pairIndex);
|
||||
HydrologyPoint end = points.get(pairIndex + 1);
|
||||
int steps = Math.max(Math.abs(end.x() - start.x()), Math.abs(end.z() - start.z()));
|
||||
if (steps == 0) {
|
||||
continue;
|
||||
}
|
||||
int firstStep = raster.isEmpty() ? 0 : 1;
|
||||
for (int step = firstStep; step <= steps; step++) {
|
||||
double progress = step / (double) steps;
|
||||
raster.add(new HydrologyPoint(
|
||||
(int) StrictMath.round(start.x() + (end.x() - start.x()) * progress),
|
||||
(int) StrictMath.round(start.y() + (end.y() - start.y()) * progress),
|
||||
(int) StrictMath.round(start.z() + (end.z() - start.z()) * progress)
|
||||
));
|
||||
}
|
||||
}
|
||||
return List.copyOf(raster);
|
||||
}
|
||||
|
||||
private boolean ownedNear(Set<Long> cells, int x, int z, int radius) {
|
||||
for (int deltaZ = -radius; deltaZ <= radius; deltaZ++) {
|
||||
for (int deltaX = -radius; deltaX <= radius; deltaX++) {
|
||||
if (cells.contains(RiverFootprint.pack(x + deltaX, z + deltaZ))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private int cruiseWidth(Set<Long> cells, List<HydrologyPoint> raster, int maximumWidth) {
|
||||
int start = Math.min(raster.size() - 2, Math.max(48, maximumWidth * 4));
|
||||
int end = Math.min(raster.size() - 1, Math.max(start + 16, maximumWidth * 6));
|
||||
ArrayList<Integer> widths = new ArrayList<>();
|
||||
for (int offset = start; offset <= end; offset++) {
|
||||
if (offset <= 0 || offset >= raster.size() - 1) {
|
||||
continue;
|
||||
}
|
||||
int width = crossSectionWidth(
|
||||
cells,
|
||||
raster.get(offset),
|
||||
raster.get(offset - 1),
|
||||
raster.get(offset + 1),
|
||||
maximumWidth
|
||||
);
|
||||
if (width > 0) {
|
||||
widths.add(width);
|
||||
}
|
||||
}
|
||||
if (widths.isEmpty()) {
|
||||
return maximumWidth;
|
||||
}
|
||||
widths.sort(Integer::compare);
|
||||
return Math.max(1, widths.get(widths.size() / 2));
|
||||
}
|
||||
|
||||
private double endpointTransitionLength(
|
||||
Set<Long> cells,
|
||||
List<HydrologyPoint> raster,
|
||||
int maximumWidth,
|
||||
int cruiseWidth
|
||||
) {
|
||||
int endpointWidth = crossSectionWidth(
|
||||
cells,
|
||||
raster.getFirst(),
|
||||
raster.getFirst(),
|
||||
raster.get(1),
|
||||
maximumWidth
|
||||
);
|
||||
int widthTolerance = Math.max(1, (int) StrictMath.ceil(cruiseWidth * 0.1D));
|
||||
if (Math.abs(endpointWidth - cruiseWidth) <= widthTolerance) {
|
||||
return 0D;
|
||||
}
|
||||
int search = Math.max(32, cruiseWidth * 6);
|
||||
double distance = 0D;
|
||||
int pointIndex = 1;
|
||||
while (pointIndex < raster.size() && distance <= search) {
|
||||
int previousIndex = pointIndex - 1;
|
||||
int nextIndex = Math.min(raster.size() - 1, pointIndex + 1);
|
||||
HydrologyPoint previous = raster.get(previousIndex);
|
||||
HydrologyPoint station = raster.get(pointIndex);
|
||||
HydrologyPoint next = raster.get(nextIndex);
|
||||
distance += StrictMath.hypot(station.x() - previous.x(), station.z() - previous.z());
|
||||
int width = crossSectionWidth(cells, station, previous, next, maximumWidth);
|
||||
if (Math.abs(width - cruiseWidth) <= widthTolerance) {
|
||||
return distance;
|
||||
}
|
||||
pointIndex++;
|
||||
}
|
||||
return distance;
|
||||
}
|
||||
|
||||
private int crossSectionWidth(
|
||||
Set<Long> cells,
|
||||
HydrologyPoint center,
|
||||
HydrologyPoint before,
|
||||
HydrologyPoint after,
|
||||
int maximumWidth
|
||||
) {
|
||||
double tangentX = after.x() - before.x();
|
||||
double tangentZ = after.z() - before.z();
|
||||
double tangentLength = StrictMath.hypot(tangentX, tangentZ);
|
||||
if (tangentLength == 0D) {
|
||||
tangentX = 1D;
|
||||
tangentLength = 1D;
|
||||
}
|
||||
double perpendicularX = -tangentZ / tangentLength;
|
||||
double perpendicularZ = tangentX / tangentLength;
|
||||
int limit = Math.max(4, maximumWidth * 2);
|
||||
HashSet<Long> sampled = new HashSet<>();
|
||||
for (int offset = -limit; offset <= limit; offset++) {
|
||||
int x = (int) StrictMath.round(center.x() + perpendicularX * offset);
|
||||
int z = (int) StrictMath.round(center.z() + perpendicularZ * offset);
|
||||
long packed = RiverFootprint.pack(x, z);
|
||||
if (cells.contains(packed)) {
|
||||
sampled.add(packed);
|
||||
}
|
||||
}
|
||||
return sampled.size();
|
||||
}
|
||||
|
||||
private ValidationCourseRaster compileValidationCourse(RiverCourse course) {
|
||||
CourseRasterKey rasterKey = new CourseRasterKey(
|
||||
course.id(),
|
||||
@@ -825,7 +423,6 @@ final class HydrologyFootprintCompiler {
|
||||
return false;
|
||||
}
|
||||
return segment.type().isSurface()
|
||||
|| segment.type() == HydrologyFeatureType.RIDGE_BORE
|
||||
|| segment.type() == HydrologyFeatureType.UNDERGROUND_POOL
|
||||
|| segment.type() == HydrologyFeatureType.UNDERGROUND_DROP
|
||||
|| segment.type() == HydrologyFeatureType.SINKHOLE
|
||||
@@ -1145,21 +742,15 @@ final class HydrologyFootprintCompiler {
|
||||
point.x(),
|
||||
point.z(),
|
||||
segment.id(),
|
||||
settings.surface().minimumTerrainBlendWidth(),
|
||||
settings.surface().maximumTerrainBlendWidth()
|
||||
settings.surface().banks().minimumBlendWidth(),
|
||||
settings.surface().banks().maximumBlendWidth()
|
||||
);
|
||||
if (type == HydrologyFeatureType.WATERFALL) {
|
||||
shoreWidth = Math.min(2.5D, shoreWidth);
|
||||
gradingWidth = Math.min(gradingWidth, Math.max(8D, channelRadius * 4D));
|
||||
}
|
||||
}
|
||||
if (type == HydrologyFeatureType.RIDGE_BORE
|
||||
|| course.type() == RiverCourseType.SURFACE
|
||||
&& type == HydrologyFeatureType.UNDERGROUND_DROP) {
|
||||
ceiling = fluidHead + settings.surface().ridgeTunnelHeadroom();
|
||||
archedChannel = !falling;
|
||||
organicBoundary = true;
|
||||
} else if (type == HydrologyFeatureType.UNDERGROUND_POOL
|
||||
if (type == HydrologyFeatureType.UNDERGROUND_POOL
|
||||
|| type == HydrologyFeatureType.UNDERGROUND_DROP
|
||||
|| type == HydrologyFeatureType.SINKHOLE) {
|
||||
ceiling = fluidHead + geometrySampler.sample(
|
||||
@@ -1213,7 +804,7 @@ final class HydrologyFootprintCompiler {
|
||||
if (course.type() == RiverCourseType.SURFACE && type.isSurface()) {
|
||||
gradingWidth = Math.max(
|
||||
gradingWidth,
|
||||
Math.min(settings.surface().maximumTerrainBlendWidth(), channelRadius * 2D)
|
||||
Math.min(settings.surface().banks().maximumBlendWidth(), channelRadius * 2D)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1807,8 +1398,7 @@ final class HydrologyFootprintCompiler {
|
||||
|
||||
private boolean terrainRoofedSurfaceTransition(RiverCourse course, HydraulicSegment segment) {
|
||||
return course.type() == RiverCourseType.SURFACE
|
||||
&& (segment.type() == HydrologyFeatureType.RIDGE_BORE
|
||||
|| segment.type() == HydrologyFeatureType.UNDERGROUND_POOL
|
||||
&& (segment.type() == HydrologyFeatureType.UNDERGROUND_POOL
|
||||
|| segment.type() == HydrologyFeatureType.UNDERGROUND_DROP);
|
||||
}
|
||||
|
||||
@@ -1943,8 +1533,7 @@ final class HydrologyFootprintCompiler {
|
||||
}
|
||||
|
||||
private HydrologyPlannerSettings.ChannelShape channelShape(HydrologyFeatureType type) {
|
||||
if (type.isGrotto() || type == HydrologyFeatureType.DEEP_POOL
|
||||
|| type == HydrologyFeatureType.RIDGE_BORE) {
|
||||
if (type.isGrotto() || type == HydrologyFeatureType.DEEP_POOL) {
|
||||
return settings.geometry().grottos();
|
||||
}
|
||||
if (type.isUnderground() || type == HydrologyFeatureType.DEEP_CHANNEL) {
|
||||
|
||||
@@ -37,10 +37,7 @@ public interface HydrologyGeometrySampler {
|
||||
return value;
|
||||
}
|
||||
|
||||
static HydrologyGeometrySampler deterministic(
|
||||
long worldSeed,
|
||||
HydrologyTerrainSampler terrainSampler
|
||||
) {
|
||||
static HydrologyGeometrySampler deterministic(HydrologyTerrainSampler terrainSampler) {
|
||||
Objects.requireNonNull(terrainSampler, "terrainSampler");
|
||||
return request -> {
|
||||
if (request.field() == Field.UNDERGROUND_FLUID_LEVEL) {
|
||||
@@ -52,7 +49,6 @@ public interface HydrologyGeometrySampler {
|
||||
}
|
||||
if (request.field() == Field.SURFACE_WIDTH
|
||||
|| request.field() == Field.SURFACE_DEPTH
|
||||
|| request.field() == Field.SURFACE_INSET
|
||||
|| request.field() == Field.UNDERGROUND_WIDTH
|
||||
|| request.field() == Field.UNDERGROUND_DEPTH) {
|
||||
return request.minimum();
|
||||
@@ -64,11 +60,6 @@ public interface HydrologyGeometrySampler {
|
||||
request.z(),
|
||||
0x424c454e44L
|
||||
);
|
||||
case TARGET_POOL_LENGTH -> HydrologyHash.mix(
|
||||
worldSeed,
|
||||
request.stableId(),
|
||||
0x504f4f4cL
|
||||
);
|
||||
case UNDERGROUND_HEADROOM -> HydrologyHash.mix(
|
||||
request.stableId(),
|
||||
request.x(),
|
||||
@@ -85,9 +76,7 @@ public interface HydrologyGeometrySampler {
|
||||
enum Field {
|
||||
SURFACE_WIDTH,
|
||||
SURFACE_DEPTH,
|
||||
SURFACE_INSET,
|
||||
SURFACE_BLEND_WIDTH,
|
||||
TARGET_POOL_LENGTH,
|
||||
UNDERGROUND_FLUID_LEVEL,
|
||||
UNDERGROUND_WIDTH,
|
||||
UNDERGROUND_DEPTH,
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -35,9 +35,9 @@ public record HydrologyPlannerSettings(
|
||||
Source undergroundSources = new Source(true, 0.25D, Integer.MIN_VALUE, 0, 1, 512);
|
||||
return new HydrologyPlannerSettings(
|
||||
63,
|
||||
new Routing(2048, 64, 8, 8192, 8192, new Branching(384, 192), 1.5D, 24D, 2D, 0.2D),
|
||||
new Surface(true, surfaceSources, 4, 8, 2, 4, 1, 1, 10, 1.5D, 4, 32, false, 1, 1, Banks.defaults()),
|
||||
new Hydraulics(80, 180, 1, 7, 24, 8),
|
||||
new Routing(2048, 64, 8192, 8192, 384, 192, 1.5D, 24D, 2D, 0.2D),
|
||||
new Surface(true, surfaceSources, 4, 8, 2, 4, 10, 1.5D, Banks.defaults()),
|
||||
new Hydraulics(8),
|
||||
new Underground(true, undergroundSources, -48, 72, 3, 8, 1, 3, 6, 14, true),
|
||||
new Outlets(
|
||||
true,
|
||||
@@ -97,7 +97,7 @@ public record HydrologyPlannerSettings(
|
||||
int radius = 0;
|
||||
int routeDisplacement = Math.multiplyExact(routing.sampleSpacing(), 3);
|
||||
if (surface.enabled() && surface.sources().enabled()) {
|
||||
int blendWidth = Math.max(surface.maximumTerrainBlendWidth(), surface.banks().maximumBlendWidth());
|
||||
int blendWidth = surface.banks().maximumBlendWidth();
|
||||
int surfaceRadius = (int) StrictMath.ceil(
|
||||
surface.maximumWidth() / 2D + surface.shoreWidth() + blendWidth
|
||||
);
|
||||
@@ -105,7 +105,7 @@ public record HydrologyPlannerSettings(
|
||||
surfaceRadius,
|
||||
(int) StrictMath.ceil(geometry.drops().basinWidth(
|
||||
geometry.drops().flowWidth(surface.maximumWidth())
|
||||
) / 2D + surface.shoreWidth() + surface.maximumTerrainBlendWidth())
|
||||
) / 2D + surface.shoreWidth() + blendWidth)
|
||||
);
|
||||
surfaceRadius = Math.max(surfaceRadius, outlets.coastalGrotto().horizontalRadius());
|
||||
surfaceRadius = Math.max(surfaceRadius, outlets.inlandGrotto().horizontalRadius());
|
||||
@@ -144,18 +144,17 @@ public record HydrologyPlannerSettings(
|
||||
public record Routing(
|
||||
int tileSize,
|
||||
int sampleSpacing,
|
||||
int refinementSpacing,
|
||||
int maximumRouteNodes,
|
||||
int maximumRouteLength,
|
||||
Branching branching,
|
||||
int minimumSurfaceCourseLength,
|
||||
int minimumUndergroundCourseLength,
|
||||
double valleyPreference,
|
||||
double uphillPenalty,
|
||||
double slopePenalty,
|
||||
double confluenceAttraction
|
||||
) {
|
||||
public Routing {
|
||||
if (tileSize < 32 || sampleSpacing < 4 || refinementSpacing < 1 || branching == null
|
||||
|| tileSize % sampleSpacing != 0 || sampleSpacing % refinementSpacing != 0) {
|
||||
if (tileSize < 32 || sampleSpacing < 4 || tileSize % sampleSpacing != 0) {
|
||||
throw new IllegalArgumentException("Routing sizes must form an exact bounded tile lattice.");
|
||||
}
|
||||
int latticeWidth = tileSize / sampleSpacing + 1;
|
||||
@@ -166,8 +165,12 @@ public record HydrologyPlannerSettings(
|
||||
if (maximumRouteNodes < latticeNodes || maximumRouteNodes > 1_000_000 || maximumRouteLength < 1) {
|
||||
throw new IllegalArgumentException("maximumRouteNodes must contain the complete lattice and remain bounded.");
|
||||
}
|
||||
if (branching.minimumSurfaceCourseLength() > maximumRouteLength
|
||||
|| branching.minimumUndergroundCourseLength() > maximumRouteLength) {
|
||||
if (minimumSurfaceCourseLength < 0 || minimumSurfaceCourseLength > 32_768
|
||||
|| minimumUndergroundCourseLength < 0 || minimumUndergroundCourseLength > 32_768) {
|
||||
throw new IllegalArgumentException("Hydrology course lengths are invalid.");
|
||||
}
|
||||
if (minimumSurfaceCourseLength > maximumRouteLength
|
||||
|| minimumUndergroundCourseLength > maximumRouteLength) {
|
||||
throw new IllegalArgumentException("Minimum course lengths cannot exceed maximum route length.");
|
||||
}
|
||||
requireFiniteNonNegative(valleyPreference, "valleyPreference");
|
||||
@@ -175,17 +178,17 @@ public record HydrologyPlannerSettings(
|
||||
requireFiniteNonNegative(slopePenalty, "slopePenalty");
|
||||
requireFiniteNonNegative(confluenceAttraction, "confluenceAttraction");
|
||||
}
|
||||
}
|
||||
|
||||
public record Branching(
|
||||
int minimumSurfaceCourseLength,
|
||||
int minimumUndergroundCourseLength
|
||||
) {
|
||||
public Branching {
|
||||
if (minimumSurfaceCourseLength < 0 || minimumSurfaceCourseLength > 32_768
|
||||
|| minimumUndergroundCourseLength < 0 || minimumUndergroundCourseLength > 32_768) {
|
||||
throw new IllegalArgumentException("Hydrology branching lengths are invalid.");
|
||||
// Route refinement is derived from the lattice, never authored.
|
||||
public static int refinementSpacing(int sampleSpacing) {
|
||||
if (sampleSpacing % 4 == 0) {
|
||||
return 4;
|
||||
}
|
||||
return sampleSpacing % 2 == 0 ? 2 : 1;
|
||||
}
|
||||
|
||||
public int refinementSpacing() {
|
||||
return refinementSpacing(sampleSpacing);
|
||||
}
|
||||
|
||||
public int minimumCourseLength(boolean surface) {
|
||||
@@ -221,15 +224,8 @@ public record HydrologyPlannerSettings(
|
||||
int maximumWidth,
|
||||
int minimumDepth,
|
||||
int maximumDepth,
|
||||
int minimumSurfaceInset,
|
||||
int maximumSurfaceInset,
|
||||
int maximumIncision,
|
||||
double shoreWidth,
|
||||
int minimumTerrainBlendWidth,
|
||||
int maximumTerrainBlendWidth,
|
||||
boolean ridgeTunnelsEnabled,
|
||||
int maximumRidgeTunnelLength,
|
||||
int ridgeTunnelHeadroom,
|
||||
Banks banks
|
||||
) {
|
||||
public Surface {
|
||||
@@ -238,14 +234,9 @@ public record HydrologyPlannerSettings(
|
||||
}
|
||||
requireRange(minimumWidth, maximumWidth, 1, 256, "surface width");
|
||||
requireRange(minimumDepth, maximumDepth, 1, 64, "surface depth");
|
||||
requireRange(minimumSurfaceInset, maximumSurfaceInset, 1, 64, "surface inset");
|
||||
if (maximumIncision < 0 || !Double.isFinite(shoreWidth) || shoreWidth < 0D || shoreWidth > 32D) {
|
||||
throw new IllegalArgumentException("Surface incision and shore width are invalid.");
|
||||
}
|
||||
requireRange(minimumTerrainBlendWidth, maximumTerrainBlendWidth, 0, 128, "terrain blend width");
|
||||
if (maximumRidgeTunnelLength < 0 || ridgeTunnelHeadroom < 0) {
|
||||
throw new IllegalArgumentException("Ridge tunnel bounds cannot be negative.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,21 +271,11 @@ public record HydrologyPlannerSettings(
|
||||
}
|
||||
}
|
||||
|
||||
public record Hydraulics(
|
||||
int minimumTargetPoolLength,
|
||||
int maximumTargetPoolLength,
|
||||
int riffleDrop,
|
||||
int maximumGradualDrop,
|
||||
int maximumGradualLength,
|
||||
int waterfallMinimumDrop
|
||||
) {
|
||||
// The routing gates read one hydraulic threshold: the drop that makes a reach a waterfall.
|
||||
public record Hydraulics(int waterfallMinimumDrop) {
|
||||
public Hydraulics {
|
||||
requireRange(minimumTargetPoolLength, maximumTargetPoolLength, 1, 4096, "target pool length");
|
||||
if (riffleDrop < 0 || maximumGradualDrop < riffleDrop
|
||||
|| maximumGradualLength < Math.max(1, maximumGradualDrop)
|
||||
|| waterfallMinimumDrop != maximumGradualDrop + 1
|
||||
|| minimumTargetPoolLength < maximumGradualLength) {
|
||||
throw new IllegalArgumentException("Hydraulic transition thresholds are inconsistent.");
|
||||
if (waterfallMinimumDrop < 1) {
|
||||
throw new IllegalArgumentException("waterfallMinimumDrop must be at least one block.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ public record RiverCourse(
|
||||
HydraulicSegment lip = segments.get(segments.size() - 3);
|
||||
HydraulicSegment sinkhole = segments.get(segments.size() - 2);
|
||||
HydraulicSegment grotto = segments.getLast();
|
||||
if (!lip.type().isSurface() && lip.type() != HydrologyFeatureType.RIDGE_BORE
|
||||
if (!lip.type().isSurface()
|
||||
|| sinkhole.type() != HydrologyFeatureType.SINKHOLE
|
||||
|| sinkhole.drop() <= 0
|
||||
|| !sinkhole.receivingPool()
|
||||
|
||||
+7
-36
@@ -65,9 +65,7 @@ public final class IrisHydrologyRuntime implements AutoCloseable {
|
||||
private static final int BIOME_PATCH_SCALE = 32;
|
||||
private static final long SURFACE_WIDTH_SALT = 0x5355524657494454L;
|
||||
private static final long SURFACE_DEPTH_SALT = 0x5355524644455054L;
|
||||
private static final long SURFACE_INSET_SALT = 0x53555246494e5345L;
|
||||
private static final long SURFACE_BLEND_SALT = 0x53555246424c454eL;
|
||||
private static final long TARGET_POOL_SALT = 0x544152474554504fL;
|
||||
private static final long UNDERGROUND_LEVEL_SALT = 0x554e4445524c564cL;
|
||||
private static final long UNDERGROUND_WIDTH_SALT = 0x554e444552574944L;
|
||||
private static final long UNDERGROUND_DEPTH_SALT = 0x554e444552444550L;
|
||||
@@ -556,9 +554,7 @@ public final class IrisHydrologyRuntime implements AutoCloseable {
|
||||
IrisUndergroundRiverConfig underground = rivers.getUnderground();
|
||||
ProceduralStream<Double> surfaceWidth = styledStream(context, channel.getWidth(), SURFACE_WIDTH_SALT);
|
||||
ProceduralStream<Double> surfaceDepth = styledStream(context, channel.getDepth(), SURFACE_DEPTH_SALT);
|
||||
ProceduralStream<Double> surfaceInset = ProceduralStream.ofDouble((x, z) -> (double) channel.getInset());
|
||||
ProceduralStream<Double> surfaceBlend = ProceduralStream.ofDouble((x, z) -> (double) banks.getMaximumBlendWidth());
|
||||
ProceduralStream<Double> targetPool = ProceduralStream.ofDouble((x, z) -> 128D);
|
||||
ProceduralStream<Double> undergroundLevel = styledStream(
|
||||
context,
|
||||
underground.getFluidLevel(),
|
||||
@@ -589,9 +585,7 @@ public final class IrisHydrologyRuntime implements AutoCloseable {
|
||||
ProceduralStream<Double> stream = switch (request.field()) {
|
||||
case SURFACE_WIDTH -> surfaceWidth;
|
||||
case SURFACE_DEPTH -> surfaceDepth;
|
||||
case SURFACE_INSET -> surfaceInset;
|
||||
case SURFACE_BLEND_WIDTH -> surfaceBlend;
|
||||
case TARGET_POOL_LENGTH -> targetPool;
|
||||
case UNDERGROUND_FLUID_LEVEL -> undergroundLevel;
|
||||
case UNDERGROUND_WIDTH -> undergroundWidth;
|
||||
case UNDERGROUND_DEPTH -> undergroundDepth;
|
||||
@@ -639,7 +633,6 @@ public final class IrisHydrologyRuntime implements AutoCloseable {
|
||||
IrisUndergroundRiverConfig underground = rivers.getUnderground();
|
||||
IrisUndergroundRiverSourceConfig undergroundSources = underground.getSources();
|
||||
int routeNodes = maximumRouteNodes(routing);
|
||||
int refinementSpacing = refinementSpacing(routing.getSampleSpacing());
|
||||
int minimumWorldY = dimension.getMinHeight();
|
||||
boolean riversEnabled = rivers.isEnabled();
|
||||
boolean surfaceEnabled = riversEnabled && surface.isEnabled();
|
||||
@@ -647,13 +640,10 @@ public final class IrisHydrologyRuntime implements AutoCloseable {
|
||||
HydrologyPlannerSettings.Routing plannerRouting = new HydrologyPlannerSettings.Routing(
|
||||
routing.getTileSize(),
|
||||
routing.getSampleSpacing(),
|
||||
refinementSpacing,
|
||||
routeNodes,
|
||||
routing.getMaximumRouteLength(),
|
||||
new HydrologyPlannerSettings.Branching(
|
||||
routing.getMinimumSurfaceCourseLength(),
|
||||
routing.getMinimumUndergroundCourseLength()
|
||||
),
|
||||
routing.getMinimumSurfaceCourseLength(),
|
||||
routing.getMinimumUndergroundCourseLength(),
|
||||
routing.getValleyPreference(),
|
||||
routing.getUphillPenalty(),
|
||||
routing.getSlopePenalty(),
|
||||
@@ -674,15 +664,8 @@ public final class IrisHydrologyRuntime implements AutoCloseable {
|
||||
maximumInt(channel.getWidth()),
|
||||
minimumInt(channel.getDepth()),
|
||||
maximumInt(channel.getDepth()),
|
||||
channel.getInset(),
|
||||
channel.getInset(),
|
||||
channel.getMaximumIncision(),
|
||||
banks.getShoreWidth(),
|
||||
banks.getMinimumBlendWidth(),
|
||||
banks.getMaximumBlendWidth(),
|
||||
false,
|
||||
1,
|
||||
1,
|
||||
new HydrologyPlannerSettings.Banks(
|
||||
channel.getInset(),
|
||||
banks.getFreeboard(),
|
||||
@@ -697,14 +680,8 @@ public final class IrisHydrologyRuntime implements AutoCloseable {
|
||||
banks.isExposeCutStrata()
|
||||
)
|
||||
);
|
||||
HydrologyPlannerSettings.Hydraulics plannerHydraulics = new HydrologyPlannerSettings.Hydraulics(
|
||||
80,
|
||||
180,
|
||||
1,
|
||||
flow.getWaterfallMinimumDrop() - 1,
|
||||
24,
|
||||
flow.getWaterfallMinimumDrop()
|
||||
);
|
||||
HydrologyPlannerSettings.Hydraulics plannerHydraulics =
|
||||
new HydrologyPlannerSettings.Hydraulics(flow.getWaterfallMinimumDrop());
|
||||
HydrologyPlannerSettings.Source plannerUndergroundSources = new HydrologyPlannerSettings.Source(
|
||||
undergroundEnabled,
|
||||
undergroundSources.getDensity(),
|
||||
@@ -760,17 +737,10 @@ public final class IrisHydrologyRuntime implements AutoCloseable {
|
||||
plannerUnderground,
|
||||
plannerOutlets,
|
||||
plannerGeometry,
|
||||
deepFluids(hydrology.getDeepFluids(), minimumWorldY, routing, refinementSpacing)
|
||||
deepFluids(hydrology.getDeepFluids(), minimumWorldY, routing, plannerRouting.refinementSpacing())
|
||||
);
|
||||
}
|
||||
|
||||
static int refinementSpacing(int sampleSpacing) {
|
||||
if (sampleSpacing % 4 == 0) {
|
||||
return 4;
|
||||
}
|
||||
return sampleSpacing % 2 == 0 ? 2 : 1;
|
||||
}
|
||||
|
||||
private static HydrologyPlannerSettings.Geometry geometry(
|
||||
IrisRiverGeometryConfig geometry,
|
||||
HydrologyPlannerSettings.ChannelShape surfaceShape
|
||||
@@ -877,7 +847,8 @@ public final class IrisHydrologyRuntime implements AutoCloseable {
|
||||
return 0;
|
||||
}
|
||||
int configuredLength = Math.max(
|
||||
refinementSpacing(routing.getSampleSpacing()), configuration.getSpacing() / 3);
|
||||
HydrologyPlannerSettings.Routing.refinementSpacing(routing.getSampleSpacing()),
|
||||
configuration.getSpacing() / 3);
|
||||
return Math.min(configuredLength, routing.getTileSize() / 2);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user