Merged fast biome locate

- Biome locate searches the biomes chunks are filled with, filtered by region and biome hints
- Locate checks hydrology only for river targets
This commit is contained in:
Brian Neumann-Fopiano committed 2026-10-15 19:27:44 -04:00
commit bf99edce87
10 files changed
+960 -85

No files matched your search

@@ -669,6 +669,10 @@ public class IrisBiome extends IrisRegistrant implements Rarity {
return IrisBiomeDerivatives.getGroundBiomeKey(this, rng, engine, x, y, z);
}
public KList<String> getVisibleBiomeKeys(Engine engine) {
return IrisBiomeDerivatives.getVisibleBiomeKeys(this, engine);
}
public Color getColor(Engine engine, RenderType type) {
return IrisBiomeColorRenderer.getColor(this, engine, type);
}
@@ -144,6 +144,20 @@ final class IrisBiomeDerivatives {
return physicalBiomeKey(engine, logicalKey);
}
static KList<String> getVisibleBiomeKeys(IrisBiome biome, Engine engine) {
KList<String> keys = new KList<>();
if (biome.getBiomeScatter().isEmpty()) {
keys.add(physicalBiomeKey(engine, biome.getDerivativeKey()));
}
for (String scatter : biome.getBiomeScatter()) {
keys.add(physicalBiomeKey(engine, namespacedBiomeKey(scatter)));
}
for (String scatter : biome.getBiomeSkyScatter()) {
keys.add(physicalBiomeKey(engine, namespacedBiomeKey(scatter)));
}
return keys;
}
static IrisBiomeCustom getCustomBiome(IrisBiome biome, RNG rng, Engine engine, double x, double y, double z) {
KList<IrisBiomeCustom> customDerivitives = biome.getCustomDerivitives();
@@ -0,0 +1,183 @@
package art.arcane.iris.generation.locator;
import art.arcane.iris.generation.biome.IrisBiome;
import art.arcane.iris.generation.hydrology.IrisRiverPolicy;
import art.arcane.iris.generation.image.IrisImageMapApplication;
import art.arcane.iris.generation.image.IrisImageMapRuntime;
import art.arcane.iris.generation.runtime.Engine;
import art.arcane.iris.generation.runtime.IrisComplex;
import art.arcane.iris.generation.subterrain.IrisSubterrainFeature;
import art.arcane.iris.generation.subterrain.SubterrainCell;
import art.arcane.iris.generation.subterrain.SubterrainPlanner;
import art.arcane.iris.generation.terrain.IrisDimension;
import art.arcane.iris.generation.terrain.IrisDimensionCarvingEntry;
import art.arcane.iris.generation.terrain.IrisDimensionCarvingResolver;
import art.arcane.iris.generation.terrain.IrisRegion;
import java.util.Objects;
import java.util.Set;
public final class BiomeSearchFilter {
private static final Column ALL = y -> true;
private static final Column NONE = y -> false;
private final Engine engine;
private final IrisComplex complex;
private final Set<String> biomes;
private final boolean unfiltered;
private final boolean hydrology;
private final HintedLocator.CoarseSample surface;
private final IrisImageMapRuntime imageMaps;
private final boolean caves;
private final boolean carving;
private final boolean subterrain;
private final boolean transitions;
private final int minHeight;
private final int caveSwitchY;
private BiomeSearchFilter(Engine engine, Set<String> biomes) {
this.engine = engine;
this.complex = engine.getComplex();
this.biomes = Set.copyOf(biomes);
IrisDimension dimension = engine.getDimension();
unfiltered = engine.getDimensionStackContext() != null || complex.getFocusBiome() != null
|| complex.getFocusRegion() != null
|| complex.getBiomeBuffet() != null && !complex.getBiomeBuffet().cells().isEmpty();
hydrology = complex.getHydrologyRuntime() != null && hostsRiverBiome(dimension);
surface = this.biomes.isEmpty() ? null : HintedLocator.surfaceCoarse(engine, this.biomes);
IrisImageMapRuntime maps = complex.getImageMapRuntime();
imageMaps = maps != null && maps.has(IrisImageMapApplication.BIOME) ? maps : null;
caves = hostsCaveBiome(dimension);
carving = carvesBiome(dimension);
subterrain = hostsSubterrainBiome(dimension);
transitions = complex.getTransitionGenerationPlan() != null;
minHeight = engine.getMinHeight();
caveSwitchY = Math.max(-8 - minHeight, 40);
}
public static BiomeSearchFilter of(Engine engine, Set<String> biomeKeys) {
return new BiomeSearchFilter(Objects.requireNonNull(engine, "engine"),
Objects.requireNonNull(biomeKeys, "biome keys"));
}
public Column column(int x, int z) {
if (unfiltered || hydrology && !engine.answersFromNaturalTerrain(x, z)) {
return ALL;
}
if (surfaceAdmits(x, z)) {
return ALL;
}
boolean cave = caves && target(complex.getCaveBiomeStream().get(x, z));
if (!cave && !carving && !subterrain && !transitions) {
return NONE;
}
return y -> admits(x, y, z, cave);
}
private boolean surfaceAdmits(int x, int z) {
IrisBiome mapped = imageMaps == null ? null : imageMaps.sampleBiome(x, z);
if (mapped != null) {
return target(mapped);
}
return surface != null && surface.test(x, z);
}
private boolean admits(int x, int y, int z, boolean cave) {
if (transitions && complex.historicalPhysicalBiomeKeyAt(x, y + minHeight, z).isPresent()) {
return true;
}
if (subterrain && target(subterrainBiome(x, y + minHeight, z))) {
return true;
}
if (y > caveSwitchY) {
return false;
}
return cave || carving && target(IrisDimensionCarvingResolver.resolveBiome(engine, x, y + minHeight, z, null));
}
private String subterrainBiome(int x, int worldY, int z) {
SubterrainPlanner planner = complex.getSubterrainPlanner();
SubterrainCell cell = planner == null ? SubterrainCell.OUTSIDE : planner.sample(x, worldY, z);
return cell.occupied() ? cell.room().biome() : null;
}
private boolean target(IrisBiome biome) {
return biome != null && target(biome.getLoadKey());
}
private boolean target(String key) {
return key != null && biomes.contains(key.trim());
}
private boolean hostsRiverBiome(IrisDimension dimension) {
if (placesTarget(dimension.getRiverPolicy())) {
return true;
}
for (IrisRegion region : dimension.getAllRegions(engine)) {
if (region != null && placesTarget(region.getRiverPolicy())) {
return true;
}
}
for (IrisBiome biome : dimension.getReachableBiomes(engine)) {
if (biome != null && placesTarget(biome.getRiverPolicy())) {
return true;
}
}
return false;
}
private boolean placesTarget(IrisRiverPolicy policy) {
if (policy == null) {
return false;
}
for (String key : policy.getAllBiomeIds()) {
if (HintedLocator.closureContains(engine, key, biomes)) {
return true;
}
}
return false;
}
private boolean hostsCaveBiome(IrisDimension dimension) {
for (IrisRegion region : dimension.getAllRegions(engine)) {
if (region == null) {
continue;
}
for (String key : region.getCaveBiomes()) {
if (target(key)) {
return true;
}
}
}
return false;
}
private boolean carvesBiome(IrisDimension dimension) {
if (dimension.getCarving() == null) {
return false;
}
for (IrisDimensionCarvingEntry entry : dimension.getCarving()) {
if (entry != null && entry.isEnabled() && target(entry.getBiome())) {
return true;
}
}
return false;
}
private boolean hostsSubterrainBiome(IrisDimension dimension) {
if (dimension.getSubterrainFeatures() == null) {
return false;
}
for (IrisSubterrainFeature feature : dimension.getSubterrainFeatures()) {
if (feature != null && feature.isEnabled() && target(feature.getBiome())) {
return true;
}
}
return false;
}
@FunctionalInterface
public interface Column {
boolean admits(int y);
}
}
@@ -41,6 +41,7 @@ import art.arcane.volmlib.util.scheduling.PrecisionStopwatch;
import java.util.ArrayDeque;
import java.util.Comparator;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -377,56 +378,16 @@ public final class HintedLocator<T> implements Locator<T> {
}
IrisComplex complex = engine.getComplex();
BiomeSource source = new BiomeSource(engine);
KSet<String> landHosts = new KSet<>();
KSet<String> seaHosts = new KSet<>();
KSet<String> shoreHosts = new KSet<>();
KSet<String> surfaceRegions = new KSet<>();
KSet<String> caveRegions = new KSet<>();
for (IrisRegion region : engine.getDimension().getAllRegions(engine)) {
if (region == null) {
continue;
}
boolean hosted = false;
for (String root : region.getLandBiomes()) {
if (source.closureContains(root, biomeKey)) {
landHosts.add(root);
hosted = true;
}
}
for (String root : region.getSeaBiomes()) {
if (source.closureContains(root, biomeKey)) {
seaHosts.add(root);
hosted = true;
}
}
for (String root : region.getShoreBiomes()) {
if (source.closureContains(root, biomeKey)) {
shoreHosts.add(root);
hosted = true;
}
}
if (hosted) {
surfaceRegions.add(region.getLoadKey());
}
if (region.getCaveBiomes().contains(biomeKey)) {
caveRegions.add(region.getLoadKey());
}
CoarseSample surface = surfaceCoarse(engine, Set.of(biomeKey));
if (surface != null) {
return SearchPlan.of(surface, BIOME_STRIDE_CHUNKS, null);
}
if (!surfaceRegions.isEmpty()) {
return SearchPlan.of(
surfaceCoarse(complex, surfaceRegions, landHosts, seaHosts, shoreHosts),
BIOME_STRIDE_CHUNKS,
null
);
KSet<String> caveRegions = new KSet<>();
for (IrisRegion region : engine.getDimension().getAllRegions(engine)) {
if (region != null && region.getCaveBiomes().contains(biomeKey)) {
caveRegions.add(region.getLoadKey());
}
}
if (!caveRegions.isEmpty()) {
@@ -567,6 +528,54 @@ public final class HintedLocator<T> implements Locator<T> {
return SearchPlan.of(coarse, 1, null);
}
static boolean closureContains(Engine engine, String rootKey, Set<String> biomeKeys) {
return new BiomeSource(engine).closureContains(rootKey, biomeKeys);
}
static CoarseSample surfaceCoarse(Engine engine, Set<String> biomeKeys) {
BiomeSource source = new BiomeSource(engine);
KSet<String> landHosts = new KSet<>();
KSet<String> seaHosts = new KSet<>();
KSet<String> shoreHosts = new KSet<>();
KSet<String> surfaceRegions = new KSet<>();
for (IrisRegion region : engine.getDimension().getAllRegions(engine)) {
if (region == null) {
continue;
}
boolean hosted = false;
for (String root : region.getLandBiomes()) {
if (source.closureContains(root, biomeKeys)) {
landHosts.add(root);
hosted = true;
}
}
for (String root : region.getSeaBiomes()) {
if (source.closureContains(root, biomeKeys)) {
seaHosts.add(root);
hosted = true;
}
}
for (String root : region.getShoreBiomes()) {
if (source.closureContains(root, biomeKeys)) {
shoreHosts.add(root);
hosted = true;
}
}
if (hosted) {
surfaceRegions.add(region.getLoadKey());
}
}
return surfaceRegions.isEmpty() ? null
: surfaceCoarse(engine.getComplex(), surfaceRegions, landHosts, seaHosts, shoreHosts);
}
private static CoarseSample surfaceCoarse(IrisComplex complex, KSet<String> regions, KSet<String> landHosts, KSet<String> seaHosts, KSet<String> shoreHosts) {
return (x, z) -> {
IrisRegion region = complex.getRegionStream().get(x, z);
@@ -654,8 +663,8 @@ public final class HintedLocator<T> implements Locator<T> {
return loaded;
}
private boolean closureContains(String rootKey, String targetKey) {
if (rootKey == null || targetKey == null) {
private boolean closureContains(String rootKey, Set<String> targetKeys) {
if (rootKey == null) {
return false;
}
@@ -670,7 +679,7 @@ public final class HintedLocator<T> implements Locator<T> {
continue;
}
if (key.equals(targetKey)) {
if (targetKeys.contains(key)) {
return true;
}
@@ -20,6 +20,7 @@ import art.arcane.iris.generation.terrain.IrisDimension;
import art.arcane.iris.generation.terrain.IrisDimensionCarvingResolver;
import art.arcane.iris.platform.generation.BukkitChunkGenerator;
import art.arcane.iris.generation.context.IrisContext;
import art.arcane.iris.generation.locator.BiomeSearchFilter;
import art.arcane.volmlib.util.collection.KMap;
import art.arcane.volmlib.util.math.RNG;
import org.bukkit.block.Biome;
@@ -116,28 +117,7 @@ public final class BukkitBiomePolicy<H, V> implements NativeBiomeSourcePolicy<H>
LinkedHashSet<H> biomes = new LinkedHashSet<>();
boolean fallbackPossible = false;
for (OwnedBiome ownedBiome : getOwnedBiomes(engine)) {
IrisBiome irisBiome = ownedBiome.biome();
if (irisBiome.isCustom()) {
for (IrisBiomeCustom customBiome : irisBiome.getCustomDerivitives()) {
H customHolder = resolveCustomBiomeHolder(
customRegistry, engine, ownedBiome.dimension(), customBiome.getId());
if (customHolder == null) {
throw new IllegalStateException("Iris custom visible biome '"
+ customBiomeKey(ownedBiome.dimension(), customBiome.getId())
+ "' is not registered");
}
biomes.add(customHolder);
}
continue;
}
fallbackPossible |= !addVisibleBiomeHolder(
biomes, registry, irisBiome.getDerivativeKey());
for (String scatter : irisBiome.getBiomeScatter()) {
fallbackPossible |= !addVisibleBiomeHolder(biomes, registry, scatter);
}
for (String scatter : irisBiome.getBiomeSkyScatter()) {
fallbackPossible |= !addVisibleBiomeHolder(biomes, registry, scatter);
}
fallbackPossible |= addVisibleBiomeHolders(biomes, customRegistry, registry, engine, ownedBiome);
}
if (fallbackPossible && fallback != null) {
biomes.add(fallback);
@@ -145,6 +125,37 @@ public final class BukkitBiomePolicy<H, V> implements NativeBiomeSourcePolicy<H>
return new ArrayList<>(biomes);
}
private static <H, V> boolean addVisibleBiomeHolders(
Set<H> biomes,
NativeBiomeRegistry<H, V> customRegistry,
NativeBiomeRegistry<H, V> registry,
Engine engine,
OwnedBiome ownedBiome
) {
IrisBiome irisBiome = ownedBiome.biome();
if (irisBiome.isCustom()) {
for (IrisBiomeCustom customBiome : irisBiome.getCustomDerivitives()) {
H customHolder = resolveCustomBiomeHolder(
customRegistry, engine, ownedBiome.dimension(), customBiome.getId());
if (customHolder == null) {
throw new IllegalStateException("Iris custom visible biome '"
+ customBiomeKey(ownedBiome.dimension(), customBiome.getId())
+ "' is not registered");
}
biomes.add(customHolder);
}
return false;
}
boolean fallbackPossible = !addVisibleBiomeHolder(biomes, registry, irisBiome.getDerivativeKey());
for (String scatter : irisBiome.getBiomeScatter()) {
fallbackPossible |= !addVisibleBiomeHolder(biomes, registry, scatter);
}
for (String scatter : irisBiome.getBiomeSkyScatter()) {
fallbackPossible |= !addVisibleBiomeHolder(biomes, registry, scatter);
}
return fallbackPossible;
}
private static <H, V> boolean addVisibleBiomeHolder(
Set<H> biomes,
NativeBiomeRegistry<H, V> registry,
@@ -382,10 +393,6 @@ public final class BukkitBiomePolicy<H, V> implements NativeBiomeSourcePolicy<H>
}
try (lease; IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) {
prepareVisibleBiomeBatch();
DimensionStackContext stackContext = engine.getDimensionStackContext();
if (stackContext == null) {
return query.fallback().get();
}
Set<H> candidates = new HashSet<>();
for (H biome : getVisibleBiomes(
biomeCustomRegistry, biomeRegistry, engine, fallbackBiome)) {
@@ -396,7 +403,16 @@ public final class BukkitBiomePolicy<H, V> implements NativeBiomeSourcePolicy<H>
if (candidates.isEmpty()) {
return null;
}
return query.search().search(candidates, (blockX, blockZ) -> visibleColumn(stackContext, blockX, blockZ));
if (platformGenerator != null && platformGenerator.usesFlatStudioTerrain()) {
H flat = getFallbackBiome();
return query.search().search(candidates, (blockX, blockZ) -> blockY -> flat);
}
DimensionStackContext stackContext = engine.getDimensionStackContext();
if (stackContext != null) {
return query.search().search(candidates, (blockX, blockZ) -> visibleColumn(stackContext, blockX, blockZ));
}
BiomeSearchFilter filter = BiomeSearchFilter.of(engine, searchBiomeKeys(candidates));
return query.search().search(candidates, (blockX, blockZ) -> visibleColumn(filter, blockX, blockZ));
}
}
@@ -407,6 +423,38 @@ public final class BukkitBiomePolicy<H, V> implements NativeBiomeSourcePolicy<H>
return blockY -> resolveVisibleBiomeHolder(quartX, blockY >> 2, quartZ, null, stackLayout);
}
private Column<H> visibleColumn(BiomeSearchFilter filter, int blockX, int blockZ) {
int quartX = blockX >> 2;
int quartZ = blockZ >> 2;
int minHeight = engine.getWorld().minHeight();
BiomeSearchFilter.Column column = filter.column(quartX << 2, quartZ << 2);
return blockY -> column.admits(((blockY >> 2) << 2) - minHeight)
? locateVisibleBiomeHolder(quartX, blockY >> 2, quartZ)
: null;
}
private H locateVisibleBiomeHolder(int quartX, int quartY, int quartZ) {
try (GenerationHistoryRuntimeRouter.CoordinateScope ignored = openHistoryCoordinateScope(
quartX << 2, quartZ << 2, "bukkit_locate_visible_biome")) {
return resolveVisibleBiomeHolder(quartX, quartY, quartZ, null);
}
}
private Set<String> searchBiomeKeys(Set<H> candidates) {
H fallback = getFallbackBiome();
Set<String> keys = new HashSet<>();
for (OwnedBiome ownedBiome : getHostOwnedBiomes(engine)) {
Set<H> holders = new HashSet<>();
if (addVisibleBiomeHolders(holders, biomeCustomRegistry, biomeRegistry, engine, ownedBiome)) {
holders.add(fallback);
}
if (!Collections.disjoint(holders, candidates)) {
keys.add(ownedBiome.biome().getLoadKey());
}
}
return keys;
}
public static int horizontalBiomeSearchQuartStep(int blockY, int searchRadius) {
return blockY == STRONGHOLD_RING_SEARCH_Y && searchRadius == STRONGHOLD_RING_SEARCH_RADIUS
? STRONGHOLD_RING_SEARCH_QUART_STEP