Opti pass 1

This commit is contained in:
Brian Neumann-Fopiano committed 2026-04-15 12:50:12 -04:00
1 parent 9a231b2bcf
commit dcb3306197
43 files changed
+1832 -410

No files matched your search

@@ -195,7 +195,6 @@ public class IrisPregenerator {
private void init() {
generator.init();
generator.save();
}
private void shutdown() {
@@ -20,6 +20,7 @@ package art.arcane.iris.core.pregenerator;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.collection.KMap;
import art.arcane.volmlib.util.math.PowerOfTwoCoordinates;
import art.arcane.volmlib.util.math.Position2;
import art.arcane.volmlib.util.math.Spiraled;
import art.arcane.volmlib.util.math.Spiraler;
@@ -31,8 +32,7 @@ import java.util.Comparator;
@Builder
@Data
public class PregenTask {
private static final Position2 ZERO = new Position2(0, 0);
private static final KMap<Position2, KList<Position2>> ORDERS = new KMap<>();
private static final KMap<Long, int[]> ORDERS = new KMap<>();
@Builder.Default
private final boolean gui = false;
@@ -54,16 +54,28 @@ public class PregenTask {
}
public static void iterateRegion(int xr, int zr, Spiraled s, Position2 pull) {
for (Position2 i : ORDERS.computeIfAbsent(pull, PregenTask::computeOrder)) {
s.on(i.getX() + (xr << 5), i.getZ() + (zr << 5));
iterateRegion(xr, zr, s, pull.getX(), pull.getZ());
}
public static void iterateRegion(int xr, int zr, Spiraled s, int pullX, int pullZ) {
for (int packed : orderForPull(pullX, pullZ)) {
s.on(PowerOfTwoCoordinates.unpackLocal32X(packed) + PowerOfTwoCoordinates.regionToChunk(xr), PowerOfTwoCoordinates.unpackLocal32Z(packed) + PowerOfTwoCoordinates.regionToChunk(zr));
}
}
public static void iterateRegion(int xr, int zr, Spiraled s) {
iterateRegion(xr, zr, s, new Position2(-(xr << 5), -(zr << 5)));
iterateRegion(xr, zr, s, -PowerOfTwoCoordinates.regionToChunk(xr), -PowerOfTwoCoordinates.regionToChunk(zr));
}
private static KList<Position2> computeOrder(Position2 pull) {
private static int[] orderForPull(int pullX, int pullZ) {
long key = orderKey(pullX, pullZ);
return ORDERS.computeIfAbsent(key, PregenTask::computeOrder);
}
private static int[] computeOrder(long key) {
int pullX = (int) (key >> 32);
int pullZ = (int) key;
Position2 pull = new Position2(pullX, pullZ);
KList<Position2> p = new KList<>();
new Spiraler(33, 33, (x, z) -> {
int xx = (x + 15);
@@ -76,18 +88,30 @@ public class PregenTask {
}).drain();
p.sort(Comparator.comparing((i) -> i.distance(pull)));
return p;
int[] packed = new int[p.size()];
for (int index = 0; index < p.size(); index++) {
Position2 position = p.get(index);
packed[index] = PowerOfTwoCoordinates.packLocal32(position.getX(), position.getZ());
}
return packed;
}
private static long orderKey(int pullX, int pullZ) {
long high = (long) pullX << 32;
long low = pullZ & 0xFFFFFFFFL;
return high | low;
}
public void iterateRegions(Spiraled s) {
var bound = bounds.region();
Bound bound = bounds.region();
new Spiraler(bound.sizeX, bound.sizeZ, ((x, z) -> {
if (bound.check(x, z)) s.on(x, z);
})).setOffset(center.getX() >> 9, center.getZ() >> 9).drain();
})).setOffset(PowerOfTwoCoordinates.blockToRegionFloor(center.getX()), PowerOfTwoCoordinates.blockToRegionFloor(center.getZ())).drain();
}
public void iterateChunks(int rX, int rZ, Spiraled s) {
var bound = bounds.chunk();
Bound bound = bounds.chunk();
iterateRegion(rX, rZ, ((x, z) -> {
if (bound.check(x, z)) s.on(x, z);
}));
@@ -103,11 +127,11 @@ public class PregenTask {
}
KList<RegionChunkCursor> cursors = new KList<>();
Bound bound = bounds.chunk();
iterateRegions((regionX, regionZ) -> {
KList<Position2> chunks = new KList<>();
iterateChunks(regionX, regionZ, (chunkX, chunkZ) -> chunks.add(new Position2(chunkX, chunkZ)));
if (!chunks.isEmpty()) {
cursors.add(new RegionChunkCursor(regionX, regionZ, chunks));
RegionChunkCursor cursor = new RegionChunkCursor(regionX, regionZ, bound);
if (cursor.hasNext()) {
cursors.add(cursor);
}
});
@@ -120,16 +144,18 @@ public class PregenTask {
}
hasProgress = true;
Position2 chunk = cursor.next();
if (chunk == null) {
long chunk = cursor.next();
if (chunk == Long.MIN_VALUE) {
continue;
}
int chunkX = (int) (chunk >> 32);
int chunkZ = (int) chunk;
boolean shouldContinue = spiraled.on(
cursor.getRegionX(),
cursor.getRegionZ(),
chunk.getX(),
chunk.getZ(),
chunkX,
chunkZ,
cursor.getIndex() == 1,
!cursor.hasNext()
);
@@ -155,8 +181,18 @@ public class PregenTask {
int minX = center.getX() - radiusX;
int minZ = center.getZ() - radiusZ;
chunk = new Bound(minX >> 4, minZ >> 4, Math.ceilDiv(maxX, 16), Math.ceilDiv(maxZ, 16));
region = new Bound(minX >> 9, minZ >> 9, Math.ceilDiv(maxX, 512), Math.ceilDiv(maxZ, 512));
chunk = new Bound(
PowerOfTwoCoordinates.blockToChunkFloor(minX),
PowerOfTwoCoordinates.blockToChunkFloor(minZ),
PowerOfTwoCoordinates.ceilDivPow2(maxX, PowerOfTwoCoordinates.CHUNK_BITS),
PowerOfTwoCoordinates.ceilDivPow2(maxZ, PowerOfTwoCoordinates.CHUNK_BITS)
);
region = new Bound(
PowerOfTwoCoordinates.blockToRegionFloor(minX),
PowerOfTwoCoordinates.blockToRegionFloor(minZ),
PowerOfTwoCoordinates.ceilDivPow2(maxX, PowerOfTwoCoordinates.REGION_BITS),
PowerOfTwoCoordinates.ceilDivPow2(maxZ, PowerOfTwoCoordinates.REGION_BITS)
);
}
public Bound chunk() {
@@ -199,28 +235,60 @@ public class PregenTask {
private static final class RegionChunkCursor {
private final int regionX;
private final int regionZ;
private final KList<Position2> chunks;
private int index;
private final Bound bound;
private final int[] order;
private final int chunkOffsetX;
private final int chunkOffsetZ;
private int scanIndex;
private int emittedIndex;
private int currentChunkX;
private int currentChunkZ;
private boolean hasCurrent;
private RegionChunkCursor(int regionX, int regionZ, KList<Position2> chunks) {
private RegionChunkCursor(int regionX, int regionZ, Bound bound) {
this.regionX = regionX;
this.regionZ = regionZ;
this.chunks = chunks;
this.index = 0;
this.bound = bound;
this.chunkOffsetX = PowerOfTwoCoordinates.regionToChunk(regionX);
this.chunkOffsetZ = PowerOfTwoCoordinates.regionToChunk(regionZ);
this.order = orderForPull(-chunkOffsetX, -chunkOffsetZ);
this.scanIndex = 0;
this.emittedIndex = 0;
advance();
}
private boolean hasNext() {
return index < chunks.size();
return hasCurrent;
}
private Position2 next() {
private long next() {
if (!hasNext()) {
return null;
return Long.MIN_VALUE;
}
Position2 value = chunks.get(index);
index++;
return value;
long high = (long) currentChunkX << 32;
long low = currentChunkZ & 0xFFFFFFFFL;
emittedIndex++;
advance();
return high | low;
}
private void advance() {
hasCurrent = false;
while (scanIndex < order.length) {
int local = order[scanIndex];
scanIndex++;
int chunkX = chunkOffsetX + PowerOfTwoCoordinates.unpackLocal32X(local);
int chunkZ = chunkOffsetZ + PowerOfTwoCoordinates.unpackLocal32Z(local);
if (!bound.check(chunkX, chunkZ)) {
continue;
}
currentChunkX = chunkX;
currentChunkZ = chunkZ;
hasCurrent = true;
return;
}
}
private int getRegionX() {
@@ -232,7 +300,7 @@ public class PregenTask {
}
private int getIndex() {
return index;
return emittedIndex;
}
}
}
@@ -5,6 +5,7 @@ import art.arcane.volmlib.util.data.Varint;
import art.arcane.volmlib.util.documentation.ChunkCoordinates;
import art.arcane.volmlib.util.documentation.RegionCoordinates;
import art.arcane.volmlib.util.io.IO;
import art.arcane.volmlib.util.math.PowerOfTwoCoordinates;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import net.jpountz.lz4.LZ4BlockInputStream;
import net.jpountz.lz4.LZ4BlockOutputStream;
@@ -39,19 +40,22 @@ public class PregenCacheImpl implements PregenCache {
@Override
@ChunkCoordinates
public boolean isChunkCached(int x, int z) {
return getPlate(x >> 10, z >> 10).isCached(
(x >> 5) & 31,
(z >> 5) & 31,
region -> region.isCached(x & 31, z & 31)
return getPlate(PowerOfTwoCoordinates.floorDivPow2(x, 10), PowerOfTwoCoordinates.floorDivPow2(z, 10)).isCached(
PowerOfTwoCoordinates.localMaskPow2(PowerOfTwoCoordinates.floorDivPow2(x, 5), PowerOfTwoCoordinates.REGION_CHUNK_BITS),
PowerOfTwoCoordinates.localMaskPow2(PowerOfTwoCoordinates.floorDivPow2(z, 5), PowerOfTwoCoordinates.REGION_CHUNK_BITS),
region -> region.isCached(
PowerOfTwoCoordinates.localMaskPow2(x, PowerOfTwoCoordinates.REGION_CHUNK_BITS),
PowerOfTwoCoordinates.localMaskPow2(z, PowerOfTwoCoordinates.REGION_CHUNK_BITS)
)
);
}
@Override
@RegionCoordinates
public boolean isRegionCached(int x, int z) {
return getPlate(x >> 5, z >> 5).isCached(
x & 31,
z & 31,
return getPlate(PowerOfTwoCoordinates.floorDivPow2(x, 5), PowerOfTwoCoordinates.floorDivPow2(z, 5)).isCached(
PowerOfTwoCoordinates.localMaskPow2(x, PowerOfTwoCoordinates.REGION_CHUNK_BITS),
PowerOfTwoCoordinates.localMaskPow2(z, PowerOfTwoCoordinates.REGION_CHUNK_BITS),
Region::isCached
);
}
@@ -59,19 +63,22 @@ public class PregenCacheImpl implements PregenCache {
@Override
@ChunkCoordinates
public void cacheChunk(int x, int z) {
getPlate(x >> 10, z >> 10).cache(
(x >> 5) & 31,
(z >> 5) & 31,
region -> region.cache(x & 31, z & 31)
getPlate(PowerOfTwoCoordinates.floorDivPow2(x, 10), PowerOfTwoCoordinates.floorDivPow2(z, 10)).cache(
PowerOfTwoCoordinates.localMaskPow2(PowerOfTwoCoordinates.floorDivPow2(x, 5), PowerOfTwoCoordinates.REGION_CHUNK_BITS),
PowerOfTwoCoordinates.localMaskPow2(PowerOfTwoCoordinates.floorDivPow2(z, 5), PowerOfTwoCoordinates.REGION_CHUNK_BITS),
region -> region.cache(
PowerOfTwoCoordinates.localMaskPow2(x, PowerOfTwoCoordinates.REGION_CHUNK_BITS),
PowerOfTwoCoordinates.localMaskPow2(z, PowerOfTwoCoordinates.REGION_CHUNK_BITS)
)
);
}
@Override
@RegionCoordinates
public void cacheRegion(int x, int z) {
getPlate(x >> 5, z >> 5).cache(
x & 31,
z & 31,
getPlate(PowerOfTwoCoordinates.floorDivPow2(x, 5), PowerOfTwoCoordinates.floorDivPow2(z, 5)).cache(
PowerOfTwoCoordinates.localMaskPow2(x, PowerOfTwoCoordinates.REGION_CHUNK_BITS),
PowerOfTwoCoordinates.localMaskPow2(z, PowerOfTwoCoordinates.REGION_CHUNK_BITS),
Region::cache
);
}
@@ -215,7 +222,7 @@ public class PregenCacheImpl implements PregenCache {
return false;
}
int index = x * 32 + z;
int index = PowerOfTwoCoordinates.packLocal32(x, z);
Region region = regions[index];
if (region == null) {
region = new Region();
@@ -240,7 +247,7 @@ public class PregenCacheImpl implements PregenCache {
return true;
}
Region region = regions[x * 32 + z];
Region region = regions[PowerOfTwoCoordinates.packLocal32(x, z)];
if (region == null) {
return false;
}
@@ -294,7 +301,7 @@ public class PregenCacheImpl implements PregenCache {
return false;
}
int index = x * 32 + z;
int index = PowerOfTwoCoordinates.packLocal32(x, z);
int wordIndex = index >> 6;
long bit = 1L << (index & 63);
boolean current = (value[wordIndex] & bit) != 0L;
@@ -317,7 +324,7 @@ public class PregenCacheImpl implements PregenCache {
}
private boolean isCached(int x, int z) {
int index = x * 32 + z;
int index = PowerOfTwoCoordinates.packLocal32(x, z);
if (count == SIZE) {
return true;
}
@@ -35,14 +35,17 @@ import io.papermc.lib.PaperLib;
import org.bukkit.Chunk;
import org.bukkit.World;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
@@ -59,6 +62,9 @@ public class AsyncPregenMethod implements PregeneratorMethod {
private final int effectiveWorkerThreads;
private final int recommendedRuntimeConcurrencyCap;
private final int configuredMaxConcurrency;
private final Method directChunkAtAsyncUrgentMethod;
private final Method directChunkAtAsyncMethod;
private final String chunkAccessMode;
private final Executor executor;
private final Semaphore semaphore;
private final int threads;
@@ -90,10 +96,16 @@ public class AsyncPregenMethod implements PregeneratorMethod {
IrisSettings.IrisSettingsPregen pregen = IrisSettings.get().getPregen();
this.runtimeSchedulerMode = IrisRuntimeSchedulerMode.resolve(pregen);
this.foliaRuntime = runtimeSchedulerMode == IrisRuntimeSchedulerMode.FOLIA;
ChunkAsyncMethodSelection chunkAsyncMethodSelection = resolveChunkAsyncMethodSelection(world);
this.directChunkAtAsyncUrgentMethod = chunkAsyncMethodSelection.urgentMethod();
this.directChunkAtAsyncMethod = chunkAsyncMethodSelection.standardMethod();
this.chunkAccessMode = chunkAsyncMethodSelection.mode();
int detectedWorkerPoolThreads = resolveWorkerPoolThreads();
int detectedCpuThreads = Math.max(1, Runtime.getRuntime().availableProcessors());
int configuredWorldGenThreads = Math.max(1, IrisSettings.get().getConcurrency().getWorldGenThreads());
int workerThreadsForCap = Math.max(detectedCpuThreads, Math.max(configuredWorldGenThreads, Math.max(1, detectedWorkerPoolThreads)));
int workerThreadsForCap = foliaRuntime
? resolveFoliaConcurrencyWorkerThreads(detectedWorkerPoolThreads, detectedCpuThreads, configuredWorldGenThreads)
: resolvePaperLikeConcurrencyWorkerThreads(detectedWorkerPoolThreads, detectedCpuThreads, configuredWorldGenThreads);
if (foliaRuntime) {
this.paperLikeBackendMode = IrisPaperLikeBackendMode.AUTO;
this.backendMode = "folia-region";
@@ -156,11 +168,14 @@ public class AsyncPregenMethod implements PregeneratorMethod {
private void unloadAndSaveAllChunks() {
if (foliaRuntime) {
// Folia requires world/chunk mutations to be region-owned; periodic global unload/save is unsafe.
lastUse.clear();
return;
}
if (lastUse.isEmpty()) {
return;
}
try {
J.sfut(() -> {
if (world == null) {
@@ -169,6 +184,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
}
long minTime = M.ms() - 10_000;
AtomicBoolean unloaded = new AtomicBoolean(false);
lastUse.entrySet().removeIf(i -> {
final Chunk chunk = i.getKey();
final Long lastUseTime = i.getValue();
@@ -176,11 +192,14 @@ public class AsyncPregenMethod implements PregeneratorMethod {
return true;
if (lastUseTime < minTime) {
chunk.unload();
unloaded.set(true);
return true;
}
return false;
});
world.save();
if (unloaded.get()) {
world.save();
}
}).get();
} catch (Throwable e) {
e.printStackTrace();
@@ -294,6 +313,14 @@ public class AsyncPregenMethod implements PregeneratorMethod {
return recommendedCap;
}
static int resolvePaperLikeConcurrencyWorkerThreads(int detectedWorkerPoolThreads, int detectedCpuThreads, int configuredWorldGenThreads) {
if (detectedWorkerPoolThreads > 0) {
return detectedWorkerPoolThreads;
}
return Math.max(1, Math.max(detectedCpuThreads, configuredWorldGenThreads));
}
static int computeFoliaRecommendedCap(int workerThreads) {
int normalizedWorkers = Math.max(1, workerThreads);
int recommendedCap = normalizedWorkers * 4;
@@ -308,6 +335,10 @@ public class AsyncPregenMethod implements PregeneratorMethod {
return recommendedCap;
}
static int resolveFoliaConcurrencyWorkerThreads(int detectedWorkerPoolThreads, int detectedCpuThreads, int configuredWorldGenThreads) {
return Math.max(detectedCpuThreads, Math.max(configuredWorldGenThreads, Math.max(1, detectedWorkerPoolThreads)));
}
static int applyRuntimeConcurrencyCap(int maxConcurrency, boolean foliaRuntime, int workerThreads) {
int normalizedMaxConcurrency = Math.max(1, maxConcurrency);
int recommendedCap = foliaRuntime
@@ -407,6 +438,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
Iris.info("Async pregen init: world=" + world.getName()
+ ", mode=" + runtimeSchedulerMode.name().toLowerCase(Locale.ROOT)
+ ", backend=" + backendMode
+ ", chunkAccess=" + chunkAccessMode
+ ", threads=" + threads
+ ", adaptiveLimit=" + adaptiveInFlightLimit.get()
+ ", workerPoolThreads=" + workerPoolThreads
@@ -487,6 +519,96 @@ public class AsyncPregenMethod implements PregeneratorMethod {
executor.generate(x, z, listener);
}
private CompletableFuture<Chunk> requestChunkAsync(int x, int z) {
Throwable failure = null;
if (directChunkAtAsyncUrgentMethod != null) {
try {
return invokeChunkFuture(directChunkAtAsyncUrgentMethod, x, z, true, urgent);
} catch (Throwable e) {
failure = e;
}
}
if (directChunkAtAsyncMethod != null) {
try {
return invokeChunkFuture(directChunkAtAsyncMethod, x, z, true, urgent);
} catch (Throwable e) {
if (failure == null) {
failure = e;
}
}
}
try {
CompletableFuture<Chunk> future = PaperLib.getChunkAtAsync(world, x, z, true, urgent);
if (future != null) {
return future;
}
} catch (Throwable e) {
if (failure == null) {
failure = e;
}
}
if (failure == null) {
failure = new IllegalStateException("Chunk async access returned no future.");
}
return CompletableFuture.failedFuture(new IllegalStateException("Failed to request async chunk " + x + "," + z + " in world " + world.getName(), failure));
}
@SuppressWarnings("unchecked")
private CompletableFuture<Chunk> invokeChunkFuture(Method method, int x, int z, boolean generate, boolean urgentRequest) throws Throwable {
Object result;
try {
if (method.getParameterCount() == 4) {
result = method.invoke(world, x, z, generate, urgentRequest);
} else {
result = method.invoke(world, x, z, generate);
}
} catch (InvocationTargetException e) {
throw e.getCause() == null ? e : e.getCause();
}
if (result instanceof CompletableFuture<?>) {
return (CompletableFuture<Chunk>) result;
}
throw new IllegalStateException("Chunk async method returned a non-future result.");
}
private static ChunkAsyncMethodSelection resolveChunkAsyncMethodSelection(World world) {
if (world == null) {
return new ChunkAsyncMethodSelection(null, null, "paperlib");
}
Class<?> worldClass = world.getClass();
Method urgentMethod = resolveChunkAsyncMethod(worldClass, int.class, int.class, boolean.class, boolean.class);
Method standardMethod = resolveChunkAsyncMethod(worldClass, int.class, int.class, boolean.class);
if (urgentMethod != null) {
return new ChunkAsyncMethodSelection(urgentMethod, standardMethod, "world#getChunkAtAsync(int,int,boolean,boolean)");
}
if (standardMethod != null) {
return new ChunkAsyncMethodSelection(null, standardMethod, "world#getChunkAtAsync(int,int,boolean)");
}
return new ChunkAsyncMethodSelection(null, null, "paperlib");
}
private static Method resolveChunkAsyncMethod(Class<?> worldClass, Class<?>... parameterTypes) {
try {
return worldClass.getMethod("getChunkAtAsync", parameterTypes);
} catch (NoSuchMethodException ignored) {
}
try {
return World.class.getMethod("getChunkAtAsync", parameterTypes);
} catch (NoSuchMethodException ignored) {
}
return null;
}
@Override
public Mantle getMantle() {
if (IrisToolbelt.isIrisWorld(world)) {
@@ -547,14 +669,14 @@ public class AsyncPregenMethod implements PregeneratorMethod {
@Override
public void generate(int x, int z, PregenListener listener) {
try {
PaperLib.getChunkAtAsync(world, x, z, true, urgent)
requestChunkAsync(x, z)
.orTimeout(timeoutSeconds, TimeUnit.SECONDS)
.whenComplete((chunk, throwable) -> completeFoliaChunk(x, z, listener, chunk, throwable));
return;
} catch (Throwable ignored) {
}
if (!J.runRegion(world, x, z, () -> PaperLib.getChunkAtAsync(world, x, z, true, urgent)
if (!J.runRegion(world, x, z, () -> requestChunkAsync(x, z)
.orTimeout(timeoutSeconds, TimeUnit.SECONDS)
.whenComplete((chunk, throwable) -> completeFoliaChunk(x, z, listener, chunk, throwable)))) {
markFinished(false);
@@ -598,7 +720,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
service.submit(() -> {
boolean success = false;
try {
Chunk i = PaperLib.getChunkAtAsync(world, x, z, true, urgent)
Chunk i = requestChunkAsync(x, z)
.orTimeout(timeoutSeconds, TimeUnit.SECONDS)
.exceptionally(e -> onChunkFutureFailure(x, z, e))
.get();
@@ -632,7 +754,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
private class TicketExecutor implements Executor {
@Override
public void generate(int x, int z, PregenListener listener) {
PaperLib.getChunkAtAsync(world, x, z, true, urgent)
requestChunkAsync(x, z)
.orTimeout(timeoutSeconds, TimeUnit.SECONDS)
.exceptionally(e -> onChunkFutureFailure(x, z, e))
.thenAccept(i -> {
@@ -653,4 +775,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
});
}
}
private record ChunkAsyncMethodSelection(Method urgentMethod, Method standardMethod, String mode) {
}
}
@@ -14,8 +14,10 @@ import art.arcane.volmlib.util.math.RNG;
import art.arcane.iris.util.common.plugin.IrisService;
import art.arcane.iris.util.common.plugin.VolmitSender;
import art.arcane.volmlib.util.scheduling.Looper;
import art.arcane.iris.util.project.stream.utility.CachedDoubleStream2D;
import art.arcane.iris.util.project.stream.utility.CachedStream2D;
import art.arcane.iris.util.project.stream.utility.CachedStream3D;
import art.arcane.iris.core.gui.PregeneratorJob;
import lombok.Synchronized;
import org.bukkit.Bukkit;
import org.bukkit.World;
@@ -98,7 +100,7 @@ public class IrisEngineSVC implements IrisService {
double total = 0D;
int count = 0;
for (var cache : preservation.getCaches()) {
if (!(cache instanceof CachedStream2D<?>)) {
if (!(cache instanceof CachedStream2D<?>) && !(cache instanceof CachedDoubleStream2D)) {
continue;
}
@@ -126,6 +128,7 @@ public class IrisEngineSVC implements IrisService {
var type = switch (cache) {
case ResourceLoader<?> ignored -> 0;
case CachedStream2D<?> ignored -> 1;
case CachedDoubleStream2D ignored -> 1;
case CachedStream3D<?> ignored -> 2;
default -> 3;
};
@@ -250,6 +253,10 @@ public class IrisEngineSVC implements IrisService {
return false;
}
static boolean shouldSkipMantleReductionForMaintenance(boolean maintenanceActive, boolean pregeneratorTargetsWorld) {
return maintenanceActive && !pregeneratorTargetsWorld;
}
private final class Registered {
private final String name;
private final PlatformChunkGenerator access;
@@ -286,7 +293,7 @@ public class IrisEngineSVC implements IrisService {
|| !shouldReduce(engine))
return;
World engineWorld = engine.getWorld().realWorld();
if (engineWorld != null && IrisToolbelt.isWorldMaintenanceActive(engineWorld)) {
if (shouldSkipForMaintenance(engineWorld)) {
return;
}
@@ -313,7 +320,7 @@ public class IrisEngineSVC implements IrisService {
|| !shouldReduce(engine))
return;
World engineWorld = engine.getWorld().realWorld();
if (engineWorld != null && IrisToolbelt.isWorldMaintenanceActive(engineWorld)) {
if (shouldSkipForMaintenance(engineWorld)) {
return;
}
@@ -363,7 +370,32 @@ public class IrisEngineSVC implements IrisService {
}
private boolean shouldReduce(Engine engine) {
return !engine.isStudio() || IrisSettings.get().getPerformance().isTrimMantleInStudio();
if (!engine.isStudio() || IrisSettings.get().getPerformance().isTrimMantleInStudio()) {
return true;
}
World world = engine.getWorld().realWorld();
if (world == null) {
return false;
}
PregeneratorJob pregeneratorJob = PregeneratorJob.getInstance();
return pregeneratorJob != null && pregeneratorJob.targetsWorld(world);
}
private boolean shouldSkipForMaintenance(@Nullable World world) {
if (world == null) {
return false;
}
boolean maintenanceActive = IrisToolbelt.isWorldMaintenanceActive(world);
if (!maintenanceActive) {
return false;
}
PregeneratorJob pregeneratorJob = PregeneratorJob.getInstance();
boolean pregeneratorTargetsWorld = pregeneratorJob != null && pregeneratorJob.targetsWorld(world);
return shouldSkipMantleReductionForMaintenance(maintenanceActive, pregeneratorTargetsWorld);
}
}
}
@@ -200,11 +200,11 @@ public class IrisComplex implements DataProvider {
heightStream = ProceduralStream.of((x, z) -> {
IrisBiome b = focusBiome != null ? focusBiome : baseBiomeStream.get(x, z);
return getHeight(engine, b, x, z, engine.getSeedManager().getHeight());
}, Interpolated.DOUBLE).cache2D("heightStream", engine, cacheSize).waste("Height Stream");
}, Interpolated.DOUBLE).cache2DDouble("heightStream", engine, cacheSize).waste("Height Stream");
roundedHeighteightStream = heightStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getHeight().get(x, z))
.round().waste("Rounded Height Stream");
slopeStream = heightStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getHeight().get(x, z))
.slope(3).cache2D("slopeStream", engine, cacheSize).waste("Slope Stream");
.slope(3).cache2DDouble("slopeStream", engine, cacheSize).waste("Slope Stream");
trueBiomeStream = focusBiome != null ? ProceduralStream.of((x, y) -> focusBiome, Interpolated.of(a -> 0D,
b -> focusBiome))
.cache2D("trueBiomeStream-focus", engine, cacheSize) : heightStream
@@ -215,7 +215,7 @@ public class IrisComplex implements DataProvider {
trueBiomeDerivativeStream = trueBiomeStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getBiome().get(x, z))
.convert(IrisBiome::getDerivative).cache2D("trueBiomeDerivativeStream", engine, cacheSize).waste("True Biome Derivative Stream");
heightFluidStream = heightStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getHeight().get(x, z))
.max(fluidHeight).cache2D("heightFluidStream", engine, cacheSize).waste("Height Fluid Stream");
.max(fluidHeight).cache2DDouble("heightFluidStream", engine, cacheSize).waste("Height Fluid Stream");
maxHeightStream = ProceduralStream.ofDouble((x, z) -> height).waste("Max Height Stream");
terrainSurfaceDecoration = trueBiomeStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getBiome().get(x, z))
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.NONE)).cache2D("terrainSurfaceDecoration", engine, cacheSize).waste("Surface Decoration Stream");
@@ -37,6 +37,7 @@ import art.arcane.volmlib.util.mantle.runtime.Mantle;
import art.arcane.volmlib.util.mantle.runtime.MantleChunk;
import art.arcane.volmlib.util.mantle.flag.MantleFlag;
import art.arcane.volmlib.util.math.M;
import art.arcane.volmlib.util.math.PowerOfTwoCoordinates;
import art.arcane.volmlib.util.math.Position2;
import art.arcane.volmlib.util.math.RNG;
import art.arcane.volmlib.util.matter.Matter;
@@ -219,8 +220,8 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
}
J.runEntity(player, () -> {
int centerX = player.getLocation().getBlockX() >> 4;
int centerZ = player.getLocation().getBlockZ() >> 4;
int centerX = PowerOfTwoCoordinates.blockToChunkFloor(player.getLocation().getBlockX());
int centerZ = PowerOfTwoCoordinates.blockToChunkFloor(player.getLocation().getBlockZ());
int radius = 1;
for (int x = -radius; x <= radius; x++) {
for (int z = -radius; z <= radius; z++) {
@@ -278,8 +279,8 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
}
J.runEntity(player, () -> {
int centerX = player.getLocation().getBlockX() >> 4;
int centerZ = player.getLocation().getBlockZ() >> 4;
int centerX = PowerOfTwoCoordinates.blockToChunkFloor(player.getLocation().getBlockX());
int centerZ = PowerOfTwoCoordinates.blockToChunkFloor(player.getLocation().getBlockZ());
int radius = 1;
for (int x = -radius; x <= radius; x++) {
@@ -632,12 +633,12 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
.filter((i) -> i.isValid(biome)),
Stream.concat(getData()
.getSpawnerLoader()
.loadAll(getEngine().getRegion(c.getX() << 4, c.getZ() << 4).getEntitySpawners())
.loadAll(getEngine().getRegion(PowerOfTwoCoordinates.chunkToBlock(c.getX()), PowerOfTwoCoordinates.chunkToBlock(c.getZ())).getEntitySpawners())
.shuffleCopy(RNG.r)
.stream()
.filter(filter),
getData().getSpawnerLoader()
.loadAll(getEngine().getSurfaceBiome(c.getX() << 4, c.getZ() << 4).getEntitySpawners())
.loadAll(getEngine().getSurfaceBiome(PowerOfTwoCoordinates.chunkToBlock(c.getX()), PowerOfTwoCoordinates.chunkToBlock(c.getZ())).getEntitySpawners())
.shuffleCopy(RNG.r)
.stream()
.filter(filter)))
@@ -668,13 +669,13 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
private void spawn(IrisPosition pos, IrisEntitySpawn i) {
IrisSpawner ref = i.getReferenceSpawner();
if (!ref.canSpawn(getEngine(), pos.getX() >> 4, pos.getZ() >> 4))
if (!ref.canSpawn(getEngine(), PowerOfTwoCoordinates.blockToChunkFloor(pos.getX()), PowerOfTwoCoordinates.blockToChunkFloor(pos.getZ())))
return;
int s = i.spawn(getEngine(), pos, RNG.r);
actuallySpawned += s;
if (s > 0) {
ref.spawn(getEngine(), pos.getX() >> 4, pos.getZ() >> 4);
ref.spawn(getEngine(), PowerOfTwoCoordinates.blockToChunkFloor(pos.getX()), PowerOfTwoCoordinates.blockToChunkFloor(pos.getZ()));
energy -= s * ((i.getEnergyMultiplier() * ref.getEnergyMultiplier() * 1));
}
}
@@ -26,6 +26,7 @@ import art.arcane.iris.engine.object.IrisCaveProfile;
import art.arcane.iris.engine.object.IrisRange;
import art.arcane.iris.util.project.noise.CNG;
import art.arcane.volmlib.util.mantle.runtime.MantleChunk;
import art.arcane.volmlib.util.math.PowerOfTwoCoordinates;
import art.arcane.volmlib.util.math.RNG;
import art.arcane.volmlib.util.matter.Matter;
import art.arcane.volmlib.util.matter.MatterCavern;
@@ -55,8 +56,11 @@ public class IrisCaveCarver3D {
private final MatterCavern carveAir;
private final MatterCavern carveLava;
private final MatterCavern carveForcedAir;
private final double normalizationFactor;
private final double baseWeight;
private final double detailWeight;
private final double detailMinContribution;
private final double detailMaxContribution;
private final double warpStrength;
private final boolean hasWarp;
private final boolean hasModules;
@@ -93,8 +97,11 @@ public class IrisCaveCarver3D {
this.modules = moduleStates.toArray(new ModuleState[0]);
double normalization = weight <= 0 ? 1 : weight;
normalizationFactor = normalization;
inverseNormalization = 1D / normalization;
hasModules = modules.length > 0;
detailMinContribution = -detailWeight;
detailMaxContribution = detailWeight;
}
public int carve(MantleWriter writer, int chunkX, int chunkZ) {
@@ -178,8 +185,8 @@ public class IrisCaveCarver3D {
return 0;
}
int x0 = chunkX << 4;
int z0 = chunkZ << 4;
int x0 = PowerOfTwoCoordinates.chunkToBlock(chunkX);
int z0 = PowerOfTwoCoordinates.chunkToBlock(chunkZ);
int[] columnMaxY = scratch.columnMaxY;
int[] surfaceBreakFloorY = scratch.surfaceBreakFloorY;
boolean[] surfaceBreakColumn = scratch.surfaceBreakColumn;
@@ -193,7 +200,7 @@ public class IrisCaveCarver3D {
int x = x0 + lx;
for (int lz = 0; lz < 16; lz++) {
int z = z0 + lz;
int index = (lx << 4) | lz;
int index = PowerOfTwoCoordinates.packLocal16(lx, lz);
int columnSurfaceY;
if (precomputedSurfaceHeights != null && precomputedSurfaceHeights.length > index) {
columnSurfaceY = precomputedSurfaceHeights[index];
@@ -202,7 +209,7 @@ public class IrisCaveCarver3D {
}
int clearanceTopY = Math.min(maxY, Math.max(minY, columnSurfaceY - surfaceClearance));
boolean breakColumn = allowSurfaceBreak
&& signed(surfaceBreakDensity.noiseFast2D(x, z)) >= surfaceBreakNoiseThreshold;
&& surfaceBreakDensity.noiseFastSigned2D(x, z) >= surfaceBreakNoiseThreshold;
int columnTopY = breakColumn
? Math.min(maxY, Math.max(minY, columnSurfaceY))
: clearanceTopY;
@@ -399,13 +406,14 @@ public class IrisCaveCarver3D {
}
int[] planeColumnIndices = scratch.planeColumnIndices;
double[] planeDensity = scratch.planeDensity;
int minSection = minY >> 4;
int maxSection = maxY >> 4;
double[] planeThresholdLimit = scratch.planeThresholdLimit;
boolean[] planeCarve = scratch.planeCarve;
int minSection = PowerOfTwoCoordinates.floorDivPow2(minY, 4);
int maxSection = PowerOfTwoCoordinates.floorDivPow2(maxY, 4);
for (int sectionIndex = minSection; sectionIndex <= maxSection; sectionIndex++) {
int sectionMinY = Math.max(minY, sectionIndex << 4);
int sectionMaxY = Math.min(maxY, (sectionIndex << 4) + 15);
int sectionMinY = Math.max(minY, PowerOfTwoCoordinates.chunkToBlock(sectionIndex));
int sectionMaxY = Math.min(maxY, PowerOfTwoCoordinates.chunkToBlock(sectionIndex) + 15);
MatterSlice<MatterCavern> cavernSlice = resolveCavernSlice(scratch, chunk, sectionIndex);
for (int y = sectionMinY; y <= sectionMaxY; y++) {
@@ -415,7 +423,14 @@ public class IrisCaveCarver3D {
continue;
}
planeColumnIndices[planeCount] = activeColumnIndices[activeIndex];
int columnIndex = activeColumnIndices[activeIndex];
planeColumnIndices[planeCount] = columnIndex;
double localThreshold = passThreshold[columnIndex];
if (surfaceBreakColumn[columnIndex] && y >= surfaceBreakFloorY[columnIndex]) {
localThreshold += surfaceBreakThresholdBoost;
}
localThreshold -= verticalEdgeFade[y - minY];
planeThresholdLimit[planeCount] = localThreshold * normalizationFactor;
planeCount++;
}
@@ -423,24 +438,19 @@ public class IrisCaveCarver3D {
continue;
}
fillDensityPlane(x0, z0, y, planeColumnIndices, planeCount, planeDensity);
classifyDensityPlane(x0, z0, y, planeColumnIndices, planeThresholdLimit, planeCount, planeCarve);
int fadeIndex = y - minY;
int localY = y & 15;
MatterCavern matter = matterByY[fadeIndex];
if (skipExistingCarved) {
for (int planeIndex = 0; planeIndex < planeCount; planeIndex++) {
int columnIndex = planeColumnIndices[planeIndex];
double localThreshold = passThreshold[columnIndex];
if (surfaceBreakColumn[columnIndex] && y >= surfaceBreakFloorY[columnIndex]) {
localThreshold += surfaceBreakThresholdBoost;
}
localThreshold -= verticalEdgeFade[fadeIndex];
if (planeDensity[planeIndex] > localThreshold) {
if (!planeCarve[planeIndex]) {
continue;
}
int localX = columnIndex >> 4;
int columnIndex = planeColumnIndices[planeIndex];
int localX = PowerOfTwoCoordinates.unpackLocal16X(columnIndex);
int localZ = columnIndex & 15;
if (cavernSlice.get(localX, localY, localZ) != null) {
continue;
@@ -453,17 +463,12 @@ public class IrisCaveCarver3D {
}
for (int planeIndex = 0; planeIndex < planeCount; planeIndex++) {
int columnIndex = planeColumnIndices[planeIndex];
double localThreshold = passThreshold[columnIndex];
if (surfaceBreakColumn[columnIndex] && y >= surfaceBreakFloorY[columnIndex]) {
localThreshold += surfaceBreakThresholdBoost;
}
localThreshold -= verticalEdgeFade[fadeIndex];
if (planeDensity[planeIndex] > localThreshold) {
if (!planeCarve[planeIndex]) {
continue;
}
int localX = columnIndex >> 4;
int columnIndex = planeColumnIndices[planeIndex];
int localX = PowerOfTwoCoordinates.unpackLocal16X(columnIndex);
int localZ = columnIndex & 15;
cavernSlice.set(localX, localY, localZ, matter);
carved++;
@@ -519,7 +524,7 @@ public class IrisCaveCarver3D {
int lz1 = lz + 1;
int activeColumns = 0;
int index00 = (lx << 4) | lz;
int index00 = PowerOfTwoCoordinates.packLocal16(lx, lz);
if (!Double.isNaN(passThreshold[index00])) {
tileIndices[activeColumns] = index00;
tileLocalX[activeColumns] = lx;
@@ -528,7 +533,7 @@ public class IrisCaveCarver3D {
activeColumns++;
}
int index01 = (lx << 4) | lz1;
int index01 = PowerOfTwoCoordinates.packLocal16(lx, lz1);
if (!Double.isNaN(passThreshold[index01])) {
tileIndices[activeColumns] = index01;
tileLocalX[activeColumns] = lx;
@@ -537,7 +542,7 @@ public class IrisCaveCarver3D {
activeColumns++;
}
int index10 = (lx1 << 4) | lz;
int index10 = PowerOfTwoCoordinates.packLocal16(lx1, lz);
if (!Double.isNaN(passThreshold[index10])) {
tileIndices[activeColumns] = index10;
tileLocalX[activeColumns] = lx1;
@@ -546,7 +551,7 @@ public class IrisCaveCarver3D {
activeColumns++;
}
int index11 = (lx1 << 4) | lz1;
int index11 = PowerOfTwoCoordinates.packLocal16(lx1, lz1);
if (!Double.isNaN(passThreshold[index11])) {
tileIndices[activeColumns] = index11;
tileLocalX[activeColumns] = lx1;
@@ -574,7 +579,7 @@ public class IrisCaveCarver3D {
int stampMaxY = Math.min(maxY, y + 1);
for (int yy = y; yy <= stampMaxY; yy++) {
MatterCavern matter = matterByY[yy - minY];
MatterSlice<MatterCavern> cavernSlice = resolveCavernSlice(scratch, chunk, yy >> 4);
MatterSlice<MatterCavern> cavernSlice = resolveCavernSlice(scratch, chunk, PowerOfTwoCoordinates.floorDivPow2(yy, 4));
int localY = yy & 15;
int fadeIndex = yy - minY;
for (int columnIndex = 0; columnIndex < activeColumns; columnIndex++) {
@@ -640,7 +645,7 @@ public class IrisCaveCarver3D {
int x = x0 + lx;
for (int lz = 0; lz < 16; lz++) {
int z = z0 + lz;
int index = (lx << 4) | lz;
int index = PowerOfTwoCoordinates.packLocal16(lx, lz);
double columnWeight = clampedWeights[index];
if (columnWeight <= minWeight) {
continue;
@@ -669,7 +674,7 @@ public class IrisCaveCarver3D {
int carveMaxY = Math.min(columnTopY, y + sampleStep - 1);
for (int yy = y; yy <= carveMaxY; yy++) {
MatterCavern matter = matterByY[yy - minY];
MatterSlice<MatterCavern> cavernSlice = resolveCavernSlice(scratch, chunk, yy >> 4);
MatterSlice<MatterCavern> cavernSlice = resolveCavernSlice(scratch, chunk, PowerOfTwoCoordinates.floorDivPow2(yy, 4));
int localY = yy & 15;
if (skipExistingCarved) {
if (cavernSlice.get(lx, localY, lz) == null) {
@@ -719,153 +724,234 @@ public class IrisCaveCarver3D {
return sampleDensityWarpModules(x, y, z);
}
private void fillDensityPlane(int x0, int z0, int y, int[] planeColumnIndices, int planeCount, double[] planeDensity) {
private void classifyDensityPlane(int x0, int z0, int y, int[] planeColumnIndices, double[] planeThresholdLimit, int planeCount, boolean[] planeCarve) {
if (!hasWarp) {
if (!hasModules) {
fillDensityPlaneNoWarpNoModules(x0, z0, y, planeColumnIndices, planeCount, planeDensity);
classifyDensityPlaneNoWarpNoModules(x0, z0, y, planeColumnIndices, planeThresholdLimit, planeCount, planeCarve);
return;
}
fillDensityPlaneNoWarpModules(x0, z0, y, planeColumnIndices, planeCount, planeDensity);
classifyDensityPlaneNoWarpModules(x0, z0, y, planeColumnIndices, planeThresholdLimit, planeCount, planeCarve);
return;
}
if (!hasModules) {
fillDensityPlaneWarpOnly(x0, z0, y, planeColumnIndices, planeCount, planeDensity);
classifyDensityPlaneWarpOnly(x0, z0, y, planeColumnIndices, planeThresholdLimit, planeCount, planeCarve);
return;
}
fillDensityPlaneWarpModules(x0, z0, y, planeColumnIndices, planeCount, planeDensity);
classifyDensityPlaneWarpModules(x0, z0, y, planeColumnIndices, planeThresholdLimit, planeCount, planeCarve);
}
private void fillDensityPlaneNoWarpNoModules(int x0, int z0, int y, int[] planeColumnIndices, int planeCount, double[] planeDensity) {
private void classifyDensityPlaneNoWarpNoModules(int x0, int z0, int y, int[] planeColumnIndices, double[] planeThresholdLimit, int planeCount, boolean[] planeCarve) {
CNG localBaseDensity = baseDensity;
CNG localDetailDensity = detailDensity;
double localBaseWeight = baseWeight;
double localDetailWeight = detailWeight;
double normalization = inverseNormalization;
double detailMin = detailMinContribution;
double detailMax = detailMaxContribution;
for (int planeIndex = 0; planeIndex < planeCount; planeIndex++) {
int columnIndex = planeColumnIndices[planeIndex];
int x = x0 + (columnIndex >> 4);
int x = x0 + PowerOfTwoCoordinates.unpackLocal16X(columnIndex);
int z = z0 + (columnIndex & 15);
double density = signed(localBaseDensity.noiseFast3D(x, y, z)) * localBaseWeight;
density += signed(localDetailDensity.noiseFast3D(x, y, z)) * localDetailWeight;
planeDensity[planeIndex] = density * normalization;
}
}
private void fillDensityPlaneNoWarpModules(int x0, int z0, int y, int[] planeColumnIndices, int planeCount, double[] planeDensity) {
CNG localBaseDensity = baseDensity;
CNG localDetailDensity = detailDensity;
ModuleState[] localModules = modules;
double localBaseWeight = baseWeight;
double localDetailWeight = detailWeight;
double normalization = inverseNormalization;
for (int planeIndex = 0; planeIndex < planeCount; planeIndex++) {
int columnIndex = planeColumnIndices[planeIndex];
int x = x0 + (columnIndex >> 4);
int z = z0 + (columnIndex & 15);
double density = signed(localBaseDensity.noiseFast3D(x, y, z)) * localBaseWeight;
density += signed(localDetailDensity.noiseFast3D(x, y, z)) * localDetailWeight;
for (int moduleIndex = 0; moduleIndex < localModules.length; moduleIndex++) {
ModuleState module = localModules[moduleIndex];
if (y < module.minY || y > module.maxY) {
continue;
}
double moduleDensity = signed(module.density.noiseFast3D(x, y, z)) - module.threshold;
if (module.invert) {
moduleDensity = -moduleDensity;
}
density += moduleDensity * module.weight;
double thresholdLimit = planeThresholdLimit[planeIndex];
double density = localBaseDensity.noiseFastSigned3D(x, y, z) * localBaseWeight;
if ((density + detailMin) > thresholdLimit) {
planeCarve[planeIndex] = false;
continue;
}
if ((density + detailMax) <= thresholdLimit) {
planeCarve[planeIndex] = true;
continue;
}
planeDensity[planeIndex] = density * normalization;
density += localDetailDensity.noiseFastSigned3D(x, y, z) * localDetailWeight;
planeCarve[planeIndex] = density <= thresholdLimit;
}
}
private void fillDensityPlaneWarpOnly(int x0, int z0, int y, int[] planeColumnIndices, int planeCount, double[] planeDensity) {
private void classifyDensityPlaneNoWarpModules(int x0, int z0, int y, int[] planeColumnIndices, double[] planeThresholdLimit, int planeCount, boolean[] planeCarve) {
CNG localBaseDensity = baseDensity;
CNG localDetailDensity = detailDensity;
Scratch scratch = SCRATCH.get();
int activeModuleCount = prepareActiveModules(scratch, y);
if (activeModuleCount == 0) {
classifyDensityPlaneNoWarpNoModules(x0, z0, y, planeColumnIndices, planeThresholdLimit, planeCount, planeCarve);
return;
}
ModuleState[] localModules = scratch.activeModules;
double[] remainingMin = scratch.activeModuleRemainingMin;
double[] remainingMax = scratch.activeModuleRemainingMax;
double localBaseWeight = baseWeight;
double localDetailWeight = detailWeight;
double detailMin = detailMinContribution;
double detailMax = detailMaxContribution;
for (int planeIndex = 0; planeIndex < planeCount; planeIndex++) {
int columnIndex = planeColumnIndices[planeIndex];
int x = x0 + PowerOfTwoCoordinates.unpackLocal16X(columnIndex);
int z = z0 + (columnIndex & 15);
double thresholdLimit = planeThresholdLimit[planeIndex];
double density = localBaseDensity.noiseFastSigned3D(x, y, z) * localBaseWeight;
if ((density + detailMin + remainingMin[0]) > thresholdLimit) {
planeCarve[planeIndex] = false;
continue;
}
if ((density + detailMax + remainingMax[0]) <= thresholdLimit) {
planeCarve[planeIndex] = true;
continue;
}
density += localDetailDensity.noiseFastSigned3D(x, y, z) * localDetailWeight;
if ((density + remainingMin[0]) > thresholdLimit) {
planeCarve[planeIndex] = false;
continue;
}
if ((density + remainingMax[0]) <= thresholdLimit) {
planeCarve[planeIndex] = true;
continue;
}
for (int moduleIndex = 0; moduleIndex < activeModuleCount; moduleIndex++) {
ModuleState module = localModules[moduleIndex];
density += module.sample(x, y, z);
if ((density + remainingMin[moduleIndex + 1]) > thresholdLimit) {
density = Double.POSITIVE_INFINITY;
break;
}
if ((density + remainingMax[moduleIndex + 1]) <= thresholdLimit) {
density = Double.NEGATIVE_INFINITY;
break;
}
}
planeCarve[planeIndex] = density <= thresholdLimit;
}
}
private void classifyDensityPlaneWarpOnly(int x0, int z0, int y, int[] planeColumnIndices, double[] planeThresholdLimit, int planeCount, boolean[] planeCarve) {
CNG localBaseDensity = baseDensity;
CNG localDetailDensity = detailDensity;
CNG localWarpDensity = warpDensity;
double localBaseWeight = baseWeight;
double localDetailWeight = detailWeight;
double localWarpStrength = warpStrength;
double normalization = inverseNormalization;
double detailMin = detailMinContribution;
double detailMax = detailMaxContribution;
for (int planeIndex = 0; planeIndex < planeCount; planeIndex++) {
int columnIndex = planeColumnIndices[planeIndex];
double x = x0 + (columnIndex >> 4);
double x = x0 + PowerOfTwoCoordinates.unpackLocal16X(columnIndex);
double z = z0 + (columnIndex & 15);
double warpA = signed(localWarpDensity.noiseFast3D(x, y, z));
double warpB = signed(localWarpDensity.noiseFast3D(x + 31.37D, y - 17.21D, z + 23.91D));
double thresholdLimit = planeThresholdLimit[planeIndex];
double warpA = localWarpDensity.noiseFastSigned3D(x, y, z);
double warpB = localWarpDensity.noiseFastSigned3D(x + 31.37D, y - 17.21D, z + 23.91D);
double warpedX = x + (warpA * localWarpStrength);
double warpedY = y + (warpB * localWarpStrength);
double warpedZ = z + ((warpA - warpB) * 0.5D * localWarpStrength);
double density = signed(localBaseDensity.noiseFast3D(warpedX, warpedY, warpedZ)) * localBaseWeight;
density += signed(localDetailDensity.noiseFast3D(warpedX, warpedY, warpedZ)) * localDetailWeight;
planeDensity[planeIndex] = density * normalization;
double density = localBaseDensity.noiseFastSigned3D(warpedX, warpedY, warpedZ) * localBaseWeight;
if ((density + detailMin) > thresholdLimit) {
planeCarve[planeIndex] = false;
continue;
}
if ((density + detailMax) <= thresholdLimit) {
planeCarve[planeIndex] = true;
continue;
}
density += localDetailDensity.noiseFastSigned3D(warpedX, warpedY, warpedZ) * localDetailWeight;
planeCarve[planeIndex] = density <= thresholdLimit;
}
}
private void fillDensityPlaneWarpModules(int x0, int z0, int y, int[] planeColumnIndices, int planeCount, double[] planeDensity) {
private void classifyDensityPlaneWarpModules(int x0, int z0, int y, int[] planeColumnIndices, double[] planeThresholdLimit, int planeCount, boolean[] planeCarve) {
CNG localBaseDensity = baseDensity;
CNG localDetailDensity = detailDensity;
CNG localWarpDensity = warpDensity;
ModuleState[] localModules = modules;
Scratch scratch = SCRATCH.get();
int activeModuleCount = prepareActiveModules(scratch, y);
if (activeModuleCount == 0) {
classifyDensityPlaneWarpOnly(x0, z0, y, planeColumnIndices, planeThresholdLimit, planeCount, planeCarve);
return;
}
ModuleState[] localModules = scratch.activeModules;
double[] remainingMin = scratch.activeModuleRemainingMin;
double[] remainingMax = scratch.activeModuleRemainingMax;
double localBaseWeight = baseWeight;
double localDetailWeight = detailWeight;
double localWarpStrength = warpStrength;
double normalization = inverseNormalization;
double detailMin = detailMinContribution;
double detailMax = detailMaxContribution;
for (int planeIndex = 0; planeIndex < planeCount; planeIndex++) {
int columnIndex = planeColumnIndices[planeIndex];
double x = x0 + (columnIndex >> 4);
double x = x0 + PowerOfTwoCoordinates.unpackLocal16X(columnIndex);
double z = z0 + (columnIndex & 15);
double warpA = signed(localWarpDensity.noiseFast3D(x, y, z));
double warpB = signed(localWarpDensity.noiseFast3D(x + 31.37D, y - 17.21D, z + 23.91D));
double thresholdLimit = planeThresholdLimit[planeIndex];
double warpA = localWarpDensity.noiseFastSigned3D(x, y, z);
double warpB = localWarpDensity.noiseFastSigned3D(x + 31.37D, y - 17.21D, z + 23.91D);
double warpedX = x + (warpA * localWarpStrength);
double warpedY = y + (warpB * localWarpStrength);
double warpedZ = z + ((warpA - warpB) * 0.5D * localWarpStrength);
double density = signed(localBaseDensity.noiseFast3D(warpedX, warpedY, warpedZ)) * localBaseWeight;
density += signed(localDetailDensity.noiseFast3D(warpedX, warpedY, warpedZ)) * localDetailWeight;
for (int moduleIndex = 0; moduleIndex < localModules.length; moduleIndex++) {
ModuleState module = localModules[moduleIndex];
if (y < module.minY || y > module.maxY) {
continue;
}
double moduleDensity = signed(module.density.noiseFast3D(warpedX, warpedY, warpedZ)) - module.threshold;
if (module.invert) {
moduleDensity = -moduleDensity;
}
density += moduleDensity * module.weight;
double density = localBaseDensity.noiseFastSigned3D(warpedX, warpedY, warpedZ) * localBaseWeight;
if ((density + detailMin + remainingMin[0]) > thresholdLimit) {
planeCarve[planeIndex] = false;
continue;
}
if ((density + detailMax + remainingMax[0]) <= thresholdLimit) {
planeCarve[planeIndex] = true;
continue;
}
planeDensity[planeIndex] = density * normalization;
density += localDetailDensity.noiseFastSigned3D(warpedX, warpedY, warpedZ) * localDetailWeight;
if ((density + remainingMin[0]) > thresholdLimit) {
planeCarve[planeIndex] = false;
continue;
}
if ((density + remainingMax[0]) <= thresholdLimit) {
planeCarve[planeIndex] = true;
continue;
}
for (int moduleIndex = 0; moduleIndex < activeModuleCount; moduleIndex++) {
ModuleState module = localModules[moduleIndex];
density += module.sample(warpedX, warpedY, warpedZ);
if ((density + remainingMin[moduleIndex + 1]) > thresholdLimit) {
density = Double.POSITIVE_INFINITY;
break;
}
if ((density + remainingMax[moduleIndex + 1]) <= thresholdLimit) {
density = Double.NEGATIVE_INFINITY;
break;
}
}
planeCarve[planeIndex] = density <= thresholdLimit;
}
}
private double sampleDensityNoWarpNoModules(int x, int y, int z) {
double density = signed(baseDensity.noiseFast3D(x, y, z)) * baseWeight;
density += signed(detailDensity.noiseFast3D(x, y, z)) * detailWeight;
double density = baseDensity.noiseFastSigned3D(x, y, z) * baseWeight;
density += detailDensity.noiseFastSigned3D(x, y, z) * detailWeight;
return density * inverseNormalization;
}
private double sampleDensityNoWarpModules(int x, int y, int z) {
double density = signed(baseDensity.noiseFast3D(x, y, z)) * baseWeight;
density += signed(detailDensity.noiseFast3D(x, y, z)) * detailWeight;
for (int moduleIndex = 0; moduleIndex < modules.length; moduleIndex++) {
ModuleState module = modules[moduleIndex];
if (y < module.minY || y > module.maxY) {
continue;
}
Scratch scratch = SCRATCH.get();
int activeModuleCount = prepareActiveModules(scratch, y);
if (activeModuleCount == 0) {
return sampleDensityNoWarpNoModules(x, y, z);
}
double moduleDensity = signed(module.density.noiseFast3D(x, y, z)) - module.threshold;
ModuleState[] localModules = scratch.activeModules;
double density = baseDensity.noiseFastSigned3D(x, y, z) * baseWeight;
density += detailDensity.noiseFastSigned3D(x, y, z) * detailWeight;
for (int moduleIndex = 0; moduleIndex < activeModuleCount; moduleIndex++) {
ModuleState module = localModules[moduleIndex];
double moduleDensity = module.density.noiseFastSigned3D(x, y, z) - module.threshold;
if (module.invert) {
moduleDensity = -moduleDensity;
}
@@ -877,31 +963,34 @@ public class IrisCaveCarver3D {
}
private double sampleDensityWarpOnly(int x, int y, int z) {
double warpA = signed(warpDensity.noiseFast3D(x, y, z));
double warpB = signed(warpDensity.noiseFast3D(x + 31.37D, y - 17.21D, z + 23.91D));
double warpA = warpDensity.noiseFastSigned3D(x, y, z);
double warpB = warpDensity.noiseFastSigned3D(x + 31.37D, y - 17.21D, z + 23.91D);
double warpedX = x + (warpA * warpStrength);
double warpedY = y + (warpB * warpStrength);
double warpedZ = z + ((warpA - warpB) * 0.5D * warpStrength);
double density = signed(baseDensity.noiseFast3D(warpedX, warpedY, warpedZ)) * baseWeight;
density += signed(detailDensity.noiseFast3D(warpedX, warpedY, warpedZ)) * detailWeight;
double density = baseDensity.noiseFastSigned3D(warpedX, warpedY, warpedZ) * baseWeight;
density += detailDensity.noiseFastSigned3D(warpedX, warpedY, warpedZ) * detailWeight;
return density * inverseNormalization;
}
private double sampleDensityWarpModules(int x, int y, int z) {
double warpA = signed(warpDensity.noiseFast3D(x, y, z));
double warpB = signed(warpDensity.noiseFast3D(x + 31.37D, y - 17.21D, z + 23.91D));
Scratch scratch = SCRATCH.get();
int activeModuleCount = prepareActiveModules(scratch, y);
if (activeModuleCount == 0) {
return sampleDensityWarpOnly(x, y, z);
}
ModuleState[] localModules = scratch.activeModules;
double warpA = warpDensity.noiseFastSigned3D(x, y, z);
double warpB = warpDensity.noiseFastSigned3D(x + 31.37D, y - 17.21D, z + 23.91D);
double warpedX = x + (warpA * warpStrength);
double warpedY = y + (warpB * warpStrength);
double warpedZ = z + ((warpA - warpB) * 0.5D * warpStrength);
double density = signed(baseDensity.noiseFast3D(warpedX, warpedY, warpedZ)) * baseWeight;
density += signed(detailDensity.noiseFast3D(warpedX, warpedY, warpedZ)) * detailWeight;
for (int moduleIndex = 0; moduleIndex < modules.length; moduleIndex++) {
ModuleState module = modules[moduleIndex];
if (y < module.minY || y > module.maxY) {
continue;
}
double moduleDensity = signed(module.density.noiseFast3D(warpedX, warpedY, warpedZ)) - module.threshold;
double density = baseDensity.noiseFastSigned3D(warpedX, warpedY, warpedZ) * baseWeight;
density += detailDensity.noiseFastSigned3D(warpedX, warpedY, warpedZ) * detailWeight;
for (int moduleIndex = 0; moduleIndex < activeModuleCount; moduleIndex++) {
ModuleState module = localModules[moduleIndex];
double moduleDensity = module.density.noiseFastSigned3D(warpedX, warpedY, warpedZ) - module.threshold;
if (module.invert) {
moduleDensity = -moduleDensity;
}
@@ -912,6 +1001,44 @@ public class IrisCaveCarver3D {
return density * inverseNormalization;
}
private int prepareActiveModules(Scratch scratch, int y) {
ModuleState[] configuredModules = modules;
int configuredCount = configuredModules.length;
if (configuredCount == 0) {
return 0;
}
if (scratch.activeModules.length < configuredCount) {
scratch.activeModules = new ModuleState[configuredCount];
}
int activeCount = 0;
for (int moduleIndex = 0; moduleIndex < configuredCount; moduleIndex++) {
ModuleState module = configuredModules[moduleIndex];
if (y < module.minY || y > module.maxY) {
continue;
}
scratch.activeModules[activeCount] = module;
activeCount++;
}
if (scratch.activeModuleRemainingMin.length < activeCount + 1) {
scratch.activeModuleRemainingMin = new double[activeCount + 1];
scratch.activeModuleRemainingMax = new double[activeCount + 1];
}
scratch.activeModuleRemainingMin[activeCount] = 0D;
scratch.activeModuleRemainingMax[activeCount] = 0D;
for (int moduleIndex = activeCount - 1; moduleIndex >= 0; moduleIndex--) {
ModuleState module = scratch.activeModules[moduleIndex];
scratch.activeModuleRemainingMin[moduleIndex] = scratch.activeModuleRemainingMin[moduleIndex + 1] + module.minContribution;
scratch.activeModuleRemainingMax[moduleIndex] = scratch.activeModuleRemainingMax[moduleIndex + 1] + module.maxContribution;
}
return activeCount;
}
private MatterSlice<MatterCavern> resolveCavernSlice(Scratch scratch, MantleChunk<Matter> chunk, int sectionIndex) {
@SuppressWarnings("unchecked")
MatterSlice<MatterCavern> cachedSlice = (MatterSlice<MatterCavern>) scratch.sectionSlices[sectionIndex];
@@ -964,8 +1091,8 @@ public class IrisCaveCarver3D {
}
private void prepareSectionCaches(Scratch scratch, int minY, int maxY) {
int minSection = Math.max(0, minY >> 4);
int maxSection = Math.max(minSection, maxY >> 4);
int minSection = Math.max(0, PowerOfTwoCoordinates.floorDivPow2(minY, 4));
int maxSection = Math.max(minSection, PowerOfTwoCoordinates.floorDivPow2(maxY, 4));
int requiredSections = maxSection + 1;
if (scratch.sectionMatter.length < requiredSections) {
scratch.sectionMatter = new Matter[requiredSections];
@@ -1038,6 +1165,8 @@ public class IrisCaveCarver3D {
private final double weight;
private final double threshold;
private final boolean invert;
private final double minContribution;
private final double maxContribution;
private ModuleState(IrisCaveFieldModule module, CNG density) {
IrisRange range = module.getVerticalRange();
@@ -1047,6 +1176,20 @@ public class IrisCaveCarver3D {
this.weight = module.getWeight();
this.threshold = module.getThreshold();
this.invert = module.isInvert();
double rawMin = invert ? threshold - 1D : -1D - threshold;
double rawMax = invert ? threshold + 1D : 1D - threshold;
this.minContribution = rawMin * weight;
this.maxContribution = rawMax * weight;
}
private double sample(double x, int y, double z) {
double sampled = density.noiseFastSigned3D(x, y, z);
return invert ? (threshold - sampled) * weight : (sampled - threshold) * weight;
}
private double sample(double x, double y, double z) {
double sampled = density.noiseFastSigned3D(x, y, z);
return invert ? (threshold - sampled) * weight : (sampled - threshold) * weight;
}
}
@@ -1061,11 +1204,15 @@ public class IrisCaveCarver3D {
private final int[] activeColumnIndices = new int[256];
private final int[] activeColumnTopY = new int[256];
private final int[] planeColumnIndices = new int[256];
private final double[] planeDensity = new double[256];
private final double[] planeThresholdLimit = new double[256];
private final boolean[] planeCarve = new boolean[256];
private final int[] tileIndices = new int[4];
private final int[] tileLocalX = new int[4];
private final int[] tileLocalZ = new int[4];
private final int[] tileTopY = new int[4];
private ModuleState[] activeModules = new ModuleState[0];
private double[] activeModuleRemainingMin = new double[0];
private double[] activeModuleRemainingMax = new double[0];
private double[] verticalEdgeFade = new double[0];
private MatterCavern[] matterByY = new MatterCavern[0];
private Matter[] sectionMatter = new Matter[0];
@@ -33,6 +33,7 @@ import art.arcane.iris.engine.object.IrisRange;
import art.arcane.iris.util.project.context.ChunkContext;
import art.arcane.volmlib.util.documentation.ChunkCoordinates;
import art.arcane.volmlib.util.mantle.flag.ReservedFlag;
import art.arcane.volmlib.util.math.PowerOfTwoCoordinates;
import art.arcane.volmlib.util.scheduling.PrecisionStopwatch;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
@@ -159,7 +160,7 @@ public class MantleCarvingComponent extends IrisMantleComponent {
continue;
}
int columnIndex = (localX << 4) | localZ;
int columnIndex = PowerOfTwoCoordinates.packLocal16(localX, localZ);
double dominantWeight = clampWeight(dominantKernelWeight / totalKernelWeight);
double[] weights = columnProfileWeights.get(dominantProfile);
if (weights == null) {
@@ -255,21 +256,21 @@ public class MantleCarvingComponent extends IrisMantleComponent {
}
private void buildDimensionColumnPlan(IrisDimensionCarvingEntry[] columnPlan, int chunkX, int chunkZ, IrisDimensionCarvingEntry entry, IrisDimensionCarvingResolver.State resolverState) {
int baseX = chunkX << 4;
int baseZ = chunkZ << 4;
int baseX = PowerOfTwoCoordinates.chunkToBlock(chunkX);
int baseZ = PowerOfTwoCoordinates.chunkToBlock(chunkZ);
for (int localX = 0; localX < CHUNK_SIZE; localX++) {
int worldX = baseX + localX;
for (int localZ = 0; localZ < CHUNK_SIZE; localZ++) {
int worldZ = baseZ + localZ;
int columnIndex = (localX << 4) | localZ;
int columnIndex = PowerOfTwoCoordinates.packLocal16(localX, localZ);
columnPlan[columnIndex] = IrisDimensionCarvingResolver.resolveFromRoot(getEngineMantle().getEngine(), entry, worldX, worldZ, resolverState);
}
}
}
private void fillProfileField(IrisCaveProfile[] profileField, int chunkX, int chunkZ, IrisComplex complex, IrisDimensionCarvingResolver.State resolverState, Long2ObjectOpenHashMap<IrisBiome> caveBiomeCache) {
int startX = (chunkX << 4) - BLEND_RADIUS;
int startZ = (chunkZ << 4) - BLEND_RADIUS;
int startX = PowerOfTwoCoordinates.chunkToBlock(chunkX) - BLEND_RADIUS;
int startZ = PowerOfTwoCoordinates.chunkToBlock(chunkZ) - BLEND_RADIUS;
for (int fieldX = 0; fieldX < FIELD_SIZE; fieldX++) {
int worldX = startX + fieldX;
@@ -356,8 +357,8 @@ public class MantleCarvingComponent extends IrisMantleComponent {
private int[] prepareChunkSurfaceHeights(int chunkX, int chunkZ, ChunkContext context, int[] scratch) {
int[] surfaceHeights = scratch;
int baseX = chunkX << 4;
int baseZ = chunkZ << 4;
int baseX = PowerOfTwoCoordinates.chunkToBlock(chunkX);
int baseZ = PowerOfTwoCoordinates.chunkToBlock(chunkZ);
boolean useContextHeight = context != null
&& context.getHeight() != null
&& context.getX() == baseX
@@ -366,7 +367,7 @@ public class MantleCarvingComponent extends IrisMantleComponent {
int worldX = baseX + localX;
for (int localZ = 0; localZ < CHUNK_SIZE; localZ++) {
int worldZ = baseZ + localZ;
int columnIndex = (localX << 4) | localZ;
int columnIndex = PowerOfTwoCoordinates.packLocal16(localX, localZ);
if (useContextHeight) {
Double cachedHeight = context.getHeight().get(localX, localZ);
if (cachedHeight != null) {
@@ -35,6 +35,7 @@ import art.arcane.volmlib.util.mantle.runtime.Mantle;
import art.arcane.volmlib.util.mantle.runtime.MantleChunk;
import art.arcane.volmlib.util.math.BlockPosition;
import art.arcane.volmlib.util.math.M;
import art.arcane.volmlib.util.math.PowerOfTwoCoordinates;
import art.arcane.volmlib.util.math.RNG;
import art.arcane.volmlib.util.matter.Matter;
import art.arcane.volmlib.util.matter.MatterCavern;
@@ -89,7 +90,7 @@ public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
int rx = xx & 15;
int rz = zz & 15;
int columnIndex = (rx << 4) | rz;
int columnIndex = PowerOfTwoCoordinates.packLocal16(rx, rz);
BlockData current = output.get(rx, yy, rz);
if (B.isFluid(current)) {
@@ -135,8 +136,8 @@ public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
PrecisionStopwatch applyStopwatch = PrecisionStopwatch.start();
try {
walls.forEach((rx, yy, rz, cavern) -> {
int worldX = rx + (x << 4);
int worldZ = rz + (z << 4);
int worldX = rx + PowerOfTwoCoordinates.chunkToBlock(x);
int worldZ = rz + PowerOfTwoCoordinates.chunkToBlock(z);
String customBiome = cavern.getCustomBiome();
IrisBiome biome = customBiome.isEmpty()
? resolveCaveBiome(caveBiomeCache, worldX, yy, worldZ, resolverState)
@@ -184,10 +185,10 @@ public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
return;
}
int rx = columnIndex >> 4;
int rx = PowerOfTwoCoordinates.unpackLocal16X(columnIndex);
int rz = columnIndex & 15;
int worldX = rx + (chunkX << 4);
int worldZ = rz + (chunkZ << 4);
int worldX = rx + PowerOfTwoCoordinates.chunkToBlock(chunkX);
int worldZ = rz + PowerOfTwoCoordinates.chunkToBlock(chunkZ);
CaveZone zone = new CaveZone();
zone.setFloor(firstHeight);
int buf = firstHeight - 1;
@@ -239,7 +240,7 @@ public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
}
for (int i = zone.floor; i <= zone.ceiling; i++) {
MatterCavern cavernData = (MatterCavern) mc.getOrCreate(i >> 4).slice(MatterCavern.class)
MatterCavern cavernData = (MatterCavern) mc.getOrCreate(PowerOfTwoCoordinates.floorDivPow2(i, 4)).slice(MatterCavern.class)
.get(rx, i & 15, rz);
if (cavernData != null && !cavernData.getCustomBiome().isEmpty()) {
@@ -447,11 +448,11 @@ public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
}
private int pack(int x, int y, int z) {
return (y << 8) | ((x & 15) << 4) | (z & 15);
return (y << 8) | PowerOfTwoCoordinates.packLocal16(x & 15, z & 15);
}
private int unpackX(int key) {
return (key >> 4) & 15;
return PowerOfTwoCoordinates.unpackLocal16X(key & 255);
}
private int unpackY(int key) {
@@ -459,7 +460,7 @@ public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
}
private int unpackZ(int key) {
return key & 15;
return PowerOfTwoCoordinates.unpackLocal16Z(key);
}
private int mix(int value) {
@@ -179,14 +179,17 @@ public class IrisBiome extends IrisRegistrant implements IRare {
if (ores.isEmpty()) {
return null;
}
BlockData b = null;
for (IrisOreGenerator i : ores) {
if (i.isGenerateSurface() != surface)
KList<IrisOreGenerator> localOres = ores;
int oreCount = localOres.size();
for (int oreIndex = 0; oreIndex < oreCount; oreIndex++) {
IrisOreGenerator oreGenerator = localOres.get(oreIndex);
if (oreGenerator.isGenerateSurface() != surface) {
continue;
}
b = i.generate(x, y, z, rng, data);
if (b != null) {
return b;
BlockData ore = oreGenerator.generate(x, y, z, rng, data);
if (ore != null) {
return ore;
}
}
return null;
@@ -77,7 +77,10 @@ public class IrisBiomePaletteLayer {
return getBlockData(data).get(0);
}
return getLayerGenerator(rng, data).fit(getBlockData(data), x / zoom, y / zoom, z / zoom);
double scaledX = x / zoom;
double scaledY = y / zoom;
double scaledZ = z / zoom;
return getLayerGenerator(rng, data).fit(getBlockData(data), scaledX, scaledY, scaledZ);
}
public CNG getLayerGenerator(RNG rng, IrisData data) {
@@ -290,22 +290,25 @@ public class IrisDimension extends IrisRegistrant {
carvingEntryIndex.reset();
}
public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) {
if (ores.isEmpty()) {
return null;
}
BlockData b = null;
for (IrisOreGenerator i : ores) {
if (i.isGenerateSurface() != surface)
continue;
b = i.generate(x, y, z, rng, data);
if (b != null) {
return b;
}
}
return null;
}
public BlockData generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) {
if (ores.isEmpty()) {
return null;
}
KList<IrisOreGenerator> localOres = ores;
int oreCount = localOres.size();
for (int oreIndex = 0; oreIndex < oreCount; oreIndex++) {
IrisOreGenerator oreGenerator = localOres.get(oreIndex);
if (oreGenerator.isGenerateSurface() != surface) {
continue;
}
BlockData ore = oreGenerator.generate(x, y, z, rng, data);
if (ore != null) {
return ore;
}
}
return null;
}
public int getFluidHeight() {
return fluidHeight - (int) dimensionHeight.getMin();
@@ -212,21 +212,23 @@ public class IrisGenerator extends IrisRegistrant {
int hc = (int) ((cliffHeightMin * 10) + 10 + cliffHeightMax * getSeed() + offsetX + offsetZ);
double h = multiplicitive ? 1 : 0;
double tp = 0;
double sampleX = (rx + offsetX) / zoom;
double sampleZ = (rz + offsetZ) / zoom;
if (composite.size() == 1) {
if (multiplicitive) {
h *= composite.get(0).getNoise(getSeed() + superSeed + hc, (rx + offsetX) / zoom, (rz + offsetZ) / zoom, getLoader());
h *= composite.get(0).getNoise(getSeed() + superSeed + hc, sampleX, sampleZ, getLoader());
} else {
tp += composite.get(0).getOpacity();
h += composite.get(0).getNoise(getSeed() + superSeed + hc, (rx + offsetX) / zoom, (rz + offsetZ) / zoom, getLoader());
h += composite.get(0).getNoise(getSeed() + superSeed + hc, sampleX, sampleZ, getLoader());
}
} else {
for (IrisNoiseGenerator i : composite) {
if (multiplicitive) {
h *= i.getNoise(getSeed() + superSeed + hc, (rx + offsetX) / zoom, (rz + offsetZ) / zoom, getLoader());
h *= i.getNoise(getSeed() + superSeed + hc, sampleX, sampleZ, getLoader());
} else {
tp += i.getOpacity();
h += i.getNoise(getSeed() + superSeed + hc, (rx + offsetX) / zoom, (rz + offsetZ) / zoom, getLoader());
h += i.getNoise(getSeed() + superSeed + hc, sampleX, sampleZ, getLoader());
}
}
}
@@ -245,7 +247,9 @@ public class IrisGenerator extends IrisRegistrant {
public double cell(double rx, double rz, double v, double superSeed) {
getCellGenerator(getSeed() + 46222).setShuffle(getCellFractureShuffle());
return getCellGenerator(getSeed() + 46222).getDistance(rx / getCellFractureZoom(), rz / getCellFractureZoom()) > getCellPercentSize() ? (v * getCellFractureHeight()) : v;
double fractureX = rx / getCellFractureZoom();
double fractureZ = rz / getCellFractureZoom();
return getCellGenerator(getSeed() + 46222).getDistance(fractureX, fractureZ) > getCellPercentSize() ? (v * getCellFractureHeight()) : v;
}
private boolean hasCellCracks() {
@@ -254,7 +258,9 @@ public class IrisGenerator extends IrisRegistrant {
public double getCliffHeight(double rx, double rz, double superSeed) {
int hc = (int) ((cliffHeightMin * 10) + 10 + cliffHeightMax * getSeed() + offsetX + offsetZ);
double h = cliffHeightGenerator.getNoise((long) (getSeed() + superSeed + hc), (rx + offsetX) / zoom, (rz + offsetZ) / zoom, getLoader());
double sampleX = (rx + offsetX) / zoom;
double sampleZ = (rz + offsetZ) / zoom;
double h = cliffHeightGenerator.getNoise((long) (getSeed() + superSeed + hc), sampleX, sampleZ, getLoader());
return IrisInterpolation.lerp(cliffHeightMin, cliffHeightMax, h);
}
@@ -33,6 +33,7 @@ import lombok.experimental.Accessors;
import java.io.File;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
@Snippet("style")
@Accessors(chain = true)
@@ -41,6 +42,7 @@ import java.util.Objects;
@Desc("A gen style")
@Data
public class IrisGeneratorStyle {
private static final ConcurrentHashMap<String, String> ACTIVE_CACHE_KEYS = new ConcurrentHashMap<>();
private final transient AtomicCache<CNG> cng = new AtomicCache<>();
@Desc("The chance is 1 in CHANCE per interval")
private NoiseStyle style = NoiseStyle.FLAT;
@@ -127,6 +129,11 @@ public class IrisGeneratorStyle {
private void clearStaleCacheEntries(IrisData data, String prefix, String key) {
File cacheFolder = new File(data.getDataFolder(), ".cache");
String cacheFolderKey = cacheFolder.getAbsolutePath() + File.separator + prefix;
String previousKey = ACTIVE_CACHE_KEYS.put(cacheFolderKey, key);
if (key.equals(previousKey)) {
return;
}
File[] files = cacheFolder.listFiles((dir, name) -> name.endsWith(".cnm") && name.startsWith(prefix));
if (files == null) {
return;
@@ -61,7 +61,10 @@ public class IrisMaterialPalette {
return getBlockData(rdata).get(0);
}
return getLayerGenerator(rng, rdata).fit(getBlockData(rdata), x / zoom, y / zoom, z / zoom);
double scaledX = x / zoom;
double scaledY = y / zoom;
double scaledZ = z / zoom;
return getLayerGenerator(rng, rdata).fit(getBlockData(rdata), scaledX, scaledY, scaledZ);
}
public Optional<TileData> getTile(RNG rng, double x, double y, double z, IrisData rdata) {
@@ -22,8 +22,8 @@ import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.engine.data.cache.AtomicCache;
import art.arcane.iris.engine.object.annotations.*;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.iris.util.project.interpolation.IrisInterpolation;
import art.arcane.volmlib.util.math.RNG;
import art.arcane.iris.util.project.interpolation.IrisInterpolation;
import art.arcane.iris.util.project.noise.CNG;
import lombok.AllArgsConstructor;
import lombok.Data;
@@ -100,14 +100,17 @@ public class IrisNoiseGenerator {
for (IrisNoiseGenerator i : fracture) {
if (i.isEnabled()) {
x += i.getNoise(superSeed + seed + g, xv, zv, data) - (i.getOpacity() / 2D);
z += i.getNoise(superSeed + seed + g, zv, xv, data) - (i.getOpacity() / 2D);
double fractureOffset = i.getOpacity() / 2D;
x += i.getNoise(superSeed + seed + g, xv, zv, data) - fractureOffset;
z += i.getNoise(superSeed + seed + g, zv, xv, data) - fractureOffset;
}
g += 819;
}
CNG cng = getGenerator(superSeed, data);
double n = cng.noiseFast2D((x / zoom) + offsetX, (z / zoom) + offsetZ) * opacity;
double sampleX = (x / zoom) + offsetX;
double sampleZ = (z / zoom) + offsetZ;
double n = cng.noiseFast2D(sampleX, sampleZ) * opacity;
n = negative ? (-n + opacity) : n;
n = (exponent != 1 ? n < 0 ? -Math.pow(-n, exponent) : Math.pow(n, exponent) : n) + offsetY;
n = parametric ? IrisInterpolation.parametric(n, 1) : n;
@@ -155,14 +155,17 @@ public class IrisRegion extends IrisRegistrant implements IRare {
if (ores.isEmpty()) {
return null;
}
BlockData b = null;
for (IrisOreGenerator i : ores) {
if (i.isGenerateSurface() != surface)
KList<IrisOreGenerator> localOres = ores;
int oreCount = localOres.size();
for (int oreIndex = 0; oreIndex < oreCount; oreIndex++) {
IrisOreGenerator oreGenerator = localOres.get(oreIndex);
if (oreGenerator.isGenerateSurface() != surface) {
continue;
}
b = i.generate(x, y, z, rng, data);
if (b != null) {
return b;
BlockData ore = oreGenerator.generate(x, y, z, rng, data);
if (ore != null) {
return ore;
}
}
return null;
@@ -21,6 +21,7 @@ package art.arcane.iris.engine.platform;
import art.arcane.iris.Iris;
import art.arcane.iris.core.IrisSettings;
import art.arcane.iris.core.IrisWorlds;
import art.arcane.iris.core.gui.PregeneratorJob;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.core.nms.INMS;
import art.arcane.iris.core.service.StudioSVC;
@@ -81,6 +82,8 @@ import java.util.concurrent.locks.ReentrantLock;
@Data
public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChunkGenerator, Listener {
private static final int LOAD_LOCKS = Runtime.getRuntime().availableProcessors() * 4;
private static final long HOTLOAD_LOOP_DELAY_MS = 250L;
private static final long HOTLOAD_MAINTENANCE_DELAY_MS = 4000L;
private final Semaphore loadLock;
private final IrisWorld world;
private final File dataLocation;
@@ -563,11 +566,15 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
this.hotloader = studio ? new Looper() {
@Override
protected long loop() {
if (shouldThrottleHotload()) {
return HOTLOAD_MAINTENANCE_DELAY_MS;
}
if (hotloadChecker.flip()) {
folder.check();
}
return 250;
return HOTLOAD_LOOP_DELAY_MS;
}
} : null;
@@ -735,10 +742,24 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
return true;
}
return isMaintenanceActive();
}
private boolean isMaintenanceActive() {
World realWorld = this.world.realWorld();
return realWorld != null && IrisToolbelt.isWorldMaintenanceActive(realWorld);
}
private boolean shouldThrottleHotload() {
if (isMaintenanceActive()) {
return true;
}
World realWorld = this.world.realWorld();
PregeneratorJob pregeneratorJob = PregeneratorJob.getInstance();
return realWorld != null && pregeneratorJob != null && pregeneratorJob.targetsWorld(realWorld);
}
@Override
public int getBaseHeight(@NotNull WorldInfo worldInfo, @NotNull Random random, int x, int z, @NotNull HeightMap heightMap) {
Engine currentEngine = engine;
@@ -19,6 +19,7 @@
package art.arcane.iris.util.common.data;
import art.arcane.iris.engine.object.IrisBiome;
import art.arcane.volmlib.util.math.PowerOfTwoCoordinates;
public class BiomeMap {
private final IrisBiome[] height;
@@ -28,10 +29,10 @@ public class BiomeMap {
}
public void setBiome(int x, int z, IrisBiome h) {
height[x * 16 + z] = h;
height[PowerOfTwoCoordinates.packLocal16(x, z)] = h;
}
public IrisBiome getBiome(int x, int z) {
return height[x * 16 + z];
return height[PowerOfTwoCoordinates.packLocal16(x, z)];
}
}
@@ -1,7 +1,7 @@
package art.arcane.iris.util.project.context;
import art.arcane.iris.util.project.stream.ProceduralStream;
import art.arcane.iris.util.project.stream.utility.CachedStream2D;
import art.arcane.iris.util.project.stream.utility.ChunkFillableStream2D;
import art.arcane.volmlib.util.documentation.BlockCoordinates;
import java.util.concurrent.Executor;
@@ -35,8 +35,8 @@ public class ChunkedDataCache<T> {
return;
}
if (stream instanceof CachedStream2D<?> cachedStream) {
cachedStream.fillChunk(x, z, data);
if (stream instanceof ChunkFillableStream2D cachedStream) {
cachedStream.fillChunkRaw(x, z, data);
return;
}
@@ -32,7 +32,6 @@ import art.arcane.volmlib.util.scheduling.PrecisionStopwatch;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashMap;
public class IrisInterpolation {
public static CNG cng = NoiseStyle.SIMPLEX.create(new RNG());
@@ -952,18 +951,10 @@ public class IrisInterpolation {
*/
public static Hunk<Double> getNoise3D(InterpolationMethod3D method, int xo, int yo, int zo, int w, int h, int d, double radX, double radY, double radZ, NoiseProvider3 n) {
Hunk<Double> hunk = Hunk.newAtomicDoubleHunk(w, h, d);
HashMap<Integer, Double> cache = new HashMap<>();
int i, j, k;
for (i = 0; i < w; i++) {
int fi = i;
for (j = 0; j < h; j++) {
int fj = j;
for (k = 0; k < d; k++) {
int fk = k;
hunk.set(i, j, k, cache.computeIfAbsent((k * w * h) + (j * w) + i, (p)
-> getNoise3D(method, fi + xo, fj + yo, fk + zo,
radX, radY, radZ, n)));
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
for (int k = 0; k < d; k++) {
hunk.set(i, j, k, getNoise3D(method, i + xo, j + yo, k + zo, radX, radY, radZ, n));
}
}
}
@@ -74,6 +74,10 @@ public class CNG {
private NoiseStyle leakStyle;
private ProceduralStream<Double> customGenerator;
private transient boolean identityPostFastPath;
private transient boolean identitySignedFastPath;
private transient boolean unityOpacity;
private transient double effectiveScale;
private transient double signedFractureScale;
private transient boolean fastPathStateDirty = true;
public CNG(RNG random) {
@@ -635,7 +639,8 @@ public class CNG {
}
private double getNoise(double... dim) {
double scale = noscale ? 1 : this.bakedScale * this.scale;
ensureFastPathState();
double scale = effectiveScale;
if (fracture == null || noscale) {
return generator.noise(
@@ -671,7 +676,8 @@ public class CNG {
}
private double getNoise(double x) {
double scl = noscale ? 1 : this.bakedScale * this.scale;
ensureFastPathState();
double scl = effectiveScale;
if (fracture == null || noscale) {
return generator.noise(x * scl, 0D, 0D) * opacity;
@@ -682,7 +688,8 @@ public class CNG {
}
private double getNoise(double x, double z) {
double scl = noscale ? 1 : this.bakedScale * this.scale;
ensureFastPathState();
double scl = effectiveScale;
if (fracture == null || noscale) {
return generator.noise(x * scl, z * scl, 0D) * opacity;
@@ -694,7 +701,8 @@ public class CNG {
}
private double getNoise(double x, double y, double z) {
double scl = noscale ? 1 : this.bakedScale * this.scale;
ensureFastPathState();
double scl = effectiveScale;
if (fracture == null || noscale) {
return generator.noise(x * scl, y * scl, z * scl) * opacity;
@@ -932,6 +940,18 @@ public class CNG {
return applyPost(getNoise(x, z), x, z);
}
public double noiseFastSigned2D(double x, double z) {
if (cache != null && isWholeCoordinate(x) && isWholeCoordinate(z)) {
return (cache.get((int) x, (int) z) * 2D) - 1D;
}
if (hasIdentitySignedFastPath()) {
return getSignedNoise(x, z);
}
return (noiseFast2D(x, z) * 2D) - 1D;
}
public double noise(double x, double y, double z) {
return applyPost(getNoise(x, y, z), x, y, z);
}
@@ -944,6 +964,14 @@ public class CNG {
return applyPost(getNoise(x, y, z), x, y, z);
}
public double noiseFastSigned3D(double x, double y, double z) {
if (hasIdentitySignedFastPath()) {
return getSignedNoise(x, y, z);
}
return (noiseFast3D(x, y, z) * 2D) - 1D;
}
public CNG pow(double power) {
this.power = power;
markFastPathStateDirty();
@@ -964,11 +992,19 @@ public class CNG {
}
private boolean isIdentityPostFastPath() {
ensureFastPathState();
return identityPostFastPath;
}
private boolean hasIdentitySignedFastPath() {
ensureFastPathState();
return identitySignedFastPath;
}
private void ensureFastPathState() {
if (fastPathStateDirty) {
refreshFastPathState();
}
return identityPostFastPath;
}
private void markFastPathStateDirty() {
@@ -976,15 +1012,62 @@ public class CNG {
}
private void refreshFastPathState() {
effectiveScale = noscale ? 1D : bakedScale * scale;
identityPostFastPath = power == 1D
&& children == null
&& fracture == null
&& down == 0D
&& up == 0D
&& patch == 1D;
identitySignedFastPath = power == 1D
&& children == null
&& down == 0D
&& up == 0D
&& patch == 1D;
unityOpacity = opacity == 1D;
signedFractureScale = 0.5D * fscale;
fastPathStateDirty = false;
}
private double signedWithOpacity(double signedNoise) {
if (unityOpacity) {
return signedNoise;
}
return ((signedNoise + 1D) * opacity) - 1D;
}
private double getSignedNoise(double x, double z) {
ensureFastPathState();
double scl = effectiveScale;
NoiseGenerator localGenerator = generator;
CNG localFracture = fracture;
if (localFracture == null || noscale) {
return signedWithOpacity(localGenerator.noiseSigned(x * scl, z * scl, 0D));
}
double fx = x + (localFracture.noiseFastSigned2D(x, z) * signedFractureScale);
double fz = z + (localFracture.noiseFastSigned2D(z, x) * signedFractureScale);
return signedWithOpacity(localGenerator.noiseSigned(fx * scl, fz * scl, 0D));
}
private double getSignedNoise(double x, double y, double z) {
ensureFastPathState();
double scl = effectiveScale;
NoiseGenerator localGenerator = generator;
CNG localFracture = fracture;
if (localFracture == null || noscale) {
return signedWithOpacity(localGenerator.noiseSigned(x * scl, y * scl, z * scl));
}
double fx = x + (localFracture.noiseFastSigned3D(x, y, z) * signedFractureScale);
double fy = y + (localFracture.noiseFastSigned2D(y, x) * signedFractureScale);
double fz = z + (localFracture.noiseFastSigned3D(z, x, y) * signedFractureScale);
return signedWithOpacity(localGenerator.noiseSigned(fx * scl, fy * scl, fz * scl));
}
private enum InjectorMode {
ADD,
SRC_SUBTRACT,
@@ -21,14 +21,22 @@ import art.arcane.volmlib.util.math.Vector2f;
import art.arcane.volmlib.util.math.Vector3f;
public class FastNoiseDouble {
private static final Double2[] GRAD_2D = {new Double2(-1, -1), new Double2(1, -1), new Double2(-1, 1), new Double2(1, 1), new Double2(0, -1), new Double2(-1, 0), new Double2(0, 1), new Double2(1, 0),
};
private static final Double3[] GRAD_3D = {new Double3(1, 1, 0), new Double3(-1, 1, 0), new Double3(1, -1, 0), new Double3(-1, -1, 0), new Double3(1, 0, 1), new Double3(-1, 0, 1), new Double3(1, 0, -1), new Double3(-1, 0, -1), new Double3(0, 1, 1), new Double3(0, -1, 1), new Double3(0, 1, -1), new Double3(0, -1, -1), new Double3(1, 1, 0), new Double3(0, -1, 1), new Double3(-1, 1, 0), new Double3(0, -1, -1),
private static final double[] GRAD_2D = {-1D, -1D, 1D, -1D, -1D, 1D, 1D, 1D, 0D, -1D, -1D, 0D, 0D, 1D, 1D, 0D};
private static final double[] GRAD_3D = {
1D, 1D, 0D, -1D, 1D, 0D, 1D, -1D, 0D, -1D, -1D, 0D,
1D, 0D, 1D, -1D, 0D, 1D, 1D, 0D, -1D, -1D, 0D, -1D,
0D, 1D, 1D, 0D, -1D, 1D, 0D, 1D, -1D, 0D, -1D, -1D,
1D, 1D, 0D, 0D, -1D, 1D, -1D, 1D, 0D, 0D, -1D, -1D
};
private static final Double2[] CELL_2D = {new Double2(-0.4313539279f, 0.1281943404f), new Double2(-0.1733316799f, 0.415278375f), new Double2(-0.2821957395f, -0.3505218461f), new Double2(-0.2806473808f, 0.3517627718f), new Double2(0.3125508975f, -0.3237467165f), new Double2(0.3383018443f, -0.2967353402f), new Double2(-0.4393982022f, -0.09710417025f), new Double2(-0.4460443703f, -0.05953502905f), new Double2(-0.302223039f, 0.3334085102f), new Double2(-0.212681052f, -0.3965687458f), new Double2(-0.2991156529f, 0.3361990872f), new Double2(0.2293323691f, 0.3871778202f), new Double2(0.4475439151f, -0.04695150755f), new Double2(0.1777518f, 0.41340573f), new Double2(0.1688522499f, -0.4171197882f), new Double2(-0.0976597166f, 0.4392750616f), new Double2(0.08450188373f, 0.4419948321f), new Double2(-0.4098760448f, -0.1857461384f), new Double2(0.3476585782f, -0.2857157906f), new Double2(-0.3350670039f, -0.30038326f), new Double2(0.2298190031f, -0.3868891648f), new Double2(-0.01069924099f, 0.449872789f), new Double2(-0.4460141246f, -0.05976119672f), new Double2(0.3650293864f, 0.2631606867f), new Double2(-0.349479423f, 0.2834856838f), new Double2(-0.4122720642f, 0.1803655873f), new Double2(-0.267327811f, 0.3619887311f), new Double2(0.322124041f, -0.3142230135f), new Double2(0.2880445931f, -0.3457315612f), new Double2(0.3892170926f, -0.2258540565f), new Double2(0.4492085018f, -0.02667811596f), new Double2(-0.4497724772f, 0.01430799601f), new Double2(0.1278175387f, -0.4314657307f), new Double2(-0.03572100503f, 0.4485799926f), new Double2(-0.4297407068f, -0.1335025276f), new Double2(-0.3217817723f, 0.3145735065f), new Double2(-0.3057158873f, 0.3302087162f), new Double2(-0.414503978f, 0.1751754899f), new Double2(-0.3738139881f, 0.2505256519f), new Double2(0.2236891408f, -0.3904653228f), new Double2(0.002967775577f, -0.4499902136f), new Double2(0.1747128327f, -0.4146991995f), new Double2(-0.4423772489f, -0.08247647938f), new Double2(-0.2763960987f, -0.355112935f), new Double2(-0.4019385906f, -0.2023496216f), new Double2(0.3871414161f, -0.2293938184f), new Double2(-0.430008727f, 0.1326367019f), new Double2(-0.03037574274f, -0.4489736231f), new Double2(-0.3486181573f, 0.2845441624f), new Double2(0.04553517144f, -0.4476902368f), new Double2(-0.0375802926f, 0.4484280562f), new Double2(0.3266408905f, 0.3095250049f), new Double2(0.06540017593f, -0.4452222108f), new Double2(0.03409025829f, 0.448706869f), new Double2(-0.4449193635f, 0.06742966669f), new Double2(-0.4255936157f, -0.1461850686f), new Double2(0.449917292f, 0.008627302568f), new Double2(0.05242606404f, 0.4469356864f), new Double2(-0.4495305179f, -0.02055026661f), new Double2(-0.1204775703f, 0.4335725488f), new Double2(-0.341986385f, -0.2924813028f), new Double2(0.3865320182f, 0.2304191809f), new Double2(0.04506097811f, -0.447738214f), new Double2(-0.06283465979f, 0.4455915232f), new Double2(0.3932600341f, -0.2187385324f), new Double2(0.4472261803f, -0.04988730975f), new Double2(0.3753571011f, -0.2482076684f), new Double2(-0.273662295f, 0.357223947f), new Double2(0.1700461538f, 0.4166344988f), new Double2(0.4102692229f, 0.1848760794f), new Double2(0.323227187f, -0.3130881435f), new Double2(-0.2882310238f, -0.3455761521f), new Double2(0.2050972664f, 0.4005435199f), new Double2(0.4414085979f, -0.08751256895f), new Double2(-0.1684700334f, 0.4172743077f), new Double2(-0.003978032396f, 0.4499824166f), new Double2(-0.2055133639f, 0.4003301853f), new Double2(-0.006095674897f, -0.4499587123f), new Double2(-0.1196228124f, -0.4338091548f), new Double2(0.3901528491f, -0.2242337048f), new Double2(0.01723531752f, 0.4496698165f), new Double2(-0.3015070339f, 0.3340561458f), new Double2(-0.01514262423f, -0.4497451511f), new Double2(-0.4142574071f, -0.1757577897f), new Double2(-0.1916377265f, -0.4071547394f), new Double2(0.3749248747f, 0.2488600778f), new Double2(-0.2237774255f, 0.3904147331f), new Double2(-0.4166343106f, -0.1700466149f), new Double2(0.3619171625f, 0.267424695f), new Double2(0.1891126846f, -0.4083336779f), new Double2(-0.3127425077f, 0.323561623f), new Double2(-0.3281807787f, 0.307891826f), new Double2(-0.2294806661f, 0.3870899429f), new Double2(-0.3445266136f, 0.2894847362f), new Double2(-0.4167095422f, -0.1698621719f), new Double2(-0.257890321f, -0.3687717212f), new Double2(-0.3612037825f, 0.2683874578f), new Double2(0.2267996491f, 0.3886668486f), new Double2(0.207157062f, 0.3994821043f), new Double2(0.08355176718f, -0.4421754202f), new Double2(-0.4312233307f, 0.1286329626f), new Double2(0.3257055497f, 0.3105090899f), new Double2(0.177701095f, -0.4134275279f), new Double2(-0.445182522f, 0.06566979625f), new Double2(0.3955143435f, 0.2146355146f), new Double2(-0.4264613988f, 0.1436338239f), new Double2(-0.3793799665f, -0.2420141339f), new Double2(0.04617599081f, -0.4476245948f), new Double2(-0.371405428f, -0.2540826796f), new Double2(0.2563570295f, -0.3698392535f), new Double2(0.03476646309f, 0.4486549822f), new Double2(-0.3065454405f, 0.3294387544f), new Double2(-0.2Line truncated
};
private static final Double3[] CELL_3D = {new Double3(0.1453787434f, -0.4149781685f, -0.0956981749f), new Double3(-0.01242829687f, -0.1457918398f, -0.4255470325f), new Double3(0.2877979582f, -0.02606483451f, -0.3449535616f), new Double3(-0.07732986802f, 0.2377094325f, 0.3741848704f), new Double3(0.1107205875f, -0.3552302079f, -0.2530858567f), new Double3(0.2755209141f, 0.2640521179f, -0.238463215f), new Double3(0.294168941f, 0.1526064594f, 0.3044271714f), new Double3(0.4000921098f, -0.2034056362f, 0.03244149937f), new Double3(-0.1697304074f, 0.3970864695f, -0.1265461359f), new Double3(-0.1483224484f, -0.3859694688f, 0.1775613147f), new Double3(0.2623596946f, -0.2354852944f, 0.2796677792f), new Double3(-0.2709003183f, 0.3505271138f, -0.07901746678f), new Double3(-0.03516550699f, 0.3885234328f, 0.2243054374f), new Double3(-0.1267712655f, 0.1920044036f, 0.3867342179f), new Double3(0.02952021915f, 0.4409685861f, 0.08470692262f), new Double3(-0.2806854217f, -0.266996757f, 0.2289725438f), new Double3(-0.171159547f, 0.2141185563f, 0.3568720405f), new Double3(0.2113227183f, 0.3902405947f, -0.07453178509f), new Double3(-0.1024352839f, 0.2128044156f, -0.3830421561f), new Double3(-0.3304249877f, -0.1566986703f, 0.2622305365f), new Double3(0.2091111325f, 0.3133278055f, -0.2461670583f), new Double3(0.344678154f, -0.1944240454f, -0.2142341261f), new Double3(0.1984478035f, -0.3214342325f, -0.2445373252f), new Double3(-0.2929008603f, 0.2262915116f, 0.2559320961f), new Double3(-0.1617332831f, 0.006314769776f, -0.4198838754f), new Double3(-0.3582060271f, -0.148303178f, -0.2284613961f), new Double3(-0.1852067326f, -0.3454119342f, -0.2211087107f), new Double3(0.3046301062f, 0.1026310383f, 0.314908508f), new Double3(-0.03816768434f, -0.2551766358f, -0.3686842991f), new Double3(-0.4084952196f, 0.1805950793f, 0.05492788837f), new Double3(-0.02687443361f, -0.2749741471f, 0.3551999201f), new Double3(-0.03801098351f, 0.3277859044f, 0.3059600725f), new Double3(0.2371120802f, 0.2900386767f, -0.2493099024f), new Double3(0.4447660503f, 0.03946930643f, 0.05590469027f), new Double3(0.01985147278f, -0.01503183293f, -0.4493105419f), new Double3(0.4274339143f, 0.03345994256f, -0.1366772882f), new Double3(-0.2072988631f, 0.2871414597f, -0.2776273824f), new Double3(-0.3791240978f, 0.1281177671f, 0.2057929936f), new Double3(-0.2098721267f, -0.1007087278f, -0.3851122467f), new Double3(0.01582798878f, 0.4263894424f, 0.1429738373f), new Double3(-0.1888129464f, -0.3160996813f, -0.2587096108f), new Double3(0.1612988974f, -0.1974805082f, -0.3707885038f), new Double3(-0.08974491322f, 0.229148752f, -0.3767448739f), new Double3(0.07041229526f, 0.4150230285f, -0.1590534329f), new Double3(-0.1082925611f, -0.1586061639f, 0.4069604477f), new Double3(0.2474100658f, -0.3309414609f, 0.1782302128f), new Double3(-0.1068836661f, -0.2701644537f, -0.3436379634f), new Double3(0.2396452163f, 0.06803600538f, -0.3747549496f), new Double3(-0.3063886072f, 0.2597428179f, 0.2028785103f), new Double3(0.1593342891f, -0.3114350249f, -0.2830561951f), new Double3(0.2709690528f, 0.1412648683f, -0.3303331794f), new Double3(-0.1519780427f, 0.3623355133f, 0.2193527988f), new Double3(0.1699773681f, 0.3456012883f, 0.2327390037f), new Double3(-0.1986155616f, 0.3836276443f, -0.1260225743f), new Double3(-0.1887482106f, -0.2050154888f, -0.353330953f), new Double3(0.2659103394f, 0.3015631259f, -0.2021172246f), new Double3(-0.08838976154f, -0.4288819642f, -0.1036702021f), new Double3(-0.04201869311f, 0.3099592485f, 0.3235115047f), new Double3(-0.3230334656f, 0.201549922f, -0.2398478873f), new Double3(0.2612720941f, 0.2759854499f, -0.2409749453f), new Double3(0.385713046f, 0.2193460345f, 0.07491837764f), new Double3(0.07654967953f, 0.3721732183f, 0.241095919f), new Double3(0.4317038818f, -0.02577753072f, 0.1243675091f), new Double3(-0.2890436293f, -0.3418179959f, -0.04598084447f), new Double3(-0.2201947582f, 0.383023377f, -0.08548310451f), new Double3(0.4161322773f, -0.1669634289f, -0.03817251927f), new Double3(0.2204718095f, 0.02654238946f, -0.391391981f), new Double3(-0.1040307469f, 0.3890079625f, -0.2008741118f), new Double3(-0.1432122615f, 0.371614387f, -0.2095065525f), new Double3(0.3978380468f, -0.06206669342f, 0.2009293758f), new Double3(-0.2599274663f, 0.2616724959f, -0.2578084893f), new Double3(0.4032618332f, -0.1124593585f, 0.1650235939f), new Double3(-0.08953470255f, -0.3048244735f, 0.3186935478f), new Double3(0.118937202f, -0.2875221847f, 0.325092195f), new Double3(0.02167047076f, -0.03284630549f, -0.4482761547f), new Double3(-0.3411343612f, 0.2500031105f, 0.1537068389f), new Double3(0.3162964612f, 0.3082064153f, -0.08640228117f), new Double3(0.2355138889f, -0.3439334267f, -0.1695376245f), new Double3(-0.02874541518f, -0.3955933019f, 0.2125550295f), new Double3(-0.2461455173f, 0.02020282325f, -0.3761704803f), new Double3(0.04208029445f, -0.4470439576f, 0.02968078139f), new Double3(0.2727458746f, 0.2288471896f, -0.2752065618f), new Double3(-0.1347522818f, -0.02720848277f, -0Line truncated
};
private static final double[] CELL_2D_X = extractCell2DComponent(true);
private static final double[] CELL_2D_Y = extractCell2DComponent(false);
private static final double[] CELL_3D_X = extractCell3DComponent(0);
private static final double[] CELL_3D_Y = extractCell3DComponent(1);
private static final double[] CELL_3D_Z = extractCell3DComponent(2);
// Hashing
private final static long X_PRIME = 1619;
private final static long Y_PRIME = 31337;
@@ -98,6 +106,24 @@ public class FastNoiseDouble {
return t * t * t * p + t * t * ((a - b) - p) + t * (c - a) + b;
}
private static double[] extractCell2DComponent(boolean xComponent) {
double[] components = new double[CELL_2D.length];
for (int i = 0; i < CELL_2D.length; i++) {
Double2 value = CELL_2D[i];
components[i] = xComponent ? value.x : value.y;
}
return components;
}
private static double[] extractCell3DComponent(int axis) {
double[] components = new double[CELL_3D.length];
for (int i = 0; i < CELL_3D.length; i++) {
Double3 value = CELL_3D[i];
components[i] = axis == 0 ? value.x : axis == 1 ? value.y : value.z;
}
return components;
}
private static long hash2D(long seed, long x, long y) {
long hash = seed;
hash ^= X_PRIME * x;
@@ -169,9 +195,8 @@ public class FastNoiseDouble {
hash = hash * hash * hash * 60493;
hash = (hash >> 13) ^ hash;
Double2 g = GRAD_2D[(int) hash & 7];
return xd * g.x + yd * g.y;
int gradientIndex = ((int) hash & 7) << 1;
return (xd * GRAD_2D[gradientIndex]) + (yd * GRAD_2D[gradientIndex + 1]);
}
private static double GradCoord3D(long seed, long x, long y, long z, double xd, double yd, double zd) {
@@ -183,9 +208,8 @@ public class FastNoiseDouble {
hash = hash * hash * hash * 60493;
hash = (hash >> 13) ^ hash;
Double3 g = GRAD_3D[(int) (hash & 15)];
return xd * g.x + yd * g.y + zd * g.z;
int gradientIndex = ((int) (hash & 15)) * 3;
return (xd * GRAD_3D[gradientIndex]) + (yd * GRAD_3D[gradientIndex + 1]) + (zd * GRAD_3D[gradientIndex + 2]);
}
private static double GradCoord4D(long seed, long x, long y, long z, long w, double xd, double yd, double zd, double wd) {
@@ -1457,6 +1481,10 @@ public class FastNoiseDouble {
long xr = fastRound(x);
long yr = fastRound(y);
long zr = fastRound(z);
long seed = m_seed;
double[] cell3dX = CELL_3D_X;
double[] cell3dY = CELL_3D_Y;
double[] cell3dZ = CELL_3D_Z;
double distance = 999999;
long xc = 0, yc = 0, zc = 0;
@@ -1464,13 +1492,15 @@ public class FastNoiseDouble {
switch (m_cellularDistanceFunction) {
case Euclidean:
for (long xi = xr - 1; xi <= xr + 1; xi++) {
double baseVecX = xi - x;
for (long yi = yr - 1; yi <= yr + 1; yi++) {
double baseVecY = yi - y;
for (long zi = zr - 1; zi <= zr + 1; zi++) {
Double3 vec = CELL_3D[(int) hash3D(m_seed, xi, yi, zi) & 255];
double vecX = xi - x + vec.x;
double vecY = yi - y + vec.y;
double vecZ = zi - z + vec.z;
double baseVecZ = zi - z;
int cellIndex3D = (int) hash3D(seed, xi, yi, zi) & 255;
double vecX = baseVecX + cell3dX[cellIndex3D];
double vecY = baseVecY + cell3dY[cellIndex3D];
double vecZ = baseVecZ + cell3dZ[cellIndex3D];
double newDistance = vecX * vecX + vecY * vecY + vecZ * vecZ;
@@ -1486,13 +1516,15 @@ public class FastNoiseDouble {
break;
case Manhattan:
for (long xi = xr - 1; xi <= xr + 1; xi++) {
double baseVecX = xi - x;
for (long yi = yr - 1; yi <= yr + 1; yi++) {
double baseVecY = yi - y;
for (long zi = zr - 1; zi <= zr + 1; zi++) {
Double3 vec = CELL_3D[(int) hash3D(m_seed, xi, yi, zi) & 255];
double vecX = xi - x + vec.x;
double vecY = yi - y + vec.y;
double vecZ = zi - z + vec.z;
double baseVecZ = zi - z;
int cellIndex3D = (int) hash3D(seed, xi, yi, zi) & 255;
double vecX = baseVecX + cell3dX[cellIndex3D];
double vecY = baseVecY + cell3dY[cellIndex3D];
double vecZ = baseVecZ + cell3dZ[cellIndex3D];
double newDistance = Math.abs(vecX) + Math.abs(vecY) + Math.abs(vecZ);
@@ -1508,13 +1540,15 @@ public class FastNoiseDouble {
break;
case Natural:
for (long xi = xr - 1; xi <= xr + 1; xi++) {
double baseVecX = xi - x;
for (long yi = yr - 1; yi <= yr + 1; yi++) {
double baseVecY = yi - y;
for (long zi = zr - 1; zi <= zr + 1; zi++) {
Double3 vec = CELL_3D[(int) hash3D(m_seed, xi, yi, zi) & 255];
double vecX = xi - x + vec.x;
double vecY = yi - y + vec.y;
double vecZ = zi - z + vec.z;
double baseVecZ = zi - z;
int cellIndex3D = (int) hash3D(seed, xi, yi, zi) & 255;
double vecX = baseVecX + cell3dX[cellIndex3D];
double vecY = baseVecY + cell3dY[cellIndex3D];
double vecZ = baseVecZ + cell3dZ[cellIndex3D];
double newDistance = (Math.abs(vecX) + Math.abs(vecY) + Math.abs(vecZ)) + (vecX * vecX + vecY * vecY + vecZ * vecZ);
@@ -1535,8 +1569,8 @@ public class FastNoiseDouble {
return valCoord3D(0, xc, yc, zc);
case NoiseLookup:
Double3 vec = CELL_3D[(int) hash3D(m_seed, xc, yc, zc) & 255];
return m_cellularNoiseLookup.GetNoise(xc + vec.x, yc + vec.y, zc + vec.z);
int lookupIndex3D = (int) hash3D(m_seed, xc, yc, zc) & 255;
return m_cellularNoiseLookup.GetNoise(xc + CELL_3D_X[lookupIndex3D], yc + CELL_3D_Y[lookupIndex3D], zc + CELL_3D_Z[lookupIndex3D]);
case Distance:
return distance - 1;
@@ -1549,6 +1583,10 @@ public class FastNoiseDouble {
long xr = fastRound(x);
long yr = fastRound(y);
long zr = fastRound(z);
long seed = m_seed;
double[] cell3dX = CELL_3D_X;
double[] cell3dY = CELL_3D_Y;
double[] cell3dZ = CELL_3D_Z;
double distance = 999999;
double distance2 = 999999;
@@ -1556,54 +1594,72 @@ public class FastNoiseDouble {
switch (m_cellularDistanceFunction) {
case Euclidean:
for (long xi = xr - 1; xi <= xr + 1; xi++) {
double baseVecX = xi - x;
for (long yi = yr - 1; yi <= yr + 1; yi++) {
double baseVecY = yi - y;
for (long zi = zr - 1; zi <= zr + 1; zi++) {
Double3 vec = CELL_3D[(int) hash3D(m_seed, xi, yi, zi) & 255];
double vecX = xi - x + vec.x;
double vecY = yi - y + vec.y;
double vecZ = zi - z + vec.z;
double baseVecZ = zi - z;
int cellIndex3D = (int) hash3D(seed, xi, yi, zi) & 255;
double vecX = baseVecX + cell3dX[cellIndex3D];
double vecY = baseVecY + cell3dY[cellIndex3D];
double vecZ = baseVecZ + cell3dZ[cellIndex3D];
double newDistance = vecX * vecX + vecY * vecY + vecZ * vecZ;
distance2 = Math.max(Math.min(distance2, newDistance), distance);
distance = Math.min(distance, newDistance);
if (newDistance < distance) {
distance2 = distance;
distance = newDistance;
} else if (newDistance < distance2) {
distance2 = newDistance;
}
}
}
}
break;
case Manhattan:
for (long xi = xr - 1; xi <= xr + 1; xi++) {
double baseVecX = xi - x;
for (long yi = yr - 1; yi <= yr + 1; yi++) {
double baseVecY = yi - y;
for (long zi = zr - 1; zi <= zr + 1; zi++) {
Double3 vec = CELL_3D[(int) hash3D(m_seed, xi, yi, zi) & 255];
double vecX = xi - x + vec.x;
double vecY = yi - y + vec.y;
double vecZ = zi - z + vec.z;
double baseVecZ = zi - z;
int cellIndex3D = (int) hash3D(seed, xi, yi, zi) & 255;
double vecX = baseVecX + cell3dX[cellIndex3D];
double vecY = baseVecY + cell3dY[cellIndex3D];
double vecZ = baseVecZ + cell3dZ[cellIndex3D];
double newDistance = Math.abs(vecX) + Math.abs(vecY) + Math.abs(vecZ);
distance2 = Math.max(Math.min(distance2, newDistance), distance);
distance = Math.min(distance, newDistance);
if (newDistance < distance) {
distance2 = distance;
distance = newDistance;
} else if (newDistance < distance2) {
distance2 = newDistance;
}
}
}
}
break;
case Natural:
for (long xi = xr - 1; xi <= xr + 1; xi++) {
double baseVecX = xi - x;
for (long yi = yr - 1; yi <= yr + 1; yi++) {
double baseVecY = yi - y;
for (long zi = zr - 1; zi <= zr + 1; zi++) {
Double3 vec = CELL_3D[(int) hash3D(m_seed, xi, yi, zi) & 255];
double vecX = xi - x + vec.x;
double vecY = yi - y + vec.y;
double vecZ = zi - z + vec.z;
double baseVecZ = zi - z;
int cellIndex3D = (int) hash3D(seed, xi, yi, zi) & 255;
double vecX = baseVecX + cell3dX[cellIndex3D];
double vecY = baseVecY + cell3dY[cellIndex3D];
double vecZ = baseVecZ + cell3dZ[cellIndex3D];
double newDistance = (Math.abs(vecX) + Math.abs(vecY) + Math.abs(vecZ)) + (vecX * vecX + vecY * vecY + vecZ * vecZ);
distance2 = Math.max(Math.min(distance2, newDistance), distance);
distance = Math.min(distance, newDistance);
if (newDistance < distance) {
distance2 = distance;
distance = newDistance;
} else if (newDistance < distance2) {
distance2 = newDistance;
}
}
}
}
@@ -1635,6 +1691,9 @@ public class FastNoiseDouble {
private double SingleCellular(double x, double y) {
long xr = fastRound(x);
long yr = fastRound(y);
long seed = m_seed;
double[] cell2dX = CELL_2D_X;
double[] cell2dY = CELL_2D_Y;
double distance = 999999;
long xc = 0, yc = 0;
@@ -1643,11 +1702,12 @@ public class FastNoiseDouble {
default:
case Euclidean:
for (long xi = xr - 1; xi <= xr + 1; xi++) {
double baseVecX = xi - x;
for (long yi = yr - 1; yi <= yr + 1; yi++) {
Double2 vec = CELL_2D[(int) hash2D(m_seed, xi, yi) & 255];
double vecX = xi - x + vec.x;
double vecY = yi - y + vec.y;
double baseVecY = yi - y;
int cellIndex2D = (int) hash2D(seed, xi, yi) & 255;
double vecX = baseVecX + cell2dX[cellIndex2D];
double vecY = baseVecY + cell2dY[cellIndex2D];
double newDistance = vecX * vecX + vecY * vecY;
@@ -1661,11 +1721,12 @@ public class FastNoiseDouble {
break;
case Manhattan:
for (long xi = xr - 1; xi <= xr + 1; xi++) {
double baseVecX = xi - x;
for (long yi = yr - 1; yi <= yr + 1; yi++) {
Double2 vec = CELL_2D[(int) hash2D(m_seed, xi, yi) & 255];
double vecX = xi - x + vec.x;
double vecY = yi - y + vec.y;
double baseVecY = yi - y;
int cellIndex2D = (int) hash2D(seed, xi, yi) & 255;
double vecX = baseVecX + cell2dX[cellIndex2D];
double vecY = baseVecY + cell2dY[cellIndex2D];
double newDistance = (Math.abs(vecX) + Math.abs(vecY));
@@ -1679,11 +1740,12 @@ public class FastNoiseDouble {
break;
case Natural:
for (long xi = xr - 1; xi <= xr + 1; xi++) {
double baseVecX = xi - x;
for (long yi = yr - 1; yi <= yr + 1; yi++) {
Double2 vec = CELL_2D[(int) hash2D(m_seed, xi, yi) & 255];
double vecX = xi - x + vec.x;
double vecY = yi - y + vec.y;
double baseVecY = yi - y;
int cellIndex2D = (int) hash2D(seed, xi, yi) & 255;
double vecX = baseVecX + cell2dX[cellIndex2D];
double vecY = baseVecY + cell2dY[cellIndex2D];
double newDistance = (Math.abs(vecX) + Math.abs(vecY)) + (vecX * vecX + vecY * vecY);
@@ -1702,8 +1764,8 @@ public class FastNoiseDouble {
return valCoord2D(0, xc, yc);
case NoiseLookup:
Double2 vec = CELL_2D[(int) hash2D(m_seed, xc, yc) & 255];
return m_cellularNoiseLookup.GetNoise(xc + vec.x, yc + vec.y);
int lookupIndex2D = (int) hash2D(m_seed, xc, yc) & 255;
return m_cellularNoiseLookup.GetNoise(xc + CELL_2D_X[lookupIndex2D], yc + CELL_2D_Y[lookupIndex2D]);
case Distance:
return distance - 1;
@@ -1715,6 +1777,9 @@ public class FastNoiseDouble {
private double SingleCellular2Edge(double x, double y) {
long xr = fastRound(x);
long yr = fastRound(y);
long seed = m_seed;
double[] cell2dX = CELL_2D_X;
double[] cell2dY = CELL_2D_Y;
double distance = 999999;
double distance2 = 999999;
@@ -1723,46 +1788,61 @@ public class FastNoiseDouble {
default:
case Euclidean:
for (long xi = xr - 1; xi <= xr + 1; xi++) {
double baseVecX = xi - x;
for (long yi = yr - 1; yi <= yr + 1; yi++) {
Double2 vec = CELL_2D[(int) hash2D(m_seed, xi, yi) & 255];
double vecX = xi - x + vec.x;
double vecY = yi - y + vec.y;
double baseVecY = yi - y;
int cellIndex2D = (int) hash2D(seed, xi, yi) & 255;
double vecX = baseVecX + cell2dX[cellIndex2D];
double vecY = baseVecY + cell2dY[cellIndex2D];
double newDistance = vecX * vecX + vecY * vecY;
distance2 = Math.max(Math.min(distance2, newDistance), distance);
distance = Math.min(distance, newDistance);
if (newDistance < distance) {
distance2 = distance;
distance = newDistance;
} else if (newDistance < distance2) {
distance2 = newDistance;
}
}
}
break;
case Manhattan:
for (long xi = xr - 1; xi <= xr + 1; xi++) {
double baseVecX = xi - x;
for (long yi = yr - 1; yi <= yr + 1; yi++) {
Double2 vec = CELL_2D[(int) hash2D(m_seed, xi, yi) & 255];
double vecX = xi - x + vec.x;
double vecY = yi - y + vec.y;
double baseVecY = yi - y;
int cellIndex2D = (int) hash2D(seed, xi, yi) & 255;
double vecX = baseVecX + cell2dX[cellIndex2D];
double vecY = baseVecY + cell2dY[cellIndex2D];
double newDistance = Math.abs(vecX) + Math.abs(vecY);
distance2 = Math.max(Math.min(distance2, newDistance), distance);
distance = Math.min(distance, newDistance);
if (newDistance < distance) {
distance2 = distance;
distance = newDistance;
} else if (newDistance < distance2) {
distance2 = newDistance;
}
}
}
break;
case Natural:
for (long xi = xr - 1; xi <= xr + 1; xi++) {
double baseVecX = xi - x;
for (long yi = yr - 1; yi <= yr + 1; yi++) {
Double2 vec = CELL_2D[(int) hash2D(m_seed, xi, yi) & 255];
double vecX = xi - x + vec.x;
double vecY = yi - y + vec.y;
double baseVecY = yi - y;
int cellIndex2D = (int) hash2D(seed, xi, yi) & 255;
double vecX = baseVecX + cell2dX[cellIndex2D];
double vecY = baseVecY + cell2dY[cellIndex2D];
double newDistance = (Math.abs(vecX) + Math.abs(vecY)) + (vecX * vecX + vecY * vecY);
distance2 = Math.max(Math.min(distance2, newDistance), distance);
distance = Math.min(distance, newDistance);
if (newDistance < distance) {
distance2 = distance;
distance = newDistance;
} else if (newDistance < distance2) {
distance2 = newDistance;
}
}
}
break;
@@ -1827,37 +1907,37 @@ public class FastNoiseDouble {
}
}
Double3 vec0 = CELL_3D[(int) hash3D(seed, x0, y0, z0) & 255];
Double3 vec1 = CELL_3D[(int) hash3D(seed, x1, y0, z0) & 255];
int vecIndex000 = (int) hash3D(seed, x0, y0, z0) & 255;
int vecIndex100 = (int) hash3D(seed, x1, y0, z0) & 255;
double lx0x = lerp(vec0.x, vec1.x, xs);
double ly0x = lerp(vec0.y, vec1.y, xs);
double lz0x = lerp(vec0.z, vec1.z, xs);
double lx0x = lerp(CELL_3D_X[vecIndex000], CELL_3D_X[vecIndex100], xs);
double ly0x = lerp(CELL_3D_Y[vecIndex000], CELL_3D_Y[vecIndex100], xs);
double lz0x = lerp(CELL_3D_Z[vecIndex000], CELL_3D_Z[vecIndex100], xs);
vec0 = CELL_3D[(int) hash3D(seed, x0, y1, z0) & 255];
vec1 = CELL_3D[(int) hash3D(seed, x1, y1, z0) & 255];
int vecIndex010 = (int) hash3D(seed, x0, y1, z0) & 255;
int vecIndex110 = (int) hash3D(seed, x1, y1, z0) & 255;
double lx1x = lerp(vec0.x, vec1.x, xs);
double ly1x = lerp(vec0.y, vec1.y, xs);
double lz1x = lerp(vec0.z, vec1.z, xs);
double lx1x = lerp(CELL_3D_X[vecIndex010], CELL_3D_X[vecIndex110], xs);
double ly1x = lerp(CELL_3D_Y[vecIndex010], CELL_3D_Y[vecIndex110], xs);
double lz1x = lerp(CELL_3D_Z[vecIndex010], CELL_3D_Z[vecIndex110], xs);
double lx0y = lerp(lx0x, lx1x, ys);
double ly0y = lerp(ly0x, ly1x, ys);
double lz0y = lerp(lz0x, lz1x, ys);
vec0 = CELL_3D[(int) hash3D(seed, x0, y0, z1) & 255];
vec1 = CELL_3D[(int) hash3D(seed, x1, y0, z1) & 255];
int vecIndex001 = (int) hash3D(seed, x0, y0, z1) & 255;
int vecIndex101 = (int) hash3D(seed, x1, y0, z1) & 255;
lx0x = lerp(vec0.x, vec1.x, xs);
ly0x = lerp(vec0.y, vec1.y, xs);
lz0x = lerp(vec0.z, vec1.z, xs);
lx0x = lerp(CELL_3D_X[vecIndex001], CELL_3D_X[vecIndex101], xs);
ly0x = lerp(CELL_3D_Y[vecIndex001], CELL_3D_Y[vecIndex101], xs);
lz0x = lerp(CELL_3D_Z[vecIndex001], CELL_3D_Z[vecIndex101], xs);
vec0 = CELL_3D[(int) hash3D(seed, x0, y1, z1) & 255];
vec1 = CELL_3D[(int) hash3D(seed, x1, y1, z1) & 255];
int vecIndex011 = (int) hash3D(seed, x0, y1, z1) & 255;
int vecIndex111 = (int) hash3D(seed, x1, y1, z1) & 255;
lx1x = lerp(vec0.x, vec1.x, xs);
ly1x = lerp(vec0.y, vec1.y, xs);
lz1x = lerp(vec0.z, vec1.z, xs);
lx1x = lerp(CELL_3D_X[vecIndex011], CELL_3D_X[vecIndex111], xs);
ly1x = lerp(CELL_3D_Y[vecIndex011], CELL_3D_Y[vecIndex111], xs);
lz1x = lerp(CELL_3D_Z[vecIndex011], CELL_3D_Z[vecIndex111], xs);
v3.x += lerp(lx0y, lerp(lx0x, lx1x, ys), zs) * perturbAmp;
v3.y += lerp(ly0y, lerp(ly0x, ly1x, ys), zs) * perturbAmp;
@@ -1907,17 +1987,17 @@ public class FastNoiseDouble {
}
}
Double2 vec0 = CELL_2D[(int) hash2D(seed, x0, y0) & 255];
Double2 vec1 = CELL_2D[(int) hash2D(seed, x1, y0) & 255];
int vecIndex00 = (int) hash2D(seed, x0, y0) & 255;
int vecIndex10 = (int) hash2D(seed, x1, y0) & 255;
double lx0x = lerp(vec0.x, vec1.x, xs);
double ly0x = lerp(vec0.y, vec1.y, xs);
double lx0x = lerp(CELL_2D_X[vecIndex00], CELL_2D_X[vecIndex10], xs);
double ly0x = lerp(CELL_2D_Y[vecIndex00], CELL_2D_Y[vecIndex10], xs);
vec0 = CELL_2D[(int) hash2D(seed, x0, y1) & 255];
vec1 = CELL_2D[(int) hash2D(seed, x1, y1) & 255];
int vecIndex01 = (int) hash2D(seed, x0, y1) & 255;
int vecIndex11 = (int) hash2D(seed, x1, y1) & 255;
double lx1x = lerp(vec0.x, vec1.x, xs);
double ly1x = lerp(vec0.y, vec1.y, xs);
double lx1x = lerp(CELL_2D_X[vecIndex01], CELL_2D_X[vecIndex11], xs);
double ly1x = lerp(CELL_2D_Y[vecIndex01], CELL_2D_Y[vecIndex11], xs);
v2.x += lerp(lx0x, lx1x, ys) * perturbAmp;
v2.y += lerp(ly0x, ly1x, ys) * perturbAmp;
@@ -38,16 +38,31 @@ public class FractalFBMSimplexNoise implements NoiseGenerator, OctaveNoise {
return f(n.GetSimplexFractal(x, 0d));
}
@Override
public double noiseSigned(double x) {
return n.GetSimplexFractal(x, 0D);
}
@Override
public double noise(double x, double z) {
return f(n.GetSimplexFractal(x, z));
}
@Override
public double noiseSigned(double x, double z) {
return n.GetSimplexFractal(x, z);
}
@Override
public double noise(double x, double y, double z) {
return f(n.GetSimplexFractal(x, y, z));
}
@Override
public double noiseSigned(double x, double y, double z) {
return n.GetSimplexFractal(x, y, z);
}
@Override
public void setOctaves(int o) {
n.setFractalOctaves(o);
@@ -24,6 +24,7 @@ public class HexJamesNoise implements NoiseGenerator, OctaveNoise {
{-1L, 1L},
{0L, 1L}
};
private static final ThreadLocal<HexScratch> SCRATCH = ThreadLocal.withInitial(HexScratch::new);
private final long seed;
private final SimplexNoise heatSimplex;
private int octaves;
@@ -221,6 +222,9 @@ public class HexJamesNoise implements NoiseGenerator, OctaveNoise {
}
private double sample(double x, double z) {
HexScratch scratch = SCRATCH.get();
double[] centersQ = scratch.centersQ;
double[] centersR = scratch.centersR;
double qWorld = (SQRT_3_OVER_3 * x) - (z / 3.0);
double rWorld = TWO_OVER_THREE * z;
long[] rounded = roundAxial(qWorld, rWorld);
@@ -233,8 +237,6 @@ public class HexJamesNoise implements NoiseGenerator, OctaveNoise {
double heat = cellHeat(centerQ, centerR, nodeHash, 0);
for (int level = 0; level < MAX_DEPTH; level++) {
double[] centersQ = new double[7];
double[] centersR = new double[7];
int childIndex = pickChildIndex(localQ, localR, centersQ, centersR);
if (childIndex < 0) {
@@ -285,4 +287,9 @@ public class HexJamesNoise implements NoiseGenerator, OctaveNoise {
public void setOctaves(int o) {
this.octaves = Math.max(1, o);
}
private static final class HexScratch {
private final double[] centersQ = new double[7];
private final double[] centersR = new double[7];
}
}
@@ -24,6 +24,7 @@ public class HexRandomSizeNoise implements NoiseGenerator, OctaveNoise {
{-1L, 1L},
{0L, 1L}
};
private static final ThreadLocal<HexScratch> SCRATCH = ThreadLocal.withInitial(HexScratch::new);
private final long seed;
private final SimplexNoise heatSimplex;
private final SimplexNoise structureSimplex;
@@ -186,6 +187,9 @@ public class HexRandomSizeNoise implements NoiseGenerator, OctaveNoise {
}
private double sample(double x, double z) {
HexScratch scratch = SCRATCH.get();
double[] centersQ = scratch.centersQ;
double[] centersR = scratch.centersR;
double qWorld = (SQRT_3_OVER_3 * x) - (z / 3.0);
double rWorld = TWO_OVER_THREE * z;
long[] rounded = roundAxial(qWorld, rWorld);
@@ -202,8 +206,6 @@ public class HexRandomSizeNoise implements NoiseGenerator, OctaveNoise {
return heat;
}
double[] centersQ = new double[7];
double[] centersR = new double[7];
int childIndex = pickChildIndex(localQ, localR, centersQ, centersR);
if (childIndex < 0) {
@@ -250,4 +252,9 @@ public class HexRandomSizeNoise implements NoiseGenerator, OctaveNoise {
public void setOctaves(int o) {
this.octaves = Math.max(1, o);
}
private static final class HexScratch {
private final double[] centersQ = new double[7];
private final double[] centersR = new double[7];
}
}
@@ -27,6 +27,18 @@ public interface NoiseGenerator {
double noise(double x, double y, double z);
default double noiseSigned(double x) {
return (noise(x) * 2D) - 1D;
}
default double noiseSigned(double x, double z) {
return (noise(x, z) * 2D) - 1D;
}
default double noiseSigned(double x, double y, double z) {
return (noise(x, y, z) * 2D) - 1D;
}
default boolean isStatic() {
return false;
}
@@ -6,11 +6,31 @@ import org.jetbrains.annotations.NotNull;
public class OffsetNoiseGenerator implements NoiseGenerator {
private static final int GENERIC = 0;
private static final int SIMPLEX = 1;
private static final int PERLIN = 2;
private static final int FRACTAL_FBM_SIMPLEX = 3;
private static final int VASCULAR = 4;
private final NoiseGenerator base;
private final double ox, oz;
private final SimplexNoise simplexBase;
private final PerlinNoise perlinBase;
private final FractalFBMSimplexNoise fractalFbmSimplexBase;
private final VascularNoise vascularBase;
private final int dispatchKind;
private final double ox;
private final double oz;
public OffsetNoiseGenerator(NoiseGenerator base, long seed) {
this.base = base;
this.simplexBase = base instanceof SimplexNoise simplex ? simplex : null;
this.perlinBase = base instanceof PerlinNoise perlin ? perlin : null;
this.fractalFbmSimplexBase = base instanceof FractalFBMSimplexNoise fractal ? fractal : null;
this.vascularBase = base instanceof VascularNoise vascular ? vascular : null;
this.dispatchKind = simplexBase != null ? SIMPLEX
: perlinBase != null ? PERLIN
: fractalFbmSimplexBase != null ? FRACTAL_FBM_SIMPLEX
: vascularBase != null ? VASCULAR
: GENERIC;
RNG rng = new RNG(seed);
ox = rng.nextInt(Short.MIN_VALUE, Short.MAX_VALUE);
oz = rng.nextInt(Short.MIN_VALUE, Short.MAX_VALUE);
@@ -18,17 +38,68 @@ public class OffsetNoiseGenerator implements NoiseGenerator {
@Override
public double noise(double x) {
return base.noise(x + ox);
return switch (dispatchKind) {
case SIMPLEX -> simplexBase.noise(x + ox);
case PERLIN -> perlinBase.noise(x + ox);
case FRACTAL_FBM_SIMPLEX -> fractalFbmSimplexBase.noise(x + ox);
case VASCULAR -> vascularBase.noise(x + ox);
default -> base.noise(x + ox);
};
}
@Override
public double noiseSigned(double x) {
return switch (dispatchKind) {
case SIMPLEX -> simplexBase.noiseSigned(x + ox);
case PERLIN -> perlinBase.noiseSigned(x + ox);
case FRACTAL_FBM_SIMPLEX -> fractalFbmSimplexBase.noiseSigned(x + ox);
case VASCULAR -> vascularBase.noiseSigned(x + ox);
default -> base.noiseSigned(x + ox);
};
}
@Override
public double noise(double x, double z) {
return base.noise(x + ox, z + oz);
return switch (dispatchKind) {
case SIMPLEX -> simplexBase.noise(x + ox, z + oz);
case PERLIN -> perlinBase.noise(x + ox, z + oz);
case FRACTAL_FBM_SIMPLEX -> fractalFbmSimplexBase.noise(x + ox, z + oz);
case VASCULAR -> vascularBase.noise(x + ox, z + oz);
default -> base.noise(x + ox, z + oz);
};
}
@Override
public double noiseSigned(double x, double z) {
return switch (dispatchKind) {
case SIMPLEX -> simplexBase.noiseSigned(x + ox, z + oz);
case PERLIN -> perlinBase.noiseSigned(x + ox, z + oz);
case FRACTAL_FBM_SIMPLEX -> fractalFbmSimplexBase.noiseSigned(x + ox, z + oz);
case VASCULAR -> vascularBase.noiseSigned(x + ox, z + oz);
default -> base.noiseSigned(x + ox, z + oz);
};
}
@Override
public double noise(double x, double y, double z) {
return base.noise(x + ox, y, z + oz);
return switch (dispatchKind) {
case SIMPLEX -> simplexBase.noise(x + ox, y, z + oz);
case PERLIN -> perlinBase.noise(x + ox, y, z + oz);
case FRACTAL_FBM_SIMPLEX -> fractalFbmSimplexBase.noise(x + ox, y, z + oz);
case VASCULAR -> vascularBase.noise(x + ox, y, z + oz);
default -> base.noise(x + ox, y, z + oz);
};
}
@Override
public double noiseSigned(double x, double y, double z) {
return switch (dispatchKind) {
case SIMPLEX -> simplexBase.noiseSigned(x + ox, y, z + oz);
case PERLIN -> perlinBase.noiseSigned(x + ox, y, z + oz);
case FRACTAL_FBM_SIMPLEX -> fractalFbmSimplexBase.noiseSigned(x + ox, y, z + oz);
case VASCULAR -> vascularBase.noiseSigned(x + ox, y, z + oz);
default -> base.noiseSigned(x + ox, y, z + oz);
};
}
@Override
@@ -45,4 +116,4 @@ public class OffsetNoiseGenerator implements NoiseGenerator {
public NoiseGenerator getBase() {
return base;
}
}
}
@@ -51,6 +51,33 @@ public class PerlinNoise implements NoiseGenerator, OctaveNoise {
return f(v / m);
}
@Override
public double noiseSigned(double x) {
if (octaves <= 1) {
return n.GetPerlin(x, 0D);
}
double frequency = 1D;
double magnitude = 0D;
double value = 0D;
for (int i = 0; i < octaves; i++) {
double sampleFrequency;
if (frequency == 1D) {
sampleFrequency = frequency;
frequency = 2D;
} else {
frequency *= 2D;
sampleFrequency = frequency;
}
value += n.GetPerlin(x * sampleFrequency, 0D) * frequency;
magnitude += frequency;
}
return value / magnitude;
}
@Override
public double noise(double x, double z) {
if (octaves <= 1) {
@@ -69,6 +96,26 @@ public class PerlinNoise implements NoiseGenerator, OctaveNoise {
return f(v / m);
}
@Override
public double noiseSigned(double x, double z) {
if (octaves <= 1) {
return n.GetPerlin(x, z);
}
double frequency = 1D;
double magnitude = 0D;
double value = 0D;
for (int i = 0; i < octaves; i++) {
double octaveFrequency = frequency == 1D ? frequency + 1D : frequency * 2D;
value += n.GetPerlin(x * octaveFrequency, z * octaveFrequency) * octaveFrequency;
magnitude += octaveFrequency;
frequency = octaveFrequency;
}
return value / magnitude;
}
@Override
public double noise(double x, double y, double z) {
if (octaves <= 1) {
@@ -87,6 +134,26 @@ public class PerlinNoise implements NoiseGenerator, OctaveNoise {
return f(v / m);
}
@Override
public double noiseSigned(double x, double y, double z) {
if (octaves <= 1) {
return n.GetPerlin(x, y, z);
}
double frequency = 1D;
double magnitude = 0D;
double value = 0D;
for (int i = 0; i < octaves; i++) {
double octaveFrequency = frequency == 1D ? frequency + 1D : frequency * 2D;
value += n.GetPerlin(x * octaveFrequency, y * octaveFrequency, z * octaveFrequency) * octaveFrequency;
magnitude += octaveFrequency;
frequency = octaveFrequency;
}
return value / magnitude;
}
@Override
public void setOctaves(int o) {
octaves = o;
@@ -51,6 +51,33 @@ public class SimplexNoise implements NoiseGenerator, OctaveNoise {
return f(v / m);
}
@Override
public double noiseSigned(double x) {
if (octaves <= 1) {
return n.GetSimplex(x, 0D);
}
double frequency = 1D;
double magnitude = 0D;
double value = 0D;
for (int i = 0; i < octaves; i++) {
double sampleFrequency;
if (frequency == 1D) {
sampleFrequency = frequency;
frequency = 2D;
} else {
frequency *= 2D;
sampleFrequency = frequency;
}
value += n.GetSimplex(x * sampleFrequency, 0D) * frequency;
magnitude += frequency;
}
return value / magnitude;
}
@Override
public double noise(double x, double z) {
if (octaves <= 1) {
@@ -69,6 +96,26 @@ public class SimplexNoise implements NoiseGenerator, OctaveNoise {
return f(v / m);
}
@Override
public double noiseSigned(double x, double z) {
if (octaves <= 1) {
return n.GetSimplex(x, z);
}
double frequency = 1D;
double magnitude = 0D;
double value = 0D;
for (int i = 0; i < octaves; i++) {
double octaveFrequency = frequency == 1D ? frequency + 1D : frequency * 2D;
value += n.GetSimplex(x * octaveFrequency, z * octaveFrequency) * octaveFrequency;
magnitude += octaveFrequency;
frequency = octaveFrequency;
}
return value / magnitude;
}
@Override
public double noise(double x, double y, double z) {
if (octaves <= 1) {
@@ -87,6 +134,26 @@ public class SimplexNoise implements NoiseGenerator, OctaveNoise {
return f(v / m);
}
@Override
public double noiseSigned(double x, double y, double z) {
if (octaves <= 1) {
return n.GetSimplex(x, y, z);
}
double frequency = 1D;
double magnitude = 0D;
double value = 0D;
for (int i = 0; i < octaves; i++) {
double octaveFrequency = frequency == 1D ? frequency + 1D : frequency * 2D;
value += n.GetSimplex(x * octaveFrequency, y * octaveFrequency, z * octaveFrequency) * octaveFrequency;
magnitude += octaveFrequency;
frequency = octaveFrequency;
}
return value / magnitude;
}
@Override
public void setOctaves(int o) {
octaves = o;
@@ -18,7 +18,6 @@
package art.arcane.iris.util.project.noise;
import art.arcane.volmlib.util.math.M;
import art.arcane.volmlib.util.math.RNG;
public class VascularNoise implements NoiseGenerator {
@@ -32,7 +31,20 @@ public class VascularNoise implements NoiseGenerator {
}
private double filter(double noise) {
return M.clip((noise / 2D) + 0.5D, 0D, 1D);
double normalized = (noise * 0.5D) + 0.5D;
if (normalized < 0D) {
return 0D;
}
return normalized > 1D ? 1D : normalized;
}
private double filterSigned(double noise) {
if (noise < -1D) {
return -1D;
}
return noise > 1D ? 1D : noise;
}
@Override
@@ -40,13 +52,28 @@ public class VascularNoise implements NoiseGenerator {
return filter(n.GetCellular(x, 0));
}
@Override
public double noiseSigned(double x) {
return filterSigned(n.GetCellular(x, 0D));
}
@Override
public double noise(double x, double z) {
return filter(n.GetCellular(x, z));
}
@Override
public double noiseSigned(double x, double z) {
return filterSigned(n.GetCellular(x, z));
}
@Override
public double noise(double x, double y, double z) {
return filter(n.GetCellular(x, y, z));
}
@Override
public double noiseSigned(double x, double y, double z) {
return filterSigned(n.GetCellular(x, y, z));
}
}
@@ -281,6 +281,11 @@ public interface ProceduralStream<T> extends ProceduralLayer, Interpolated<T> {
return new CachedStream2D<T>(name, engine, this, size);
}
@SuppressWarnings("unchecked")
default ProceduralStream<Double> cache2DDouble(String name, Engine engine, int size) {
return new CachedDoubleStream2D(name, engine, (ProceduralStream<Double>) this, size);
}
default ProceduralStream<T> cache3D(String name, Engine engine, int maxSize) {
return new CachedStream3D<T>(name, engine, this, maxSize);
}
@@ -0,0 +1,76 @@
package art.arcane.iris.util.project.stream.utility;
import art.arcane.iris.Iris;
import art.arcane.iris.core.service.PreservationSVC;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.framework.MeteredCache;
import art.arcane.iris.util.project.stream.BasicStream;
import art.arcane.iris.util.project.stream.ProceduralStream;
import art.arcane.volmlib.util.cache.WorldCache2DDouble;
import art.arcane.volmlib.util.data.KCache;
public class CachedDoubleStream2D extends BasicStream<Double> implements ProceduralStream<Double>, MeteredCache, ChunkFillableStream2D {
private final ProceduralStream<Double> stream;
private final WorldCache2DDouble cache;
private final Engine engine;
public CachedDoubleStream2D(String name, Engine engine, ProceduralStream<Double> stream, int size) {
super();
this.stream = stream;
this.engine = engine;
this.cache = new WorldCache2DDouble((x, z) -> stream.getDouble(x, z), size);
Iris.service(PreservationSVC.class).registerCache(this);
}
@Override
public double toDouble(Double t) {
return t;
}
@Override
public Double fromDouble(double d) {
return d;
}
@Override
public Double get(double x, double z) {
return cache.get((int) x, (int) z);
}
@Override
public Double get(double x, double y, double z) {
return stream.get(x, y, z);
}
@Override
public double getDouble(double x, double z) {
return cache.get((int) x, (int) z);
}
@Override
public long getSize() {
return cache.getSize();
}
@Override
public KCache<?, ?> getRawCache() {
return null;
}
@Override
public long getMaxSize() {
return cache.getMaxSize();
}
@Override
public boolean isClosed() {
return engine.isClosed();
}
@Override
public void fillChunkRaw(int worldX, int worldZ, Object[] target) {
int chunkX = worldX >> 4;
int chunkZ = worldZ >> 4;
cache.fillChunk(chunkX, chunkZ, target);
}
}
@@ -27,7 +27,7 @@ import art.arcane.volmlib.util.cache.WorldCache2D;
import art.arcane.volmlib.util.data.KCache;
import art.arcane.iris.util.project.stream.BasicStream;
import art.arcane.iris.util.project.stream.ProceduralStream;
public class CachedStream2D<T> extends BasicStream<T> implements ProceduralStream<T>, MeteredCache {
public class CachedStream2D<T> extends BasicStream<T> implements ProceduralStream<T>, MeteredCache, ChunkFillableStream2D {
private final ProceduralStream<T> stream;
private final WorldCache2D<T> cache;
private final Engine engine;
@@ -81,7 +81,7 @@ public class CachedStream2D<T> extends BasicStream<T> implements ProceduralStrea
return engine.isClosed();
}
public void fillChunk(int worldX, int worldZ, Object[] target) {
public void fillChunkRaw(int worldX, int worldZ, Object[] target) {
int chunkX = worldX >> 4;
int chunkZ = worldZ >> 4;
cache.fillChunk(chunkX, chunkZ, target);
@@ -0,0 +1,5 @@
package art.arcane.iris.util.project.stream.utility;
public interface ChunkFillableStream2D {
void fillChunkRaw(int worldX, int worldZ, Object[] target);
}