- added slider to control the frequency of rivers
- increased the default frequency of rivers
This commit is contained in:
parent
0ec938690c
commit
d12ab48560
@ -68,7 +68,7 @@ public abstract class Renderer {
|
|||||||
return height * el;
|
return height * el;
|
||||||
} else if (applet.controller.getColorMode() == Applet.BIOME_TYPE) {
|
} else if (applet.controller.getColorMode() == Applet.BIOME_TYPE) {
|
||||||
Color c = cell.biomeType.getColor();
|
Color c = cell.biomeType.getColor();
|
||||||
if (cell.riverMask < 0.2) {
|
if (cell.riverMask < 0.025) {
|
||||||
c = Color.white;
|
c = Color.white;
|
||||||
}
|
}
|
||||||
float[] hsb = Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), null);
|
float[] hsb = Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), null);
|
||||||
|
@ -35,6 +35,10 @@ public class RiverSettings {
|
|||||||
/**
|
/**
|
||||||
* RIVER PROPERTIES
|
* RIVER PROPERTIES
|
||||||
*/
|
*/
|
||||||
|
@Range(min = 0.0F, max = 5F)
|
||||||
|
@Comment("Controls how frequently rivers generate")
|
||||||
|
public float riverFrequency = 1;
|
||||||
|
|
||||||
public River primaryRivers = new River(5, 2, 8, 25, 8, 0.75F);
|
public River primaryRivers = new River(5, 2, 8, 25, 8, 0.75F);
|
||||||
|
|
||||||
public River secondaryRiver = new River(4, 1, 6, 15, 5, 0.75F);
|
public River secondaryRiver = new River(4, 1, 6, 15, 5, 0.75F);
|
||||||
|
@ -40,6 +40,7 @@ import java.util.Random;
|
|||||||
*/
|
*/
|
||||||
public class PosGenerator {
|
public class PosGenerator {
|
||||||
|
|
||||||
|
private final int size;
|
||||||
private final int quadSize;
|
private final int quadSize;
|
||||||
private final Vec2i[] quads = new Vec2i[4];
|
private final Vec2i[] quads = new Vec2i[4];
|
||||||
|
|
||||||
@ -57,6 +58,7 @@ public class PosGenerator {
|
|||||||
this.lookup = lookup;
|
this.lookup = lookup;
|
||||||
this.padding = padding;
|
this.padding = padding;
|
||||||
this.heightmap = heightmap;
|
this.heightmap = heightmap;
|
||||||
|
this.size = size;
|
||||||
this.quadSize = (size - (padding * 2)) / 4;
|
this.quadSize = (size - (padding * 2)) / 4;
|
||||||
int x1 = 0;
|
int x1 = 0;
|
||||||
int y1 = 0;
|
int y1 = 0;
|
||||||
@ -130,6 +132,49 @@ public class PosGenerator {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RiverNode nextRelaxed(int x, int z, Random random, int attempts) {
|
||||||
|
for (int i = 0; i < attempts; i++) {
|
||||||
|
int px = x + random.nextInt(size);
|
||||||
|
int pz = z + random.nextInt(size);
|
||||||
|
int wx = (int) domain.getX(px, pz);
|
||||||
|
int wz = (int) domain.getY(px, pz);
|
||||||
|
float value1 = getHeight(px, pz);
|
||||||
|
float value2 = getHeight(wx, wz);
|
||||||
|
RiverNode.Type type1 = RiverNode.getType(value1);
|
||||||
|
RiverNode.Type type2 = RiverNode.getType(value2);
|
||||||
|
if (type1 == type2 && type1 != RiverNode.Type.NONE) {
|
||||||
|
if (type1 == RiverNode.Type.END) {
|
||||||
|
return new RiverNode(wx, wz, type1);
|
||||||
|
}
|
||||||
|
return new RiverNode(px, pz, type1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RiverNode nextFromRelaxed(int x, int z, Random random, int attempts, RiverNode point, int mindDist2) {
|
||||||
|
for (int i = 0; i < attempts; i++) {
|
||||||
|
int px = x + random.nextInt(size);
|
||||||
|
int pz = z + random.nextInt(size);
|
||||||
|
if (dist2(px, pz, point.x, point.z) < mindDist2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int wx = (int) domain.getX(px, pz);
|
||||||
|
int wz = (int) domain.getY(px, pz);
|
||||||
|
float value1 = getHeight(px, pz);
|
||||||
|
float value2 = getHeight(wx, wz);
|
||||||
|
RiverNode.Type type1 = RiverNode.getType(value1);
|
||||||
|
RiverNode.Type type2 = RiverNode.getType(value2);
|
||||||
|
if (type1 == type2 && type1 == point.type.opposite()) {
|
||||||
|
if (type1 == RiverNode.Type.END) {
|
||||||
|
return new RiverNode(wx, wz, type1);
|
||||||
|
}
|
||||||
|
return new RiverNode(px, pz, type1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public RiverNode nextType(int x, int z, Random random, int attempts, RiverNode.Type match) {
|
public RiverNode nextType(int x, int z, Random random, int attempts, RiverNode.Type match) {
|
||||||
for (int i = 0; i < attempts; i++) {
|
for (int i = 0; i < attempts; i++) {
|
||||||
nextSeed(random);
|
nextSeed(random);
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.terraforged.core.world.river;
|
||||||
|
|
||||||
|
public class RiverContext {
|
||||||
|
|
||||||
|
public final float frequency;
|
||||||
|
public final RiverConfig primary;
|
||||||
|
public final RiverConfig secondary;
|
||||||
|
public final RiverConfig tertiary;
|
||||||
|
public final LakeConfig lakes;
|
||||||
|
|
||||||
|
public RiverContext(float frequency, RiverConfig primary, RiverConfig secondary, RiverConfig tertiary, LakeConfig lakes) {
|
||||||
|
this.frequency = frequency;
|
||||||
|
this.primary = primary;
|
||||||
|
this.secondary = secondary;
|
||||||
|
this.tertiary = tertiary;
|
||||||
|
this.lakes = lakes;
|
||||||
|
}
|
||||||
|
}
|
@ -39,18 +39,13 @@ public class RiverManager {
|
|||||||
|
|
||||||
private static final int QUAD_SIZE = (1 << RiverRegion.SCALE) / 2;
|
private static final int QUAD_SIZE = (1 << RiverRegion.SCALE) / 2;
|
||||||
|
|
||||||
private final LakeConfig lakes;
|
|
||||||
private final RiverConfig primary;
|
|
||||||
private final RiverConfig secondary;
|
|
||||||
private final RiverConfig tertiary;
|
|
||||||
private final Heightmap heightmap;
|
private final Heightmap heightmap;
|
||||||
private final GeneratorContext context;
|
private final GeneratorContext context;
|
||||||
|
private final RiverContext riverContext;
|
||||||
private final Cache<Long, RiverRegion> cache = new Cache<>(60, 60, TimeUnit.SECONDS, () -> new ConcurrentHashMap<>());
|
private final Cache<Long, RiverRegion> cache = new Cache<>(60, 60, TimeUnit.SECONDS, () -> new ConcurrentHashMap<>());
|
||||||
|
|
||||||
public RiverManager(Heightmap heightmap, GeneratorContext context) {
|
public RiverManager(Heightmap heightmap, GeneratorContext context) {
|
||||||
this.heightmap = heightmap;
|
RiverConfig primary = RiverConfig.builder(context.levels)
|
||||||
this.context = context;
|
|
||||||
this.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)
|
||||||
.bedWidth(context.settings.rivers.primaryRivers.bedWidth)
|
.bedWidth(context.settings.rivers.primaryRivers.bedWidth)
|
||||||
@ -59,7 +54,7 @@ public class RiverManager {
|
|||||||
.length(2500)
|
.length(2500)
|
||||||
.main(true)
|
.main(true)
|
||||||
.build();
|
.build();
|
||||||
this.secondary = RiverConfig.builder(context.levels)
|
RiverConfig secondary = RiverConfig.builder(context.levels)
|
||||||
.bankHeight(context.settings.rivers.secondaryRiver.minBankHeight, context.settings.rivers.secondaryRiver.maxBankHeight)
|
.bankHeight(context.settings.rivers.secondaryRiver.minBankHeight, context.settings.rivers.secondaryRiver.maxBankHeight)
|
||||||
.bankWidth(context.settings.rivers.secondaryRiver.bankWidth)
|
.bankWidth(context.settings.rivers.secondaryRiver.bankWidth)
|
||||||
.bedWidth(context.settings.rivers.secondaryRiver.bedWidth)
|
.bedWidth(context.settings.rivers.secondaryRiver.bedWidth)
|
||||||
@ -67,7 +62,7 @@ public class RiverManager {
|
|||||||
.fade(context.settings.rivers.secondaryRiver.fade)
|
.fade(context.settings.rivers.secondaryRiver.fade)
|
||||||
.length(1000)
|
.length(1000)
|
||||||
.build();
|
.build();
|
||||||
this.tertiary = RiverConfig.builder(context.levels)
|
RiverConfig tertiary = RiverConfig.builder(context.levels)
|
||||||
.bankHeight(context.settings.rivers.tertiaryRivers.minBankHeight, context.settings.rivers.tertiaryRivers.maxBankHeight)
|
.bankHeight(context.settings.rivers.tertiaryRivers.minBankHeight, context.settings.rivers.tertiaryRivers.maxBankHeight)
|
||||||
.bankWidth(context.settings.rivers.tertiaryRivers.bankWidth)
|
.bankWidth(context.settings.rivers.tertiaryRivers.bankWidth)
|
||||||
.bedWidth(context.settings.rivers.tertiaryRivers.bedWidth)
|
.bedWidth(context.settings.rivers.tertiaryRivers.bedWidth)
|
||||||
@ -75,7 +70,11 @@ public class RiverManager {
|
|||||||
.fade(context.settings.rivers.tertiaryRivers.fade)
|
.fade(context.settings.rivers.tertiaryRivers.fade)
|
||||||
.length(500)
|
.length(500)
|
||||||
.build();
|
.build();
|
||||||
this.lakes = LakeConfig.of(context.settings.rivers.lake, context.levels);
|
LakeConfig lakes = LakeConfig.of(context.settings.rivers.lake, context.levels);
|
||||||
|
|
||||||
|
this.heightmap = heightmap;
|
||||||
|
this.context = context;
|
||||||
|
this.riverContext = new RiverContext(context.settings.rivers.riverFrequency, primary, secondary, tertiary, lakes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void apply(Cell<Terrain> cell, float x, float z) {
|
public void apply(Cell<Terrain> cell, float x, float z) {
|
||||||
@ -103,7 +102,7 @@ public class RiverManager {
|
|||||||
long id = NoiseUtil.seed(rx, rz);
|
long id = NoiseUtil.seed(rx, rz);
|
||||||
RiverRegion region = cache.get(id);
|
RiverRegion region = cache.get(id);
|
||||||
if (region == null) {
|
if (region == null) {
|
||||||
region = new RiverRegion(rx, rz, heightmap, context, primary, secondary, tertiary, lakes);
|
region = new RiverRegion(rx, rz, heightmap, context, riverContext);
|
||||||
cache.put(id, region);
|
cache.put(id, region);
|
||||||
}
|
}
|
||||||
return region;
|
return region;
|
||||||
|
@ -57,15 +57,39 @@ public class RiverRegion {
|
|||||||
private final List<River> rivers;
|
private final List<River> rivers;
|
||||||
private final List<Lake> lakes = new LinkedList<>();
|
private final List<Lake> lakes = new LinkedList<>();
|
||||||
|
|
||||||
public RiverRegion(int regionX, int regionZ, Heightmap heightmap, GeneratorContext context, RiverConfig primary, RiverConfig secondary, RiverConfig tertiary, LakeConfig lake) {
|
private final int primaryLimit;
|
||||||
|
private final int secondaryLimit;
|
||||||
|
private final int secondaryRelaxedLimit;
|
||||||
|
private final int forkLimit;
|
||||||
|
private final int tertiaryLimit;
|
||||||
|
private final int primaryAttempts;
|
||||||
|
private final int secondaryAttempts;
|
||||||
|
private final int secondaryRelaxedAttempts;
|
||||||
|
private final int forkAttempts;
|
||||||
|
private final int tertiaryAttempts;
|
||||||
|
|
||||||
|
public RiverRegion(int regionX, int regionZ, Heightmap heightmap, GeneratorContext context, RiverContext riverContext) {
|
||||||
int seed = new Random(NoiseUtil.seed(regionX, regionZ)).nextInt();
|
int seed = new Random(NoiseUtil.seed(regionX, regionZ)).nextInt();
|
||||||
this.lake = lake;
|
this.lake = riverContext.lakes;
|
||||||
this.primary = primary;
|
this.primary = riverContext.primary;
|
||||||
this.secondary = secondary;
|
this.secondary = riverContext.secondary;
|
||||||
this.tertiary = tertiary;
|
this.tertiary = riverContext.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.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.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);
|
||||||
|
|
||||||
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);
|
||||||
this.rivers = generate(regionX, regionZ, pos);
|
this.rivers = generate(regionX, regionZ, pos);
|
||||||
@ -91,19 +115,23 @@ public class RiverRegion {
|
|||||||
Random random = new Random(regionSeed);
|
Random random = new Random(regionSeed);
|
||||||
List<River> rivers = new LinkedList<>();
|
List<River> rivers = new LinkedList<>();
|
||||||
|
|
||||||
for (int i = 0; rivers.size() < 4 && i < 50; i++) {
|
for (int i = 0; rivers.size() < primaryLimit && i < primaryAttempts; i++) {
|
||||||
generateRiver(x, z, pos, primary, random, rivers);
|
generateRiver(x, z, pos, primary, random, rivers);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; rivers.size() < 15 && i < 100; i++) {
|
for (int i = 0; rivers.size() < secondaryLimit && i < secondaryAttempts; i++) {
|
||||||
generateRiver(x, z, pos, secondary, random, rivers);
|
generateRiver(x, z, pos, secondary, random, rivers);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; rivers.size() < 25 && i < 75; i++) {
|
for (int i = 0; rivers.size() < secondaryRelaxedLimit && i < secondaryRelaxedAttempts; i++) {
|
||||||
|
generateRiverRelaxed(x, z, pos, secondary, random, rivers);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; rivers.size() < forkLimit && i < forkAttempts; i++) {
|
||||||
generateRiverFork(x, z, pos, tertiary, random, rivers);
|
generateRiverFork(x, z, pos, tertiary, random, rivers);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; rivers.size() < 40 && i < 50; i++) {
|
for (int i = 0; rivers.size() < tertiaryLimit && i < tertiaryAttempts; i++) {
|
||||||
generateRiver(x, z, pos, tertiary, random, rivers);
|
generateRiver(x, z, pos, tertiary, random, rivers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,6 +246,32 @@ public class RiverRegion {
|
|||||||
return rivers.add(new River(bounds, forkConfig, terrains, forkConfig.fade, 0, true));
|
return rivers.add(new River(bounds, forkConfig, terrains, forkConfig.fade, 0, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean generateRiverRelaxed(int x, int z, PosGenerator pos, RiverConfig config, Random random, List<River> rivers) {
|
||||||
|
// generate either a river start or end node
|
||||||
|
RiverNode p1 = pos.nextRelaxed(x, z, random, 50);
|
||||||
|
if (p1 == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate a node with a min distance from p1 and that has the opposite node type to p1
|
||||||
|
RiverNode p2 = pos.nextFromRelaxed(x, z, random,50, p1, config.length2);
|
||||||
|
if (p2 == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// avoid collisions with existing rivers
|
||||||
|
RiverBounds bounds = RiverBounds.fromNodes(p1, p2);
|
||||||
|
for (River river : rivers) {
|
||||||
|
if (bounds.overlaps(river.bounds)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generateLake(bounds, random);
|
||||||
|
|
||||||
|
return rivers.add(new River(bounds, config, terrains, config.fade, 0));
|
||||||
|
}
|
||||||
|
|
||||||
private void generateLake(RiverBounds bounds, Random random) {
|
private void generateLake(RiverBounds bounds, Random random) {
|
||||||
if (random.nextFloat() < lake.chance) {
|
if (random.nextFloat() < lake.chance) {
|
||||||
float size = lake.sizeMin + (random.nextFloat() * lake.sizeMax);
|
float size = lake.sizeMin + (random.nextFloat() * lake.sizeMax);
|
||||||
|
@ -208,7 +208,7 @@ public class TerraChunkGenerator extends ObfHelperChunkGenerator<GenerationSetti
|
|||||||
postProcess(container.getChunkReader(), container, context);
|
postProcess(container.getChunkReader(), container, context);
|
||||||
|
|
||||||
// bake biome array & discard gen data
|
// bake biome array & discard gen data
|
||||||
((ChunkPrimer) chunk).func_225548_a_(container.bakeBiomes());
|
((ChunkPrimer) chunk).func_225548_a_(container.bakeBiomes(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
package com.terraforged.mod.chunk;
|
package com.terraforged.mod.chunk;
|
||||||
|
|
||||||
|
import com.terraforged.api.biome.BiomeVariant;
|
||||||
import com.terraforged.core.cell.Cell;
|
import com.terraforged.core.cell.Cell;
|
||||||
import com.terraforged.core.region.chunk.ChunkReader;
|
import com.terraforged.core.region.chunk.ChunkReader;
|
||||||
import com.terraforged.core.util.PosIterator;
|
import com.terraforged.core.util.PosIterator;
|
||||||
@ -74,7 +75,18 @@ public class TerraContainer extends BiomeContainer {
|
|||||||
return getBiome(8, 8);
|
return getBiome(8, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BiomeContainer bakeBiomes() {
|
public BiomeContainer bakeBiomes(boolean convertToVanilla) {
|
||||||
|
if (convertToVanilla) {
|
||||||
|
Biome[] biomeArray = new Biome[biomes.length];
|
||||||
|
for (int i = 0; i < biomes.length; i++) {
|
||||||
|
Biome biome = biomes[i];
|
||||||
|
if (biome instanceof BiomeVariant) {
|
||||||
|
biome = ((BiomeVariant) biome).getBase();
|
||||||
|
}
|
||||||
|
biomeArray[i] = biome;
|
||||||
|
}
|
||||||
|
return new BiomeContainer(biomeArray);
|
||||||
|
}
|
||||||
return new BiomeContainer(biomes);
|
return new BiomeContainer(biomes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user