- added slider to control the frequency of rivers

- increased the default frequency of rivers
This commit is contained in:
dags- 2020-03-17 09:52:25 +00:00
parent 0ec938690c
commit d12ab48560
8 changed files with 156 additions and 24 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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