improve river-fork angle restrictions

This commit is contained in:
dags- 2020-02-18 12:31:58 +00:00
parent 5b14cad343
commit 2996fff6bd
8 changed files with 41 additions and 71 deletions

View File

@ -153,7 +153,7 @@ public class Controller {
case 'v': case 'v':
try { try {
Object data = Toolkit.getDefaultToolkit().getSystemClipboard().getData(DataFlavor.stringFlavor); Object data = Toolkit.getDefaultToolkit().getSystemClipboard().getData(DataFlavor.stringFlavor);
newSeed = Integer.parseInt(data.toString()); newSeed = (int) Long.parseLong(data.toString());
} catch (Throwable t) { } catch (Throwable t) {
t.printStackTrace(); t.printStackTrace();
} }

View File

@ -1,11 +1,11 @@
package com.terraforged.app; package com.terraforged.app;
import com.terraforged.app.biome.BiomeProvider; import com.terraforged.app.biome.BiomeProvider;
import me.dags.noise.util.NoiseUtil;
import com.terraforged.core.cell.Cell; import com.terraforged.core.cell.Cell;
import com.terraforged.core.world.biome.BiomeData; import com.terraforged.core.world.biome.BiomeData;
import com.terraforged.core.world.biome.BiomeType; import com.terraforged.core.world.biome.BiomeType;
import com.terraforged.core.world.terrain.Terrain; import com.terraforged.core.world.terrain.Terrain;
import me.dags.noise.util.NoiseUtil;
import java.awt.*; import java.awt.*;
import java.util.Random; import java.util.Random;

View File

@ -1,7 +1,7 @@
package com.terraforged.app.mesh; package com.terraforged.app.mesh;
import com.terraforged.app.renderer.Renderer;
import com.terraforged.app.Applet; import com.terraforged.app.Applet;
import com.terraforged.app.renderer.Renderer;
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;
import processing.core.PApplet; import processing.core.PApplet;

View File

@ -1,7 +1,7 @@
package com.terraforged.app.mesh; package com.terraforged.app.mesh;
import com.terraforged.app.renderer.Renderer;
import com.terraforged.app.Applet; import com.terraforged.app.Applet;
import com.terraforged.app.renderer.Renderer;
public class TestRenderer extends Renderer { public class TestRenderer extends Renderer {

View File

@ -1,38 +0,0 @@
package com.terraforged.core.util;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class FutureValue<T> implements Future<T> {
private final T value;
public FutureValue(T value) {
this.value = value;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
return true;
}
@Override
public T get() {
return value;
}
@Override
public T get(long timeout, TimeUnit unit) {
return value;
}
}

View File

@ -139,7 +139,7 @@ public class PosGenerator {
return null; return null;
} }
private float getHeight(int x, int z) { public float getHeight(int x, int z) {
heightmap.visit(lookup, x, z); heightmap.visit(lookup, x, z);
return lookup.value; return lookup.value;
} }

View File

@ -19,11 +19,8 @@ import java.util.Random;
public class RiverRegion { public class RiverRegion {
public static final int SCALE = 12; public static final int SCALE = 12;
// private static final float LAKE_CHANCE = 0.05F; private static final double MIN_FORK_ANGLE = 20D;
// private static final float LAKE_MIN_DIST = 0.025F; private static final double MAX_FORK_ANGLE = 60D;
// private static final float LAKE_MAX_DIST = 0.05F;
// private static final float LAKE_MIN_SIZE = 50;
// private static final float LAKE_MAX_SIZE = 100;
private final Domain domain; private final Domain domain;
private final Terrains terrains; private final Terrains terrains;
@ -42,7 +39,8 @@ public class RiverRegion {
this.secondary = secondary; this.secondary = secondary;
this.tertiary = tertiary; this.tertiary = tertiary;
this.terrains = context.terrain; this.terrains = context.terrain;
this.domain = Domain.warp(seed, 400, 1, 400).add(Domain.warp(seed + 1, 50, 1, 25)); this.domain = Domain.warp(seed, 400, 1, 400)
.add(Domain.warp(seed + 1, 80, 1, 35));;
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);
@ -68,24 +66,22 @@ public class RiverRegion {
Random random = new Random(regionSeed); Random random = new Random(regionSeed);
List<River> rivers = new LinkedList<>(); List<River> rivers = new LinkedList<>();
// generates main rivers until either 10 attempts have passed or 2 rivers generate for (int i = 0; rivers.size() < 4 && i < 50; i++) {
for (int i = 0; rivers.size() < 2 && i < 50; i++) {
generateRiver(x, z, pos, primary, random, rivers); generateRiver(x, z, pos, primary, random, rivers);
} }
for (int i = 0; rivers.size() < 10 && i < 15; i++) { for (int i = 0; rivers.size() < 10 && i < 50; i++) {
generateRiver(x, z, pos, secondary, random, rivers); generateRiver(x, z, pos, secondary, random, rivers);
} }
for (int i = 0; rivers.size() < 10 && i < 15; i++) { for (int i = 0; rivers.size() < 20 && i < 50; i++) {
generateConnectingRiver(x, z, pos, tertiary, random, rivers); generateRiverFork(x, z, pos, tertiary, random, rivers);
} }
for (int i = 0; rivers.size() < 20 && i < 40; i++) { for (int i = 0; rivers.size() < 30 && i < 50; i++) {
generateRiver(x, z, pos, tertiary, random, rivers); generateRiver(x, z, pos, tertiary, random, rivers);
} }
Collections.reverse(rivers); Collections.reverse(rivers);
return rivers; return rivers;
@ -124,7 +120,7 @@ public class RiverRegion {
* Attempts to generate an inland position (p1), finds the nearest river (AB) to it, and tries to connect * Attempts to generate an inland position (p1), finds the nearest river (AB) to it, and tries to connect
* a line from p1 to some position along AB * a line from p1 to some position along AB
*/ */
private boolean generateConnectingRiver(int x, int z, PosGenerator pos, RiverConfig config, Random random, List<River> rivers) { private boolean generateRiverFork(int x, int z, PosGenerator pos, RiverConfig config, Random random, List<River> rivers) {
// generate a river start node // generate a river start node
RiverNode p1 = pos.nextType(x, z, random, 50, RiverNode.Type.START); RiverNode p1 = pos.nextType(x, z, random, 50, RiverNode.Type.START);
if (p1 == null) { if (p1 == null) {
@ -147,25 +143,37 @@ public class RiverRegion {
return false; return false;
} }
// p1 is too close to start of the closest river // connection should occur no less than 20% along the river length and no greater than 40%
if (startDist2 < 640000) { float distance = 0.3F + (random.nextFloat() * 0.3F);
Vec2i fork = closest.bounds.pos(distance).toInt();
// connection should not occur in the ocean
float height = pos.getHeight(fork.x, fork.y);
RiverNode.Type type = RiverNode.getType(height);
if (type == RiverNode.Type.END) {
return false; return false;
} }
// p1 should be closer to the main river start than it's end, otherwise we're likely to get forks // calc angle between vector a (AB) & b (CB)
// in the wrong direction (ie one river splitting into two, rather two rivers joining) // A - existing river's start
float endDist2 = dist2(p1.x, p1.z, closest.bounds.x2(), closest.bounds.y2()); // B - connection point
if (endDist2 < startDist2) { // C - new river's start
int ax = fork.x - closest.bounds.x1();
int az = fork.y - closest.bounds.y1();
int bx = fork.x - p1.x;
int bz = fork.y - p1.z;
// radians = arccos(AB.BC / |AB||BC|)
int dotAB = ax * bx + az * bz;
double lenA = Math.sqrt(ax * ax + az * az);
double lenB = Math.sqrt(bx * bx + bz * bz);
double radians = Math.acos(dotAB / (lenA * lenB));
double angle = Math.toDegrees(radians);
if (angle < MIN_FORK_ANGLE || angle > MAX_FORK_ANGLE) {
return false; return false;
} }
// pick a point some random distance along the main river, between 30%-70% along the main river length RiverBounds bounds = new RiverBounds(p1.x, p1.z, fork.x, fork.y, 300);
// 0.4 offset should ensure the main river has become sufficiently wide for it not to look weird having
// a second river connect to it
float dist = 0.4F + (random.nextFloat() * 0.3F);
Vec2i p2 = closest.bounds.pos(dist).toInt();
RiverBounds bounds = new RiverBounds(p1.x, p1.z, p2.x, p2.y, 300);
// check that the new river does not clash/overlap any other nearby rivers // check that the new river does not clash/overlap any other nearby rivers
for (River river : rivers) { for (River river : rivers) {
if (river == closest) { if (river == closest) {

@ -1 +1 @@
Subproject commit d8b7591c569df9f4a24a2d4e2f1bed447ccf1788 Subproject commit 04b79407108d3b2cf84d1c0cd287b887ebc4d0cd