Merge pull request #1213 from VolmitSoftware/feat/caves

Feat/caves
This commit is contained in:
Julian Krings 2025-07-19 17:59:32 +02:00 committed by GitHub
commit 1c41dc69ce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 185 additions and 42 deletions

View File

@ -35,6 +35,7 @@ import com.volmit.iris.util.mantle.Mantle;
import com.volmit.iris.util.mantle.MantleChunk;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.matter.Matter;
import com.volmit.iris.util.noise.CNG;
import lombok.Data;
import org.bukkit.block.data.BlockData;
import org.bukkit.util.Vector;
@ -71,6 +72,7 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
private static Set<IrisPosition> getBallooned(Set<IrisPosition> vset, double radius) {
Set<IrisPosition> returnset = new HashSet<>();
int ceilrad = (int) Math.ceil(radius);
double r2 = Math.pow(radius, 2);
for (IrisPosition v : vset) {
int tipx = v.getX();
@ -80,7 +82,7 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
for (int loopx = tipx - ceilrad; loopx <= tipx + ceilrad; loopx++) {
for (int loopy = tipy - ceilrad; loopy <= tipy + ceilrad; loopy++) {
for (int loopz = tipz - ceilrad; loopz <= tipz + ceilrad; loopz++) {
if (hypot(loopx - tipx, loopy - tipy, loopz - tipz) <= radius) {
if (hypot(loopx - tipx, loopy - tipy, loopz - tipz) <= r2) {
returnset.add(new IrisPosition(loopx, loopy, loopz));
}
}
@ -113,7 +115,7 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
for (double d : pars) {
sum += Math.pow(d, 2);
}
return Math.sqrt(sum);
return sum;
}
private static double lengthSq(double x, double y, double z) {
@ -453,6 +455,62 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
* @param <T> the type of data to apply to the mantle
*/
public <T> void setLineConsumer(List<IrisPosition> vectors, double radius, boolean filled, Function3<Integer, Integer, Integer, T> data) {
Set<IrisPosition> vset = cleanup(vectors);
vset = getBallooned(vset, radius);
if (!filled) {
vset = getHollowed(vset);
}
setConsumer(vset, data);
}
/**
* Set lines for points
*
* @param vectors the points
* @param radius the radius
* @param filled hollow or filled?
* @param data the data to set
* @param <T> the type of data to apply to the mantle
*/
public <T> void setNoiseMasked(List<IrisPosition> vectors, double radius, double threshold, CNG shape, Set<IrisPosition> masks, boolean filled, Function3<Integer, Integer, Integer, T> data) {
Set<IrisPosition> vset = cleanup(vectors);
vset = masks == null ? getBallooned(vset, radius) : getMasked(vset, masks, radius);
vset.removeIf(p -> shape.noise(p.getX(), p.getY(), p.getZ()) < threshold);
if (!filled) {
vset = getHollowed(vset);
}
setConsumer(vset, data);
}
private static Set<IrisPosition> getMasked(Set<IrisPosition> vectors, Set<IrisPosition> masks, double radius) {
Set<IrisPosition> vset = new KSet<>();
int ceil = (int) Math.ceil(radius);
double r2 = Math.pow(radius, 2);
for (IrisPosition v : vectors) {
int tipX = v.getX();
int tipY = v.getY();
int tipZ = v.getZ();
for (int x = -ceil; x <= ceil; x++) {
for (int y = -ceil; y <= ceil; y++) {
for (int z = -ceil; z <= ceil; z++) {
if (hypot(x, y, z) > r2 || !masks.contains(new IrisPosition(x, y, z)))
continue;
vset.add(new IrisPosition(tipX + x, tipY + y, tipZ + z));
}
}
}
}
return vset;
}
private static Set<IrisPosition> cleanup(List<IrisPosition> vectors) {
Set<IrisPosition> vset = new KSet<>();
for (int i = 0; vectors.size() != 0 && i < vectors.size() - 1; i++) {
@ -504,13 +562,7 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
}
}
vset = getBallooned(vset, radius);
if (!filled) {
vset = getHollowed(vset);
}
setConsumer(vset, data);
return vset;
}
/**

View File

@ -62,31 +62,31 @@ public class IrisCarving {
@BlockCoordinates
public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int depth) {
doCarving(writer, rng, engine, x, y, z, depth, -1);
doCarving(writer, rng, new RNG(engine.getSeedManager().getCarve()), engine, x, y, z, depth, -1);
}
@BlockCoordinates
public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) {
public void doCarving(MantleWriter writer, RNG rng, RNG base, Engine engine, int x, int y, int z, int recursion, int waterHint) {
int nextRecursion = recursion + 1;
if (caves.isNotEmpty()) {
for (IrisCavePlacer i : caves) {
if (recursion > i.getMaxRecursion()) continue;
i.generateCave(writer, rng, engine, x, y, z, nextRecursion, waterHint);
i.generateCave(writer, rng, base, engine, x, y, z, nextRecursion, waterHint);
}
}
if (ravines.isNotEmpty()) {
for (IrisRavinePlacer i : ravines) {
if (recursion > i.getMaxRecursion()) continue;
i.generateRavine(writer, rng, engine, x, y, z, nextRecursion, waterHint);
i.generateRavine(writer, rng, base, engine, x, y, z, nextRecursion, waterHint);
}
}
if (spheres.isNotEmpty()) {
for (IrisSphere i : spheres) {
if (rng.nextInt(i.getRarity()) == 0) {
i.generate(rng, engine, writer, x, y, z);
i.generate(base, engine, writer, x, y, z);
}
}
}
@ -94,7 +94,7 @@ public class IrisCarving {
if (elipsoids.isNotEmpty()) {
for (IrisElipsoid i : elipsoids) {
if (rng.nextInt(i.getRarity()) == 0) {
i.generate(rng, engine, writer, x, y, z);
i.generate(base, engine, writer, x, y, z);
}
}
}
@ -102,7 +102,7 @@ public class IrisCarving {
if (pyramids.isNotEmpty()) {
for (IrisPyramid i : pyramids) {
if (rng.nextInt(i.getRarity()) == 0) {
i.generate(rng, engine, writer, x, y, z);
i.generate(base, engine, writer, x, y, z);
}
}
}

View File

@ -25,9 +25,11 @@ import com.volmit.iris.engine.mantle.MantleWriter;
import com.volmit.iris.engine.object.annotations.Desc;
import com.volmit.iris.engine.object.annotations.RegistryListResource;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.matter.MatterCavern;
import com.volmit.iris.util.noise.CNG;
import com.volmit.iris.util.plugin.VolmitSender;
import lombok.AllArgsConstructor;
import lombok.Data;
@ -55,6 +57,9 @@ public class IrisCave extends IrisRegistrant {
@Desc("Limit the worm from ever getting higher or lower than this range")
private IrisRange verticalRange = new IrisRange(3, 255);
@Desc("Shape of the caves")
private IrisCaveShape shape = new IrisCaveShape();
@Override
public String getFolderName() {
return "caves";
@ -66,12 +71,12 @@ public class IrisCave extends IrisRegistrant {
}
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) {
generate(writer, rng, engine, x, y, z, 0, -1, true);
generate(writer, rng, new RNG(engine.getSeedManager().getCarve()), engine, x, y, z, 0, -1, true);
}
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint, boolean breakSurface) {
double girth = getWorm().getGirth().get(rng, x, z, engine.getData());
KList<IrisPosition> points = getWorm().generate(rng, engine.getData(), writer, verticalRange, x, y, z, breakSurface, girth + 9);
public void generate(MantleWriter writer, RNG rng, RNG base, Engine engine, int x, int y, int z, int recursion, int waterHint, boolean breakSurface) {
double girth = getWorm().getGirth().get(base.nextParallelRNG(465156), x, z, engine.getData());
KList<IrisPosition> points = getWorm().generate(base.nextParallelRNG(784684), engine.getData(), writer, verticalRange, x, y, z, breakSurface, girth + 9);
int highestWater = Math.max(waterHint, -1);
if (highestWater == -1) {
@ -87,17 +92,19 @@ public class IrisCave extends IrisRegistrant {
}
int h = Math.min(Math.max(highestWater, waterHint), engine.getDimension().getFluidHeight());
int h = Math.min(highestWater, engine.getDimension().getFluidHeight());
for (IrisPosition i : points) {
fork.doCarving(writer, rng, engine, i.getX(), i.getY(), i.getZ(), recursion, h);
fork.doCarving(writer, rng, base, engine, i.getX(), i.getY(), i.getZ(), recursion, h);
}
MatterCavern c = new MatterCavern(true, customBiome, (byte) 0);
MatterCavern w = new MatterCavern(true, customBiome, (byte) 1);
writer.setLineConsumer(points,
girth, true,
CNG cng = shape.getNoise(base.nextParallelRNG(8131545), engine);
KSet<IrisPosition> mask = shape.getMasked(rng, engine);
writer.setNoiseMasked(points,
girth, cng.noise(x, y, z), cng, mask, true,
(xf, yf, zf) -> yf <= h ? w : c);
}
@ -107,6 +114,6 @@ public class IrisCave extends IrisRegistrant {
}
public int getMaxSize(IrisData data, int depth) {
return getWorm().getMaxDistance() + fork.getMaxRange(data, depth);
return (int) (Math.ceil(getWorm().getGirth().getMax() * 2) + getWorm().getMaxDistance() + fork.getMaxRange(data, depth));
}
}

View File

@ -64,10 +64,10 @@ public class IrisCavePlacer implements IRare {
}
public void generateCave(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z) {
generateCave(mantle, rng, engine, x, y, z, 0, -1);
generateCave(mantle, rng, new RNG(engine.getSeedManager().getCarve()), engine, x, y, z, 0, -1);
}
public void generateCave(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) {
public void generateCave(MantleWriter mantle, RNG rng, RNG base, Engine engine, int x, int y, int z, int recursion, int waterHint) {
if (fail.get()) {
return;
}
@ -86,13 +86,13 @@ public class IrisCavePlacer implements IRare {
}
if (y == -1) {
int h = (int) caveStartHeight.get(rng, x, z, data);
int h = (int) caveStartHeight.get(base, x, z, data);
int ma = breakSurface ? h : (int) (engine.getComplex().getHeightStream().get(x, z) - 9);
y = Math.min(h, ma);
}
try {
cave.generate(mantle, rng, engine, x + rng.nextInt(15), y, z + rng.nextInt(15), recursion, waterHint, breakSurface);
cave.generate(mantle, rng, base, engine, x + rng.nextInt(15), y, z + rng.nextInt(15), recursion, waterHint, breakSurface);
} catch (Throwable e) {
e.printStackTrace();
fail.set(true);

View File

@ -0,0 +1,76 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.annotations.Desc;
import com.volmit.iris.engine.object.annotations.RegistryListResource;
import com.volmit.iris.engine.object.annotations.Snippet;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.noise.CNG;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Snippet("cave-shape")
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@Desc("Cave Shape")
@Data
public class IrisCaveShape {
private transient final KMap<IrisPosition, KSet<IrisPosition>> cache = new KMap<>();
@Desc("Noise used for the shape of the cave")
private IrisGeneratorStyle noise = new IrisGeneratorStyle();
@RegistryListResource(IrisObject.class)
@Desc("Object used as mask for the shape of the cave")
private String object = null;
@Desc("Rotation to apply to objects before using them as mask")
private IrisObjectRotation objectRotation = new IrisObjectRotation();
public CNG getNoise(RNG rng, Engine engine) {
return noise.create(rng, engine.getData());
}
public KSet<IrisPosition> getMasked(RNG rng, Engine engine) {
if (object == null) return null;
return cache.computeIfAbsent(randomRotation(rng), pos -> {
var rotated = new KSet<IrisPosition>();
engine.getData().getObjectLoader().load(object).getBlocks().forEach((vector, data) -> {
if (data.getMaterial().isAir()) return;
rotated.add(new IrisPosition(objectRotation.rotate(vector, pos.getX(), pos.getY(), pos.getZ())));
});
return rotated;
});
}
private IrisPosition randomRotation(RNG rng) {
if (objectRotation == null || !objectRotation.canRotate())
return new IrisPosition(0,0,0);
return new IrisPosition(
randomDegree(rng, objectRotation.getXAxis()),
randomDegree(rng, objectRotation.getYAxis()),
randomDegree(rng, objectRotation.getZAxis())
);
}
private int randomDegree(RNG rng, IrisAxisRotationClamp clamp) {
if (!clamp.isEnabled()) return 0;
if (clamp.isLocked()) return (int) clamp.getMax();
double interval = clamp.getInterval();
if (interval < 1) interval = 1;
double min = clamp.getMin(), max = clamp.getMax();
double value = (interval * (Math.ceil(Math.abs(rng.d(0, 360) / interval)))) % 360D;
if (clamp.isUnlimited()) return (int) value;
if (min > max) {
max = clamp.getMin();
min = clamp.getMax();
}
return (int) (double) M.clip(value, min, max);
}
}

View File

@ -93,13 +93,13 @@ public class IrisRavine extends IrisRegistrant {
}
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) {
generate(writer, rng, engine, x, y, z, 0, -1);
generate(writer, rng, new RNG(engine.getSeedManager().getCarve()), engine, x, y, z, 0, -1);
}
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) {
KList<IrisPosition> pos = getWorm().generate(rng, engine.getData(), writer, null, x, y, z, true, 0);
CNG dg = depthStyle.getGenerator().createNoCache(rng, engine.getData());
CNG bw = baseWidthStyle.getGenerator().createNoCache(rng, engine.getData());
public void generate(MantleWriter writer, RNG rng, RNG base, Engine engine, int x, int y, int z, int recursion, int waterHint) {
KList<IrisPosition> pos = getWorm().generate(base.nextParallelRNG(879615), engine.getData(), writer, null, x, y, z, true, 0);
CNG dg = depthStyle.getGenerator().create(base.nextParallelRNG(7894156), engine.getData());
CNG bw = baseWidthStyle.getGenerator().create(base.nextParallelRNG(15315456), engine.getData());
int highestWater = Math.max(waterHint, -1);
boolean water = false;
@ -134,7 +134,7 @@ public class IrisRavine extends IrisRegistrant {
int width = (int) Math.round(bw.fitDouble(baseWidthStyle.getMin(), baseWidthStyle.getMax(), p.getX(), p.getZ()));
int surface = (int) Math.round(rsurface - depth * 0.45);
fork.doCarving(writer, rng, engine, p.getX(), rng.i(surface - depth, surface), p.getZ(), recursion, Math.max(highestWater, waterHint));
fork.doCarving(writer, rng, base, engine, p.getX(), rng.i(surface - depth, surface), p.getZ(), recursion, highestWater);
for (int i = surface + depth; i >= surface; i--) {
if (i % ribThickness == 0) {

View File

@ -60,10 +60,10 @@ public class IrisRavinePlacer implements IRare {
}
public void generateRavine(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z) {
generateRavine(mantle, rng, engine, x, y, z, 0, -1);
generateRavine(mantle, rng, new RNG(engine.getSeedManager().getCarve()), engine, x, y, z, 0, -1);
}
public void generateRavine(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) {
public void generateRavine(MantleWriter mantle, RNG rng, RNG base, Engine engine, int x, int y, int z, int recursion, int waterHint) {
if (fail.get()) {
return;
}
@ -84,7 +84,7 @@ public class IrisRavinePlacer implements IRare {
try {
int xx = x + rng.nextInt(15);
int zz = z + rng.nextInt(15);
ravine.generate(mantle, rng, engine, xx, y, zz, recursion, waterHint);
ravine.generate(mantle, rng, base, engine, xx, y, zz, recursion, waterHint);
} catch (Throwable e) {
e.printStackTrace();
fail.set(true);

View File

@ -71,9 +71,9 @@ public class IrisWorm {
IrisPosition start = new IrisPosition(x, y, z);
KList<IrisPosition> pos = new KList<>();
KSet<IrisPosition> check = allowLoops ? null : new KSet<>();
CNG gx = xStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data);
CNG gy = yStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data);
CNG gz = zStyle.getGenerator().createNoCache(new RNG(rng.lmax()), data);
CNG gx = xStyle.getGenerator().create(rng.nextParallelRNG(14567), data);
CNG gy = yStyle.getGenerator().create(rng.nextParallelRNG(64789), data);
CNG gz = zStyle.getGenerator().create(rng.nextParallelRNG(34790), data);
while (itr-- > 0) {
IrisPosition current = new IrisPosition(Math.round(cx), Math.round(cy), Math.round(cz));

View File

@ -27,6 +27,7 @@ import com.volmit.iris.util.matter.IrisMatter;
import com.volmit.iris.util.matter.Matter;
import com.volmit.iris.util.matter.MatterSlice;
import lombok.Getter;
import lombok.SneakyThrows;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
@ -111,9 +112,11 @@ public class MantleChunk {
}
}
public void close() throws InterruptedException {
@SneakyThrows
public void close() {
closed.set(true);
ref.acquire(Integer.MAX_VALUE);
ref.release(Integer.MAX_VALUE);
}
public boolean inUse() {
@ -123,6 +126,10 @@ public class MantleChunk {
public MantleChunk use() {
if (closed.get()) throw new IllegalStateException("Chunk is closed!");
ref.acquireUninterruptibly();
if (closed.get()) {
ref.release();
throw new IllegalStateException("Chunk is closed!");
}
return this;
}
@ -213,6 +220,7 @@ public class MantleChunk {
* @throws IOException shit happens
*/
public void write(DataOutputStream dos) throws IOException {
close();
dos.writeByte(x);
dos.writeByte(z);
dos.writeByte(sections.length());