mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-22 08:07:42 +00:00
Opti pass 1
This commit is contained in:
1 parent
9a231b2bcf
commit
dcb3306197
43 files changed
+1832
-410
No files matched your search
Vendored
+1
-1
@@ -1 +1 @@
|
||||
149256635
|
||||
-1511497018
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
-18
@@ -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;
|
||||
}
|
||||
|
||||
+132
-7
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
+279
-132
@@ -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];
|
||||
|
||||
+10
-9
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+4
-13
@@ -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);
|
||||
}
|
||||
|
||||
+76
@@ -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);
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package art.arcane.iris.util.project.stream.utility;
|
||||
|
||||
public interface ChunkFillableStream2D {
|
||||
void fillChunkRaw(int worldX, int worldZ, Object[] target);
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package art.arcane.iris.core.pregenerator;
|
||||
|
||||
import art.arcane.iris.core.IrisSettings;
|
||||
import art.arcane.volmlib.util.mantle.runtime.Mantle;
|
||||
import art.arcane.volmlib.util.math.Position2;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class IrisPregeneratorInitTest {
|
||||
@Test
|
||||
public void initDoesNotSaveBeforeGenerationStarts() throws Exception {
|
||||
IrisSettings previousSettings = IrisSettings.settings;
|
||||
IrisSettings.settings = new IrisSettings();
|
||||
TrackingPregeneratorMethod method = new TrackingPregeneratorMethod();
|
||||
PregenTask task = PregenTask.builder()
|
||||
.center(new Position2(0, 0))
|
||||
.radiusX(16)
|
||||
.radiusZ(16)
|
||||
.build();
|
||||
try {
|
||||
IrisPregenerator pregenerator = new IrisPregenerator(task, method, new NoOpPregenListener());
|
||||
Method initMethod = IrisPregenerator.class.getDeclaredMethod("init");
|
||||
initMethod.setAccessible(true);
|
||||
|
||||
initMethod.invoke(pregenerator);
|
||||
|
||||
assertEquals(1, method.initCalls.get());
|
||||
assertEquals(0, method.saveCalls.get());
|
||||
} finally {
|
||||
IrisSettings.settings = previousSettings;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class TrackingPregeneratorMethod implements PregeneratorMethod {
|
||||
private final AtomicInteger initCalls = new AtomicInteger();
|
||||
private final AtomicInteger saveCalls = new AtomicInteger();
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
initCalls.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
saveCalls.incrementAndGet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRegions(int x, int z, PregenListener listener) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethod(int x, int z) {
|
||||
return "test";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateRegion(int x, int z, PregenListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateChunk(int x, int z, PregenListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mantle getMantle() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class NoOpPregenListener implements PregenListener {
|
||||
@Override
|
||||
public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, long generated, long totalChunks, long chunksRemaining, long eta, long elapsed, String method, boolean cached) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkGenerating(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkGenerated(int x, int z, boolean cached) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegionGenerated(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegionGenerating(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkCleaned(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegionSkipped(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkStarted(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkFailed(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkReclaim(int revert) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkGeneratedChunk(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkDownloaded(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaving() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkExistsInRegionGen(int x, int z) {
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -29,4 +29,16 @@ public class AsyncPregenMethodConcurrencyCapTest {
|
||||
assertEquals(16, AsyncPregenMethod.applyRuntimeConcurrencyCap(256, false, 8));
|
||||
assertEquals(20, AsyncPregenMethod.applyRuntimeConcurrencyCap(20, false, 40));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void paperLikeConcurrencyUsesChunkWorkerPoolWhenAvailable() {
|
||||
assertEquals(4, AsyncPregenMethod.resolvePaperLikeConcurrencyWorkerThreads(4, 16, 32));
|
||||
assertEquals(24, AsyncPregenMethod.resolvePaperLikeConcurrencyWorkerThreads(-1, 16, 24));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void foliaConcurrencyStillUsesBroaderRuntimeCapacity() {
|
||||
assertEquals(32, AsyncPregenMethod.resolveFoliaConcurrencyWorkerThreads(4, 16, 32));
|
||||
assertEquals(16, AsyncPregenMethod.resolveFoliaConcurrencyWorkerThreads(-1, 16, 12));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package art.arcane.iris.core.service;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class IrisEngineSVCTest {
|
||||
@Test
|
||||
public void maintenanceSkipsReductionWhenPregenDoesNotTargetWorld() {
|
||||
assertTrue(IrisEngineSVC.shouldSkipMantleReductionForMaintenance(true, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void maintenanceDoesNotSkipReductionForActivePregenWorld() {
|
||||
assertFalse(IrisEngineSVC.shouldSkipMantleReductionForMaintenance(true, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noMaintenanceNeverSkipsReduction() {
|
||||
assertFalse(IrisEngineSVC.shouldSkipMantleReductionForMaintenance(false, false));
|
||||
assertFalse(IrisEngineSVC.shouldSkipMantleReductionForMaintenance(false, true));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package art.arcane.iris.engine.object;
|
||||
|
||||
import art.arcane.iris.util.project.interpolation.InterpolationMethod3D;
|
||||
import art.arcane.iris.util.project.interpolation.IrisInterpolation;
|
||||
import art.arcane.iris.util.project.noise.HexJamesNoise;
|
||||
import art.arcane.iris.util.project.noise.HexRandomSizeNoise;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.function.NoiseProvider3;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class IrisMathNoiseHotPathParityTest {
|
||||
@Test
|
||||
public void hexNoiseSamplesRemainBitExact() {
|
||||
HexRandomSizeNoise randomSize = new HexRandomSizeNoise(12345L);
|
||||
HexJamesNoise james = new HexJamesNoise(54321L);
|
||||
|
||||
assertEquals(0.4670864519829246D, randomSize.noise(12.5D, -8.25D), 0D);
|
||||
assertEquals(0.4935590260372404D, randomSize.noise(-31.75D, 44.5D, 9.125D), 0D);
|
||||
assertEquals(0.5185375921658786D, james.noise(12.5D, -8.25D), 0D);
|
||||
assertEquals(0.4557738681128938D, james.noise(-31.75D, 44.5D, 9.125D), 0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generatorNoiseSamplesRemainBitExact() {
|
||||
IrisNoiseGenerator noiseGenerator = new IrisNoiseGenerator()
|
||||
.setZoom(7.25D)
|
||||
.setOffsetX(1.75D)
|
||||
.setOffsetZ(-3.5D)
|
||||
.setOpacity(0.82D)
|
||||
.setStyle(NoiseStyle.PERLIN_IRIS.style())
|
||||
.setExponent(1.15D);
|
||||
|
||||
assertEquals(0.3886291844359823D, noiseGenerator.getNoise(9988L, 31.25D, -17.5D, null), 0D);
|
||||
|
||||
IrisGeneratorStyle style = NoiseStyle.IRIS_DOUBLE.style();
|
||||
assertEquals(0.8640377573263068D, style.createNoCache(new RNG(112233L), null).noiseFast2D(42.5D, -19.75D), 0D);
|
||||
|
||||
IrisGenerator generator = new IrisGenerator()
|
||||
.setZoom(9.5D)
|
||||
.setOpacity(0.91D)
|
||||
.setComposite(new KList<IrisNoiseGenerator>().qadd(noiseGenerator));
|
||||
|
||||
assertEquals(0.451949817597527D, generator.getHeight(63.0D, -27.0D, 445566L), 0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void interpolationSamplesRemainBitExact() {
|
||||
NoiseProvider3 provider = (x, y, z) -> {
|
||||
double angle = (x * 0.017D) + (y * 0.011D) + (z * 0.023D);
|
||||
return 0.5D + (Math.sin(angle) * 0.25D);
|
||||
};
|
||||
|
||||
assertEquals(
|
||||
0.5231950552025616D,
|
||||
IrisInterpolation.getNoise3D(InterpolationMethod3D.TRILINEAR, 5, 7, -3, 2.5D, 3.5D, 4.5D, provider),
|
||||
0D
|
||||
);
|
||||
assertEquals(
|
||||
0.5259208842929466D,
|
||||
IrisInterpolation.getNoise3D(InterpolationMethod3D.TRICUBIC, 5, 7, -3, 2.5D, 3.5D, 4.5D, provider),
|
||||
0D
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,16 @@ public class CNGFastPathParityTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signedFastPathMatchesLegacyAcrossRepresentativeGenerators() {
|
||||
for (long seed = 31L; seed <= 35L; seed++) {
|
||||
List<CNG> generators = createSignedGenerators(seed);
|
||||
for (int index = 0; index < generators.size(); index++) {
|
||||
assertSignedFastPathParity("signed-seed-" + seed + "-case-" + index, generators.get(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void assertFastPathParity(String label, CNG generator) {
|
||||
for (int x = -320; x <= 320; x += 19) {
|
||||
for (int z = -320; z <= 320; z += 23) {
|
||||
@@ -47,6 +57,26 @@ public class CNGFastPathParityTest {
|
||||
}
|
||||
}
|
||||
|
||||
private void assertSignedFastPathParity(String label, CNG generator) {
|
||||
for (int x = -320; x <= 320; x += 19) {
|
||||
for (int z = -320; z <= 320; z += 23) {
|
||||
double expected = (generator.noiseFast2D(x, z) * 2D) - 1D;
|
||||
double actual = generator.noiseFastSigned2D(x, z);
|
||||
assertEquals(label + " signed-2D x=" + x + " z=" + z, expected, actual, 1.0E-12D);
|
||||
}
|
||||
}
|
||||
|
||||
for (int x = -128; x <= 128; x += 17) {
|
||||
for (int y = -96; y <= 96; y += 13) {
|
||||
for (int z = -128; z <= 128; z += 19) {
|
||||
double expected = (generator.noiseFast3D(x, y, z) * 2D) - 1D;
|
||||
double actual = generator.noiseFastSigned3D(x, y, z);
|
||||
assertEquals(label + " signed-3D x=" + x + " y=" + y + " z=" + z, expected, actual, 1.0E-12D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CNG createIdentityGenerator(long seed) {
|
||||
DeterministicNoiseGenerator generator = new DeterministicNoiseGenerator(0.31D + (seed * 0.01D));
|
||||
return new CNG(new RNG(seed), generator, 1D, 1).bake();
|
||||
@@ -72,6 +102,24 @@ public class CNGFastPathParityTest {
|
||||
return generators;
|
||||
}
|
||||
|
||||
private List<CNG> createSignedGenerators(long seed) {
|
||||
List<CNG> generators = new ArrayList<>();
|
||||
|
||||
generators.add(new CNG(new RNG(seed), new SimplexNoise(seed + 100L), 1D, 1).bake());
|
||||
generators.add(new CNG(new RNG(seed + 1L), new FractalFBMSimplexNoise(seed + 101L), 1D, 1).bake());
|
||||
generators.add(new CNG(new RNG(seed + 2L), new VascularNoise(seed + 102L), 1D, 1).bake());
|
||||
generators.add(new CNG(new RNG(seed + 3L), new OffsetNoiseGenerator(new SimplexNoise(seed + 103L), seed + 104L), 1D, 1).bake());
|
||||
generators.add(new CNG(new RNG(seed + 4L), new OffsetNoiseGenerator(new PerlinNoise(seed + 107L), seed + 108L), 1D, 1).bake());
|
||||
generators.add(new CNG(new RNG(seed + 5L), new OffsetNoiseGenerator(new VascularNoise(seed + 109L), seed + 110L), 1D, 1).bake());
|
||||
generators.add(new CNG(new RNG(seed + 6L), new OffsetNoiseGenerator(new FractalFBMSimplexNoise(seed + 111L), seed + 112L), 1D, 1).bake());
|
||||
|
||||
CNG fractured = new CNG(new RNG(seed + 7L), new OffsetNoiseGenerator(new FractalFBMSimplexNoise(seed + 105L), seed + 106L), 1D, 1).bake();
|
||||
fractured.fractureWith(createIdentityGenerator(seed + 500L), 9.5D);
|
||||
generators.add(fractured);
|
||||
|
||||
return generators;
|
||||
}
|
||||
|
||||
private static class DeterministicNoiseGenerator implements NoiseGenerator {
|
||||
private final double offset;
|
||||
|
||||
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
package art.arcane.iris.util.project.noise;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class FastNoiseDoubleCellularParityTest {
|
||||
private static final FastNoiseDouble.CellularDistanceFunction[] DISTANCE_FUNCTIONS = new FastNoiseDouble.CellularDistanceFunction[]{
|
||||
FastNoiseDouble.CellularDistanceFunction.Euclidean,
|
||||
FastNoiseDouble.CellularDistanceFunction.Manhattan,
|
||||
FastNoiseDouble.CellularDistanceFunction.Natural
|
||||
};
|
||||
private static final FastNoiseDouble.CellularReturnType[] TWO_EDGE_RETURN_TYPES = new FastNoiseDouble.CellularReturnType[]{
|
||||
FastNoiseDouble.CellularReturnType.Distance2,
|
||||
FastNoiseDouble.CellularReturnType.Distance2Add,
|
||||
FastNoiseDouble.CellularReturnType.Distance2Sub,
|
||||
FastNoiseDouble.CellularReturnType.Distance2Mul,
|
||||
FastNoiseDouble.CellularReturnType.Distance2Div
|
||||
};
|
||||
private static final Method HASH_2D = getDeclaredMethod("hash2D", long.class, long.class, long.class);
|
||||
private static final Method HASH_3D = getDeclaredMethod("hash3D", long.class, long.class, long.class, long.class);
|
||||
private static final double[] CELL_2D_X = getDeclaredDoubleArray("CELL_2D_X");
|
||||
private static final double[] CELL_2D_Y = getDeclaredDoubleArray("CELL_2D_Y");
|
||||
private static final double[] CELL_3D_X = getDeclaredDoubleArray("CELL_3D_X");
|
||||
private static final double[] CELL_3D_Y = getDeclaredDoubleArray("CELL_3D_Y");
|
||||
private static final double[] CELL_3D_Z = getDeclaredDoubleArray("CELL_3D_Z");
|
||||
|
||||
@Test
|
||||
public void cellular2DTwoEdgeModesMatchLegacyUpdateLogic() throws Exception {
|
||||
for (long seed = 11L; seed <= 17L; seed++) {
|
||||
double frequency = 0.037D + (seed * 0.001D);
|
||||
for (FastNoiseDouble.CellularDistanceFunction distanceFunction : DISTANCE_FUNCTIONS) {
|
||||
for (FastNoiseDouble.CellularReturnType returnType : TWO_EDGE_RETURN_TYPES) {
|
||||
FastNoiseDouble noise = new FastNoiseDouble(seed);
|
||||
noise.setFrequency(frequency);
|
||||
noise.setCellularDistanceFunction(distanceFunction);
|
||||
noise.setCellularReturnType(returnType);
|
||||
for (int x = -96; x <= 96; x += 11) {
|
||||
for (int y = -96; y <= 96; y += 13) {
|
||||
double sampleX = x + 0.37D;
|
||||
double sampleY = y - 0.41D;
|
||||
double expected = legacyGetCellular2D(seed, frequency, distanceFunction, returnType, sampleX, sampleY);
|
||||
double actual = noise.GetCellular(sampleX, sampleY);
|
||||
assertEquals("2D seed=" + seed + " distance=" + distanceFunction + " return=" + returnType + " x=" + sampleX + " y=" + sampleY, expected, actual, 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cellular3DTwoEdgeModesMatchLegacyUpdateLogic() throws Exception {
|
||||
for (long seed = 31L; seed <= 35L; seed++) {
|
||||
double frequency = 0.029D + (seed * 0.001D);
|
||||
for (FastNoiseDouble.CellularDistanceFunction distanceFunction : DISTANCE_FUNCTIONS) {
|
||||
for (FastNoiseDouble.CellularReturnType returnType : TWO_EDGE_RETURN_TYPES) {
|
||||
FastNoiseDouble noise = new FastNoiseDouble(seed);
|
||||
noise.setFrequency(frequency);
|
||||
noise.setCellularDistanceFunction(distanceFunction);
|
||||
noise.setCellularReturnType(returnType);
|
||||
for (int x = -48; x <= 48; x += 9) {
|
||||
for (int y = -32; y <= 32; y += 7) {
|
||||
for (int z = -48; z <= 48; z += 11) {
|
||||
double sampleX = x + 0.19D;
|
||||
double sampleY = y - 0.27D;
|
||||
double sampleZ = z + 0.43D;
|
||||
double expected = legacyGetCellular3D(seed, frequency, distanceFunction, returnType, sampleX, sampleY, sampleZ);
|
||||
double actual = noise.GetCellular(sampleX, sampleY, sampleZ);
|
||||
assertEquals("3D seed=" + seed + " distance=" + distanceFunction + " return=" + returnType + " x=" + sampleX + " y=" + sampleY + " z=" + sampleZ, expected, actual, 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double legacyGetCellular2D(long seed, double frequency, FastNoiseDouble.CellularDistanceFunction distanceFunction, FastNoiseDouble.CellularReturnType returnType, double x, double y) throws Exception {
|
||||
double scaledX = x * frequency;
|
||||
double scaledY = y * frequency;
|
||||
long xr = fastRound(scaledX);
|
||||
long yr = fastRound(scaledY);
|
||||
double distance = 999999D;
|
||||
double distance2 = 999999D;
|
||||
|
||||
for (long xi = xr - 1; xi <= xr + 1; xi++) {
|
||||
for (long yi = yr - 1; yi <= yr + 1; yi++) {
|
||||
int cellIndex = (int) hash2D(seed, xi, yi) & 255;
|
||||
double vecX = xi - scaledX + CELL_2D_X[cellIndex];
|
||||
double vecY = yi - scaledY + CELL_2D_Y[cellIndex];
|
||||
double newDistance = switch (distanceFunction) {
|
||||
case Euclidean -> vecX * vecX + vecY * vecY;
|
||||
case Manhattan -> Math.abs(vecX) + Math.abs(vecY);
|
||||
case Natural -> (Math.abs(vecX) + Math.abs(vecY)) + (vecX * vecX + vecY * vecY);
|
||||
};
|
||||
distance2 = Math.max(Math.min(distance2, newDistance), distance);
|
||||
distance = Math.min(distance, newDistance);
|
||||
}
|
||||
}
|
||||
|
||||
return applyTwoEdgeReturn(distance, distance2, returnType);
|
||||
}
|
||||
|
||||
private double legacyGetCellular3D(long seed, double frequency, FastNoiseDouble.CellularDistanceFunction distanceFunction, FastNoiseDouble.CellularReturnType returnType, double x, double y, double z) throws Exception {
|
||||
double scaledX = x * frequency;
|
||||
double scaledY = y * frequency;
|
||||
double scaledZ = z * frequency;
|
||||
long xr = fastRound(scaledX);
|
||||
long yr = fastRound(scaledY);
|
||||
long zr = fastRound(scaledZ);
|
||||
double distance = 999999D;
|
||||
double distance2 = 999999D;
|
||||
|
||||
for (long xi = xr - 1; xi <= xr + 1; xi++) {
|
||||
for (long yi = yr - 1; yi <= yr + 1; yi++) {
|
||||
for (long zi = zr - 1; zi <= zr + 1; zi++) {
|
||||
int cellIndex = (int) hash3D(seed, xi, yi, zi) & 255;
|
||||
double vecX = xi - scaledX + CELL_3D_X[cellIndex];
|
||||
double vecY = yi - scaledY + CELL_3D_Y[cellIndex];
|
||||
double vecZ = zi - scaledZ + CELL_3D_Z[cellIndex];
|
||||
double newDistance = switch (distanceFunction) {
|
||||
case Euclidean -> vecX * vecX + vecY * vecY + vecZ * vecZ;
|
||||
case Manhattan -> Math.abs(vecX) + Math.abs(vecY) + Math.abs(vecZ);
|
||||
case Natural -> (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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return applyTwoEdgeReturn(distance, distance2, returnType);
|
||||
}
|
||||
|
||||
private double applyTwoEdgeReturn(double distance, double distance2, FastNoiseDouble.CellularReturnType returnType) {
|
||||
return switch (returnType) {
|
||||
case Distance2 -> distance2 - 1D;
|
||||
case Distance2Add -> distance2 + distance - 1D;
|
||||
case Distance2Sub -> distance2 - distance - 1D;
|
||||
case Distance2Mul -> distance2 * distance - 1D;
|
||||
case Distance2Div -> distance / distance2 - 1D;
|
||||
default -> 0D;
|
||||
};
|
||||
}
|
||||
|
||||
private static long fastRound(double value) {
|
||||
return value >= 0D ? (long) (value + 0.5D) : (long) (value - 0.5D);
|
||||
}
|
||||
|
||||
private static long hash2D(long seed, long x, long y) throws Exception {
|
||||
Long value = (Long) HASH_2D.invoke(null, seed, x, y);
|
||||
return value.longValue();
|
||||
}
|
||||
|
||||
private static long hash3D(long seed, long x, long y, long z) throws Exception {
|
||||
Long value = (Long) HASH_3D.invoke(null, seed, x, y, z);
|
||||
return value.longValue();
|
||||
}
|
||||
|
||||
private static Method getDeclaredMethod(String name, Class<?>... parameterTypes) {
|
||||
try {
|
||||
Method method = FastNoiseDouble.class.getDeclaredMethod(name, parameterTypes);
|
||||
method.setAccessible(true);
|
||||
return method;
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static double[] getDeclaredDoubleArray(String name) {
|
||||
try {
|
||||
Field field = FastNoiseDouble.class.getDeclaredField(name);
|
||||
field.setAccessible(true);
|
||||
return (double[]) field.get(null);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user