mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 05:50:39 +00:00
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:
commit
bf99edce87
10 files changed
+960
-85
No files matched your search
@@ -42,6 +42,7 @@ import art.arcane.iris.generation.biome.IrisBiome;
|
||||
import art.arcane.iris.generation.biome.IrisBiomeCustom;
|
||||
import art.arcane.iris.generation.terrain.IrisDimension;
|
||||
import art.arcane.iris.generation.context.IrisContext;
|
||||
import art.arcane.iris.generation.locator.BiomeSearchFilter;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -300,10 +301,6 @@ final class ModdedBiomePolicy<H, S> implements NativeModdedBiomePolicy<H, S> {
|
||||
if (!isReady(engine)) {
|
||||
throw new IllegalStateException("Iris visible biome search has no active engine runtime");
|
||||
}
|
||||
DimensionStackContext stackContext = engine.getDimensionStackContext();
|
||||
if (stackContext == null) {
|
||||
return query.defaultFallback().get();
|
||||
}
|
||||
Set<H> candidates = new HashSet<>();
|
||||
for (H biome : collectVisibleBiomeHolders()) {
|
||||
if (query.allowed().test(biome)) {
|
||||
@@ -313,16 +310,86 @@ final class ModdedBiomePolicy<H, S> implements NativeModdedBiomePolicy<H, S> {
|
||||
if (candidates.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
DimensionStackContext stackContext = engine.getDimensionStackContext();
|
||||
if (stackContext != null) {
|
||||
return query.search().search(candidates, (blockX, blockZ) -> {
|
||||
int quartX = blockX >> 2;
|
||||
int quartZ = blockZ >> 2;
|
||||
DimensionStackLayout stackLayout = stackContext.sample(quartX << 2, quartZ << 2);
|
||||
return blockY -> resolveVisibleBiome(engine, quartX, blockY >> 2, quartZ,
|
||||
query.sampler(), stackLayout);
|
||||
});
|
||||
}
|
||||
BiomeSearchFilter filter = BiomeSearchFilter.of(engine, searchBiomeKeys(engine, candidates));
|
||||
int minHeight = engine.getMinHeight();
|
||||
return query.search().search(candidates, (blockX, blockZ) -> {
|
||||
int quartX = blockX >> 2;
|
||||
int quartZ = blockZ >> 2;
|
||||
DimensionStackLayout stackLayout = stackContext.sample(quartX << 2, quartZ << 2);
|
||||
return blockY -> resolveVisibleBiome(engine, quartX, blockY >> 2, quartZ,
|
||||
query.sampler(), stackLayout);
|
||||
BiomeSearchFilter.Column column = filter.column(quartX << 2, quartZ << 2);
|
||||
return blockY -> column.admits(((blockY >> 2) << 2) - minHeight)
|
||||
? locateVisibleBiome(engine, quartX, blockY >> 2, quartZ, query.sampler())
|
||||
: null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private H locateVisibleBiome(Engine engine, int quartX, int quartY, int quartZ, S sampler) {
|
||||
try (GenerationHistoryRuntimeRouter.CoordinateScope ignored = openHistoryCoordinateScope(
|
||||
engine, quartX << 2, quartZ << 2, "modded_locate_visible_biome")) {
|
||||
return resolveVisibleBiome(engine, quartX, quartY, quartZ, sampler, null);
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> searchBiomeKeys(Engine engine, Set<H> candidates) {
|
||||
NativeBiomeSourceAccess.RegistryView<H> registry = biomeRegistry();
|
||||
if (registry == null) {
|
||||
throw new IllegalStateException("Iris visible biome search has no biome registry");
|
||||
}
|
||||
H plains = resolveHolder(registry, "minecraft:plains");
|
||||
boolean fallbackCandidate = plains != null && candidates.contains(plains);
|
||||
Set<String> keys = new HashSet<>();
|
||||
for (OwnedBiome ownedBiome : getOwnedBiomes(engine)) {
|
||||
if (emitsCandidate(engine, ownedBiome, registry, candidates, fallbackCandidate)) {
|
||||
keys.add(ownedBiome.biome().getLoadKey());
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
private boolean emitsCandidate(Engine engine, OwnedBiome ownedBiome, NativeBiomeSourceAccess.RegistryView<H> registry,
|
||||
Set<H> candidates, boolean fallbackCandidate) {
|
||||
IrisBiome irisBiome = ownedBiome.biome();
|
||||
if (irisBiome.isCustom()) {
|
||||
for (IrisBiomeCustom customBiome : irisBiome.getCustomDerivitives()) {
|
||||
if (customBiome == null) {
|
||||
if (fallbackCandidate) {
|
||||
return true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
String key = ownedBiome.dimension() == engine.getDimension() && ownedBiome.data() == engine.getData()
|
||||
? ModdedWorldgenIds.biomeRef(engine, customBiome.getId())
|
||||
: ownedBiome.data().customBiomeResourceKey(ownedBiome.dimension(), customBiome);
|
||||
if (emits(registry, key, candidates, fallbackCandidate)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
for (String key : irisBiome.getVisibleBiomeKeys(engine)) {
|
||||
if (emits(registry, key, candidates, fallbackCandidate)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static <H> boolean emits(NativeBiomeSourceAccess.RegistryView<H> registry, String key,
|
||||
Set<H> candidates, boolean fallbackCandidate) {
|
||||
H holder = key == null ? null : resolveHolder(registry, key);
|
||||
return holder == null ? fallbackCandidate : candidates.contains(holder);
|
||||
}
|
||||
|
||||
private H getNoiseBiome(Engine engine, int quartX, int quartY, int quartZ,
|
||||
S sampler) {
|
||||
if (isGuaranteedSurfaceBiome(engine, quartY)) {
|
||||
|
||||
+98
@@ -6,11 +6,17 @@ import art.arcane.iris.generation.runtime.DimensionStackLayout;
|
||||
import art.arcane.iris.generation.terrain.IrisDimension;
|
||||
import art.arcane.iris.generation.subterrain.IrisSubterrainFeature;
|
||||
import art.arcane.iris.pack.loading.IrisData;
|
||||
import art.arcane.iris.pack.loading.ResourceLoader;
|
||||
import art.arcane.iris.generation.terrain.IrisRegion;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.iris.generation.runtime.GenerationSessionException;
|
||||
import art.arcane.iris.generation.runtime.GenerationSessionLease;
|
||||
import art.arcane.iris.generation.runtime.IrisComplex;
|
||||
import art.arcane.iris.generation.runtime.IrisEngine;
|
||||
import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeBiomeSourceAccess;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeBiomeSourcePolicy;
|
||||
import art.arcane.volmlib.nativelib.terrain.NativeModdedBiomePolicy;
|
||||
import art.arcane.volmlib.util.stream.ProceduralStream;
|
||||
import art.arcane.volmlib.util.stream.interpolation.Interpolated;
|
||||
import org.junit.Test;
|
||||
@@ -20,7 +26,9 @@ import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -29,8 +37,12 @@ import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -140,6 +152,92 @@ public class ModdedBiomePolicyTest {
|
||||
assertThrows(IllegalStateException.class, () -> boundTo(unready).getVisibleSurfaceBiome(8, 8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Vanilla /locate biome samples a 6400 block radius every 32 blocks. Each Iris surface sample needs the column's
|
||||
* terrain height, so the search may only resolve columns whose region and biome streams can hold the target, and
|
||||
* it must answer with the visible biome a chunk is filled with.
|
||||
*/
|
||||
@Test
|
||||
public void locateResolvesVisibleBiomesOnlyInColumnsThatCanHoldTheTarget() throws Exception {
|
||||
BiomeToken mirage = new BiomeToken("terralith:mirage_isles");
|
||||
BiomeToken plains = new BiomeToken("minecraft:plains");
|
||||
IrisBiome isles = new IrisBiome().setDerivative("terralith:mirage_isles");
|
||||
isles.setLoadKey("isles");
|
||||
IrisBiome meadow = new IrisBiome().setDerivative("minecraft:plains");
|
||||
meadow.setLoadKey("meadow");
|
||||
IrisRegion main = new IrisRegion().setLandBiomes(new KList<>("meadow"));
|
||||
main.setLoadKey("main");
|
||||
IrisRegion archipelago = new IrisRegion().setLandBiomes(new KList<>("isles"));
|
||||
archipelago.setLoadKey("archipelago");
|
||||
IrisDimension dimension = mock(IrisDimension.class);
|
||||
when(dimension.getReachableBiomes(any())).thenReturn(new KList<>(meadow, isles));
|
||||
when(dimension.getAllRegions(any())).thenReturn(new KList<>(main, archipelago));
|
||||
when(dimension.getCarving()).thenReturn(new KList<>());
|
||||
when(dimension.getSubterrainFeatures()).thenReturn(new KList<>());
|
||||
@SuppressWarnings("unchecked")
|
||||
ResourceLoader<IrisBiome> biomes = (ResourceLoader<IrisBiome>) mock(ResourceLoader.class);
|
||||
when(biomes.load("meadow")).thenReturn(meadow);
|
||||
when(biomes.load("isles")).thenReturn(isles);
|
||||
IrisData data = mock(IrisData.class);
|
||||
when(data.getDataFolder()).thenReturn(new File("synthetic-pack"));
|
||||
when(data.getBiomeLoader()).thenReturn(biomes);
|
||||
when(data.physicalBiomeResourceKey(any(), anyString())).thenAnswer(invocation -> invocation.getArgument(1));
|
||||
IrisComplex complex = mock(IrisComplex.class);
|
||||
when(complex.getRegionStream()).thenReturn(ProceduralStream.of(
|
||||
(x, z) -> x >= 4096D ? archipelago : main, Interpolated.of(value -> 0D, value -> main)));
|
||||
when(complex.getLandBiomeStream()).thenReturn(ProceduralStream.of(
|
||||
(x, z) -> x >= 4096D ? isles : meadow, Interpolated.of(value -> 0D, value -> meadow)));
|
||||
when(complex.getSeaBiomeStream()).thenReturn(ProceduralStream.of(
|
||||
(x, z) -> meadow, Interpolated.of(value -> 0D, value -> meadow)));
|
||||
when(complex.getShoreBiomeStream()).thenReturn(ProceduralStream.of(
|
||||
(x, z) -> meadow, Interpolated.of(value -> 0D, value -> meadow)));
|
||||
when(complex.historicalPhysicalBiomeKeyAt(anyInt(), anyInt(), anyInt())).thenReturn(Optional.empty());
|
||||
GenerationHistoryRuntimeRouter router = mock(GenerationHistoryRuntimeRouter.class);
|
||||
GenerationHistoryRuntimeRouter.CoordinateScope scope = mock(GenerationHistoryRuntimeRouter.CoordinateScope.class);
|
||||
when(router.openCoordinateScope(anyInt(), anyInt())).thenReturn(scope);
|
||||
IrisEngine engine = mock(IrisEngine.class);
|
||||
when(engine.getGenerationHistoryRuntimeRouter()).thenReturn(Optional.of(router));
|
||||
when(engine.getDimension()).thenReturn(dimension);
|
||||
when(engine.getData()).thenReturn(data);
|
||||
when(engine.getComplex()).thenReturn(complex);
|
||||
when(engine.getMinHeight()).thenReturn(-64);
|
||||
when(engine.getCacheID()).thenReturn(1);
|
||||
when(engine.acquireGenerationLease(anyString())).thenReturn(GenerationSessionLease.noop());
|
||||
when(engine.answersFromNaturalTerrain(anyInt(), anyInt())).thenReturn(true);
|
||||
AtomicInteger heightSamples = new AtomicInteger();
|
||||
when(engine.getHostSurfaceBiome(anyInt(), anyInt())).thenAnswer(invocation -> {
|
||||
heightSamples.incrementAndGet();
|
||||
return invocation.<Integer>getArgument(0) >= 4096 ? isles : meadow;
|
||||
});
|
||||
ModdedBiomePolicy<BiomeToken, Object> policy = new ModdedBiomePolicy<>(
|
||||
new Access(new ArrayList<>(List.of(plains, mirage))));
|
||||
policy.bind(new ModdedBiomePolicy.RuntimeCallbacks(() -> engine, () -> engine, bound -> false,
|
||||
() -> Set.of(plains.key(), mirage.key()), bound -> Set.of(), () -> 0L));
|
||||
int[] columns = {0, 1024, 2048, 3072, 4096, 5120};
|
||||
|
||||
NativeBiomeSourcePolicy.BiomeLocation<BiomeToken> found = policy.findClosestBiome3d(
|
||||
new NativeModdedBiomePolicy.ClosestQuery<>(mirage::equals, null,
|
||||
() -> {
|
||||
throw new AssertionError("A bound engine must not use the unbound search");
|
||||
},
|
||||
(candidates, resolver) -> {
|
||||
for (int x : columns) {
|
||||
NativeBiomeSourcePolicy.Column<BiomeToken> column = resolver.resolve(x, 0);
|
||||
BiomeToken biome = column.sample(100);
|
||||
if (candidates.contains(biome)) {
|
||||
return new NativeBiomeSourcePolicy.BiomeLocation<>(x, 100, 0, biome);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}));
|
||||
|
||||
assertEquals(new NativeBiomeSourcePolicy.BiomeLocation<>(4096, 100, 0, mirage), found);
|
||||
assertEquals(1, heightSamples.get());
|
||||
verify(router).openCoordinateScope(4096, 0);
|
||||
verify(router, times(1)).openCoordinateScope(anyInt(), anyInt());
|
||||
verify(scope).close();
|
||||
}
|
||||
|
||||
private static ModdedBiomePolicy<BiomeToken, Object> boundTo(Engine engine) {
|
||||
ModdedBiomePolicy<BiomeToken, Object> policy = new ModdedBiomePolicy<>(new Access(new ArrayList<>()));
|
||||
policy.bind(new ModdedBiomePolicy.RuntimeCallbacks(() -> engine, () -> engine, bound -> true,
|
||||
|
||||
Reference in new issue
Block a user