- use fast-util long2obj maps

- reorganised river stuff
- improvements to TerrainHelper - fixes buildings spawning with terrain inside
This commit is contained in:
dags- 2020-03-20 09:36:13 +00:00
parent 04a85e8ea0
commit de1f845b71
26 changed files with 224 additions and 278 deletions

View File

@ -8,12 +8,12 @@ repositories {
dependencies { dependencies {
compile "org.processing:core:3.3.7" compile "org.processing:core:3.3.7"
compile "it.unimi.dsi:fastutil:8.2.1"
compile project(":TerraForgedCore") compile project(":TerraForgedCore")
} }
jar { jar {
manifest { attributes "Main-Class": "com.terraforged.app.Main" } manifest { attributes "Main-Class": "com.terraforged.app.Main" }
from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
} }

View File

@ -1,7 +1,12 @@
apply plugin: "maven-publish" apply plugin: "maven-publish"
repositories {
mavenCentral()
}
dependencies { dependencies {
compile project(":Noise2D") compile project(":Noise2D")
compile "it.unimi.dsi:fastutil:8.2.1"
} }
publishing { publishing {

View File

@ -28,14 +28,11 @@ package com.terraforged.core.region;
import com.terraforged.core.cell.Cell; import com.terraforged.core.cell.Cell;
import com.terraforged.core.cell.Extent; import com.terraforged.core.cell.Extent;
import com.terraforged.core.filter.Filterable; 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.ChunkReader;
import com.terraforged.core.region.chunk.ChunkWriter; 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.decorator.Decorator;
import com.terraforged.core.world.heightmap.Heightmap; 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 com.terraforged.core.world.terrain.Terrain;
import java.util.Collection; import java.util.Collection;
@ -173,47 +170,22 @@ public class Region implements Extent {
} }
} }
public void generate(Heightmap heightmap, Batcher batcher) { public void generateZoom(Heightmap heightmap, float offsetX, float offsetZ, float zoom) {
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) {
float translateX = offsetX - ((blockSize.size * zoom) / 2F); float translateX = offsetX - ((blockSize.size * zoom) / 2F);
float translateZ = offsetZ - ((blockSize.size * zoom) / 2F); float translateZ = offsetZ - ((blockSize.size * zoom) / 2F);
for (int cz = 0; cz < chunkSize.total; cz++) { for (int cz = 0; cz < chunkSize.total; cz++) {
for (int cx = 0; cx < chunkSize.total; cx++) { for (int cx = 0; cx < chunkSize.total; cx++) {
int index = chunkSize.indexOf(cx, cz); int index = chunkSize.indexOf(cx, cz);
GenChunk chunk = computeChunk(index, cx, cz); GenChunk chunk = computeChunk(index, cx, cz);
Runnable task = new ChunkZoomTask(chunk, heightmap, translateX, translateZ, zoom); for (int dz = 0; dz < 16; dz++) {
batcher.submit(task); 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);
} }
} }
} }
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);
}
}
} }
} }

View File

@ -38,7 +38,7 @@ public class RegionCache implements RegionExtent {
private final boolean queuing; private final boolean queuing;
private final RegionGenerator renderer; private final RegionGenerator renderer;
private final Cache<Long, CacheEntry<Region>> cache; private final Cache<CacheEntry<Region>> cache;
public RegionCache(boolean queueNeighbours, RegionGenerator renderer) { public RegionCache(boolean queueNeighbours, RegionGenerator renderer) {
this.renderer = renderer; this.renderer = renderer;

View File

@ -31,7 +31,7 @@ import com.terraforged.core.util.concurrent.cache.CacheEntry;
import com.terraforged.core.world.WorldGenerator; import com.terraforged.core.world.WorldGenerator;
import com.terraforged.core.world.WorldGeneratorFactory; import com.terraforged.core.world.WorldGeneratorFactory;
import com.terraforged.core.world.heightmap.RegionExtent; 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; import java.util.concurrent.CompletableFuture;
@ -89,7 +89,7 @@ public class RegionGenerator implements RegionExtent {
public Region generateRegion(int regionX, int regionZ) { public Region generateRegion(int regionX, int regionZ) {
WorldGenerator generator = genPool.get(); WorldGenerator generator = genPool.get();
Region region = regions.create(regionX, regionZ, factor, border); 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.generateBase(generator.getHeightmap());
region.generateRivers(generator.getHeightmap(), rivers); region.generateRivers(generator.getHeightmap(), rivers);
postProcess(region, generator); postProcess(region, generator);
@ -104,15 +104,7 @@ public class RegionGenerator implements RegionExtent {
public Region generateRegion(float centerX, float centerZ, float zoom, boolean filter) { public Region generateRegion(float centerX, float centerZ, float zoom, boolean filter) {
WorldGenerator generator = genPool.get(); WorldGenerator generator = genPool.get();
Region region = regions.create(0, 0, factor, border); Region region = regions.create(0, 0, factor, border);
float translateX = centerX - ((region.getBlockSize().size * zoom) / 2F); region.generateZoom(generator.getHeightmap(), centerX, centerZ, zoom);
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);
});
});
postProcess(region, generator, centerX, centerZ, zoom, filter); postProcess(region, generator, centerX, centerZ, zoom, filter);
return region; return region;
} }

View File

@ -2,31 +2,30 @@ package com.terraforged.core.util.concurrent.cache;
import com.terraforged.core.util.concurrent.ThreadPool; import com.terraforged.core.util.concurrent.ThreadPool;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Function; import java.util.function.LongFunction;
import java.util.function.Supplier; 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 expireMS;
private final long intervalMS; private final long intervalMS;
private final Map<K, V> map = new ConcurrentHashMap<>(); private final SynchronizedLongMap<V> map;
private volatile long timestamp = 0L; private volatile long timestamp = 0L;
public Cache(long expireTime, long interval, TimeUnit unit) { public Cache(long expireTime, long interval, TimeUnit unit) {
this.expireMS = unit.toMillis(expireTime); this.expireMS = unit.toMillis(expireTime);
this.intervalMS = unit.toMillis(interval); this.intervalMS = unit.toMillis(interval);
this.map = new SynchronizedLongMap<>(100);
} }
public V computeIfAbsent(K k, Supplier<V> supplier) { public V computeIfAbsent(long key, Supplier<V> supplier) {
return computeIfAbsent(k, o -> supplier.get()); return computeIfAbsent(key, o -> supplier.get());
} }
public V computeIfAbsent(K k, Function<K, V> func) { public V computeIfAbsent(long key, LongFunction<V> func) {
V v = map.computeIfAbsent(k, func); V v = map.computeIfAbsent(key, func);
queueUpdate(); queueUpdate();
return v; return v;
} }
@ -42,10 +41,6 @@ public class Cache<K, V extends ExpiringEntry> implements Runnable {
@Override @Override
public void run() { public void run() {
final long now = timestamp; final long now = timestamp;
map.forEach((key, val) -> { map.removeIf(val -> now - val.getTimestamp() > expireMS);
if (now - val.getTimestamp() > expireMS) {
map.remove(key);
}
});
} }
} }

View File

@ -1,16 +1,17 @@
package com.terraforged.core.util.concurrent.cache; 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.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 volatile long timestamp;
private CacheEntry(Callable<T> supplier) { private final Supplier<T> supplier;
super(supplier);
private CacheEntry(Supplier<T> supplier) {
this.supplier = supplier;
this.timestamp = System.currentTimeMillis(); this.timestamp = System.currentTimeMillis();
} }
@ -20,16 +21,30 @@ public class CacheEntry<T> extends FutureTask<T> implements ExpiringEntry {
} }
@Override @Override
public T get() { public void run() {
try { this.result = supplier.get();
this.timestamp = System.currentTimeMillis(); this.timestamp = System.currentTimeMillis();
return super.get();
} catch (Throwable t) {
throw new CompletionException(t);
}
} }
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); CacheEntry<T> entry = new CacheEntry<>(supplier);
executor.execute(entry); executor.execute(entry);
return entry; return entry;

View File

@ -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();
}
}
}
}
}

View File

@ -31,15 +31,15 @@ import com.terraforged.core.cell.Populator;
import com.terraforged.core.region.Size; import com.terraforged.core.region.Size;
import com.terraforged.core.util.concurrent.ObjectPool; import com.terraforged.core.util.concurrent.ObjectPool;
import com.terraforged.core.world.climate.Climate; import com.terraforged.core.world.climate.Climate;
import com.terraforged.core.world.river.RiverManager; import com.terraforged.core.world.rivermap.RiverMap;
import com.terraforged.core.world.river.RiverRegionList; import com.terraforged.core.world.rivermap.RiverRegionList;
import com.terraforged.core.world.terrain.Terrain; import com.terraforged.core.world.terrain.Terrain;
public interface Heightmap extends Populator, Extent { public interface Heightmap extends Populator, Extent {
Climate getClimate(); Climate getClimate();
RiverManager getRiverManager(); RiverMap getRiverMap();
void visit(Cell<Terrain> cell, float x, float z); void visit(Cell<Terrain> cell, float x, float z);

View File

@ -36,8 +36,8 @@ import com.terraforged.core.world.climate.Climate;
import com.terraforged.core.world.continent.ContinentLerper2; import com.terraforged.core.world.continent.ContinentLerper2;
import com.terraforged.core.world.continent.ContinentLerper3; import com.terraforged.core.world.continent.ContinentLerper3;
import com.terraforged.core.world.continent.ContinentModule; import com.terraforged.core.world.continent.ContinentModule;
import com.terraforged.core.world.river.RiverManager; import com.terraforged.core.world.rivermap.RiverMap;
import com.terraforged.core.world.river.RiverRegionList; import com.terraforged.core.world.rivermap.RiverRegionList;
import com.terraforged.core.world.terrain.Terrain; import com.terraforged.core.world.terrain.Terrain;
import com.terraforged.core.world.terrain.TerrainPopulator; import com.terraforged.core.world.terrain.TerrainPopulator;
import com.terraforged.core.world.terrain.Terrains; import com.terraforged.core.world.terrain.Terrains;
@ -67,7 +67,7 @@ public class WorldHeightmap implements Heightmap {
private final Climate climate; private final Climate climate;
private final Populator root; private final Populator root;
private final RiverManager riverManager; private final RiverMap riverMap;
private final TerrainProvider terrainProvider; private final TerrainProvider terrainProvider;
public WorldHeightmap(GeneratorContext context) { public WorldHeightmap(GeneratorContext context) {
@ -154,7 +154,7 @@ public class WorldHeightmap implements Heightmap {
COAST_VALUE - 0.05F COAST_VALUE - 0.05F
); );
riverManager = new RiverManager(this, context); riverMap = new RiverMap(this, context);
} }
@Override @Override
@ -167,7 +167,7 @@ public class WorldHeightmap implements Heightmap {
@Override @Override
public void apply(Cell<Terrain> cell, float x, float z) { public void apply(Cell<Terrain> cell, float x, float z) {
applyBase(cell, x, 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); applyClimate(cell, x, z);
} }
@ -219,8 +219,8 @@ public class WorldHeightmap implements Heightmap {
} }
@Override @Override
public RiverManager getRiverManager() { public RiverMap getRiverMap() {
return riverManager; return riverMap;
} }
public Populator getPopulator(Terrain terrain) { public Populator getPopulator(Terrain terrain) {

View File

@ -23,10 +23,11 @@
* SOFTWARE. * SOFTWARE.
*/ */
package com.terraforged.core.world.river; package com.terraforged.core.world.rivermap;
import com.terraforged.core.cell.Cell; import com.terraforged.core.cell.Cell;
import com.terraforged.core.world.heightmap.Heightmap; import com.terraforged.core.world.heightmap.Heightmap;
import com.terraforged.core.world.rivermap.river.RiverNode;
import com.terraforged.core.world.terrain.Terrain; import com.terraforged.core.world.terrain.Terrain;
import me.dags.noise.domain.Domain; import me.dags.noise.domain.Domain;
import me.dags.noise.util.Vec2i; import me.dags.noise.util.Vec2i;

View File

@ -23,7 +23,7 @@
* SOFTWARE. * SOFTWARE.
*/ */
package com.terraforged.core.world.river; package com.terraforged.core.world.rivermap;
import com.terraforged.core.cell.Cell; import com.terraforged.core.cell.Cell;
import com.terraforged.core.region.Region; 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.util.concurrent.cache.CacheEntry;
import com.terraforged.core.world.GeneratorContext; import com.terraforged.core.world.GeneratorContext;
import com.terraforged.core.world.heightmap.Heightmap; 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 com.terraforged.core.world.terrain.Terrain;
import me.dags.noise.util.NoiseUtil; import me.dags.noise.util.NoiseUtil;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class RiverManager { public class RiverMap {
private static final int QUAD_SIZE = (1 << RiverRegion.SCALE) / 2; private static final int QUAD_SIZE = (1 << RiverRegion.SCALE) / 2;
private final Heightmap heightmap; private final Heightmap heightmap;
private final GeneratorContext context; private final GeneratorContext context;
private final RiverContext riverContext; private final RiverMapConfig riverMapConfig;
private final Cache<Long, CacheEntry<RiverRegion>> cache = new Cache<>(120, 60, TimeUnit.SECONDS); private final Cache<CacheEntry<RiverRegion>> cache;
public RiverManager(Heightmap heightmap, GeneratorContext context) { public RiverMap(Heightmap heightmap, GeneratorContext context) {
RiverConfig primary = RiverConfig.builder(context.levels) RiverConfig primary = RiverConfig.builder(context.levels)
.bankHeight(context.settings.rivers.primaryRivers.minBankHeight, context.settings.rivers.primaryRivers.maxBankHeight) .bankHeight(context.settings.rivers.primaryRivers.minBankHeight, context.settings.rivers.primaryRivers.maxBankHeight)
.bankWidth(context.settings.rivers.primaryRivers.bankWidth) .bankWidth(context.settings.rivers.primaryRivers.bankWidth)
@ -76,7 +79,8 @@ public class RiverManager {
this.heightmap = heightmap; this.heightmap = heightmap;
this.context = context; 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) { public RiverRegionList getRivers(Region region) {
@ -100,8 +104,7 @@ public class RiverManager {
RiverRegionList list = new RiverRegionList(); RiverRegionList list = new RiverRegionList();
for (int dz = minZ; dz <= maxZ; dz++) { for (int dz = minZ; dz <= maxZ; dz++) {
for (int dx = minX; dx <= maxX; dx++) { for (int dx = minX; dx <= maxX; dx++) {
CacheEntry<RiverRegion> entry = getRegion(rx + dx, rz + dz); list.add(getRegion(rx + dx, rz + dz));
list.add(entry);
} }
} }
@ -148,6 +151,6 @@ public class RiverManager {
} }
private CacheEntry<RiverRegion> generateRegion(int rx, int rz) { 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());
} }
} }

View File

@ -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 float frequency;
public final RiverConfig primary; public final RiverConfig primary;
@ -8,7 +11,7 @@ public class RiverContext {
public final RiverConfig tertiary; public final RiverConfig tertiary;
public final LakeConfig lakes; 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.frequency = frequency;
this.primary = primary; this.primary = primary;
this.secondary = secondary; this.secondary = secondary;

View File

@ -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.cell.Cell;
import com.terraforged.core.util.concurrent.cache.CacheEntry; import com.terraforged.core.util.concurrent.cache.CacheEntry;
import com.terraforged.core.world.rivermap.river.RiverRegion;
import com.terraforged.core.world.terrain.Terrain; import com.terraforged.core.world.terrain.Terrain;
public class RiverRegionList { public class RiverRegionList {
@ -10,7 +11,10 @@ public class RiverRegionList {
private final CacheEntry<RiverRegion>[] regions = new CacheEntry[4]; private final CacheEntry<RiverRegion>[] regions = new CacheEntry[4];
protected void add(CacheEntry<RiverRegion> entry) { 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) { public void apply(Cell<Terrain> cell, float x, float z) {

View File

@ -23,9 +23,10 @@
* SOFTWARE. * SOFTWARE.
*/ */
package com.terraforged.core.world.river; package com.terraforged.core.world.rivermap.lake;
import com.terraforged.core.cell.Cell; 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.Terrain;
import com.terraforged.core.world.terrain.TerrainPopulator; import com.terraforged.core.world.terrain.TerrainPopulator;
import com.terraforged.core.world.terrain.Terrains; import com.terraforged.core.world.terrain.Terrains;

View File

@ -23,7 +23,7 @@
* SOFTWARE. * SOFTWARE.
*/ */
package com.terraforged.core.world.river; package com.terraforged.core.world.rivermap.lake;
import com.terraforged.core.settings.RiverSettings; import com.terraforged.core.settings.RiverSettings;
import com.terraforged.core.world.heightmap.Levels; import com.terraforged.core.world.heightmap.Levels;

View File

@ -23,7 +23,7 @@
* SOFTWARE. * SOFTWARE.
*/ */
package com.terraforged.core.world.river; package com.terraforged.core.world.rivermap.river;
import com.terraforged.core.cell.Cell; import com.terraforged.core.cell.Cell;
import com.terraforged.core.world.terrain.Terrain; import com.terraforged.core.world.terrain.Terrain;

View File

@ -23,7 +23,7 @@
* SOFTWARE. * SOFTWARE.
*/ */
package com.terraforged.core.world.river; package com.terraforged.core.world.rivermap.river;
import me.dags.noise.util.Vec2f; import me.dags.noise.util.Vec2f;

View File

@ -23,7 +23,7 @@
* SOFTWARE. * SOFTWARE.
*/ */
package com.terraforged.core.world.river; package com.terraforged.core.world.rivermap.river;
import com.terraforged.core.world.heightmap.Levels; import com.terraforged.core.world.heightmap.Levels;
import me.dags.noise.util.NoiseUtil; import me.dags.noise.util.NoiseUtil;

View File

@ -23,7 +23,7 @@
* SOFTWARE. * SOFTWARE.
*/ */
package com.terraforged.core.world.river; package com.terraforged.core.world.rivermap.river;
public class RiverNode { public class RiverNode {

View File

@ -23,13 +23,17 @@
* SOFTWARE. * SOFTWARE.
*/ */
package com.terraforged.core.world.river; package com.terraforged.core.world.rivermap.river;
import com.terraforged.core.cell.Cell; import com.terraforged.core.cell.Cell;
import com.terraforged.core.util.concurrent.ObjectPool; import com.terraforged.core.util.concurrent.ObjectPool;
import com.terraforged.core.util.concurrent.cache.ExpiringEntry; import com.terraforged.core.util.concurrent.cache.ExpiringEntry;
import com.terraforged.core.world.GeneratorContext; import com.terraforged.core.world.GeneratorContext;
import com.terraforged.core.world.heightmap.Heightmap; 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.Terrain;
import com.terraforged.core.world.terrain.Terrains; import com.terraforged.core.world.terrain.Terrains;
import me.dags.noise.domain.Domain; import me.dags.noise.domain.Domain;
@ -73,27 +77,27 @@ public class RiverRegion implements ExpiringEntry {
private final long timestamp = System.currentTimeMillis() + EXPIRE_TIME; 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(); int seed = new Random(NoiseUtil.seed(regionX, regionZ)).nextInt();
this.lake = riverContext.lakes; this.lake = riverMapConfig.lakes;
this.primary = riverContext.primary; this.primary = riverMapConfig.primary;
this.secondary = riverContext.secondary; this.secondary = riverMapConfig.secondary;
this.tertiary = riverContext.tertiary; this.tertiary = riverMapConfig.tertiary;
this.terrains = context.terrain; this.terrains = context.terrain;
this.domain = Domain.warp(seed, 400, 1, 400) this.domain = Domain.warp(seed, 400, 1, 400)
.add(Domain.warp(seed + 1, 80, 1, 35)); .add(Domain.warp(seed + 1, 80, 1, 35));
this.primaryLimit = NoiseUtil.round(10 * riverContext.frequency); this.primaryLimit = NoiseUtil.round(10 * riverMapConfig.frequency);
this.secondaryLimit = NoiseUtil.round(20 * riverContext.frequency); this.secondaryLimit = NoiseUtil.round(20 * riverMapConfig.frequency);
this.secondaryRelaxedLimit = NoiseUtil.round(30 * riverContext.frequency); this.secondaryRelaxedLimit = NoiseUtil.round(30 * riverMapConfig.frequency);
this.forkLimit = NoiseUtil.round(40 * riverContext.frequency); this.forkLimit = NoiseUtil.round(40 * riverMapConfig.frequency);
this.tertiaryLimit = NoiseUtil.round(50 * riverContext.frequency); this.tertiaryLimit = NoiseUtil.round(50 * riverMapConfig.frequency);
this.primaryAttempts = NoiseUtil.round(100 * riverContext.frequency); this.primaryAttempts = NoiseUtil.round(100 * riverMapConfig.frequency);
this.secondaryAttempts = NoiseUtil.round(100 * riverContext.frequency); this.secondaryAttempts = NoiseUtil.round(100 * riverMapConfig.frequency);
this.secondaryRelaxedAttempts = NoiseUtil.round(50 * riverContext.frequency); this.secondaryRelaxedAttempts = NoiseUtil.round(50 * riverMapConfig.frequency);
this.forkAttempts = NoiseUtil.round(75 * riverContext.frequency); this.forkAttempts = NoiseUtil.round(75 * riverMapConfig.frequency);
this.tertiaryAttempts = NoiseUtil.round(50 * riverContext.frequency); this.tertiaryAttempts = NoiseUtil.round(50 * riverMapConfig.frequency);
try (ObjectPool.Item<Cell<Terrain>> cell = Cell.pooled()) { try (ObjectPool.Item<Cell<Terrain>> cell = Cell.pooled()) {
PosGenerator pos = new PosGenerator(heightmap, domain, cell.getValue(),1 << SCALE, River.VALLEY_WIDTH); PosGenerator pos = new PosGenerator(heightmap, domain, cell.getValue(),1 << SCALE, River.VALLEY_WIDTH);

View File

@ -24,15 +24,9 @@ repositories {
dependencies { dependencies {
minecraft "net.minecraftforge:forge:${mc_version}-${forge_version}" minecraft "net.minecraftforge:forge:${mc_version}-${forge_version}"
compile project(":Noise2D") compile project(":Noise2D")
compile (project(":TerraForgedCore")) { compile (project(":TerraForgedCore")) { transitive false }
transitive false compile (project(":FeatureManager")) { transitive false }
} compile (project(":TerraForgedAPI")) { transitive false }
compile (project(":FeatureManager")) {
transitive false
}
compile (project(":TerraForgedAPI")) {
transitive false
}
} }
minecraft { minecraft {
@ -81,7 +75,6 @@ task collectResources(type: Copy) {
processResources { processResources {
dependsOn(collectResources) dependsOn(collectResources)
filesMatching("**/mods.toml") { filesMatching("**/mods.toml") {
expand( expand(
"version": "${mod_version}${getClassifier()}", "version": "${mod_version}${getClassifier()}",

View File

@ -25,6 +25,7 @@
package com.terraforged.mod.feature; package com.terraforged.mod.feature;
import com.terraforged.api.material.state.States;
import it.unimi.dsi.fastutil.longs.LongIterator; import it.unimi.dsi.fastutil.longs.LongIterator;
import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectList; 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) { private void buildBases(IChunk chunk, ObjectList<AbstractVillagePiece> pieces, int chunkStartX, int chunkStartZ) {
BlockPos.Mutable pos = new BlockPos.Mutable(); BlockPos.Mutable pos = new BlockPos.Mutable();
MutableBoundingBox chunkBounds = new MutableBoundingBox(chunkStartX, chunkStartZ, chunkStartX + 15, chunkStartZ + 15); 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 endX = Math.min(chunkStartX + 15, expanded.maxX);
int endZ = Math.min(chunkStartZ + 15, expanded.maxZ); int endZ = Math.min(chunkStartZ + 15, expanded.maxZ);
// y position of the structure piece
int level = pieceBounds.minY + piece.getGroundLevelDelta();
// iterate the intersecting area // iterate the intersecting area
for (int z = startZ; z <= endZ; z++) { for (int z = startZ; z <= endZ; z++) {
for (int x = startX; x <= endX; x++) { for (int x = startX; x <= endX; x++) {
@ -117,40 +121,56 @@ public class TerrainHelper {
int dx = x & 15; int dx = x & 15;
int dz = z & 15; int dz = z & 15;
// the paste position of the village piece int surface = chunk.getTopBlockY(Heightmap.Type.OCEAN_FLOOR_WG, dx, dz);
BlockPos position = piece.getPos(); if (surface == level) {
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) {
continue; continue;
} }
float radius2 = Math.max(1, borderRadius * borderRadius * noise.getValue(x, z)); if (surface > level) {
float alpha = getAlpha(pieceBounds, radius2, x, z); // 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) { if (alpha == 0F) {
continue; // outside of the raise-able radius
return;
} }
int heightDelta = level - surface - 1;
if (alpha < 1F) { if (alpha < 1F) {
// sharper fall-off
alpha = alpha * alpha; alpha = alpha * alpha;
height = NoiseUtil.round(alpha * height); heightDelta = NoiseUtil.round(alpha * heightDelta);
} }
BlockState state = Blocks.STONE.getDefaultState(); BlockState state = States.STONE.getDefaultState();
for (int dy = surface + height; dy >= surface; dy--) { for (int dy = surface + heightDelta; dy >= surface; dy--) {
pos.setPos(dx, dy, dz); pos.setPos(dx, dy, dz);
if (chunk.getBlockState(pos).isSolid()) { if (chunk.getBlockState(pos).isSolid()) {
break; return;
} }
chunk.setBlockState(pos.setPos(dx, dy, dz), state, false); chunk.setBlockState(pos.setPos(dx, dy, dz), state, false);
} }
} }
}
}
}
private static MutableBoundingBox expand(MutableBoundingBox box, int radius) { private static MutableBoundingBox expand(MutableBoundingBox box, int radius) {
return new MutableBoundingBox( return new MutableBoundingBox(

View File

@ -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;
});
}
}

View File

@ -1,8 +0,0 @@
package com.terraforged.mod.feature.placement;
import net.minecraft.util.math.BlockPos;
public interface PosGenerator {
boolean generate(BlockPos.Mutable next);
}

View File

@ -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;
}
}
}