Skip to content

Commit

Permalink
re-implement noise carvers
Browse files Browse the repository at this point in the history
  • Loading branch information
dfsek committed Feb 7, 2021
1 parent 76eb0f3 commit 213182e
Show file tree
Hide file tree
Showing 16 changed files with 704 additions and 5 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import com.dfsek.terra.getGitHash

val versionObj = Version("3", "2", "0", true)
val versionObj = Version("4", "0", "0", true)

allprojects {
version = versionObj
Expand Down
38 changes: 38 additions & 0 deletions common/src/main/java/com/dfsek/terra/api/world/carving/Carver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.dfsek.terra.api.world.carving;

import com.dfsek.terra.api.math.vector.Vector3;
import com.dfsek.terra.api.platform.world.World;
import net.jafama.FastMath;

import java.util.Random;
import java.util.function.BiConsumer;

public abstract class Carver {
private final int minY;
private final int maxY;
private final double sixtyFourSq = FastMath.pow(64, 2);
private int carvingRadius = 4;

public Carver(int minY, int maxY) {
this.minY = minY;
this.maxY = maxY;
}

public abstract void carve(int chunkX, int chunkZ, World w, BiConsumer<Vector3, CarvingType> consumer);

public int getCarvingRadius() {
return carvingRadius;
}

public void setCarvingRadius(int carvingRadius) {
this.carvingRadius = carvingRadius;
}

public abstract Worm getWorm(long seed, Vector3 l);

public abstract boolean isChunkCarved(World w, int chunkX, int chunkZ, Random r);

public enum CarvingType {
CENTER, WALL, TOP, BOTTOM
}
}
119 changes: 119 additions & 0 deletions common/src/main/java/com/dfsek/terra/api/world/carving/Worm.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package com.dfsek.terra.api.world.carving;

import com.dfsek.terra.api.math.vector.Vector3;
import net.jafama.FastMath;

import java.util.Random;
import java.util.function.BiConsumer;

public abstract class Worm {
private final Random r;
private final Vector3 origin;
private final Vector3 running;
private final int length;
private int topCut = 0;
private int bottomCut = 0;
private int[] radius = new int[] {0, 0, 0};

public Worm(int length, Random r, Vector3 origin) {
this.r = r;
this.length = length;
this.origin = origin;
this.running = origin;
}

public void setBottomCut(int bottomCut) {
this.bottomCut = bottomCut;
}

public void setTopCut(int topCut) {
this.topCut = topCut;
}

public Vector3 getOrigin() {
return origin;
}

public int getLength() {
return length;
}

public Vector3 getRunning() {
return running;
}

public WormPoint getPoint() {
return new WormPoint(running, radius, topCut, bottomCut);
}

public int[] getRadius() {
return radius;
}

public void setRadius(int[] radius) {
this.radius = radius;
}

public Random getRandom() {
return r;
}

public abstract void step();

public static class WormPoint {
private final Vector3 origin;
private final int topCut;
private final int bottomCut;
private final int[] rad;

public WormPoint(Vector3 origin, int[] rad, int topCut, int bottomCut) {
this.origin = origin;
this.rad = rad;
this.topCut = topCut;
this.bottomCut = bottomCut;
}

private static double ellipseEquation(int x, int y, int z, double xr, double yr, double zr) {
return (FastMath.pow2(x) / FastMath.pow2(xr + 0.5D)) + (FastMath.pow2(y) / FastMath.pow2(yr + 0.5D)) + (FastMath.pow2(z) / FastMath.pow2(zr + 0.5D));
}

public Vector3 getOrigin() {
return origin;
}

public int getRadius(int index) {
return rad[index];
}

public void carve(int chunkX, int chunkZ, BiConsumer<Vector3, Carver.CarvingType> consumer) {
int xRad = getRadius(0);
int yRad = getRadius(1);
int zRad = getRadius(2);
int originX = (chunkX << 4);
int originZ = (chunkZ << 4);
for(int x = -xRad - 1; x <= xRad + 1; x++) {
if(!(FastMath.floorDiv(origin.getBlockX() + x, 16) == chunkX)) continue;
for(int z = -zRad - 1; z <= zRad + 1; z++) {
if(!(FastMath.floorDiv(origin.getBlockZ() + z, 16) == chunkZ)) continue;
for(int y = -yRad - 1; y <= yRad + 1; y++) {
Vector3 position = origin.clone().add(new Vector3(x, y, z));
if(position.getY() < 0 || position.getY() > 255) continue;
double eq = ellipseEquation(x, y, z, xRad, yRad, zRad);
if(eq <= 1 &&
y >= -yRad - 1 + bottomCut && y <= yRad + 1 - topCut) {
consumer.accept(new Vector3(position.getBlockX() - originX, position.getBlockY(), position.getBlockZ() - originZ), Carver.CarvingType.CENTER);
} else if(eq <= 1.5) {
Carver.CarvingType type = Carver.CarvingType.WALL;
if(y <= -yRad - 1 + bottomCut) {
type = Carver.CarvingType.BOTTOM;
} else if(y >= yRad + 1 - topCut) {
type = Carver.CarvingType.TOP;
}
consumer.accept(new Vector3(position.getBlockX() - originX, position.getBlockY(), position.getBlockZ() - originZ), type);
}
}
}
}
}
}
}
59 changes: 59 additions & 0 deletions common/src/main/java/com/dfsek/terra/carving/CarverCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.dfsek.terra.carving;

import com.dfsek.terra.api.core.TerraPlugin;
import com.dfsek.terra.api.math.MathUtil;
import com.dfsek.terra.api.math.vector.Vector3;
import com.dfsek.terra.api.platform.world.World;
import com.dfsek.terra.api.util.FastRandom;
import com.dfsek.terra.api.util.GlueList;
import com.dfsek.terra.api.world.carving.Worm;
import com.dfsek.terra.biome.TerraBiome;
import com.dfsek.terra.biome.UserDefinedBiome;
import com.dfsek.terra.biome.provider.BiomeProvider;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.Random;

public class CarverCache {

private final LoadingCache<Long, List<Worm.WormPoint>> cache;
private final UserDefinedCarver carver;

public CarverCache(World w, TerraPlugin main, UserDefinedCarver carver) {
this.carver = carver;
cache = CacheBuilder.newBuilder().maximumSize(main.getTerraConfig().getCarverCacheSize())
.build(new CacheLoader<Long, List<Worm.WormPoint>>() {
@Override
public List<Worm.WormPoint> load(@NotNull Long key) {
int chunkX = (int) (key >> 32);
int chunkZ = (int) key.longValue();
BiomeProvider provider = main.getWorld(w).getBiomeProvider();
if(CarverCache.this.carver.isChunkCarved(w, chunkX, chunkZ, new FastRandom(MathUtil.getCarverChunkSeed(chunkX, chunkZ, w.getSeed() + CarverCache.this.carver.hashCode())))) {
long seed = MathUtil.getCarverChunkSeed(chunkX, chunkZ, w.getSeed());
CarverCache.this.carver.getSeedVar().setValue(seed);
Random r = new FastRandom(seed);
Worm carving = CarverCache.this.carver.getWorm(seed, new Vector3((chunkX << 4) + r.nextInt(16), CarverCache.this.carver.getConfig().getHeight().get(r), (chunkZ << 4) + r.nextInt(16)));
List<Worm.WormPoint> points = new GlueList<>();
for(int i = 0; i < carving.getLength(); i++) {
carving.step();
TerraBiome biome = provider.getBiome(carving.getRunning().toLocation(w));
if(!((UserDefinedBiome) biome).getConfig().getCarvers().containsKey(CarverCache.this.carver)) { // Stop if we enter a biome this carver is not present in
return new GlueList<>();
}
points.add(carving.getPoint());
}
return points;
}
return new GlueList<>();
}
});
}

public List<Worm.WormPoint> getPoints(int chunkX, int chunkZ) {
return cache.getUnchecked(MathUtil.squash(chunkX, chunkZ));
}
}
54 changes: 54 additions & 0 deletions common/src/main/java/com/dfsek/terra/carving/CarverPalette.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.dfsek.terra.carving;

import com.dfsek.terra.api.math.ProbabilityCollection;
import com.dfsek.terra.api.platform.block.BlockData;
import com.dfsek.terra.api.platform.block.MaterialData;
import com.dfsek.terra.util.MaterialSet;

import java.util.Map;
import java.util.TreeMap;

@SuppressWarnings({"unchecked", "rawtypes", "RedundantSuppression"})
public class CarverPalette {
private final boolean blacklist;
private final MaterialSet replace;
private final TreeMap<Integer, ProbabilityCollection<BlockData>> map = new TreeMap<>();
private ProbabilityCollection<BlockData>[] layers;

public CarverPalette(MaterialSet replaceable, boolean blacklist) {
this.blacklist = blacklist;
this.replace = replaceable;
}

public CarverPalette add(ProbabilityCollection<BlockData> collection, int y) {
map.put(y, collection);
return this;
}

public ProbabilityCollection<BlockData> get(int y) {
return layers[y];
}

public boolean canReplace(MaterialData material) {
return blacklist != replace.contains(material);
}

/**
* Build the palette to an array.
*/
public void build() {
int size = map.lastKey() + 1;
layers = new ProbabilityCollection[size];
for(int y = 0; y < size; y++) {
ProbabilityCollection<BlockData> d = null;
for(Map.Entry<Integer, ProbabilityCollection<BlockData>> e : map.entrySet()) {
if(e.getKey() >= y) {
d = e.getValue();
break;
}
}
if(d == null) throw new IllegalArgumentException("Null collection at Y=" + y);
layers[y] = d;
}
}
}
Loading

0 comments on commit 213182e

Please sign in to comment.