Skip to content

Commit 213182e

Browse files
committed
re-implement noise carvers
1 parent 76eb0f3 commit 213182e

File tree

16 files changed

+704
-5
lines changed

16 files changed

+704
-5
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import com.dfsek.terra.getGitHash
22

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

55
allprojects {
66
version = versionObj
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.dfsek.terra.api.world.carving;
2+
3+
import com.dfsek.terra.api.math.vector.Vector3;
4+
import com.dfsek.terra.api.platform.world.World;
5+
import net.jafama.FastMath;
6+
7+
import java.util.Random;
8+
import java.util.function.BiConsumer;
9+
10+
public abstract class Carver {
11+
private final int minY;
12+
private final int maxY;
13+
private final double sixtyFourSq = FastMath.pow(64, 2);
14+
private int carvingRadius = 4;
15+
16+
public Carver(int minY, int maxY) {
17+
this.minY = minY;
18+
this.maxY = maxY;
19+
}
20+
21+
public abstract void carve(int chunkX, int chunkZ, World w, BiConsumer<Vector3, CarvingType> consumer);
22+
23+
public int getCarvingRadius() {
24+
return carvingRadius;
25+
}
26+
27+
public void setCarvingRadius(int carvingRadius) {
28+
this.carvingRadius = carvingRadius;
29+
}
30+
31+
public abstract Worm getWorm(long seed, Vector3 l);
32+
33+
public abstract boolean isChunkCarved(World w, int chunkX, int chunkZ, Random r);
34+
35+
public enum CarvingType {
36+
CENTER, WALL, TOP, BOTTOM
37+
}
38+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package com.dfsek.terra.api.world.carving;
2+
3+
import com.dfsek.terra.api.math.vector.Vector3;
4+
import net.jafama.FastMath;
5+
6+
import java.util.Random;
7+
import java.util.function.BiConsumer;
8+
9+
public abstract class Worm {
10+
private final Random r;
11+
private final Vector3 origin;
12+
private final Vector3 running;
13+
private final int length;
14+
private int topCut = 0;
15+
private int bottomCut = 0;
16+
private int[] radius = new int[] {0, 0, 0};
17+
18+
public Worm(int length, Random r, Vector3 origin) {
19+
this.r = r;
20+
this.length = length;
21+
this.origin = origin;
22+
this.running = origin;
23+
}
24+
25+
public void setBottomCut(int bottomCut) {
26+
this.bottomCut = bottomCut;
27+
}
28+
29+
public void setTopCut(int topCut) {
30+
this.topCut = topCut;
31+
}
32+
33+
public Vector3 getOrigin() {
34+
return origin;
35+
}
36+
37+
public int getLength() {
38+
return length;
39+
}
40+
41+
public Vector3 getRunning() {
42+
return running;
43+
}
44+
45+
public WormPoint getPoint() {
46+
return new WormPoint(running, radius, topCut, bottomCut);
47+
}
48+
49+
public int[] getRadius() {
50+
return radius;
51+
}
52+
53+
public void setRadius(int[] radius) {
54+
this.radius = radius;
55+
}
56+
57+
public Random getRandom() {
58+
return r;
59+
}
60+
61+
public abstract void step();
62+
63+
public static class WormPoint {
64+
private final Vector3 origin;
65+
private final int topCut;
66+
private final int bottomCut;
67+
private final int[] rad;
68+
69+
public WormPoint(Vector3 origin, int[] rad, int topCut, int bottomCut) {
70+
this.origin = origin;
71+
this.rad = rad;
72+
this.topCut = topCut;
73+
this.bottomCut = bottomCut;
74+
}
75+
76+
private static double ellipseEquation(int x, int y, int z, double xr, double yr, double zr) {
77+
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));
78+
}
79+
80+
public Vector3 getOrigin() {
81+
return origin;
82+
}
83+
84+
public int getRadius(int index) {
85+
return rad[index];
86+
}
87+
88+
public void carve(int chunkX, int chunkZ, BiConsumer<Vector3, Carver.CarvingType> consumer) {
89+
int xRad = getRadius(0);
90+
int yRad = getRadius(1);
91+
int zRad = getRadius(2);
92+
int originX = (chunkX << 4);
93+
int originZ = (chunkZ << 4);
94+
for(int x = -xRad - 1; x <= xRad + 1; x++) {
95+
if(!(FastMath.floorDiv(origin.getBlockX() + x, 16) == chunkX)) continue;
96+
for(int z = -zRad - 1; z <= zRad + 1; z++) {
97+
if(!(FastMath.floorDiv(origin.getBlockZ() + z, 16) == chunkZ)) continue;
98+
for(int y = -yRad - 1; y <= yRad + 1; y++) {
99+
Vector3 position = origin.clone().add(new Vector3(x, y, z));
100+
if(position.getY() < 0 || position.getY() > 255) continue;
101+
double eq = ellipseEquation(x, y, z, xRad, yRad, zRad);
102+
if(eq <= 1 &&
103+
y >= -yRad - 1 + bottomCut && y <= yRad + 1 - topCut) {
104+
consumer.accept(new Vector3(position.getBlockX() - originX, position.getBlockY(), position.getBlockZ() - originZ), Carver.CarvingType.CENTER);
105+
} else if(eq <= 1.5) {
106+
Carver.CarvingType type = Carver.CarvingType.WALL;
107+
if(y <= -yRad - 1 + bottomCut) {
108+
type = Carver.CarvingType.BOTTOM;
109+
} else if(y >= yRad + 1 - topCut) {
110+
type = Carver.CarvingType.TOP;
111+
}
112+
consumer.accept(new Vector3(position.getBlockX() - originX, position.getBlockY(), position.getBlockZ() - originZ), type);
113+
}
114+
}
115+
}
116+
}
117+
}
118+
}
119+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.dfsek.terra.carving;
2+
3+
import com.dfsek.terra.api.core.TerraPlugin;
4+
import com.dfsek.terra.api.math.MathUtil;
5+
import com.dfsek.terra.api.math.vector.Vector3;
6+
import com.dfsek.terra.api.platform.world.World;
7+
import com.dfsek.terra.api.util.FastRandom;
8+
import com.dfsek.terra.api.util.GlueList;
9+
import com.dfsek.terra.api.world.carving.Worm;
10+
import com.dfsek.terra.biome.TerraBiome;
11+
import com.dfsek.terra.biome.UserDefinedBiome;
12+
import com.dfsek.terra.biome.provider.BiomeProvider;
13+
import com.google.common.cache.CacheBuilder;
14+
import com.google.common.cache.CacheLoader;
15+
import com.google.common.cache.LoadingCache;
16+
import org.jetbrains.annotations.NotNull;
17+
18+
import java.util.List;
19+
import java.util.Random;
20+
21+
public class CarverCache {
22+
23+
private final LoadingCache<Long, List<Worm.WormPoint>> cache;
24+
private final UserDefinedCarver carver;
25+
26+
public CarverCache(World w, TerraPlugin main, UserDefinedCarver carver) {
27+
this.carver = carver;
28+
cache = CacheBuilder.newBuilder().maximumSize(main.getTerraConfig().getCarverCacheSize())
29+
.build(new CacheLoader<Long, List<Worm.WormPoint>>() {
30+
@Override
31+
public List<Worm.WormPoint> load(@NotNull Long key) {
32+
int chunkX = (int) (key >> 32);
33+
int chunkZ = (int) key.longValue();
34+
BiomeProvider provider = main.getWorld(w).getBiomeProvider();
35+
if(CarverCache.this.carver.isChunkCarved(w, chunkX, chunkZ, new FastRandom(MathUtil.getCarverChunkSeed(chunkX, chunkZ, w.getSeed() + CarverCache.this.carver.hashCode())))) {
36+
long seed = MathUtil.getCarverChunkSeed(chunkX, chunkZ, w.getSeed());
37+
CarverCache.this.carver.getSeedVar().setValue(seed);
38+
Random r = new FastRandom(seed);
39+
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)));
40+
List<Worm.WormPoint> points = new GlueList<>();
41+
for(int i = 0; i < carving.getLength(); i++) {
42+
carving.step();
43+
TerraBiome biome = provider.getBiome(carving.getRunning().toLocation(w));
44+
if(!((UserDefinedBiome) biome).getConfig().getCarvers().containsKey(CarverCache.this.carver)) { // Stop if we enter a biome this carver is not present in
45+
return new GlueList<>();
46+
}
47+
points.add(carving.getPoint());
48+
}
49+
return points;
50+
}
51+
return new GlueList<>();
52+
}
53+
});
54+
}
55+
56+
public List<Worm.WormPoint> getPoints(int chunkX, int chunkZ) {
57+
return cache.getUnchecked(MathUtil.squash(chunkX, chunkZ));
58+
}
59+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.dfsek.terra.carving;
2+
3+
import com.dfsek.terra.api.math.ProbabilityCollection;
4+
import com.dfsek.terra.api.platform.block.BlockData;
5+
import com.dfsek.terra.api.platform.block.MaterialData;
6+
import com.dfsek.terra.util.MaterialSet;
7+
8+
import java.util.Map;
9+
import java.util.TreeMap;
10+
11+
@SuppressWarnings({"unchecked", "rawtypes", "RedundantSuppression"})
12+
public class CarverPalette {
13+
private final boolean blacklist;
14+
private final MaterialSet replace;
15+
private final TreeMap<Integer, ProbabilityCollection<BlockData>> map = new TreeMap<>();
16+
private ProbabilityCollection<BlockData>[] layers;
17+
18+
public CarverPalette(MaterialSet replaceable, boolean blacklist) {
19+
this.blacklist = blacklist;
20+
this.replace = replaceable;
21+
}
22+
23+
public CarverPalette add(ProbabilityCollection<BlockData> collection, int y) {
24+
map.put(y, collection);
25+
return this;
26+
}
27+
28+
public ProbabilityCollection<BlockData> get(int y) {
29+
return layers[y];
30+
}
31+
32+
public boolean canReplace(MaterialData material) {
33+
return blacklist != replace.contains(material);
34+
}
35+
36+
/**
37+
* Build the palette to an array.
38+
*/
39+
public void build() {
40+
int size = map.lastKey() + 1;
41+
layers = new ProbabilityCollection[size];
42+
for(int y = 0; y < size; y++) {
43+
ProbabilityCollection<BlockData> d = null;
44+
for(Map.Entry<Integer, ProbabilityCollection<BlockData>> e : map.entrySet()) {
45+
if(e.getKey() >= y) {
46+
d = e.getValue();
47+
break;
48+
}
49+
}
50+
if(d == null) throw new IllegalArgumentException("Null collection at Y=" + y);
51+
layers[y] = d;
52+
}
53+
}
54+
}

0 commit comments

Comments
 (0)