- use fast-util long2obj maps
- reorganised river stuff - improvements to TerrainHelper - fixes buildings spawning with terrain inside
This commit is contained in:
parent
04a85e8ea0
commit
de1f845b71
@ -8,12 +8,12 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
compile "org.processing:core:3.3.7"
|
||||
compile "it.unimi.dsi:fastutil:8.2.1"
|
||||
compile project(":TerraForgedCore")
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest { attributes "Main-Class": "com.terraforged.app.Main" }
|
||||
|
||||
from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,12 @@
|
||||
apply plugin: "maven-publish"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(":Noise2D")
|
||||
compile "it.unimi.dsi:fastutil:8.2.1"
|
||||
}
|
||||
|
||||
publishing {
|
||||
|
@ -28,14 +28,11 @@ package com.terraforged.core.region;
|
||||
import com.terraforged.core.cell.Cell;
|
||||
import com.terraforged.core.cell.Extent;
|
||||
import com.terraforged.core.filter.Filterable;
|
||||
import com.terraforged.core.region.chunk.ChunkGenTask;
|
||||
import com.terraforged.core.region.chunk.ChunkReader;
|
||||
import com.terraforged.core.region.chunk.ChunkWriter;
|
||||
import com.terraforged.core.region.chunk.ChunkZoomTask;
|
||||
import com.terraforged.core.util.concurrent.batcher.Batcher;
|
||||
import com.terraforged.core.world.decorator.Decorator;
|
||||
import com.terraforged.core.world.heightmap.Heightmap;
|
||||
import com.terraforged.core.world.river.RiverRegionList;
|
||||
import com.terraforged.core.world.rivermap.RiverRegionList;
|
||||
import com.terraforged.core.world.terrain.Terrain;
|
||||
|
||||
import java.util.Collection;
|
||||
@ -173,45 +170,20 @@ public class Region implements Extent {
|
||||
}
|
||||
}
|
||||
|
||||
public void generate(Heightmap heightmap, Batcher batcher) {
|
||||
for (int cz = 0; cz < chunkSize.total; cz++) {
|
||||
for (int cx = 0; cx < chunkSize.total; cx++) {
|
||||
int index = chunkSize.indexOf(cx, cz);
|
||||
GenChunk chunk = computeChunk(index, cx, cz);
|
||||
Runnable task = new ChunkGenTask(chunk, heightmap);
|
||||
batcher.submit(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void generateZoom(Heightmap heightmap, float offsetX, float offsetZ, float zoom, Batcher batcher) {
|
||||
public void generateZoom(Heightmap heightmap, float offsetX, float offsetZ, float zoom) {
|
||||
float translateX = offsetX - ((blockSize.size * zoom) / 2F);
|
||||
float translateZ = offsetZ - ((blockSize.size * zoom) / 2F);
|
||||
for (int cz = 0; cz < chunkSize.total; cz++) {
|
||||
for (int cx = 0; cx < chunkSize.total; cx++) {
|
||||
int index = chunkSize.indexOf(cx, cz);
|
||||
GenChunk chunk = computeChunk(index, cx, cz);
|
||||
Runnable task = new ChunkZoomTask(chunk, heightmap, translateX, translateZ, zoom);
|
||||
batcher.submit(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void check() {
|
||||
for (int dz = 0; dz < chunkSize.total; dz++) {
|
||||
for (int dx = 0; dx < chunkSize.total; dx++) {
|
||||
int index = chunkSize.indexOf(dx, dz);
|
||||
if (chunks[index] == null) {
|
||||
throw new NullPointerException("Null chunk " + dx + ":" + dz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int dz = 0; dz < blockSize.total; dz++) {
|
||||
for (int dx = 0; dx < blockSize.total; dx++) {
|
||||
int index = blockSize.indexOf(dx, dz);
|
||||
if (blocks[index] == null) {
|
||||
throw new NullPointerException("Null block " + dx + ":" + dz);
|
||||
for (int dz = 0; dz < 16; dz++) {
|
||||
for (int dx = 0; dx < 16; dx++) {
|
||||
float x = ((chunk.getBlockX() + dx) * zoom) + translateX;
|
||||
float z = ((chunk.getBlockZ() + dz) * zoom) + translateZ;
|
||||
Cell<Terrain> cell = chunk.genCell(dx, dz);
|
||||
heightmap.apply(cell, x, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ public class RegionCache implements RegionExtent {
|
||||
|
||||
private final boolean queuing;
|
||||
private final RegionGenerator renderer;
|
||||
private final Cache<Long, CacheEntry<Region>> cache;
|
||||
private final Cache<CacheEntry<Region>> cache;
|
||||
|
||||
public RegionCache(boolean queueNeighbours, RegionGenerator renderer) {
|
||||
this.renderer = renderer;
|
||||
|
@ -31,7 +31,7 @@ import com.terraforged.core.util.concurrent.cache.CacheEntry;
|
||||
import com.terraforged.core.world.WorldGenerator;
|
||||
import com.terraforged.core.world.WorldGeneratorFactory;
|
||||
import com.terraforged.core.world.heightmap.RegionExtent;
|
||||
import com.terraforged.core.world.river.RiverRegionList;
|
||||
import com.terraforged.core.world.rivermap.RiverRegionList;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@ -89,7 +89,7 @@ public class RegionGenerator implements RegionExtent {
|
||||
public Region generateRegion(int regionX, int regionZ) {
|
||||
WorldGenerator generator = genPool.get();
|
||||
Region region = regions.create(regionX, regionZ, factor, border);
|
||||
RiverRegionList rivers = generator.getHeightmap().getRiverManager().getRivers(region);
|
||||
RiverRegionList rivers = generator.getHeightmap().getRiverMap().getRivers(region);
|
||||
region.generateBase(generator.getHeightmap());
|
||||
region.generateRivers(generator.getHeightmap(), rivers);
|
||||
postProcess(region, generator);
|
||||
@ -104,15 +104,7 @@ public class RegionGenerator implements RegionExtent {
|
||||
public Region generateRegion(float centerX, float centerZ, float zoom, boolean filter) {
|
||||
WorldGenerator generator = genPool.get();
|
||||
Region region = regions.create(0, 0, factor, border);
|
||||
float translateX = centerX - ((region.getBlockSize().size * zoom) / 2F);
|
||||
float translateZ = centerZ - ((region.getBlockSize().size * zoom) / 2F);
|
||||
region.generate(chunk -> {
|
||||
chunk.generate((cell, dx, dz) -> {
|
||||
float x = ((chunk.getBlockX() + dx) * zoom) + translateX;
|
||||
float z = ((chunk.getBlockZ() + dz) * zoom) + translateZ;
|
||||
generator.getHeightmap().apply(cell, x, z);
|
||||
});
|
||||
});
|
||||
region.generateZoom(generator.getHeightmap(), centerX, centerZ, zoom);
|
||||
postProcess(region, generator, centerX, centerZ, zoom, filter);
|
||||
return region;
|
||||
}
|
||||
|
@ -2,31 +2,30 @@ package com.terraforged.core.util.concurrent.cache;
|
||||
|
||||
import com.terraforged.core.util.concurrent.ThreadPool;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.LongFunction;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class Cache<K, V extends ExpiringEntry> implements Runnable {
|
||||
public class Cache<V extends ExpiringEntry> implements Runnable {
|
||||
|
||||
private final long expireMS;
|
||||
private final long intervalMS;
|
||||
private final Map<K, V> map = new ConcurrentHashMap<>();
|
||||
private final SynchronizedLongMap<V> map;
|
||||
|
||||
private volatile long timestamp = 0L;
|
||||
|
||||
public Cache(long expireTime, long interval, TimeUnit unit) {
|
||||
this.expireMS = unit.toMillis(expireTime);
|
||||
this.intervalMS = unit.toMillis(interval);
|
||||
this.map = new SynchronizedLongMap<>(100);
|
||||
}
|
||||
|
||||
public V computeIfAbsent(K k, Supplier<V> supplier) {
|
||||
return computeIfAbsent(k, o -> supplier.get());
|
||||
public V computeIfAbsent(long key, Supplier<V> supplier) {
|
||||
return computeIfAbsent(key, o -> supplier.get());
|
||||
}
|
||||
|
||||
public V computeIfAbsent(K k, Function<K, V> func) {
|
||||
V v = map.computeIfAbsent(k, func);
|
||||
public V computeIfAbsent(long key, LongFunction<V> func) {
|
||||
V v = map.computeIfAbsent(key, func);
|
||||
queueUpdate();
|
||||
return v;
|
||||
}
|
||||
@ -42,10 +41,6 @@ public class Cache<K, V extends ExpiringEntry> implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
final long now = timestamp;
|
||||
map.forEach((key, val) -> {
|
||||
if (now - val.getTimestamp() > expireMS) {
|
||||
map.remove(key);
|
||||
}
|
||||
});
|
||||
map.removeIf(val -> now - val.getTimestamp() > expireMS);
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,17 @@
|
||||
package com.terraforged.core.util.concurrent.cache;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class CacheEntry<T> extends FutureTask<T> implements ExpiringEntry {
|
||||
public class CacheEntry<T> implements Runnable, ExpiringEntry {
|
||||
|
||||
private volatile T result;
|
||||
private volatile long timestamp;
|
||||
|
||||
private CacheEntry(Callable<T> supplier) {
|
||||
super(supplier);
|
||||
private final Supplier<T> supplier;
|
||||
|
||||
private CacheEntry(Supplier<T> supplier) {
|
||||
this.supplier = supplier;
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@ -20,16 +21,30 @@ public class CacheEntry<T> extends FutureTask<T> implements ExpiringEntry {
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
try {
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
return super.get();
|
||||
} catch (Throwable t) {
|
||||
throw new CompletionException(t);
|
||||
}
|
||||
public void run() {
|
||||
this.result = supplier.get();
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public static <T> CacheEntry<T> supplyAsync(Callable<T> supplier, Executor executor) {
|
||||
public boolean isDone() {
|
||||
return result != null;
|
||||
}
|
||||
|
||||
public T get() {
|
||||
T value = result;
|
||||
if (value == null) {
|
||||
value = getNow();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private T getNow() {
|
||||
T result = supplier.get();
|
||||
this.result = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <T> CacheEntry<T> supplyAsync(Supplier<T> supplier, Executor executor) {
|
||||
CacheEntry<T> entry = new CacheEntry<>(supplier);
|
||||
executor.execute(entry);
|
||||
return entry;
|
||||
|
@ -0,0 +1,54 @@
|
||||
package com.terraforged.core.util.concurrent.cache;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||
|
||||
import java.util.function.LongFunction;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class SynchronizedLongMap<V> {
|
||||
|
||||
private final Object lock;
|
||||
private final Long2ObjectOpenHashMap<V> map;
|
||||
|
||||
public SynchronizedLongMap(int size) {
|
||||
this.map = new Long2ObjectOpenHashMap<>(size);
|
||||
this.lock = this;
|
||||
}
|
||||
|
||||
public void remove(long key) {
|
||||
synchronized (lock) {
|
||||
map.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public void put(long key, V v) {
|
||||
synchronized (lock) {
|
||||
map.put(key, v);
|
||||
}
|
||||
}
|
||||
|
||||
public V get(long key) {
|
||||
synchronized (lock) {
|
||||
return map.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
public V computeIfAbsent(long key, LongFunction<V> func) {
|
||||
synchronized (lock) {
|
||||
return map.computeIfAbsent(key, func);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeIf(Predicate<V> predicate) {
|
||||
synchronized (lock) {
|
||||
ObjectIterator<Long2ObjectMap.Entry<V>> iterator = map.long2ObjectEntrySet().fastIterator();
|
||||
while (iterator.hasNext()) {
|
||||
if (predicate.test(iterator.next().getValue())) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -31,15 +31,15 @@ import com.terraforged.core.cell.Populator;
|
||||
import com.terraforged.core.region.Size;
|
||||
import com.terraforged.core.util.concurrent.ObjectPool;
|
||||
import com.terraforged.core.world.climate.Climate;
|
||||
import com.terraforged.core.world.river.RiverManager;
|
||||
import com.terraforged.core.world.river.RiverRegionList;
|
||||
import com.terraforged.core.world.rivermap.RiverMap;
|
||||
import com.terraforged.core.world.rivermap.RiverRegionList;
|
||||
import com.terraforged.core.world.terrain.Terrain;
|
||||
|
||||
public interface Heightmap extends Populator, Extent {
|
||||
|
||||
Climate getClimate();
|
||||
|
||||
RiverManager getRiverManager();
|
||||
RiverMap getRiverMap();
|
||||
|
||||
void visit(Cell<Terrain> cell, float x, float z);
|
||||
|
||||
|
@ -36,8 +36,8 @@ import com.terraforged.core.world.climate.Climate;
|
||||
import com.terraforged.core.world.continent.ContinentLerper2;
|
||||
import com.terraforged.core.world.continent.ContinentLerper3;
|
||||
import com.terraforged.core.world.continent.ContinentModule;
|
||||
import com.terraforged.core.world.river.RiverManager;
|
||||
import com.terraforged.core.world.river.RiverRegionList;
|
||||
import com.terraforged.core.world.rivermap.RiverMap;
|
||||
import com.terraforged.core.world.rivermap.RiverRegionList;
|
||||
import com.terraforged.core.world.terrain.Terrain;
|
||||
import com.terraforged.core.world.terrain.TerrainPopulator;
|
||||
import com.terraforged.core.world.terrain.Terrains;
|
||||
@ -67,7 +67,7 @@ public class WorldHeightmap implements Heightmap {
|
||||
|
||||
private final Climate climate;
|
||||
private final Populator root;
|
||||
private final RiverManager riverManager;
|
||||
private final RiverMap riverMap;
|
||||
private final TerrainProvider terrainProvider;
|
||||
|
||||
public WorldHeightmap(GeneratorContext context) {
|
||||
@ -154,7 +154,7 @@ public class WorldHeightmap implements Heightmap {
|
||||
COAST_VALUE - 0.05F
|
||||
);
|
||||
|
||||
riverManager = new RiverManager(this, context);
|
||||
riverMap = new RiverMap(this, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -167,7 +167,7 @@ public class WorldHeightmap implements Heightmap {
|
||||
@Override
|
||||
public void apply(Cell<Terrain> cell, float x, float z) {
|
||||
applyBase(cell, x, z);
|
||||
applyRivers(cell, x, z, riverManager.getRivers((int) x, (int) z));
|
||||
applyRivers(cell, x, z, riverMap.getRivers((int) x, (int) z));
|
||||
applyClimate(cell, x, z);
|
||||
}
|
||||
|
||||
@ -219,8 +219,8 @@ public class WorldHeightmap implements Heightmap {
|
||||
}
|
||||
|
||||
@Override
|
||||
public RiverManager getRiverManager() {
|
||||
return riverManager;
|
||||
public RiverMap getRiverMap() {
|
||||
return riverMap;
|
||||
}
|
||||
|
||||
public Populator getPopulator(Terrain terrain) {
|
||||
|
@ -23,10 +23,11 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.terraforged.core.world.river;
|
||||
package com.terraforged.core.world.rivermap;
|
||||
|
||||
import com.terraforged.core.cell.Cell;
|
||||
import com.terraforged.core.world.heightmap.Heightmap;
|
||||
import com.terraforged.core.world.rivermap.river.RiverNode;
|
||||
import com.terraforged.core.world.terrain.Terrain;
|
||||
import me.dags.noise.domain.Domain;
|
||||
import me.dags.noise.util.Vec2i;
|
@ -23,7 +23,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.terraforged.core.world.river;
|
||||
package com.terraforged.core.world.rivermap;
|
||||
|
||||
import com.terraforged.core.cell.Cell;
|
||||
import com.terraforged.core.region.Region;
|
||||
@ -32,21 +32,24 @@ import com.terraforged.core.util.concurrent.cache.Cache;
|
||||
import com.terraforged.core.util.concurrent.cache.CacheEntry;
|
||||
import com.terraforged.core.world.GeneratorContext;
|
||||
import com.terraforged.core.world.heightmap.Heightmap;
|
||||
import com.terraforged.core.world.rivermap.lake.LakeConfig;
|
||||
import com.terraforged.core.world.rivermap.river.RiverConfig;
|
||||
import com.terraforged.core.world.rivermap.river.RiverRegion;
|
||||
import com.terraforged.core.world.terrain.Terrain;
|
||||
import me.dags.noise.util.NoiseUtil;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class RiverManager {
|
||||
public class RiverMap {
|
||||
|
||||
private static final int QUAD_SIZE = (1 << RiverRegion.SCALE) / 2;
|
||||
|
||||
private final Heightmap heightmap;
|
||||
private final GeneratorContext context;
|
||||
private final RiverContext riverContext;
|
||||
private final Cache<Long, CacheEntry<RiverRegion>> cache = new Cache<>(120, 60, TimeUnit.SECONDS);
|
||||
private final RiverMapConfig riverMapConfig;
|
||||
private final Cache<CacheEntry<RiverRegion>> cache;
|
||||
|
||||
public RiverManager(Heightmap heightmap, GeneratorContext context) {
|
||||
public RiverMap(Heightmap heightmap, GeneratorContext context) {
|
||||
RiverConfig primary = RiverConfig.builder(context.levels)
|
||||
.bankHeight(context.settings.rivers.primaryRivers.minBankHeight, context.settings.rivers.primaryRivers.maxBankHeight)
|
||||
.bankWidth(context.settings.rivers.primaryRivers.bankWidth)
|
||||
@ -76,7 +79,8 @@ public class RiverManager {
|
||||
|
||||
this.heightmap = heightmap;
|
||||
this.context = context;
|
||||
this.riverContext = new RiverContext(context.settings.rivers.riverFrequency, primary, secondary, tertiary, lakes);
|
||||
this.riverMapConfig = new RiverMapConfig(context.settings.rivers.riverFrequency, primary, secondary, tertiary, lakes);
|
||||
this.cache = new Cache<>(120, 60, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public RiverRegionList getRivers(Region region) {
|
||||
@ -100,8 +104,7 @@ public class RiverManager {
|
||||
RiverRegionList list = new RiverRegionList();
|
||||
for (int dz = minZ; dz <= maxZ; dz++) {
|
||||
for (int dx = minX; dx <= maxX; dx++) {
|
||||
CacheEntry<RiverRegion> entry = getRegion(rx + dx, rz + dz);
|
||||
list.add(entry);
|
||||
list.add(getRegion(rx + dx, rz + dz));
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,6 +151,6 @@ public class RiverManager {
|
||||
}
|
||||
|
||||
private CacheEntry<RiverRegion> generateRegion(int rx, int rz) {
|
||||
return CacheEntry.supplyAsync(() -> new RiverRegion(rx, rz, heightmap, context, riverContext), ThreadPool.getDefaultPool());
|
||||
return CacheEntry.supplyAsync(() -> new RiverRegion(rx, rz, heightmap, context, riverMapConfig), ThreadPool.getDefaultPool());
|
||||
}
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
package com.terraforged.core.world.river;
|
||||
package com.terraforged.core.world.rivermap;
|
||||
|
||||
public class RiverContext {
|
||||
import com.terraforged.core.world.rivermap.lake.LakeConfig;
|
||||
import com.terraforged.core.world.rivermap.river.RiverConfig;
|
||||
|
||||
public class RiverMapConfig {
|
||||
|
||||
public final float frequency;
|
||||
public final RiverConfig primary;
|
||||
@ -8,7 +11,7 @@ public class RiverContext {
|
||||
public final RiverConfig tertiary;
|
||||
public final LakeConfig lakes;
|
||||
|
||||
public RiverContext(float frequency, RiverConfig primary, RiverConfig secondary, RiverConfig tertiary, LakeConfig lakes) {
|
||||
public RiverMapConfig(float frequency, RiverConfig primary, RiverConfig secondary, RiverConfig tertiary, LakeConfig lakes) {
|
||||
this.frequency = frequency;
|
||||
this.primary = primary;
|
||||
this.secondary = secondary;
|
@ -1,7 +1,8 @@
|
||||
package com.terraforged.core.world.river;
|
||||
package com.terraforged.core.world.rivermap;
|
||||
|
||||
import com.terraforged.core.cell.Cell;
|
||||
import com.terraforged.core.util.concurrent.cache.CacheEntry;
|
||||
import com.terraforged.core.world.rivermap.river.RiverRegion;
|
||||
import com.terraforged.core.world.terrain.Terrain;
|
||||
|
||||
public class RiverRegionList {
|
||||
@ -10,7 +11,10 @@ public class RiverRegionList {
|
||||
private final CacheEntry<RiverRegion>[] regions = new CacheEntry[4];
|
||||
|
||||
protected void add(CacheEntry<RiverRegion> entry) {
|
||||
regions[index++] = entry;
|
||||
if (index < regions.length) {
|
||||
regions[index] = entry;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
public void apply(Cell<Terrain> cell, float x, float z) {
|
@ -23,9 +23,10 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.terraforged.core.world.river;
|
||||
package com.terraforged.core.world.rivermap.lake;
|
||||
|
||||
import com.terraforged.core.cell.Cell;
|
||||
import com.terraforged.core.world.rivermap.river.River;
|
||||
import com.terraforged.core.world.terrain.Terrain;
|
||||
import com.terraforged.core.world.terrain.TerrainPopulator;
|
||||
import com.terraforged.core.world.terrain.Terrains;
|
@ -23,7 +23,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.terraforged.core.world.river;
|
||||
package com.terraforged.core.world.rivermap.lake;
|
||||
|
||||
import com.terraforged.core.settings.RiverSettings;
|
||||
import com.terraforged.core.world.heightmap.Levels;
|
@ -23,7 +23,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.terraforged.core.world.river;
|
||||
package com.terraforged.core.world.rivermap.river;
|
||||
|
||||
import com.terraforged.core.cell.Cell;
|
||||
import com.terraforged.core.world.terrain.Terrain;
|
@ -23,7 +23,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.terraforged.core.world.river;
|
||||
package com.terraforged.core.world.rivermap.river;
|
||||
|
||||
import me.dags.noise.util.Vec2f;
|
||||
|
@ -23,7 +23,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.terraforged.core.world.river;
|
||||
package com.terraforged.core.world.rivermap.river;
|
||||
|
||||
import com.terraforged.core.world.heightmap.Levels;
|
||||
import me.dags.noise.util.NoiseUtil;
|
@ -23,7 +23,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.terraforged.core.world.river;
|
||||
package com.terraforged.core.world.rivermap.river;
|
||||
|
||||
public class RiverNode {
|
||||
|
@ -23,13 +23,17 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.terraforged.core.world.river;
|
||||
package com.terraforged.core.world.rivermap.river;
|
||||
|
||||
import com.terraforged.core.cell.Cell;
|
||||
import com.terraforged.core.util.concurrent.ObjectPool;
|
||||
import com.terraforged.core.util.concurrent.cache.ExpiringEntry;
|
||||
import com.terraforged.core.world.GeneratorContext;
|
||||
import com.terraforged.core.world.heightmap.Heightmap;
|
||||
import com.terraforged.core.world.rivermap.PosGenerator;
|
||||
import com.terraforged.core.world.rivermap.RiverMapConfig;
|
||||
import com.terraforged.core.world.rivermap.lake.Lake;
|
||||
import com.terraforged.core.world.rivermap.lake.LakeConfig;
|
||||
import com.terraforged.core.world.terrain.Terrain;
|
||||
import com.terraforged.core.world.terrain.Terrains;
|
||||
import me.dags.noise.domain.Domain;
|
||||
@ -73,27 +77,27 @@ public class RiverRegion implements ExpiringEntry {
|
||||
|
||||
private final long timestamp = System.currentTimeMillis() + EXPIRE_TIME;
|
||||
|
||||
public RiverRegion(int regionX, int regionZ, Heightmap heightmap, GeneratorContext context, RiverContext riverContext) {
|
||||
public RiverRegion(int regionX, int regionZ, Heightmap heightmap, GeneratorContext context, RiverMapConfig riverMapConfig) {
|
||||
int seed = new Random(NoiseUtil.seed(regionX, regionZ)).nextInt();
|
||||
this.lake = riverContext.lakes;
|
||||
this.primary = riverContext.primary;
|
||||
this.secondary = riverContext.secondary;
|
||||
this.tertiary = riverContext.tertiary;
|
||||
this.lake = riverMapConfig.lakes;
|
||||
this.primary = riverMapConfig.primary;
|
||||
this.secondary = riverMapConfig.secondary;
|
||||
this.tertiary = riverMapConfig.tertiary;
|
||||
this.terrains = context.terrain;
|
||||
this.domain = Domain.warp(seed, 400, 1, 400)
|
||||
.add(Domain.warp(seed + 1, 80, 1, 35));
|
||||
|
||||
this.primaryLimit = NoiseUtil.round(10 * riverContext.frequency);
|
||||
this.secondaryLimit = NoiseUtil.round(20 * riverContext.frequency);
|
||||
this.secondaryRelaxedLimit = NoiseUtil.round(30 * riverContext.frequency);
|
||||
this.forkLimit = NoiseUtil.round(40 * riverContext.frequency);
|
||||
this.tertiaryLimit = NoiseUtil.round(50 * riverContext.frequency);
|
||||
this.primaryLimit = NoiseUtil.round(10 * riverMapConfig.frequency);
|
||||
this.secondaryLimit = NoiseUtil.round(20 * riverMapConfig.frequency);
|
||||
this.secondaryRelaxedLimit = NoiseUtil.round(30 * riverMapConfig.frequency);
|
||||
this.forkLimit = NoiseUtil.round(40 * riverMapConfig.frequency);
|
||||
this.tertiaryLimit = NoiseUtil.round(50 * riverMapConfig.frequency);
|
||||
|
||||
this.primaryAttempts = NoiseUtil.round(100 * riverContext.frequency);
|
||||
this.secondaryAttempts = NoiseUtil.round(100 * riverContext.frequency);
|
||||
this.secondaryRelaxedAttempts = NoiseUtil.round(50 * riverContext.frequency);
|
||||
this.forkAttempts = NoiseUtil.round(75 * riverContext.frequency);
|
||||
this.tertiaryAttempts = NoiseUtil.round(50 * riverContext.frequency);
|
||||
this.primaryAttempts = NoiseUtil.round(100 * riverMapConfig.frequency);
|
||||
this.secondaryAttempts = NoiseUtil.round(100 * riverMapConfig.frequency);
|
||||
this.secondaryRelaxedAttempts = NoiseUtil.round(50 * riverMapConfig.frequency);
|
||||
this.forkAttempts = NoiseUtil.round(75 * riverMapConfig.frequency);
|
||||
this.tertiaryAttempts = NoiseUtil.round(50 * riverMapConfig.frequency);
|
||||
|
||||
try (ObjectPool.Item<Cell<Terrain>> cell = Cell.pooled()) {
|
||||
PosGenerator pos = new PosGenerator(heightmap, domain, cell.getValue(),1 << SCALE, River.VALLEY_WIDTH);
|
@ -24,15 +24,9 @@ repositories {
|
||||
dependencies {
|
||||
minecraft "net.minecraftforge:forge:${mc_version}-${forge_version}"
|
||||
compile project(":Noise2D")
|
||||
compile (project(":TerraForgedCore")) {
|
||||
transitive false
|
||||
}
|
||||
compile (project(":FeatureManager")) {
|
||||
transitive false
|
||||
}
|
||||
compile (project(":TerraForgedAPI")) {
|
||||
transitive false
|
||||
}
|
||||
compile (project(":TerraForgedCore")) { transitive false }
|
||||
compile (project(":FeatureManager")) { transitive false }
|
||||
compile (project(":TerraForgedAPI")) { transitive false }
|
||||
}
|
||||
|
||||
minecraft {
|
||||
@ -81,7 +75,6 @@ task collectResources(type: Copy) {
|
||||
|
||||
processResources {
|
||||
dependsOn(collectResources)
|
||||
|
||||
filesMatching("**/mods.toml") {
|
||||
expand(
|
||||
"version": "${mod_version}${getClassifier()}",
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
package com.terraforged.mod.feature;
|
||||
|
||||
import com.terraforged.api.material.state.States;
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectList;
|
||||
@ -89,7 +90,7 @@ public class TerrainHelper {
|
||||
}
|
||||
}
|
||||
|
||||
// try to fill in type air beneath village pieces with the biomes default filler block
|
||||
// lowers or raises the terrain matcher the base height of each structure piece
|
||||
private void buildBases(IChunk chunk, ObjectList<AbstractVillagePiece> pieces, int chunkStartX, int chunkStartZ) {
|
||||
BlockPos.Mutable pos = new BlockPos.Mutable();
|
||||
MutableBoundingBox chunkBounds = new MutableBoundingBox(chunkStartX, chunkStartZ, chunkStartX + 15, chunkStartZ + 15);
|
||||
@ -110,6 +111,9 @@ public class TerrainHelper {
|
||||
int endX = Math.min(chunkStartX + 15, expanded.maxX);
|
||||
int endZ = Math.min(chunkStartZ + 15, expanded.maxZ);
|
||||
|
||||
// y position of the structure piece
|
||||
int level = pieceBounds.minY + piece.getGroundLevelDelta();
|
||||
|
||||
// iterate the intersecting area
|
||||
for (int z = startZ; z <= endZ; z++) {
|
||||
for (int x = startX; x <= endX; x++) {
|
||||
@ -117,41 +121,57 @@ public class TerrainHelper {
|
||||
int dx = x & 15;
|
||||
int dz = z & 15;
|
||||
|
||||
// the paste position of the village piece
|
||||
BlockPos position = piece.getPos();
|
||||
|
||||
int offset = piece.getGroundLevelDelta();
|
||||
int level = position.getY() + (offset - 1);
|
||||
int surface = chunk.getTopBlockY(Heightmap.Type.OCEAN_FLOOR_WG, dx, dz) - 1;
|
||||
int height = level - surface;
|
||||
if (height <= 0) {
|
||||
int surface = chunk.getTopBlockY(Heightmap.Type.OCEAN_FLOOR_WG, dx, dz);
|
||||
if (surface == level) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float radius2 = Math.max(1, borderRadius * borderRadius * noise.getValue(x, z));
|
||||
float alpha = getAlpha(pieceBounds, radius2, x, z);
|
||||
if (alpha == 0F) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (alpha < 1F) {
|
||||
alpha = alpha * alpha;
|
||||
height = NoiseUtil.round(alpha * height);
|
||||
}
|
||||
|
||||
BlockState state = Blocks.STONE.getDefaultState();
|
||||
for (int dy = surface + height; dy >= surface; dy--) {
|
||||
pos.setPos(dx, dy, dz);
|
||||
if (chunk.getBlockState(pos).isSolid()) {
|
||||
break;
|
||||
}
|
||||
chunk.setBlockState(pos.setPos(dx, dy, dz), state, false);
|
||||
if (surface > level) {
|
||||
// world-surface is higher than the piece's base .: flatten terrain
|
||||
flatten(chunk, pieceBounds, pos.setPos(x, surface, z), dx, dz, level, surface, borderRadius);
|
||||
} else {
|
||||
// piece is higher than world-surface .: raise ground to form a base
|
||||
raise(chunk, pieceBounds, pos.setPos(x, surface, z), dx, dz, level, surface, borderRadius);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void flatten(IChunk chunk, MutableBoundingBox bounds, BlockPos.Mutable pos, int dx, int dz, int level, int surface, int borderRadius) {
|
||||
// only flatten terrain within the footprint of the structure piece
|
||||
if (pos.getX() >= bounds.minX && pos.getX() <= bounds.maxX && pos.getZ() >= bounds.minZ && pos.getZ() <= bounds.maxZ) {
|
||||
for (int dy = level + 1; dy <= surface; dy++) {
|
||||
chunk.setBlockState(pos.setPos(dx, dy, dz), Blocks.AIR.getDefaultState(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void raise(IChunk chunk, MutableBoundingBox bounds, BlockPos.Mutable pos, int dx, int dz, int level, int surface, int borderRadius) {
|
||||
float radius2 = Math.max(1, borderRadius * borderRadius * noise.getValue(pos.getX(), pos.getZ()));
|
||||
float alpha = getAlpha(bounds, radius2, pos.getX(), pos.getZ());
|
||||
if (alpha == 0F) {
|
||||
// outside of the raise-able radius
|
||||
return;
|
||||
}
|
||||
|
||||
int heightDelta = level - surface - 1;
|
||||
if (alpha < 1F) {
|
||||
// sharper fall-off
|
||||
alpha = alpha * alpha;
|
||||
heightDelta = NoiseUtil.round(alpha * heightDelta);
|
||||
}
|
||||
|
||||
BlockState state = States.STONE.getDefaultState();
|
||||
for (int dy = surface + heightDelta; dy >= surface; dy--) {
|
||||
pos.setPos(dx, dy, dz);
|
||||
if (chunk.getBlockState(pos).isSolid()) {
|
||||
return;
|
||||
}
|
||||
chunk.setBlockState(pos.setPos(dx, dy, dz), state, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static MutableBoundingBox expand(MutableBoundingBox box, int radius) {
|
||||
return new MutableBoundingBox(
|
||||
box.minX - radius,
|
||||
|
@ -1,32 +0,0 @@
|
||||
package com.terraforged.mod.feature.placement;
|
||||
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.IWorld;
|
||||
import net.minecraft.world.gen.ChunkGenerator;
|
||||
import net.minecraft.world.gen.Heightmap;
|
||||
import net.minecraft.world.gen.placement.HeightWithChanceConfig;
|
||||
import net.minecraft.world.gen.placement.Placement;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class BetterAtSurfaceWithChanceMultiple extends Placement<HeightWithChanceConfig> {
|
||||
|
||||
public BetterAtSurfaceWithChanceMultiple() {
|
||||
super(HeightWithChanceConfig::deserialize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<BlockPos> getPositions(IWorld world, ChunkGenerator<?> generator, Random random, HeightWithChanceConfig config, BlockPos pos) {
|
||||
return PosStream.of(config.count, next -> {
|
||||
if (random.nextFloat() < config.chance) {
|
||||
int x = random.nextInt(16) + pos.getX();
|
||||
int z = random.nextInt(16) + pos.getZ();
|
||||
int y = world.getHeight(Heightmap.Type.MOTION_BLOCKING, x, z);
|
||||
next.setPos(x, y, z);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package com.terraforged.mod.feature.placement;
|
||||
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
public interface PosGenerator {
|
||||
|
||||
boolean generate(BlockPos.Mutable next);
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
package com.terraforged.mod.feature.placement;
|
||||
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import java.util.Spliterator;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
public final class PosStream {
|
||||
|
||||
private static final ThreadLocal<BlockPos.Mutable> MUTABLE_THREAD_LOCAL = ThreadLocal.withInitial(BlockPos.Mutable::new);
|
||||
|
||||
private PosStream() {
|
||||
|
||||
}
|
||||
|
||||
public static Stream<BlockPos> of(int count, Populator populator) {
|
||||
return StreamSupport.stream(new PosSpliterator(count, populator), false);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Populator {
|
||||
|
||||
/**
|
||||
* Populates the BlockPos.Mutable with the next x,y,z coordinates
|
||||
*
|
||||
* @param next the BlockPos.Mutable to populate
|
||||
* @return true if the populated BlockPos.Mutable should be used next in the stream
|
||||
*/
|
||||
boolean populate(BlockPos.Mutable next);
|
||||
}
|
||||
|
||||
private static class PosSpliterator implements Spliterator<BlockPos> {
|
||||
|
||||
private final int attempts;
|
||||
private final Populator populator;
|
||||
private final BlockPos.Mutable pos = MUTABLE_THREAD_LOCAL.get();
|
||||
|
||||
private int counter = -1;
|
||||
|
||||
private PosSpliterator(int attempts, Populator populator) {
|
||||
this.attempts = attempts;
|
||||
this.populator = populator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryAdvance(Consumer<? super BlockPos> action) {
|
||||
if (counter < attempts) {
|
||||
counter++;
|
||||
if (populator.populate(pos)) {
|
||||
action.accept(pos);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<BlockPos> trySplit() {
|
||||
// cannot split
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long estimateSize() {
|
||||
return attempts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int characteristics() {
|
||||
// assume that positions are generated from a seeded random .: ordered
|
||||
return Spliterator.ORDERED;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user