mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-06-18 23:01:07 +00:00
f
This commit is contained in:
@@ -342,11 +342,16 @@ public class IrisCreator {
|
|||||||
int chunkX = rawLocation.getBlockX() >> 4;
|
int chunkX = rawLocation.getBlockX() >> 4;
|
||||||
int chunkZ = rawLocation.getBlockZ() >> 4;
|
int chunkZ = rawLocation.getBlockZ() >> 4;
|
||||||
try {
|
try {
|
||||||
CompletableFuture<Chunk> chunkFuture = PaperLib.getChunkAtAsync(world, chunkX, chunkZ, true);
|
CompletableFuture<Chunk> chunkFuture = PaperLib.getChunkAtAsync(world, chunkX, chunkZ, false);
|
||||||
if (chunkFuture != null) {
|
if (chunkFuture != null) {
|
||||||
chunkFuture.get(15, TimeUnit.SECONDS);
|
chunkFuture.get(10, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
} catch (Throwable ignored) {
|
} catch (Throwable ignored) {
|
||||||
|
return rawLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!world.isChunkLoaded(chunkX, chunkZ)) {
|
||||||
|
return rawLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
CompletableFuture<Location> regionFuture = new CompletableFuture<>();
|
CompletableFuture<Location> regionFuture = new CompletableFuture<>();
|
||||||
@@ -376,19 +381,19 @@ public class IrisCreator {
|
|||||||
int z = source.getBlockZ();
|
int z = source.getBlockZ();
|
||||||
int minY = world.getMinHeight() + 1;
|
int minY = world.getMinHeight() + 1;
|
||||||
int maxY = world.getMaxHeight() - 2;
|
int maxY = world.getMaxHeight() - 2;
|
||||||
int topY = world.getHighestBlockYAt(x, z, HeightMap.MOTION_BLOCKING_NO_LEAVES);
|
int sourceY = source.getBlockY();
|
||||||
int startY = Math.max(minY, Math.min(maxY, topY + 1));
|
int startY = Math.max(minY, Math.min(maxY, sourceY));
|
||||||
float yaw = source.getYaw();
|
float yaw = source.getYaw();
|
||||||
float pitch = source.getPitch();
|
float pitch = source.getPitch();
|
||||||
|
|
||||||
int upperBound = Math.min(maxY, startY + 16);
|
int upperBound = Math.min(maxY, startY + 32);
|
||||||
for (int y = startY; y <= upperBound; y++) {
|
for (int y = startY; y <= upperBound; y++) {
|
||||||
if (isSafeStandingLocation(world, x, y, z)) {
|
if (isSafeStandingLocation(world, x, y, z)) {
|
||||||
return new Location(world, x + 0.5D, y, z + 0.5D, yaw, pitch);
|
return new Location(world, x + 0.5D, y, z + 0.5D, yaw, pitch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int lowerBound = Math.max(minY, startY - 24);
|
int lowerBound = Math.max(minY, startY - 64);
|
||||||
for (int y = startY - 1; y >= lowerBound; y--) {
|
for (int y = startY - 1; y >= lowerBound; y--) {
|
||||||
if (isSafeStandingLocation(world, x, y, z)) {
|
if (isSafeStandingLocation(world, x, y, z)) {
|
||||||
return new Location(world, x + 0.5D, y, z + 0.5D, yaw, pitch);
|
return new Location(world, x + 0.5D, y, z + 0.5D, yaw, pitch);
|
||||||
|
|||||||
@@ -433,6 +433,10 @@ public class IrisComplex implements DataProvider {
|
|||||||
|
|
||||||
for (IrisGenerator gen : generators) {
|
for (IrisGenerator gen : generators) {
|
||||||
String key = gen.getLoadKey();
|
String key = gen.getLoadKey();
|
||||||
|
if (key == null || key.isBlank()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
max += biome.getGenLinkMax(key, engine);
|
max += biome.getGenLinkMax(key, engine);
|
||||||
min += biome.getGenLinkMin(key, engine);
|
min += biome.getGenLinkMin(key, engine);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ public class IrisEngine implements Engine {
|
|||||||
private double maxBiomeLayerDensity;
|
private double maxBiomeLayerDensity;
|
||||||
private double maxBiomeDecoratorDensity;
|
private double maxBiomeDecoratorDensity;
|
||||||
private IrisComplex complex;
|
private IrisComplex complex;
|
||||||
|
private final AtomicBoolean modeFallbackLogged;
|
||||||
|
|
||||||
public IrisEngine(EngineTarget target, boolean studio) {
|
public IrisEngine(EngineTarget target, boolean studio) {
|
||||||
this.studio = studio;
|
this.studio = studio;
|
||||||
@@ -129,6 +130,7 @@ public class IrisEngine implements Engine {
|
|||||||
context = new IrisContext(this);
|
context = new IrisContext(this);
|
||||||
cleaning = new AtomicBoolean(false);
|
cleaning = new AtomicBoolean(false);
|
||||||
noisemapPrebakeRunning = new AtomicBoolean(false);
|
noisemapPrebakeRunning = new AtomicBoolean(false);
|
||||||
|
modeFallbackLogged = new AtomicBoolean(false);
|
||||||
execution = getData().getEnvironment().with(this);
|
execution = getData().getEnvironment().with(this);
|
||||||
if (studio) {
|
if (studio) {
|
||||||
getData().dump();
|
getData().dump();
|
||||||
@@ -165,10 +167,29 @@ public class IrisEngine implements Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void prehotload() {
|
private void prehotload() {
|
||||||
worldManager.close();
|
EngineWorldManager currentWorldManager = worldManager;
|
||||||
complex.close();
|
worldManager = null;
|
||||||
effects.close();
|
if (currentWorldManager != null) {
|
||||||
mode.close();
|
currentWorldManager.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
IrisComplex currentComplex = complex;
|
||||||
|
complex = null;
|
||||||
|
if (currentComplex != null) {
|
||||||
|
currentComplex.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
EngineEffects currentEffects = effects;
|
||||||
|
effects = null;
|
||||||
|
if (currentEffects != null) {
|
||||||
|
currentEffects.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
EngineMode currentMode = mode;
|
||||||
|
mode = null;
|
||||||
|
if (currentMode != null) {
|
||||||
|
currentMode.close();
|
||||||
|
}
|
||||||
execution = getData().getEnvironment().with(this);
|
execution = getData().getEnvironment().with(this);
|
||||||
|
|
||||||
J.a(() -> new IrisProject(getData().getDataFolder()).updateWorkspace());
|
J.a(() -> new IrisProject(getData().getDataFolder()).updateWorkspace());
|
||||||
@@ -178,12 +199,14 @@ public class IrisEngine implements Engine {
|
|||||||
try {
|
try {
|
||||||
Iris.debug("Setup Engine " + getCacheID());
|
Iris.debug("Setup Engine " + getCacheID());
|
||||||
cacheId = RNG.r.nextInt();
|
cacheId = RNG.r.nextInt();
|
||||||
worldManager = new IrisWorldManager(this);
|
complex = ensureComplex();
|
||||||
complex = new IrisComplex(this);
|
|
||||||
effects = new IrisEngineEffects(this);
|
effects = new IrisEngineEffects(this);
|
||||||
hash32 = new CompletableFuture<>();
|
hash32 = new CompletableFuture<>();
|
||||||
mantle.hotload();
|
mantle.hotload();
|
||||||
setupMode();
|
setupMode();
|
||||||
|
IrisWorldManager manager = new IrisWorldManager(this);
|
||||||
|
worldManager = manager;
|
||||||
|
manager.startManager();
|
||||||
getDimension().getEngineScripts().forEach(execution::execute);
|
getDimension().getEngineScripts().forEach(execution::execute);
|
||||||
J.a(this::computeBiomeMaxes);
|
J.a(this::computeBiomeMaxes);
|
||||||
J.a(() -> {
|
J.a(() -> {
|
||||||
@@ -207,11 +230,76 @@ public class IrisEngine implements Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setupMode() {
|
private void setupMode() {
|
||||||
if (mode != null) {
|
EngineMode currentMode = mode;
|
||||||
mode.close();
|
if (currentMode != null) {
|
||||||
|
currentMode.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
mode = getDimension().getMode().create(this);
|
mode = null;
|
||||||
|
mode = ensureMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private EngineMode ensureMode() {
|
||||||
|
EngineMode currentMode = mode;
|
||||||
|
if (currentMode != null) {
|
||||||
|
return currentMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (this) {
|
||||||
|
currentMode = mode;
|
||||||
|
if (currentMode != null) {
|
||||||
|
return currentMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
IrisComplex readyComplex = ensureComplex();
|
||||||
|
if (readyComplex == null) {
|
||||||
|
throw new IllegalStateException("Iris complex is unavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
IrisDimensionMode configuredMode = getDimension().getMode();
|
||||||
|
if (configuredMode == null) {
|
||||||
|
configuredMode = new IrisDimensionMode();
|
||||||
|
getDimension().setMode(configuredMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentMode = configuredMode.create(this);
|
||||||
|
if (currentMode == null) {
|
||||||
|
throw new IllegalStateException("Dimension mode factory returned null");
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Iris.reportError(e);
|
||||||
|
if (modeFallbackLogged.compareAndSet(false, true)) {
|
||||||
|
Iris.warn("Failed to initialize configured dimension mode for " + getDimension().getLoadKey() + ", falling back to OVERWORLD mode.");
|
||||||
|
}
|
||||||
|
currentMode = IrisDimensionModeType.OVERWORLD.create(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
mode = currentMode;
|
||||||
|
return currentMode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IrisComplex ensureComplex() {
|
||||||
|
IrisComplex currentComplex = complex;
|
||||||
|
if (currentComplex != null) {
|
||||||
|
return currentComplex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closed) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (this) {
|
||||||
|
currentComplex = complex;
|
||||||
|
if (currentComplex != null) {
|
||||||
|
return currentComplex;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentComplex = new IrisComplex(this);
|
||||||
|
complex = currentComplex;
|
||||||
|
return currentComplex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleStartupNoisemapPrebake() {
|
private void scheduleStartupNoisemapPrebake() {
|
||||||
@@ -447,17 +535,39 @@ public class IrisEngine implements Engine {
|
|||||||
PregeneratorJob.shutdownInstance();
|
PregeneratorJob.shutdownInstance();
|
||||||
closed = true;
|
closed = true;
|
||||||
J.car(art);
|
J.car(art);
|
||||||
getWorldManager().close();
|
EngineWorldManager currentWorldManager = getWorldManager();
|
||||||
|
if (currentWorldManager != null) {
|
||||||
|
currentWorldManager.close();
|
||||||
|
}
|
||||||
getTarget().close();
|
getTarget().close();
|
||||||
saveEngineData();
|
saveEngineData();
|
||||||
getMantle().close();
|
getMantle().close();
|
||||||
getComplex().close();
|
IrisComplex currentComplex = complex;
|
||||||
mode.close();
|
if (currentComplex != null) {
|
||||||
|
currentComplex.close();
|
||||||
|
}
|
||||||
|
complex = null;
|
||||||
|
EngineMode currentMode = mode;
|
||||||
|
if (currentMode != null) {
|
||||||
|
currentMode.close();
|
||||||
|
}
|
||||||
|
mode = null;
|
||||||
|
effects = null;
|
||||||
|
worldManager = null;
|
||||||
getData().dump();
|
getData().dump();
|
||||||
getData().clearLists();
|
getData().clearLists();
|
||||||
Iris.service(PreservationSVC.class).dereference();
|
Iris.service(PreservationSVC.class).dereference();
|
||||||
Iris.debug("Engine Fully Shutdown!");
|
Iris.debug("Engine Fully Shutdown!");
|
||||||
complex = null;
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IrisComplex getComplex() {
|
||||||
|
return ensureComplex();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EngineMode getMode() {
|
||||||
|
return ensureMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -510,7 +620,8 @@ public class IrisEngine implements Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mode.generate(x, z, blocks, vbiomes, multicore);
|
EngineMode activeMode = ensureMode();
|
||||||
|
activeMode.generate(x, z, blocks, vbiomes, multicore);
|
||||||
}
|
}
|
||||||
|
|
||||||
World realWorld = getWorld().realWorld();
|
World realWorld = getWorld().realWorld();
|
||||||
|
|||||||
@@ -195,7 +195,12 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
|||||||
};
|
};
|
||||||
looper.setPriority(Thread.MIN_PRIORITY);
|
looper.setPriority(Thread.MIN_PRIORITY);
|
||||||
looper.setName("Iris World Manager " + getTarget().getWorld().name());
|
looper.setName("Iris World Manager " + getTarget().getWorld().name());
|
||||||
looper.start();
|
}
|
||||||
|
|
||||||
|
public void startManager() {
|
||||||
|
if (!looper.isAlive()) {
|
||||||
|
looper.start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void discoverChunks() {
|
private void discoverChunks() {
|
||||||
@@ -528,6 +533,11 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IrisComplex complex = getEngine().getComplex();
|
||||||
|
if (complex == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (initial) {
|
if (initial) {
|
||||||
energy += 1.2;
|
energy += 1.2;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,265 @@
|
|||||||
|
/*
|
||||||
|
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||||
|
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package art.arcane.iris.engine.mantle.components;
|
||||||
|
|
||||||
|
import art.arcane.iris.core.loader.IrisData;
|
||||||
|
import art.arcane.iris.engine.framework.Engine;
|
||||||
|
import art.arcane.iris.engine.mantle.MantleWriter;
|
||||||
|
import art.arcane.iris.engine.object.IrisCaveFieldModule;
|
||||||
|
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.collection.KList;
|
||||||
|
import art.arcane.volmlib.util.math.RNG;
|
||||||
|
import art.arcane.volmlib.util.matter.MatterCavern;
|
||||||
|
|
||||||
|
public class IrisCaveCarver3D {
|
||||||
|
private static final byte LIQUID_AIR = 0;
|
||||||
|
private static final byte LIQUID_WATER = 1;
|
||||||
|
private static final byte LIQUID_LAVA = 2;
|
||||||
|
private static final byte LIQUID_FORCED_AIR = 3;
|
||||||
|
|
||||||
|
private final Engine engine;
|
||||||
|
private final IrisData data;
|
||||||
|
private final IrisCaveProfile profile;
|
||||||
|
private final CNG baseDensity;
|
||||||
|
private final CNG detailDensity;
|
||||||
|
private final CNG warpDensity;
|
||||||
|
private final CNG surfaceBreakDensity;
|
||||||
|
private final RNG thresholdRng;
|
||||||
|
private final KList<ModuleState> modules;
|
||||||
|
private final double normalization;
|
||||||
|
private final MatterCavern carveAir;
|
||||||
|
private final MatterCavern carveWater;
|
||||||
|
private final MatterCavern carveLava;
|
||||||
|
private final MatterCavern carveForcedAir;
|
||||||
|
|
||||||
|
public IrisCaveCarver3D(Engine engine, IrisCaveProfile profile) {
|
||||||
|
this.engine = engine;
|
||||||
|
this.data = engine.getData();
|
||||||
|
this.profile = profile;
|
||||||
|
this.carveAir = new MatterCavern(true, "", LIQUID_AIR);
|
||||||
|
this.carveWater = new MatterCavern(true, "", LIQUID_WATER);
|
||||||
|
this.carveLava = new MatterCavern(true, "", LIQUID_LAVA);
|
||||||
|
this.carveForcedAir = new MatterCavern(true, "", LIQUID_FORCED_AIR);
|
||||||
|
this.modules = new KList<>();
|
||||||
|
|
||||||
|
RNG baseRng = new RNG(engine.getSeedManager().getCarve());
|
||||||
|
this.baseDensity = profile.getBaseDensityStyle().create(baseRng.nextParallelRNG(934_447), data);
|
||||||
|
this.detailDensity = profile.getDetailDensityStyle().create(baseRng.nextParallelRNG(612_991), data);
|
||||||
|
this.warpDensity = profile.getWarpStyle().create(baseRng.nextParallelRNG(770_713), data);
|
||||||
|
this.surfaceBreakDensity = profile.getSurfaceBreakStyle().create(baseRng.nextParallelRNG(341_219), data);
|
||||||
|
this.thresholdRng = baseRng.nextParallelRNG(489_112);
|
||||||
|
|
||||||
|
double weight = Math.abs(profile.getBaseWeight()) + Math.abs(profile.getDetailWeight());
|
||||||
|
int index = 0;
|
||||||
|
for (IrisCaveFieldModule module : profile.getModules()) {
|
||||||
|
CNG moduleDensity = module.getStyle().create(baseRng.nextParallelRNG(1_000_003L + (index * 65_537L)), data);
|
||||||
|
ModuleState state = new ModuleState(module, moduleDensity);
|
||||||
|
modules.add(state);
|
||||||
|
weight += Math.abs(state.weight);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
normalization = weight <= 0 ? 1 : weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int carve(MantleWriter writer, int chunkX, int chunkZ) {
|
||||||
|
int worldHeight = writer.getMantle().getWorldHeight();
|
||||||
|
int minY = Math.max(0, (int) Math.floor(profile.getVerticalRange().getMin()));
|
||||||
|
int maxY = Math.min(worldHeight - 1, (int) Math.ceil(profile.getVerticalRange().getMax()));
|
||||||
|
int sampleStep = Math.max(1, profile.getSampleStep());
|
||||||
|
int surfaceClearance = Math.max(0, profile.getSurfaceClearance());
|
||||||
|
int surfaceBreakDepth = Math.max(0, profile.getSurfaceBreakDepth());
|
||||||
|
double surfaceBreakNoiseThreshold = profile.getSurfaceBreakNoiseThreshold();
|
||||||
|
double surfaceBreakThresholdBoost = Math.max(0, profile.getSurfaceBreakThresholdBoost());
|
||||||
|
int waterMinDepthBelowSurface = Math.max(0, profile.getWaterMinDepthBelowSurface());
|
||||||
|
boolean waterRequiresFloor = profile.isWaterRequiresFloor();
|
||||||
|
boolean allowSurfaceBreak = profile.isAllowSurfaceBreak();
|
||||||
|
if (maxY < minY) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int x0 = chunkX << 4;
|
||||||
|
int z0 = chunkZ << 4;
|
||||||
|
int carved = 0;
|
||||||
|
|
||||||
|
for (int x = x0; x < x0 + 16; x++) {
|
||||||
|
for (int z = z0; z < z0 + 16; z++) {
|
||||||
|
double threshold = profile.getDensityThreshold().get(thresholdRng, x, z, data) - profile.getThresholdBias();
|
||||||
|
int columnSurface = engine.getHeight(x, z);
|
||||||
|
int clearanceTopY = Math.min(maxY, Math.max(minY, columnSurface - surfaceClearance));
|
||||||
|
boolean surfaceBreakColumn = allowSurfaceBreak
|
||||||
|
&& signed(surfaceBreakDensity.noise(x, z)) >= surfaceBreakNoiseThreshold;
|
||||||
|
int columnMaxY = surfaceBreakColumn
|
||||||
|
? Math.min(maxY, Math.max(minY, columnSurface))
|
||||||
|
: clearanceTopY;
|
||||||
|
int surfaceBreakFloorY = Math.max(minY, columnSurface - surfaceBreakDepth);
|
||||||
|
if (columnMaxY < minY) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int y = minY; y <= columnMaxY; y += sampleStep) {
|
||||||
|
double localThreshold = threshold;
|
||||||
|
if (surfaceBreakColumn && y >= surfaceBreakFloorY) {
|
||||||
|
localThreshold += surfaceBreakThresholdBoost;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sampleDensity(x, y, z) <= localThreshold) {
|
||||||
|
int carveMaxY = Math.min(columnMaxY, y + sampleStep - 1);
|
||||||
|
for (int yy = y; yy <= carveMaxY; yy++) {
|
||||||
|
writer.setData(x, yy, z, resolveMatter(x, yy, z, localThreshold, waterMinDepthBelowSurface, waterRequiresFloor));
|
||||||
|
carved++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return carved;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double sampleDensity(int x, int y, int z) {
|
||||||
|
double warpedX = x;
|
||||||
|
double warpedY = y;
|
||||||
|
double warpedZ = z;
|
||||||
|
double warpStrength = profile.getWarpStrength();
|
||||||
|
|
||||||
|
if (warpStrength > 0) {
|
||||||
|
double offsetX = signed(warpDensity.noise(x, y, z)) * warpStrength;
|
||||||
|
double offsetY = signed(warpDensity.noise(y, z, x)) * warpStrength;
|
||||||
|
double offsetZ = signed(warpDensity.noise(z, x, y)) * warpStrength;
|
||||||
|
warpedX += offsetX;
|
||||||
|
warpedY += offsetY;
|
||||||
|
warpedZ += offsetZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
double density = signed(baseDensity.noise(warpedX, warpedY, warpedZ)) * profile.getBaseWeight();
|
||||||
|
density += signed(detailDensity.noise(warpedX, warpedY, warpedZ)) * profile.getDetailWeight();
|
||||||
|
|
||||||
|
for (ModuleState module : modules) {
|
||||||
|
if (y < module.minY || y > module.maxY) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
double moduleDensity = signed(module.density.noise(warpedX, warpedY, warpedZ)) - module.threshold;
|
||||||
|
if (module.invert) {
|
||||||
|
moduleDensity = -moduleDensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
density += moduleDensity * module.weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
return density / normalization;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MatterCavern resolveMatter(int x, int y, int z, double localThreshold, int waterMinDepthBelowSurface, boolean waterRequiresFloor) {
|
||||||
|
int lavaHeight = engine.getDimension().getCaveLavaHeight();
|
||||||
|
int fluidHeight = engine.getDimension().getFluidHeight();
|
||||||
|
|
||||||
|
if (profile.isAllowLava() && y <= lavaHeight) {
|
||||||
|
return carveLava;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile.isAllowWater() && y <= fluidHeight) {
|
||||||
|
int surfaceY = engine.getHeight(x, z);
|
||||||
|
if (surfaceY - y < waterMinDepthBelowSurface) {
|
||||||
|
return carveAir;
|
||||||
|
}
|
||||||
|
|
||||||
|
double depthFactor = Math.max(0, Math.min(1.5, (fluidHeight - y) / 48D));
|
||||||
|
double cutoff = 0.35 + (depthFactor * 0.2);
|
||||||
|
double aquifer = signed(detailDensity.noise(x, y * 0.5D, z));
|
||||||
|
if (aquifer <= cutoff) {
|
||||||
|
return carveAir;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (waterRequiresFloor && !hasAquiferCupSupport(x, y, z, localThreshold)) {
|
||||||
|
return carveAir;
|
||||||
|
}
|
||||||
|
|
||||||
|
return carveWater;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!profile.isAllowLava() && y <= lavaHeight) {
|
||||||
|
return carveForcedAir;
|
||||||
|
}
|
||||||
|
|
||||||
|
return carveAir;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasAquiferCupSupport(int x, int y, int z, double threshold) {
|
||||||
|
int floorY = Math.max(0, y - 1);
|
||||||
|
int deepFloorY = Math.max(0, y - 2);
|
||||||
|
int aboveY = Math.min(engine.getHeight() - 1, y + 1);
|
||||||
|
if (!isDensitySolid(x, floorY, z, threshold)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isDensitySolid(x, deepFloorY, z, threshold - 0.05D)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int support = 0;
|
||||||
|
if (isDensitySolid(x + 1, y, z, threshold)) {
|
||||||
|
support++;
|
||||||
|
}
|
||||||
|
if (isDensitySolid(x - 1, y, z, threshold)) {
|
||||||
|
support++;
|
||||||
|
}
|
||||||
|
if (isDensitySolid(x, y, z + 1, threshold)) {
|
||||||
|
support++;
|
||||||
|
}
|
||||||
|
if (isDensitySolid(x, y, z - 1, threshold)) {
|
||||||
|
support++;
|
||||||
|
}
|
||||||
|
if (isDensitySolid(x, aboveY, z, threshold)) {
|
||||||
|
support++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return support >= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isDensitySolid(int x, int y, int z, double threshold) {
|
||||||
|
return sampleDensity(x, y, z) > threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double signed(double value) {
|
||||||
|
return (value * 2D) - 1D;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ModuleState {
|
||||||
|
private final CNG density;
|
||||||
|
private final int minY;
|
||||||
|
private final int maxY;
|
||||||
|
private final double weight;
|
||||||
|
private final double threshold;
|
||||||
|
private final boolean invert;
|
||||||
|
|
||||||
|
private ModuleState(IrisCaveFieldModule module, CNG density) {
|
||||||
|
IrisRange range = module.getVerticalRange();
|
||||||
|
this.density = density;
|
||||||
|
this.minY = (int) Math.floor(range.getMin());
|
||||||
|
this.maxY = (int) Math.ceil(range.getMax());
|
||||||
|
this.weight = module.getWeight();
|
||||||
|
this.threshold = module.getThreshold();
|
||||||
|
this.invert = module.isInvert();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+65
-6
@@ -25,14 +25,21 @@ import art.arcane.iris.engine.mantle.IrisMantleComponent;
|
|||||||
import art.arcane.iris.engine.mantle.MantleWriter;
|
import art.arcane.iris.engine.mantle.MantleWriter;
|
||||||
import art.arcane.iris.engine.object.IrisBiome;
|
import art.arcane.iris.engine.object.IrisBiome;
|
||||||
import art.arcane.iris.engine.object.IrisCarving;
|
import art.arcane.iris.engine.object.IrisCarving;
|
||||||
|
import art.arcane.iris.engine.object.IrisCaveProfile;
|
||||||
|
import art.arcane.iris.engine.object.IrisDimension;
|
||||||
import art.arcane.iris.engine.object.IrisRegion;
|
import art.arcane.iris.engine.object.IrisRegion;
|
||||||
import art.arcane.iris.util.project.context.ChunkContext;
|
import art.arcane.iris.util.project.context.ChunkContext;
|
||||||
import art.arcane.volmlib.util.documentation.ChunkCoordinates;
|
import art.arcane.volmlib.util.documentation.ChunkCoordinates;
|
||||||
import art.arcane.volmlib.util.mantle.flag.ReservedFlag;
|
import art.arcane.volmlib.util.mantle.flag.ReservedFlag;
|
||||||
import art.arcane.volmlib.util.math.RNG;
|
import art.arcane.volmlib.util.math.RNG;
|
||||||
|
|
||||||
|
import java.util.IdentityHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@ComponentFlag(ReservedFlag.CARVED)
|
@ComponentFlag(ReservedFlag.CARVED)
|
||||||
public class MantleCarvingComponent extends IrisMantleComponent {
|
public class MantleCarvingComponent extends IrisMantleComponent {
|
||||||
|
private final Map<IrisCaveProfile, IrisCaveCarver3D> profileCarvers = new IdentityHashMap<>();
|
||||||
|
|
||||||
public MantleCarvingComponent(EngineMantle engineMantle) {
|
public MantleCarvingComponent(EngineMantle engineMantle) {
|
||||||
super(engineMantle, ReservedFlag.CARVED, 0);
|
super(engineMantle, ReservedFlag.CARVED, 0);
|
||||||
}
|
}
|
||||||
@@ -49,9 +56,18 @@ public class MantleCarvingComponent extends IrisMantleComponent {
|
|||||||
|
|
||||||
@ChunkCoordinates
|
@ChunkCoordinates
|
||||||
private void carve(MantleWriter writer, RNG rng, int cx, int cz, IrisRegion region, IrisBiome biome) {
|
private void carve(MantleWriter writer, RNG rng, int cx, int cz, IrisRegion region, IrisBiome biome) {
|
||||||
carve(getDimension().getCarving(), writer, new RNG((rng.nextLong() * cx) + 490495 + cz), cx, cz);
|
IrisCaveProfile dimensionProfile = getDimension().getCaveProfile();
|
||||||
carve(biome.getCarving(), writer, new RNG((rng.nextLong() * cx) + 490495 + cz), cx, cz);
|
IrisCaveProfile biomeProfile = biome.getCaveProfile();
|
||||||
carve(region.getCarving(), writer, new RNG((rng.nextLong() * cx) + 490495 + cz), cx, cz);
|
IrisCaveProfile regionProfile = region.getCaveProfile();
|
||||||
|
IrisCaveProfile activeProfile = resolveActiveProfile(dimensionProfile, regionProfile, biomeProfile);
|
||||||
|
if (isProfileEnabled(activeProfile)) {
|
||||||
|
carveProfile(activeProfile, writer, cx, cz);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
carve(getDimension().getCarving(), writer, nextCarveRng(rng, cx, cz), cx, cz);
|
||||||
|
carve(biome.getCarving(), writer, nextCarveRng(rng, cx, cz), cx, cz);
|
||||||
|
carve(region.getCarving(), writer, nextCarveRng(rng, cx, cz), cx, cz);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ChunkCoordinates
|
@ChunkCoordinates
|
||||||
@@ -59,17 +75,60 @@ public class MantleCarvingComponent extends IrisMantleComponent {
|
|||||||
carving.doCarving(writer, rng, getEngineMantle().getEngine(), cx << 4, -1, cz << 4, 0);
|
carving.doCarving(writer, rng, getEngineMantle().getEngine(), cx << 4, -1, cz << 4, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RNG nextCarveRng(RNG rng, int cx, int cz) {
|
||||||
|
return new RNG((rng.nextLong() * cx) + 490495L + cz);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ChunkCoordinates
|
||||||
|
private void carveProfile(IrisCaveProfile profile, MantleWriter writer, int cx, int cz) {
|
||||||
|
if (!isProfileEnabled(profile)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IrisCaveCarver3D carver = getCarver(profile);
|
||||||
|
carver.carve(writer, cx, cz);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IrisCaveCarver3D getCarver(IrisCaveProfile profile) {
|
||||||
|
synchronized (profileCarvers) {
|
||||||
|
IrisCaveCarver3D carver = profileCarvers.get(profile);
|
||||||
|
if (carver != null) {
|
||||||
|
return carver;
|
||||||
|
}
|
||||||
|
|
||||||
|
IrisCaveCarver3D createdCarver = new IrisCaveCarver3D(getEngineMantle().getEngine(), profile);
|
||||||
|
profileCarvers.put(profile, createdCarver);
|
||||||
|
return createdCarver;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isProfileEnabled(IrisCaveProfile profile) {
|
||||||
|
return profile != null && profile.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
private IrisCaveProfile resolveActiveProfile(IrisCaveProfile dimensionProfile, IrisCaveProfile regionProfile, IrisCaveProfile biomeProfile) {
|
||||||
|
if (isProfileEnabled(biomeProfile)) {
|
||||||
|
return biomeProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isProfileEnabled(regionProfile)) {
|
||||||
|
return regionProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dimensionProfile;
|
||||||
|
}
|
||||||
|
|
||||||
protected int computeRadius() {
|
protected int computeRadius() {
|
||||||
var dimension = getDimension();
|
IrisDimension dimension = getDimension();
|
||||||
int max = 0;
|
int max = 0;
|
||||||
|
|
||||||
max = Math.max(max, dimension.getCarving().getMaxRange(getData(), 0));
|
max = Math.max(max, dimension.getCarving().getMaxRange(getData(), 0));
|
||||||
|
|
||||||
for (var i : dimension.getAllRegions(this::getData)) {
|
for (IrisRegion i : dimension.getAllRegions(this::getData)) {
|
||||||
max = Math.max(max, i.getCarving().getMaxRange(getData(), 0));
|
max = Math.max(max, i.getCarving().getMaxRange(getData(), 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i : dimension.getAllBiomes(this::getData)) {
|
for (IrisBiome i : dimension.getAllBiomes(this::getData)) {
|
||||||
max = Math.max(max, i.getCarving().getMaxRange(getData(), 0));
|
max = Math.max(max, i.getCarving().getMaxRange(getData(), 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+380
-32
@@ -63,21 +63,29 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
int xxx = 8 + (x << 4);
|
int xxx = 8 + (x << 4);
|
||||||
int zzz = 8 + (z << 4);
|
int zzz = 8 + (z << 4);
|
||||||
IrisRegion region = getComplex().getRegionStream().get(xxx, zzz);
|
IrisRegion region = getComplex().getRegionStream().get(xxx, zzz);
|
||||||
IrisBiome biome = getComplex().getTrueBiomeStream().get(xxx, zzz);
|
IrisBiome surfaceBiome = getComplex().getTrueBiomeStream().get(xxx, zzz);
|
||||||
|
IrisBiome caveBiome = getComplex().getCaveBiomeStream().get(xxx, zzz);
|
||||||
if (traceRegen) {
|
if (traceRegen) {
|
||||||
Iris.info("Regen object layer start: chunk=" + x + "," + z
|
Iris.info("Regen object layer start: chunk=" + x + "," + z
|
||||||
+ " biome=" + biome.getLoadKey()
|
+ " surfaceBiome=" + surfaceBiome.getLoadKey()
|
||||||
|
+ " caveBiome=" + caveBiome.getLoadKey()
|
||||||
+ " region=" + region.getLoadKey()
|
+ " region=" + region.getLoadKey()
|
||||||
+ " biomePlacers=" + biome.getSurfaceObjects().size()
|
+ " biomeSurfacePlacers=" + surfaceBiome.getSurfaceObjects().size()
|
||||||
+ " regionPlacers=" + region.getSurfaceObjects().size());
|
+ " biomeCavePlacers=" + caveBiome.getCarvingObjects().size()
|
||||||
|
+ " regionSurfacePlacers=" + region.getSurfaceObjects().size()
|
||||||
|
+ " regionCavePlacers=" + region.getCarvingObjects().size());
|
||||||
}
|
}
|
||||||
ObjectPlacementSummary summary = placeObjects(writer, rng, x, z, biome, region, traceRegen);
|
ObjectPlacementSummary summary = placeObjects(writer, rng, x, z, surfaceBiome, caveBiome, region, traceRegen);
|
||||||
if (traceRegen) {
|
if (traceRegen) {
|
||||||
Iris.info("Regen object layer done: chunk=" + x + "," + z
|
Iris.info("Regen object layer done: chunk=" + x + "," + z
|
||||||
+ " biomePlacersChecked=" + summary.biomePlacersChecked()
|
+ " biomeSurfacePlacersChecked=" + summary.biomeSurfacePlacersChecked()
|
||||||
+ " biomePlacersTriggered=" + summary.biomePlacersTriggered()
|
+ " biomeSurfacePlacersTriggered=" + summary.biomeSurfacePlacersTriggered()
|
||||||
+ " regionPlacersChecked=" + summary.regionPlacersChecked()
|
+ " biomeCavePlacersChecked=" + summary.biomeCavePlacersChecked()
|
||||||
+ " regionPlacersTriggered=" + summary.regionPlacersTriggered()
|
+ " biomeCavePlacersTriggered=" + summary.biomeCavePlacersTriggered()
|
||||||
|
+ " regionSurfacePlacersChecked=" + summary.regionSurfacePlacersChecked()
|
||||||
|
+ " regionSurfacePlacersTriggered=" + summary.regionSurfacePlacersTriggered()
|
||||||
|
+ " regionCavePlacersChecked=" + summary.regionCavePlacersChecked()
|
||||||
|
+ " regionCavePlacersTriggered=" + summary.regionCavePlacersTriggered()
|
||||||
+ " objectAttempts=" + summary.objectAttempts()
|
+ " objectAttempts=" + summary.objectAttempts()
|
||||||
+ " objectPlaced=" + summary.objectPlaced()
|
+ " objectPlaced=" + summary.objectPlaced()
|
||||||
+ " objectRejected=" + summary.objectRejected()
|
+ " objectRejected=" + summary.objectRejected()
|
||||||
@@ -92,32 +100,40 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ChunkCoordinates
|
@ChunkCoordinates
|
||||||
private ObjectPlacementSummary placeObjects(MantleWriter writer, RNG rng, int x, int z, IrisBiome biome, IrisRegion region, boolean traceRegen) {
|
private ObjectPlacementSummary placeObjects(MantleWriter writer, RNG rng, int x, int z, IrisBiome surfaceBiome, IrisBiome caveBiome, IrisRegion region, boolean traceRegen) {
|
||||||
int biomeChecked = 0;
|
int biomeSurfaceChecked = 0;
|
||||||
int biomeTriggered = 0;
|
int biomeSurfaceTriggered = 0;
|
||||||
int regionChecked = 0;
|
int biomeCaveChecked = 0;
|
||||||
int regionTriggered = 0;
|
int biomeCaveTriggered = 0;
|
||||||
|
int regionSurfaceChecked = 0;
|
||||||
|
int regionSurfaceTriggered = 0;
|
||||||
|
int regionCaveChecked = 0;
|
||||||
|
int regionCaveTriggered = 0;
|
||||||
int attempts = 0;
|
int attempts = 0;
|
||||||
int placed = 0;
|
int placed = 0;
|
||||||
int rejected = 0;
|
int rejected = 0;
|
||||||
int nullObjects = 0;
|
int nullObjects = 0;
|
||||||
int errors = 0;
|
int errors = 0;
|
||||||
|
IrisCaveProfile biomeCaveProfile = resolveCaveProfile(caveBiome.getCaveProfile(), region.getCaveProfile());
|
||||||
|
IrisCaveProfile regionCaveProfile = resolveCaveProfile(region.getCaveProfile(), caveBiome.getCaveProfile());
|
||||||
|
int biomeSurfaceExclusionDepth = resolveSurfaceObjectExclusionDepth(biomeCaveProfile);
|
||||||
|
int regionSurfaceExclusionDepth = resolveSurfaceObjectExclusionDepth(regionCaveProfile);
|
||||||
|
|
||||||
for (IrisObjectPlacement i : biome.getSurfaceObjects()) {
|
for (IrisObjectPlacement i : surfaceBiome.getSurfaceObjects()) {
|
||||||
biomeChecked++;
|
biomeSurfaceChecked++;
|
||||||
boolean chance = rng.chance(i.getChance() + rng.d(-0.005, 0.005));
|
boolean chance = rng.chance(i.getChance() + rng.d(-0.005, 0.005));
|
||||||
if (traceRegen) {
|
if (traceRegen) {
|
||||||
Iris.info("Regen object placer chance: chunk=" + x + "," + z
|
Iris.info("Regen object placer chance: chunk=" + x + "," + z
|
||||||
+ " scope=biome"
|
+ " scope=biome-surface"
|
||||||
+ " chanceResult=" + chance
|
+ " chanceResult=" + chance
|
||||||
+ " chanceBase=" + i.getChance()
|
+ " chanceBase=" + i.getChance()
|
||||||
+ " densityMid=" + i.getDensity()
|
+ " densityMid=" + i.getDensity()
|
||||||
+ " objects=" + i.getPlace().size());
|
+ " objects=" + i.getPlace().size());
|
||||||
}
|
}
|
||||||
if (chance) {
|
if (chance) {
|
||||||
biomeTriggered++;
|
biomeSurfaceTriggered++;
|
||||||
try {
|
try {
|
||||||
ObjectPlacementResult result = placeObject(writer, rng, x << 4, z << 4, i, traceRegen, x, z, "biome");
|
ObjectPlacementResult result = placeObject(writer, rng, x << 4, z << 4, i, biomeSurfaceExclusionDepth, traceRegen, x, z, "biome-surface");
|
||||||
attempts += result.attempts();
|
attempts += result.attempts();
|
||||||
placed += result.placed();
|
placed += result.placed();
|
||||||
rejected += result.rejected();
|
rejected += result.rejected();
|
||||||
@@ -126,7 +142,41 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
errors++;
|
errors++;
|
||||||
Iris.reportError(e);
|
Iris.reportError(e);
|
||||||
Iris.error("Failed to place objects in the following biome: " + biome.getName());
|
Iris.error("Failed to place objects in the following biome: " + surfaceBiome.getName());
|
||||||
|
Iris.error("Object(s) " + i.getPlace().toString(", ") + " (" + e.getClass().getSimpleName() + ").");
|
||||||
|
Iris.error("Are these objects missing?");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (IrisObjectPlacement i : caveBiome.getCarvingObjects()) {
|
||||||
|
if (!i.getCarvingSupport().equals(CarvingMode.CARVING_ONLY)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
biomeCaveChecked++;
|
||||||
|
boolean chance = rng.chance(i.getChance() + rng.d(-0.005, 0.005));
|
||||||
|
if (traceRegen) {
|
||||||
|
Iris.info("Regen object placer chance: chunk=" + x + "," + z
|
||||||
|
+ " scope=biome-cave"
|
||||||
|
+ " chanceResult=" + chance
|
||||||
|
+ " chanceBase=" + i.getChance()
|
||||||
|
+ " densityMid=" + i.getDensity()
|
||||||
|
+ " objects=" + i.getPlace().size());
|
||||||
|
}
|
||||||
|
if (chance) {
|
||||||
|
biomeCaveTriggered++;
|
||||||
|
try {
|
||||||
|
ObjectPlacementResult result = placeCaveObject(writer, rng, x, z, i, biomeCaveProfile, traceRegen, x, z, "biome-cave");
|
||||||
|
attempts += result.attempts();
|
||||||
|
placed += result.placed();
|
||||||
|
rejected += result.rejected();
|
||||||
|
nullObjects += result.nullObjects();
|
||||||
|
errors += result.errors();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
errors++;
|
||||||
|
Iris.reportError(e);
|
||||||
|
Iris.error("Failed to place cave objects in the following biome: " + caveBiome.getName());
|
||||||
Iris.error("Object(s) " + i.getPlace().toString(", ") + " (" + e.getClass().getSimpleName() + ").");
|
Iris.error("Object(s) " + i.getPlace().toString(", ") + " (" + e.getClass().getSimpleName() + ").");
|
||||||
Iris.error("Are these objects missing?");
|
Iris.error("Are these objects missing?");
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -135,20 +185,20 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (IrisObjectPlacement i : region.getSurfaceObjects()) {
|
for (IrisObjectPlacement i : region.getSurfaceObjects()) {
|
||||||
regionChecked++;
|
regionSurfaceChecked++;
|
||||||
boolean chance = rng.chance(i.getChance() + rng.d(-0.005, 0.005));
|
boolean chance = rng.chance(i.getChance() + rng.d(-0.005, 0.005));
|
||||||
if (traceRegen) {
|
if (traceRegen) {
|
||||||
Iris.info("Regen object placer chance: chunk=" + x + "," + z
|
Iris.info("Regen object placer chance: chunk=" + x + "," + z
|
||||||
+ " scope=region"
|
+ " scope=region-surface"
|
||||||
+ " chanceResult=" + chance
|
+ " chanceResult=" + chance
|
||||||
+ " chanceBase=" + i.getChance()
|
+ " chanceBase=" + i.getChance()
|
||||||
+ " densityMid=" + i.getDensity()
|
+ " densityMid=" + i.getDensity()
|
||||||
+ " objects=" + i.getPlace().size());
|
+ " objects=" + i.getPlace().size());
|
||||||
}
|
}
|
||||||
if (chance) {
|
if (chance) {
|
||||||
regionTriggered++;
|
regionSurfaceTriggered++;
|
||||||
try {
|
try {
|
||||||
ObjectPlacementResult result = placeObject(writer, rng, x << 4, z << 4, i, traceRegen, x, z, "region");
|
ObjectPlacementResult result = placeObject(writer, rng, x << 4, z << 4, i, regionSurfaceExclusionDepth, traceRegen, x, z, "region-surface");
|
||||||
attempts += result.attempts();
|
attempts += result.attempts();
|
||||||
placed += result.placed();
|
placed += result.placed();
|
||||||
rejected += result.rejected();
|
rejected += result.rejected();
|
||||||
@@ -165,11 +215,49 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (IrisObjectPlacement i : region.getCarvingObjects()) {
|
||||||
|
if (!i.getCarvingSupport().equals(CarvingMode.CARVING_ONLY)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
regionCaveChecked++;
|
||||||
|
boolean chance = rng.chance(i.getChance() + rng.d(-0.005, 0.005));
|
||||||
|
if (traceRegen) {
|
||||||
|
Iris.info("Regen object placer chance: chunk=" + x + "," + z
|
||||||
|
+ " scope=region-cave"
|
||||||
|
+ " chanceResult=" + chance
|
||||||
|
+ " chanceBase=" + i.getChance()
|
||||||
|
+ " densityMid=" + i.getDensity()
|
||||||
|
+ " objects=" + i.getPlace().size());
|
||||||
|
}
|
||||||
|
if (chance) {
|
||||||
|
regionCaveTriggered++;
|
||||||
|
try {
|
||||||
|
ObjectPlacementResult result = placeCaveObject(writer, rng, x, z, i, regionCaveProfile, traceRegen, x, z, "region-cave");
|
||||||
|
attempts += result.attempts();
|
||||||
|
placed += result.placed();
|
||||||
|
rejected += result.rejected();
|
||||||
|
nullObjects += result.nullObjects();
|
||||||
|
errors += result.errors();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
errors++;
|
||||||
|
Iris.reportError(e);
|
||||||
|
Iris.error("Failed to place cave objects in the following region: " + region.getName());
|
||||||
|
Iris.error("Object(s) " + i.getPlace().toString(", ") + " (" + e.getClass().getSimpleName() + ").");
|
||||||
|
Iris.error("Are these objects missing?");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return new ObjectPlacementSummary(
|
return new ObjectPlacementSummary(
|
||||||
biomeChecked,
|
biomeSurfaceChecked,
|
||||||
biomeTriggered,
|
biomeSurfaceTriggered,
|
||||||
regionChecked,
|
biomeCaveChecked,
|
||||||
regionTriggered,
|
biomeCaveTriggered,
|
||||||
|
regionSurfaceChecked,
|
||||||
|
regionSurfaceTriggered,
|
||||||
|
regionCaveChecked,
|
||||||
|
regionCaveTriggered,
|
||||||
attempts,
|
attempts,
|
||||||
placed,
|
placed,
|
||||||
rejected,
|
rejected,
|
||||||
@@ -185,6 +273,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
int x,
|
int x,
|
||||||
int z,
|
int z,
|
||||||
IrisObjectPlacement objectPlacement,
|
IrisObjectPlacement objectPlacement,
|
||||||
|
int surfaceObjectExclusionDepth,
|
||||||
boolean traceRegen,
|
boolean traceRegen,
|
||||||
int chunkX,
|
int chunkX,
|
||||||
int chunkZ,
|
int chunkZ,
|
||||||
@@ -213,6 +302,11 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
}
|
}
|
||||||
int xx = rng.i(x, x + 15);
|
int xx = rng.i(x, x + 15);
|
||||||
int zz = rng.i(z, z + 15);
|
int zz = rng.i(z, z + 15);
|
||||||
|
int surfaceObjectExclusionRadius = resolveSurfaceObjectExclusionRadius(v);
|
||||||
|
if (surfaceObjectExclusionDepth > 0 && hasSurfaceCarveExposure(writer, xx, zz, surfaceObjectExclusionDepth, surfaceObjectExclusionRadius)) {
|
||||||
|
rejected++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
int id = rng.i(0, Integer.MAX_VALUE);
|
int id = rng.i(0, Integer.MAX_VALUE);
|
||||||
try {
|
try {
|
||||||
int result = v.place(xx, -1, zz, writer, objectPlacement, rng, (b, data) -> {
|
int result = v.place(xx, -1, zz, writer, objectPlacement, rng, (b, data) -> {
|
||||||
@@ -253,16 +347,270 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
return new ObjectPlacementResult(attempts, placed, rejected, nullObjects, errors);
|
return new ObjectPlacementResult(attempts, placed, rejected, nullObjects, errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ChunkCoordinates
|
||||||
|
private ObjectPlacementResult placeCaveObject(
|
||||||
|
MantleWriter writer,
|
||||||
|
RNG rng,
|
||||||
|
int chunkX,
|
||||||
|
int chunkZ,
|
||||||
|
IrisObjectPlacement objectPlacement,
|
||||||
|
IrisCaveProfile caveProfile,
|
||||||
|
boolean traceRegen,
|
||||||
|
int metricChunkX,
|
||||||
|
int metricChunkZ,
|
||||||
|
String scope
|
||||||
|
) {
|
||||||
|
int attempts = 0;
|
||||||
|
int placed = 0;
|
||||||
|
int rejected = 0;
|
||||||
|
int nullObjects = 0;
|
||||||
|
int errors = 0;
|
||||||
|
int minX = chunkX << 4;
|
||||||
|
int minZ = chunkZ << 4;
|
||||||
|
int density = objectPlacement.getDensity(rng, minX, minZ, getData());
|
||||||
|
KMap<Long, KList<Integer>> anchorCache = new KMap<>();
|
||||||
|
IrisCaveAnchorMode anchorMode = resolveAnchorMode(objectPlacement, caveProfile);
|
||||||
|
int anchorScanStep = resolveAnchorScanStep(caveProfile);
|
||||||
|
int objectMinDepthBelowSurface = resolveObjectMinDepthBelowSurface(caveProfile);
|
||||||
|
int anchorSearchAttempts = resolveAnchorSearchAttempts(caveProfile);
|
||||||
|
|
||||||
|
for (int i = 0; i < density; i++) {
|
||||||
|
attempts++;
|
||||||
|
IrisObject object = objectPlacement.getScale().get(rng, objectPlacement.getObject(getComplex(), rng));
|
||||||
|
if (object == null) {
|
||||||
|
nullObjects++;
|
||||||
|
if (traceRegen) {
|
||||||
|
Iris.warn("Regen cave object placement null object: chunk=" + metricChunkX + "," + metricChunkZ
|
||||||
|
+ " scope=" + scope
|
||||||
|
+ " densityIndex=" + i
|
||||||
|
+ " density=" + density
|
||||||
|
+ " placementKeys=" + objectPlacement.getPlace().toString(","));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int x = 0;
|
||||||
|
int z = 0;
|
||||||
|
int y = -1;
|
||||||
|
for (int search = 0; search < anchorSearchAttempts; search++) {
|
||||||
|
int candidateX = rng.i(minX, minX + 15);
|
||||||
|
int candidateZ = rng.i(minZ, minZ + 15);
|
||||||
|
int candidateY = findCaveAnchorY(writer, rng, candidateX, candidateZ, anchorMode, anchorScanStep, objectMinDepthBelowSurface, anchorCache);
|
||||||
|
if (candidateY < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
x = candidateX;
|
||||||
|
z = candidateZ;
|
||||||
|
y = candidateY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y < 0) {
|
||||||
|
rejected++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int id = rng.i(0, Integer.MAX_VALUE);
|
||||||
|
|
||||||
|
try {
|
||||||
|
int result = object.place(x, y, z, writer, objectPlacement, rng, (b, data) -> {
|
||||||
|
writer.setData(b.getX(), b.getY(), b.getZ(), object.getLoadKey() + "@" + id);
|
||||||
|
if (objectPlacement.isDolphinTarget() && objectPlacement.isUnderwater() && B.isStorageChest(data)) {
|
||||||
|
writer.setData(b.getX(), b.getY(), b.getZ(), MatterStructurePOI.BURIED_TREASURE);
|
||||||
|
}
|
||||||
|
}, null, getData());
|
||||||
|
|
||||||
|
if (result >= 0) {
|
||||||
|
placed++;
|
||||||
|
} else {
|
||||||
|
rejected++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (traceRegen) {
|
||||||
|
Iris.info("Regen cave object placement result: chunk=" + metricChunkX + "," + metricChunkZ
|
||||||
|
+ " scope=" + scope
|
||||||
|
+ " object=" + object.getLoadKey()
|
||||||
|
+ " resultY=" + result
|
||||||
|
+ " anchorY=" + y
|
||||||
|
+ " px=" + x
|
||||||
|
+ " pz=" + z
|
||||||
|
+ " densityIndex=" + i
|
||||||
|
+ " density=" + density);
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
errors++;
|
||||||
|
Iris.reportError(e);
|
||||||
|
Iris.error("Regen cave object placement exception: chunk=" + metricChunkX + "," + metricChunkZ
|
||||||
|
+ " scope=" + scope
|
||||||
|
+ " object=" + object.getLoadKey()
|
||||||
|
+ " densityIndex=" + i
|
||||||
|
+ " density=" + density
|
||||||
|
+ " error=" + e.getClass().getSimpleName() + ":" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ObjectPlacementResult(attempts, placed, rejected, nullObjects, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int findCaveAnchorY(MantleWriter writer, RNG rng, int x, int z, IrisCaveAnchorMode anchorMode, int anchorScanStep, int objectMinDepthBelowSurface, KMap<Long, KList<Integer>> anchorCache) {
|
||||||
|
long key = Cache.key(x, z);
|
||||||
|
KList<Integer> anchors = anchorCache.computeIfAbsent(key, (k) -> scanCaveAnchorColumn(writer, anchorMode, anchorScanStep, objectMinDepthBelowSurface, x, z));
|
||||||
|
if (anchors.isEmpty()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (anchors.size() == 1) {
|
||||||
|
return anchors.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return anchors.get(rng.i(0, anchors.size() - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private KList<Integer> scanCaveAnchorColumn(MantleWriter writer, IrisCaveAnchorMode anchorMode, int anchorScanStep, int objectMinDepthBelowSurface, int x, int z) {
|
||||||
|
KList<Integer> anchors = new KList<>();
|
||||||
|
int height = getEngineMantle().getEngine().getHeight();
|
||||||
|
int step = Math.max(1, anchorScanStep);
|
||||||
|
int surfaceY = getEngineMantle().getEngine().getHeight(x, z);
|
||||||
|
int maxAnchorY = Math.min(height - 1, surfaceY - Math.max(0, objectMinDepthBelowSurface));
|
||||||
|
if (maxAnchorY <= 1) {
|
||||||
|
return anchors;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int y = 1; y < maxAnchorY; y += step) {
|
||||||
|
if (!writer.isCarved(x, y, z)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean solidBelow = y <= 0 || !writer.isCarved(x, y - 1, z);
|
||||||
|
boolean solidAbove = y >= (height - 1) || !writer.isCarved(x, y + 1, z);
|
||||||
|
if (matchesCaveAnchor(anchorMode, solidBelow, solidAbove)) {
|
||||||
|
anchors.add(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return anchors;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean matchesCaveAnchor(IrisCaveAnchorMode anchorMode, boolean solidBelow, boolean solidAbove) {
|
||||||
|
return switch (anchorMode) {
|
||||||
|
case PROFILE_DEFAULT, FLOOR -> solidBelow;
|
||||||
|
case CEILING -> solidAbove;
|
||||||
|
case CENTER -> !solidBelow && !solidAbove;
|
||||||
|
case ANY -> true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private IrisCaveProfile resolveCaveProfile(IrisCaveProfile preferred, IrisCaveProfile secondary) {
|
||||||
|
IrisCaveProfile dimensionProfile = getDimension().getCaveProfile();
|
||||||
|
if (preferred != null && preferred.isEnabled()) {
|
||||||
|
return preferred;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (secondary != null && secondary.isEnabled()) {
|
||||||
|
return secondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dimensionProfile != null) {
|
||||||
|
return dimensionProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new IrisCaveProfile();
|
||||||
|
}
|
||||||
|
|
||||||
|
private IrisCaveAnchorMode resolveAnchorMode(IrisObjectPlacement objectPlacement, IrisCaveProfile caveProfile) {
|
||||||
|
IrisCaveAnchorMode placementMode = objectPlacement.getCaveAnchorMode();
|
||||||
|
if (placementMode != null && !placementMode.equals(IrisCaveAnchorMode.PROFILE_DEFAULT)) {
|
||||||
|
return placementMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (caveProfile == null) {
|
||||||
|
return IrisCaveAnchorMode.FLOOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
IrisCaveAnchorMode profileMode = caveProfile.getDefaultObjectAnchor();
|
||||||
|
if (profileMode == null || profileMode.equals(IrisCaveAnchorMode.PROFILE_DEFAULT)) {
|
||||||
|
return IrisCaveAnchorMode.FLOOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return profileMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int resolveAnchorScanStep(IrisCaveProfile caveProfile) {
|
||||||
|
if (caveProfile == null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.max(1, caveProfile.getAnchorScanStep());
|
||||||
|
}
|
||||||
|
|
||||||
|
private int resolveObjectMinDepthBelowSurface(IrisCaveProfile caveProfile) {
|
||||||
|
if (caveProfile == null) {
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.max(0, caveProfile.getObjectMinDepthBelowSurface());
|
||||||
|
}
|
||||||
|
|
||||||
|
private int resolveSurfaceObjectExclusionDepth(IrisCaveProfile caveProfile) {
|
||||||
|
if (caveProfile == null) {
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.max(0, caveProfile.getSurfaceObjectExclusionDepth());
|
||||||
|
}
|
||||||
|
|
||||||
|
private int resolveSurfaceObjectExclusionRadius(IrisObject object) {
|
||||||
|
if (object == null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxDimension = Math.max(object.getW(), object.getD());
|
||||||
|
return Math.max(1, Math.min(8, Math.floorDiv(Math.max(1, maxDimension), 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int resolveAnchorSearchAttempts(IrisCaveProfile caveProfile) {
|
||||||
|
if (caveProfile == null) {
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.max(1, caveProfile.getAnchorSearchAttempts());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasSurfaceCarveExposure(MantleWriter writer, int x, int z, int depth, int radius) {
|
||||||
|
int horizontalRadius = Math.max(0, radius);
|
||||||
|
for (int dx = -horizontalRadius; dx <= horizontalRadius; dx++) {
|
||||||
|
for (int dz = -horizontalRadius; dz <= horizontalRadius; dz++) {
|
||||||
|
int columnX = x + dx;
|
||||||
|
int columnZ = z + dz;
|
||||||
|
int surfaceY = getEngineMantle().getEngine().getHeight(columnX, columnZ, true);
|
||||||
|
int fromY = Math.max(1, surfaceY - Math.max(0, depth));
|
||||||
|
int toY = Math.min(getEngineMantle().getEngine().getHeight() - 1, surfaceY + 1);
|
||||||
|
for (int y = fromY; y <= toY; y++) {
|
||||||
|
if (writer.isCarved(columnX, y, columnZ)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isRegenTraceThread() {
|
private boolean isRegenTraceThread() {
|
||||||
return Thread.currentThread().getName().startsWith("Iris-Regen-")
|
return Thread.currentThread().getName().startsWith("Iris-Regen-")
|
||||||
&& IrisSettings.get().getGeneral().isDebug();
|
&& IrisSettings.get().getGeneral().isDebug();
|
||||||
}
|
}
|
||||||
|
|
||||||
private record ObjectPlacementSummary(
|
private record ObjectPlacementSummary(
|
||||||
int biomePlacersChecked,
|
int biomeSurfacePlacersChecked,
|
||||||
int biomePlacersTriggered,
|
int biomeSurfacePlacersTriggered,
|
||||||
int regionPlacersChecked,
|
int biomeCavePlacersChecked,
|
||||||
int regionPlacersTriggered,
|
int biomeCavePlacersTriggered,
|
||||||
|
int regionSurfacePlacersChecked,
|
||||||
|
int regionSurfacePlacersTriggered,
|
||||||
|
int regionCavePlacersChecked,
|
||||||
|
int regionCavePlacersTriggered,
|
||||||
int objectAttempts,
|
int objectAttempts,
|
||||||
int objectPlaced,
|
int objectPlaced,
|
||||||
int objectRejected,
|
int objectRejected,
|
||||||
|
|||||||
@@ -108,6 +108,8 @@ public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
|
|||||||
output.set(rx, yy, rz, context.getFluid().get(rx, rz));
|
output.set(rx, yy, rz, context.getFluid().get(rx, rz));
|
||||||
} else if (c.isLava()) {
|
} else if (c.isLava()) {
|
||||||
output.set(rx, yy, rz, LAVA);
|
output.set(rx, yy, rz, LAVA);
|
||||||
|
} else if (c.getLiquid() == 3) {
|
||||||
|
output.set(rx, yy, rz, AIR);
|
||||||
} else {
|
} else {
|
||||||
if (getEngine().getDimension().getCaveLavaHeight() > yy) {
|
if (getEngine().getDimension().getCaveLavaHeight() > yy) {
|
||||||
output.set(rx, yy, rz, LAVA);
|
output.set(rx, yy, rz, LAVA);
|
||||||
|
|||||||
@@ -102,6 +102,8 @@ public class IrisBiome extends IrisRegistrant implements IRare {
|
|||||||
private int lockLayersMax = 7;
|
private int lockLayersMax = 7;
|
||||||
@Desc("Carving configuration for the dimension")
|
@Desc("Carving configuration for the dimension")
|
||||||
private IrisCarving carving = new IrisCarving();
|
private IrisCarving carving = new IrisCarving();
|
||||||
|
@Desc("Profile-driven 3D cave configuration")
|
||||||
|
private IrisCaveProfile caveProfile = new IrisCaveProfile();
|
||||||
@Desc("Configuration of fluid bodies such as rivers & lakes")
|
@Desc("Configuration of fluid bodies such as rivers & lakes")
|
||||||
private IrisFluidBodies fluidBodies = new IrisFluidBodies();
|
private IrisFluidBodies fluidBodies = new IrisFluidBodies();
|
||||||
@MinNumber(1)
|
@MinNumber(1)
|
||||||
@@ -194,12 +196,21 @@ public class IrisBiome extends IrisRegistrant implements IRare {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public double getGenLinkMax(String loadKey, Engine engine) {
|
public double getGenLinkMax(String loadKey, Engine engine) {
|
||||||
|
if (loadKey == null || loadKey.isBlank()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
Integer v = genCacheMax.aquire(() ->
|
Integer v = genCacheMax.aquire(() ->
|
||||||
{
|
{
|
||||||
KMap<String, Integer> l = new KMap<>();
|
KMap<String, Integer> l = new KMap<>();
|
||||||
|
|
||||||
for (IrisBiomeGeneratorLink i : getGenerators()) {
|
for (IrisBiomeGeneratorLink i : getGenerators()) {
|
||||||
l.put(i.getGenerator(), i.getMax());
|
String generatorKey = i.getGenerator();
|
||||||
|
if (generatorKey == null || generatorKey.isBlank()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
l.put(generatorKey, i.getMax());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,12 +221,21 @@ public class IrisBiome extends IrisRegistrant implements IRare {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public double getGenLinkMin(String loadKey, Engine engine) {
|
public double getGenLinkMin(String loadKey, Engine engine) {
|
||||||
|
if (loadKey == null || loadKey.isBlank()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
Integer v = genCacheMin.aquire(() ->
|
Integer v = genCacheMin.aquire(() ->
|
||||||
{
|
{
|
||||||
KMap<String, Integer> l = new KMap<>();
|
KMap<String, Integer> l = new KMap<>();
|
||||||
|
|
||||||
for (IrisBiomeGeneratorLink i : getGenerators()) {
|
for (IrisBiomeGeneratorLink i : getGenerators()) {
|
||||||
l.put(i.getGenerator(), i.getMin());
|
String generatorKey = i.getGenerator();
|
||||||
|
if (generatorKey == null || generatorKey.isBlank()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
l.put(generatorKey, i.getMin());
|
||||||
}
|
}
|
||||||
|
|
||||||
return l;
|
return l;
|
||||||
@@ -225,12 +245,21 @@ public class IrisBiome extends IrisRegistrant implements IRare {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public IrisBiomeGeneratorLink getGenLink(String loadKey) {
|
public IrisBiomeGeneratorLink getGenLink(String loadKey) {
|
||||||
|
if (loadKey == null || loadKey.isBlank()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return genCache.aquire(() ->
|
return genCache.aquire(() ->
|
||||||
{
|
{
|
||||||
KMap<String, IrisBiomeGeneratorLink> l = new KMap<>();
|
KMap<String, IrisBiomeGeneratorLink> l = new KMap<>();
|
||||||
|
|
||||||
for (IrisBiomeGeneratorLink i : getGenerators()) {
|
for (IrisBiomeGeneratorLink i : getGenerators()) {
|
||||||
l.put(i.getGenerator(), i);
|
String generatorKey = i.getGenerator();
|
||||||
|
if (generatorKey == null || generatorKey.isBlank()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
l.put(generatorKey, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
return l;
|
return l;
|
||||||
|
|||||||
@@ -52,12 +52,21 @@ public class IrisBiomeGeneratorLink {
|
|||||||
|
|
||||||
public IrisGenerator getCachedGenerator(DataProvider g) {
|
public IrisGenerator getCachedGenerator(DataProvider g) {
|
||||||
return gen.aquire(() -> {
|
return gen.aquire(() -> {
|
||||||
IrisGenerator gen = g.getData().getGeneratorLoader().load(getGenerator());
|
String generatorKey = getGenerator();
|
||||||
|
if (generatorKey == null || generatorKey.isBlank()) {
|
||||||
|
generatorKey = "default";
|
||||||
|
}
|
||||||
|
|
||||||
|
IrisGenerator gen = g.getData().getGeneratorLoader().load(generatorKey);
|
||||||
|
|
||||||
if (gen == null) {
|
if (gen == null) {
|
||||||
gen = new IrisGenerator();
|
gen = new IrisGenerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gen.getLoadKey() == null || gen.getLoadKey().isBlank()) {
|
||||||
|
gen.setLoadKey(generatorKey);
|
||||||
|
}
|
||||||
|
|
||||||
return gen;
|
return gen;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package art.arcane.iris.engine.object;
|
||||||
|
|
||||||
|
import art.arcane.iris.engine.object.annotations.Desc;
|
||||||
|
|
||||||
|
@Desc("Defines which carved-space anchor to target for cave object placement.")
|
||||||
|
public enum IrisCaveAnchorMode {
|
||||||
|
@Desc("Use the active cave profile default anchor mode.")
|
||||||
|
PROFILE_DEFAULT,
|
||||||
|
|
||||||
|
@Desc("Target cave floor anchors where carved space has solid support below.")
|
||||||
|
FLOOR,
|
||||||
|
|
||||||
|
@Desc("Target cave ceiling anchors where carved space has solid support above.")
|
||||||
|
CEILING,
|
||||||
|
|
||||||
|
@Desc("Target carved positions with no immediate solid support above or below.")
|
||||||
|
CENTER,
|
||||||
|
|
||||||
|
@Desc("Target any carved-space anchor.")
|
||||||
|
ANY
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package art.arcane.iris.engine.object;
|
||||||
|
|
||||||
|
import art.arcane.iris.engine.object.annotations.Desc;
|
||||||
|
import art.arcane.iris.engine.object.annotations.MaxNumber;
|
||||||
|
import art.arcane.iris.engine.object.annotations.MinNumber;
|
||||||
|
import art.arcane.iris.engine.object.annotations.Snippet;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
@Snippet("cave-field-module")
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Desc("Represents a modular cave-density layer.")
|
||||||
|
@Data
|
||||||
|
public class IrisCaveFieldModule {
|
||||||
|
@Desc("Density style used by this module.")
|
||||||
|
private IrisGeneratorStyle style = NoiseStyle.CELLULAR_IRIS_DOUBLE.style();
|
||||||
|
|
||||||
|
@MinNumber(0)
|
||||||
|
@Desc("Layer contribution multiplier.")
|
||||||
|
private double weight = 1;
|
||||||
|
|
||||||
|
@MinNumber(-1)
|
||||||
|
@MaxNumber(1)
|
||||||
|
@Desc("Threshold offset applied to this layer before blending.")
|
||||||
|
private double threshold = 0;
|
||||||
|
|
||||||
|
@Desc("Vertical bounds where this module can contribute.")
|
||||||
|
private IrisRange verticalRange = new IrisRange(0, 384);
|
||||||
|
|
||||||
|
@Desc("Invert this module before weighting.")
|
||||||
|
private boolean invert = false;
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
package art.arcane.iris.engine.object;
|
||||||
|
|
||||||
|
import art.arcane.iris.engine.object.annotations.ArrayType;
|
||||||
|
import art.arcane.iris.engine.object.annotations.Desc;
|
||||||
|
import art.arcane.iris.engine.object.annotations.MaxNumber;
|
||||||
|
import art.arcane.iris.engine.object.annotations.MinNumber;
|
||||||
|
import art.arcane.iris.engine.object.annotations.Snippet;
|
||||||
|
import art.arcane.volmlib.util.collection.KList;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
@Snippet("cave-profile")
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Desc("Represents a configurable 3D cave profile.")
|
||||||
|
@Data
|
||||||
|
public class IrisCaveProfile {
|
||||||
|
@Desc("Enable profile-driven cave carving.")
|
||||||
|
private boolean enabled = false;
|
||||||
|
|
||||||
|
@Desc("Global vertical bounds for profile cave carving.")
|
||||||
|
private IrisRange verticalRange = new IrisRange(0, 384);
|
||||||
|
|
||||||
|
@Desc("Base density style for cave field generation.")
|
||||||
|
private IrisGeneratorStyle baseDensityStyle = NoiseStyle.CELLULAR_IRIS_DOUBLE.style();
|
||||||
|
|
||||||
|
@Desc("Detail density style blended into base caves.")
|
||||||
|
private IrisGeneratorStyle detailDensityStyle = new IrisGeneratorStyle(NoiseStyle.SIMPLEX);
|
||||||
|
|
||||||
|
@Desc("Warp style used to distort cave coordinates.")
|
||||||
|
private IrisGeneratorStyle warpStyle = new IrisGeneratorStyle(NoiseStyle.FLAT);
|
||||||
|
|
||||||
|
@MinNumber(0)
|
||||||
|
@Desc("Base cave field multiplier.")
|
||||||
|
private double baseWeight = 1;
|
||||||
|
|
||||||
|
@MinNumber(0)
|
||||||
|
@Desc("Detail cave field multiplier.")
|
||||||
|
private double detailWeight = 0.35;
|
||||||
|
|
||||||
|
@MinNumber(0)
|
||||||
|
@Desc("Coordinate warp strength for cave fields.")
|
||||||
|
private double warpStrength = 0;
|
||||||
|
|
||||||
|
@Desc("Threshold range used for carve cutoff decisions.")
|
||||||
|
private IrisStyledRange densityThreshold = new IrisStyledRange(-0.2, 0.2, NoiseStyle.CELLULAR_IRIS_DOUBLE.style());
|
||||||
|
|
||||||
|
@MinNumber(0)
|
||||||
|
@MaxNumber(1)
|
||||||
|
@Desc("Extra threshold bias subtracted from sampled threshold before carve tests.")
|
||||||
|
private double thresholdBias = 0.16;
|
||||||
|
|
||||||
|
@MinNumber(1)
|
||||||
|
@MaxNumber(8)
|
||||||
|
@Desc("Vertical sample step used while evaluating cave density.")
|
||||||
|
private int sampleStep = 2;
|
||||||
|
|
||||||
|
@MinNumber(0)
|
||||||
|
@MaxNumber(64)
|
||||||
|
@Desc("Minimum solid clearance below terrain surface where carving may occur.")
|
||||||
|
private int surfaceClearance = 4;
|
||||||
|
|
||||||
|
@Desc("Allow profile-driven cave carving to break through terrain surface in selected columns.")
|
||||||
|
private boolean allowSurfaceBreak = true;
|
||||||
|
|
||||||
|
@Desc("Noise style used to decide where surface-breaking cave columns are allowed.")
|
||||||
|
private IrisGeneratorStyle surfaceBreakStyle = new IrisGeneratorStyle(NoiseStyle.SIMPLEX).zoomed(0.08);
|
||||||
|
|
||||||
|
@MinNumber(-1)
|
||||||
|
@MaxNumber(1)
|
||||||
|
@Desc("Minimum signed surface-break noise value required before near-surface carving is allowed.")
|
||||||
|
private double surfaceBreakNoiseThreshold = 0.62;
|
||||||
|
|
||||||
|
@MinNumber(0)
|
||||||
|
@MaxNumber(64)
|
||||||
|
@Desc("Near-surface depth window used for surface-break carve logic.")
|
||||||
|
private int surfaceBreakDepth = 18;
|
||||||
|
|
||||||
|
@MinNumber(0)
|
||||||
|
@MaxNumber(1)
|
||||||
|
@Desc("Additional threshold boost applied while carving in the surface-break depth window.")
|
||||||
|
private double surfaceBreakThresholdBoost = 0.2;
|
||||||
|
|
||||||
|
@MinNumber(0)
|
||||||
|
@MaxNumber(64)
|
||||||
|
@Desc("Minimum depth below terrain surface required for cave-only object anchor placement.")
|
||||||
|
private int objectMinDepthBelowSurface = 6;
|
||||||
|
|
||||||
|
@MinNumber(0)
|
||||||
|
@MaxNumber(32)
|
||||||
|
@Desc("Skip surface-object placement when carved cells exist this many blocks below terrain surface.")
|
||||||
|
private int surfaceObjectExclusionDepth = 5;
|
||||||
|
|
||||||
|
@ArrayType(type = IrisCaveFieldModule.class, min = 1)
|
||||||
|
@Desc("Additional layered cave-density modules.")
|
||||||
|
private KList<IrisCaveFieldModule> modules = new KList<>();
|
||||||
|
|
||||||
|
@Desc("Default cave anchor mode for cave-only object placement.")
|
||||||
|
private IrisCaveAnchorMode defaultObjectAnchor = IrisCaveAnchorMode.FLOOR;
|
||||||
|
|
||||||
|
@MinNumber(1)
|
||||||
|
@MaxNumber(8)
|
||||||
|
@Desc("Vertical scan step used while searching cave anchors.")
|
||||||
|
private int anchorScanStep = 1;
|
||||||
|
|
||||||
|
@MinNumber(1)
|
||||||
|
@MaxNumber(64)
|
||||||
|
@Desc("Maximum random column retries while searching a valid cave object anchor in the chunk.")
|
||||||
|
private int anchorSearchAttempts = 6;
|
||||||
|
|
||||||
|
@Desc("Allow cave water placement below fluid level.")
|
||||||
|
private boolean allowWater = true;
|
||||||
|
|
||||||
|
@MinNumber(0)
|
||||||
|
@MaxNumber(64)
|
||||||
|
@Desc("Minimum depth below terrain surface required before cave water may be placed.")
|
||||||
|
private int waterMinDepthBelowSurface = 12;
|
||||||
|
|
||||||
|
@Desc("Require solid floor support below cave water to reduce cascading cave waterfalls.")
|
||||||
|
private boolean waterRequiresFloor = true;
|
||||||
|
|
||||||
|
@Desc("Allow cave lava placement based on lava height.")
|
||||||
|
private boolean allowLava = true;
|
||||||
|
}
|
||||||
@@ -144,6 +144,8 @@ public class IrisDimension extends IrisRegistrant {
|
|||||||
private boolean postProcessingWalls = true;
|
private boolean postProcessingWalls = true;
|
||||||
@Desc("Carving configuration for the dimension")
|
@Desc("Carving configuration for the dimension")
|
||||||
private IrisCarving carving = new IrisCarving();
|
private IrisCarving carving = new IrisCarving();
|
||||||
|
@Desc("Profile-driven 3D cave configuration")
|
||||||
|
private IrisCaveProfile caveProfile = new IrisCaveProfile();
|
||||||
@Desc("Configuration of fluid bodies such as rivers & lakes")
|
@Desc("Configuration of fluid bodies such as rivers & lakes")
|
||||||
private IrisFluidBodies fluidBodies = new IrisFluidBodies();
|
private IrisFluidBodies fluidBodies = new IrisFluidBodies();
|
||||||
@Desc("forceConvertTo320Height")
|
@Desc("forceConvertTo320Height")
|
||||||
|
|||||||
@@ -689,10 +689,7 @@ public class IrisObject extends IrisRegistrant {
|
|||||||
// todo Convert this to a dedicated mode.
|
// todo Convert this to a dedicated mode.
|
||||||
y = (getH() + 1) + rty;
|
y = (getH() + 1) + rty;
|
||||||
if (!config.isForcePlace()) {
|
if (!config.isForcePlace()) {
|
||||||
if (placer.isCarved(x, y, z) ||
|
if (shouldBailForCarvingAnchor(placer, config, x, y, z)) {
|
||||||
placer.isCarved(x, y - 1, z) ||
|
|
||||||
placer.isCarved(x, y - 2, z) ||
|
|
||||||
placer.isCarved(x, y - 3, z)) {
|
|
||||||
bail = true;
|
bail = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -700,7 +697,7 @@ public class IrisObject extends IrisRegistrant {
|
|||||||
if (config.getMode().equals(ObjectPlaceMode.CENTER_HEIGHT) || config.getMode() == ObjectPlaceMode.CENTER_STILT) {
|
if (config.getMode().equals(ObjectPlaceMode.CENTER_HEIGHT) || config.getMode() == ObjectPlaceMode.CENTER_STILT) {
|
||||||
y = (c != null ? c.getSurface() : placer.getHighest(x, z, getLoader(), config.isUnderwater())) + rty;
|
y = (c != null ? c.getSurface() : placer.getHighest(x, z, getLoader(), config.isUnderwater())) + rty;
|
||||||
if (!config.isForcePlace()) {
|
if (!config.isForcePlace()) {
|
||||||
if (placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z)) {
|
if (shouldBailForCarvingAnchor(placer, config, x, y, z)) {
|
||||||
bail = true;
|
bail = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -717,7 +714,7 @@ public class IrisObject extends IrisRegistrant {
|
|||||||
for (int ii = minZ; ii <= maxZ; ii++) {
|
for (int ii = minZ; ii <= maxZ; ii++) {
|
||||||
int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty;
|
int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty;
|
||||||
if (!config.isForcePlace()) {
|
if (!config.isForcePlace()) {
|
||||||
if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) {
|
if (shouldBailForCarvingAnchor(placer, config, i, h, ii)) {
|
||||||
bail = true;
|
bail = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -743,7 +740,7 @@ public class IrisObject extends IrisRegistrant {
|
|||||||
for (int ii = minZ; ii <= maxZ; ii += Math.abs(zRadius) + 1) {
|
for (int ii = minZ; ii <= maxZ; ii += Math.abs(zRadius) + 1) {
|
||||||
int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty;
|
int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty;
|
||||||
if (!config.isForcePlace()) {
|
if (!config.isForcePlace()) {
|
||||||
if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) {
|
if (shouldBailForCarvingAnchor(placer, config, i, h, ii)) {
|
||||||
bail = true;
|
bail = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -767,7 +764,7 @@ public class IrisObject extends IrisRegistrant {
|
|||||||
for (int ii = minZ; ii <= maxZ; ii++) {
|
for (int ii = minZ; ii <= maxZ; ii++) {
|
||||||
int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty;
|
int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty;
|
||||||
if (!config.isForcePlace()) {
|
if (!config.isForcePlace()) {
|
||||||
if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) {
|
if (shouldBailForCarvingAnchor(placer, config, i, h, ii)) {
|
||||||
bail = true;
|
bail = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -795,7 +792,7 @@ public class IrisObject extends IrisRegistrant {
|
|||||||
for (int ii = minZ; ii <= maxZ; ii += Math.abs(zRadius) + 1) {
|
for (int ii = minZ; ii <= maxZ; ii += Math.abs(zRadius) + 1) {
|
||||||
int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty;
|
int h = placer.getHighest(i, ii, getLoader(), config.isUnderwater()) + rty;
|
||||||
if (!config.isForcePlace()) {
|
if (!config.isForcePlace()) {
|
||||||
if (placer.isCarved(i, h, ii) || placer.isCarved(i, h - 1, ii) || placer.isCarved(i, h - 2, ii) || placer.isCarved(i, h - 3, ii)) {
|
if (shouldBailForCarvingAnchor(placer, config, i, h, ii)) {
|
||||||
bail = true;
|
bail = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -808,7 +805,7 @@ public class IrisObject extends IrisRegistrant {
|
|||||||
} else if (config.getMode().equals(ObjectPlaceMode.PAINT)) {
|
} else if (config.getMode().equals(ObjectPlaceMode.PAINT)) {
|
||||||
y = placer.getHighest(x, z, getLoader(), config.isUnderwater()) + rty;
|
y = placer.getHighest(x, z, getLoader(), config.isUnderwater()) + rty;
|
||||||
if (!config.isForcePlace()) {
|
if (!config.isForcePlace()) {
|
||||||
if (placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z)) {
|
if (shouldBailForCarvingAnchor(placer, config, x, y, z)) {
|
||||||
bail = true;
|
bail = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -816,7 +813,7 @@ public class IrisObject extends IrisRegistrant {
|
|||||||
} else {
|
} else {
|
||||||
y = yv;
|
y = yv;
|
||||||
if (!config.isForcePlace()) {
|
if (!config.isForcePlace()) {
|
||||||
if (placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z)) {
|
if (shouldBailForCarvingAnchor(placer, config, x, y, z)) {
|
||||||
bail = true;
|
bail = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -825,7 +822,7 @@ public class IrisObject extends IrisRegistrant {
|
|||||||
if (yv >= 0 && config.isBottom()) {
|
if (yv >= 0 && config.isBottom()) {
|
||||||
y += Math.floorDiv(h, 2);
|
y += Math.floorDiv(h, 2);
|
||||||
if (!config.isForcePlace()) {
|
if (!config.isForcePlace()) {
|
||||||
bail = placer.isCarved(x, y, z) || placer.isCarved(x, y - 1, z) || placer.isCarved(x, y - 2, z) || placer.isCarved(x, y - 3, z);
|
bail = shouldBailForCarvingAnchor(placer, config, x, y, z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1160,6 +1157,23 @@ public class IrisObject extends IrisRegistrant {
|
|||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean shouldBailForCarvingAnchor(IObjectPlacer placer, IrisObjectPlacement placement, int x, int y, int z) {
|
||||||
|
boolean carved = isCarvedAnchor(placer, x, y, z);
|
||||||
|
CarvingMode carvingMode = placement.getCarvingSupport();
|
||||||
|
return switch (carvingMode) {
|
||||||
|
case SURFACE_ONLY -> carved;
|
||||||
|
case CARVING_ONLY -> !carved;
|
||||||
|
case ANYWHERE -> false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCarvedAnchor(IObjectPlacer placer, int x, int y, int z) {
|
||||||
|
return placer.isCarved(x, y, z)
|
||||||
|
|| placer.isCarved(x, y - 1, z)
|
||||||
|
|| placer.isCarved(x, y - 2, z)
|
||||||
|
|| placer.isCarved(x, y - 3, z);
|
||||||
|
}
|
||||||
|
|
||||||
public IrisObject rotateCopy(IrisObjectRotation rt) {
|
public IrisObject rotateCopy(IrisObjectRotation rt) {
|
||||||
IrisObject copy = copy();
|
IrisObject copy = copy();
|
||||||
copy.rotate(rt, 0, 0, 0);
|
copy.rotate(rt, 0, 0, 0);
|
||||||
|
|||||||
@@ -99,6 +99,8 @@ public class IrisObjectPlacement {
|
|||||||
private boolean underwater = false;
|
private boolean underwater = false;
|
||||||
@Desc("If set to true, objects will place in carvings (such as underground) or under an overhang.")
|
@Desc("If set to true, objects will place in carvings (such as underground) or under an overhang.")
|
||||||
private CarvingMode carvingSupport = CarvingMode.SURFACE_ONLY;
|
private CarvingMode carvingSupport = CarvingMode.SURFACE_ONLY;
|
||||||
|
@Desc("When carving placement is enabled, select which carved-space anchor this placement targets.")
|
||||||
|
private IrisCaveAnchorMode caveAnchorMode = IrisCaveAnchorMode.PROFILE_DEFAULT;
|
||||||
@Desc("If this is defined, this object wont place on the terrain heightmap, but instead on this virtual heightmap")
|
@Desc("If this is defined, this object wont place on the terrain heightmap, but instead on this virtual heightmap")
|
||||||
private IrisNoiseGenerator heightmap;
|
private IrisNoiseGenerator heightmap;
|
||||||
@Desc("If set to true, Iris will try to fill the insides of 'rooms' and 'pockets' where air should fit based off of raytrace checks. This prevents a village house placing in an area where a tree already exists, and instead replaces the parts of the tree where the interior of the structure is. \n\nThis operation does not affect warmed-up generation speed however it does slow down loading objects.")
|
@Desc("If set to true, Iris will try to fill the insides of 'rooms' and 'pockets' where air should fit based off of raytrace checks. This prevents a village house placing in an area where a tree already exists, and instead replaces the parts of the tree where the interior of the structure is. \n\nThis operation does not affect warmed-up generation speed however it does slow down loading objects.")
|
||||||
@@ -165,6 +167,7 @@ public class IrisObjectPlacement {
|
|||||||
p.setOnwater(onwater);
|
p.setOnwater(onwater);
|
||||||
p.setSmartBore(smartBore);
|
p.setSmartBore(smartBore);
|
||||||
p.setCarvingSupport(carvingSupport);
|
p.setCarvingSupport(carvingSupport);
|
||||||
|
p.setCaveAnchorMode(caveAnchorMode);
|
||||||
p.setUnderwater(underwater);
|
p.setUnderwater(underwater);
|
||||||
p.setBoreExtendMaxY(boreExtendMaxY);
|
p.setBoreExtendMaxY(boreExtendMaxY);
|
||||||
p.setBoreExtendMinY(boreExtendMinY);
|
p.setBoreExtendMinY(boreExtendMinY);
|
||||||
|
|||||||
@@ -114,6 +114,8 @@ public class IrisRegion extends IrisRegistrant implements IRare {
|
|||||||
private double caveBiomeZoom = 1;
|
private double caveBiomeZoom = 1;
|
||||||
@Desc("Carving configuration for the dimension")
|
@Desc("Carving configuration for the dimension")
|
||||||
private IrisCarving carving = new IrisCarving();
|
private IrisCarving carving = new IrisCarving();
|
||||||
|
@Desc("Profile-driven 3D cave configuration")
|
||||||
|
private IrisCaveProfile caveProfile = new IrisCaveProfile();
|
||||||
@Desc("Configuration of fluid bodies such as rivers & lakes")
|
@Desc("Configuration of fluid bodies such as rivers & lakes")
|
||||||
private IrisFluidBodies fluidBodies = new IrisFluidBodies();
|
private IrisFluidBodies fluidBodies = new IrisFluidBodies();
|
||||||
@RegistryListResource(IrisBiome.class)
|
@RegistryListResource(IrisBiome.class)
|
||||||
|
|||||||
+12
-3
@@ -203,8 +203,15 @@ public class CustomBiomeSource extends BiomeSource {
|
|||||||
|
|
||||||
int blockX = x << 2;
|
int blockX = x << 2;
|
||||||
int blockZ = z << 2;
|
int blockZ = z << 2;
|
||||||
int blockY = (y - engine.getMinHeight()) << 2;
|
int blockY = y << 2;
|
||||||
IrisBiome irisBiome = engine.getComplex().getTrueBiomeStream().get(blockX, blockZ);
|
int surfaceY = engine.getComplex().getHeightStream().get(blockX, blockZ).intValue();
|
||||||
|
boolean underground = blockY <= surfaceY - 2;
|
||||||
|
IrisBiome irisBiome = underground
|
||||||
|
? engine.getComplex().getCaveBiomeStream().get(blockX, blockZ)
|
||||||
|
: engine.getComplex().getTrueBiomeStream().get(blockX, blockZ);
|
||||||
|
if (irisBiome == null && underground) {
|
||||||
|
irisBiome = engine.getComplex().getTrueBiomeStream().get(blockX, blockZ);
|
||||||
|
}
|
||||||
if (irisBiome == null) {
|
if (irisBiome == null) {
|
||||||
return getFallbackBiome();
|
return getFallbackBiome();
|
||||||
}
|
}
|
||||||
@@ -226,7 +233,9 @@ public class CustomBiomeSource extends BiomeSource {
|
|||||||
return getFallbackBiome();
|
return getFallbackBiome();
|
||||||
}
|
}
|
||||||
|
|
||||||
org.bukkit.block.Biome vanillaBiome = irisBiome.getSkyBiome(noiseRng, blockX, blockY, blockZ);
|
org.bukkit.block.Biome vanillaBiome = underground
|
||||||
|
? irisBiome.getGroundBiome(noiseRng, blockX, blockY, blockZ)
|
||||||
|
: irisBiome.getSkyBiome(noiseRng, blockX, blockY, blockZ);
|
||||||
Holder<Biome> holder = NMSBinding.biomeToBiomeBase(biomeRegistry, vanillaBiome);
|
Holder<Biome> holder = NMSBinding.biomeToBiomeBase(biomeRegistry, vanillaBiome);
|
||||||
if (holder != null) {
|
if (holder != null) {
|
||||||
return holder;
|
return holder;
|
||||||
|
|||||||
Reference in New Issue
Block a user