town places roads
This commit is contained in:
parent
0cd7248b94
commit
97e32fabdb
@ -1910,6 +1910,11 @@ Transparent block support
|
|||||||
Grab hardware data on init rendering engine
|
Grab hardware data on init rendering engine
|
||||||
Delete unused class
|
Delete unused class
|
||||||
|
|
||||||
|
(05/20/2025)
|
||||||
|
Calculate road-interection nodes for town layout
|
||||||
|
Place roads using line segments instead of splines
|
||||||
|
Town layout tries to connect intersection nodes with roads
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -374,7 +374,7 @@ public class Realm {
|
|||||||
throw new Error("Null position!");
|
throw new Error("Null position!");
|
||||||
}
|
}
|
||||||
if(macroData != null){
|
if(macroData != null){
|
||||||
MacroDataUpdater.update(macroData, playerPosition);
|
MacroDataUpdater.update(this, macroData, playerPosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package electrosphere.server.macro;
|
|||||||
|
|
||||||
import org.joml.Vector3d;
|
import org.joml.Vector3d;
|
||||||
|
|
||||||
|
import electrosphere.server.datacell.Realm;
|
||||||
import electrosphere.server.macro.town.Town;
|
import electrosphere.server.macro.town.Town;
|
||||||
import electrosphere.server.macro.town.TownLayout;
|
import electrosphere.server.macro.town.TownLayout;
|
||||||
|
|
||||||
@ -17,10 +18,11 @@ public class MacroDataUpdater {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the macro data
|
* Updates the macro data
|
||||||
|
* @param realm The realm related to this data
|
||||||
* @param macroData The data
|
* @param macroData The data
|
||||||
* @param playerPos The player's position
|
* @param playerPos The player's position
|
||||||
*/
|
*/
|
||||||
public static void update(MacroData macroData, Vector3d playerPos){
|
public static void update(Realm realm, MacroData macroData, Vector3d playerPos){
|
||||||
//scan for all towns within update range
|
//scan for all towns within update range
|
||||||
for(Town town : macroData.getTowns()){
|
for(Town town : macroData.getTowns()){
|
||||||
//only generate data for towns that aren't already full resolution
|
//only generate data for towns that aren't already full resolution
|
||||||
@ -29,7 +31,7 @@ public class MacroDataUpdater {
|
|||||||
}
|
}
|
||||||
Vector3d townPos = town.getPos();
|
Vector3d townPos = town.getPos();
|
||||||
if(townPos.distance(playerPos) < TOWN_GENERATION_DIST){
|
if(townPos.distance(playerPos) < TOWN_GENERATION_DIST){
|
||||||
TownLayout.layoutTown(macroData, town);
|
TownLayout.layoutTown(realm, macroData, town);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,11 +5,9 @@ import org.joml.Vector3d;
|
|||||||
import electrosphere.data.Config;
|
import electrosphere.data.Config;
|
||||||
import electrosphere.server.datacell.ServerWorldData;
|
import electrosphere.server.datacell.ServerWorldData;
|
||||||
import electrosphere.server.macro.MacroData;
|
import electrosphere.server.macro.MacroData;
|
||||||
import electrosphere.server.macro.civilization.road.Road;
|
|
||||||
import electrosphere.server.macro.race.Race;
|
import electrosphere.server.macro.race.Race;
|
||||||
import electrosphere.server.macro.town.Town;
|
import electrosphere.server.macro.town.Town;
|
||||||
import electrosphere.server.physics.terrain.manager.ServerTerrainChunk;
|
import electrosphere.server.physics.terrain.manager.ServerTerrainChunk;
|
||||||
import electrosphere.util.ds.Spline3d;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates civilizations
|
* Generates civilizations
|
||||||
@ -35,12 +33,6 @@ public class CivilizationGenerator {
|
|||||||
Civilization newCiv = Civilization.createCivilization(macroData, race);
|
Civilization newCiv = Civilization.createCivilization(macroData, race);
|
||||||
newCiv.addRace(race);
|
newCiv.addRace(race);
|
||||||
Town.createTown(macroData, spawnPoint, INITIAL_TOWN_RADIUS, newCiv.getId());
|
Town.createTown(macroData, spawnPoint, INITIAL_TOWN_RADIUS, newCiv.getId());
|
||||||
Road.createRoad(macroData, Spline3d.createCatmullRom(new Vector3d[]{
|
|
||||||
new Vector3d(spawnPoint).add(-20,0,0),
|
|
||||||
new Vector3d(spawnPoint).add(-10,0,0),
|
|
||||||
new Vector3d(spawnPoint).add( 10,0,0),
|
|
||||||
new Vector3d(spawnPoint).add( 20,0,0),
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import org.joml.Vector3d;
|
|||||||
|
|
||||||
import electrosphere.server.macro.MacroData;
|
import electrosphere.server.macro.MacroData;
|
||||||
import electrosphere.server.macro.spatial.MacroAreaObject;
|
import electrosphere.server.macro.spatial.MacroAreaObject;
|
||||||
import electrosphere.util.ds.Spline3d;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A road
|
* A road
|
||||||
@ -15,7 +14,7 @@ public class Road implements MacroAreaObject {
|
|||||||
/**
|
/**
|
||||||
* The default radius
|
* The default radius
|
||||||
*/
|
*/
|
||||||
public static final double DEFAULT_RADIUS = 3;
|
public static final double DEFAULT_RADIUS = 5;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default material
|
* The default material
|
||||||
@ -28,9 +27,14 @@ public class Road implements MacroAreaObject {
|
|||||||
int id;
|
int id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The spline that the road is aligned along
|
* The start position of the road segment
|
||||||
*/
|
*/
|
||||||
Spline3d spline;
|
Vector3d startPos;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The end position of the road segment
|
||||||
|
*/
|
||||||
|
Vector3d endPos;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The radius of the road
|
* The radius of the road
|
||||||
@ -55,13 +59,15 @@ public class Road implements MacroAreaObject {
|
|||||||
/**
|
/**
|
||||||
* Creates a road
|
* Creates a road
|
||||||
* @param macroData The macro data
|
* @param macroData The macro data
|
||||||
* @param spline The spline
|
* @param start The start position
|
||||||
* @param material The material for the road
|
* @param end The end position
|
||||||
* @return The road
|
* @return The road
|
||||||
*/
|
*/
|
||||||
public static Road createRoad(MacroData macroData, Spline3d spline){
|
public static Road createRoad(MacroData macroData, Vector3d start, Vector3d endPos){
|
||||||
Road road = new Road();
|
Road road = new Road();
|
||||||
road.setSpline(spline);
|
road.startPos = start;
|
||||||
|
road.endPos = endPos;
|
||||||
|
road.computeAABB();
|
||||||
macroData.addRoad(road);
|
macroData.addRoad(road);
|
||||||
return road;
|
return road;
|
||||||
}
|
}
|
||||||
@ -82,23 +88,6 @@ public class Road implements MacroAreaObject {
|
|||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the spline that the road is aligned along
|
|
||||||
* @return The spline
|
|
||||||
*/
|
|
||||||
public Spline3d getSpline() {
|
|
||||||
return spline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the spline that the road is aligned along
|
|
||||||
* @param spline The spline
|
|
||||||
*/
|
|
||||||
public void setSpline(Spline3d spline) {
|
|
||||||
this.spline = spline;
|
|
||||||
this.computeAABB();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the radius of the road
|
* Gets the radius of the road
|
||||||
* @return The radius
|
* @return The radius
|
||||||
@ -136,32 +125,28 @@ public class Road implements MacroAreaObject {
|
|||||||
*/
|
*/
|
||||||
private void computeAABB(){
|
private void computeAABB(){
|
||||||
this.aabb = new AABBd();
|
this.aabb = new AABBd();
|
||||||
for(Vector3d point : this.spline.getPoints()){
|
this.aabb.minX = Math.min(this.startPos.x,this.endPos.x) - radius;
|
||||||
if(this.aabb.minX > point.x){
|
this.aabb.minY = Math.min(this.startPos.y,this.endPos.y) - radius;
|
||||||
this.aabb.minX = point.x;
|
this.aabb.minZ = Math.min(this.startPos.z,this.endPos.z) - radius;
|
||||||
}
|
this.aabb.maxX = Math.max(this.startPos.x,this.endPos.x) + radius;
|
||||||
if(this.aabb.minY > point.y){
|
this.aabb.maxY = Math.max(this.startPos.y,this.endPos.y) + radius;
|
||||||
this.aabb.minY = point.y;
|
this.aabb.maxZ = Math.max(this.startPos.z,this.endPos.z) + radius;
|
||||||
}
|
}
|
||||||
if(this.aabb.minZ > point.z){
|
|
||||||
this.aabb.minZ = point.z;
|
/**
|
||||||
}
|
* Gets the first point of the road segment
|
||||||
if(this.aabb.maxX < point.x){
|
* @return The first point
|
||||||
this.aabb.maxX = point.x;
|
*/
|
||||||
}
|
public Vector3d getPoint1(){
|
||||||
if(this.aabb.maxY < point.y){
|
return startPos;
|
||||||
this.aabb.maxY = point.y;
|
}
|
||||||
}
|
|
||||||
if(this.aabb.maxZ < point.z){
|
/**
|
||||||
this.aabb.maxZ = point.z;
|
* Gets the send point of the road segment
|
||||||
}
|
* @return The second point
|
||||||
}
|
*/
|
||||||
this.aabb.minX = this.aabb.minX - radius;
|
public Vector3d getPoint2(){
|
||||||
this.aabb.minY = this.aabb.minY - radius;
|
return endPos;
|
||||||
this.aabb.minZ = this.aabb.minZ - radius;
|
|
||||||
this.aabb.maxX = this.aabb.maxX + radius;
|
|
||||||
this.aabb.maxY = this.aabb.maxY + radius;
|
|
||||||
this.aabb.maxZ = this.aabb.maxZ + radius;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -4,24 +4,42 @@ import java.util.LinkedList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.joml.Vector3d;
|
||||||
|
|
||||||
import electrosphere.data.struct.StructureData;
|
import electrosphere.data.struct.StructureData;
|
||||||
import electrosphere.engine.Globals;
|
import electrosphere.engine.Globals;
|
||||||
import electrosphere.logger.LoggerInterface;
|
import electrosphere.logger.LoggerInterface;
|
||||||
|
import electrosphere.server.datacell.Realm;
|
||||||
import electrosphere.server.macro.MacroData;
|
import electrosphere.server.macro.MacroData;
|
||||||
import electrosphere.server.macro.civilization.Civilization;
|
import electrosphere.server.macro.civilization.Civilization;
|
||||||
|
import electrosphere.server.macro.civilization.road.Road;
|
||||||
import electrosphere.server.macro.race.Race;
|
import electrosphere.server.macro.race.Race;
|
||||||
|
import electrosphere.util.math.VoronoiUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lays out town objects
|
* Lays out town objects
|
||||||
*/
|
*/
|
||||||
public class TownLayout {
|
public class TownLayout {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scaler for town layout nodes
|
||||||
|
*/
|
||||||
|
public static final double TOWN_LAYOUT_SCALER = 16;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relaxation factor for regularizing placement of town center nodes
|
||||||
|
*/
|
||||||
|
public static final double VORONOI_RELAXATION_FACTOR = 0.5;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lays out structures for a town
|
* Lays out structures for a town
|
||||||
* @param macroData The macro data
|
* @param macroData The macro data
|
||||||
* @param town The town
|
* @param town The town
|
||||||
*/
|
*/
|
||||||
public static void layoutTown(MacroData macroData, Town town){
|
public static void layoutTown(Realm realm, MacroData macroData, Town town){
|
||||||
|
|
||||||
|
//
|
||||||
|
//figure out what structures we're allowed to place
|
||||||
Civilization parentCiv = town.getParent(macroData);
|
Civilization parentCiv = town.getParent(macroData);
|
||||||
List<String> raceIds = parentCiv.getRaceIds();
|
List<String> raceIds = parentCiv.getRaceIds();
|
||||||
List<Race> races = raceIds.stream().map((String raceId) -> Globals.gameConfigCurrent.getRaceMap().getRace(raceId)).filter((Race race) -> race != null).collect(Collectors.toList());
|
List<Race> races = raceIds.stream().map((String raceId) -> Globals.gameConfigCurrent.getRaceMap().getRace(raceId)).filter((Race race) -> race != null).collect(Collectors.toList());
|
||||||
@ -41,6 +59,45 @@ public class TownLayout {
|
|||||||
throw new Error("No structures found! " + raceIds);
|
throw new Error("No structures found! " + raceIds);
|
||||||
}
|
}
|
||||||
LoggerInterface.loggerEngine.DEBUG("Allowed structure count: " + allowedStructures.size());
|
LoggerInterface.loggerEngine.DEBUG("Allowed structure count: " + allowedStructures.size());
|
||||||
|
|
||||||
|
//
|
||||||
|
//find the nodes to connect
|
||||||
|
Vector3d townCenter = town.getPos();
|
||||||
|
|
||||||
|
//get center loc
|
||||||
|
Vector3d centerNodeLoc = VoronoiUtils.solveClosestVoronoiNode(
|
||||||
|
townCenter.x / TOWN_LAYOUT_SCALER,
|
||||||
|
0,
|
||||||
|
townCenter.z / TOWN_LAYOUT_SCALER,
|
||||||
|
VORONOI_RELAXATION_FACTOR
|
||||||
|
).mul(TOWN_LAYOUT_SCALER,1,TOWN_LAYOUT_SCALER);
|
||||||
|
centerNodeLoc.y = realm.getServerWorldData().getServerTerrainManager().getElevation(centerNodeLoc);
|
||||||
|
|
||||||
|
//get north node
|
||||||
|
Vector3d upNodeLoc = VoronoiUtils.solveClosestVoronoiNode(
|
||||||
|
townCenter.x / TOWN_LAYOUT_SCALER,
|
||||||
|
0,
|
||||||
|
townCenter.z / TOWN_LAYOUT_SCALER + TOWN_LAYOUT_SCALER,
|
||||||
|
VORONOI_RELAXATION_FACTOR).mul(TOWN_LAYOUT_SCALER,1,TOWN_LAYOUT_SCALER);
|
||||||
|
upNodeLoc.y = realm.getServerWorldData().getServerTerrainManager().getElevation(upNodeLoc);
|
||||||
|
|
||||||
|
//get left node
|
||||||
|
Vector3d leftNodeLoc = VoronoiUtils.solveClosestVoronoiNode(
|
||||||
|
townCenter.x / TOWN_LAYOUT_SCALER - TOWN_LAYOUT_SCALER,
|
||||||
|
0,
|
||||||
|
townCenter.z / TOWN_LAYOUT_SCALER,
|
||||||
|
VORONOI_RELAXATION_FACTOR
|
||||||
|
).mul(TOWN_LAYOUT_SCALER,1,TOWN_LAYOUT_SCALER);
|
||||||
|
leftNodeLoc.y = realm.getServerWorldData().getServerTerrainManager().getElevation(leftNodeLoc);
|
||||||
|
|
||||||
|
|
||||||
|
//up-facing road
|
||||||
|
Road.createRoad(macroData, upNodeLoc, centerNodeLoc);
|
||||||
|
|
||||||
|
//up-facing road
|
||||||
|
Road.createRoad(macroData, leftNodeLoc, centerNodeLoc);
|
||||||
|
|
||||||
|
|
||||||
town.setResolution(Town.TOWN_RES_MAX);
|
town.setResolution(Town.TOWN_RES_MAX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -274,7 +274,7 @@ public class ProceduralChunkGenerator implements ChunkGenerator {
|
|||||||
Road road = (Road)object;
|
Road road = (Road)object;
|
||||||
//broad phase intersection
|
//broad phase intersection
|
||||||
if(road.getAABB().testPoint(realX, realY, realZ)){
|
if(road.getAABB().testPoint(realX, realY, realZ)){
|
||||||
if(GeomUtils.pointIntersectsSpline(realPt, road.getSpline(), road.getRadius())){
|
if(GeomUtils.pointIntersectsLineSegment(realPt, road.getPoint1(), road.getPoint2(), road.getRadius())){
|
||||||
if(voxel.type != ServerTerrainChunk.VOXEL_TYPE_AIR){
|
if(voxel.type != ServerTerrainChunk.VOXEL_TYPE_AIR){
|
||||||
voxel.type = 1;
|
voxel.type = 1;
|
||||||
rVal = true;
|
rVal = true;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package electrosphere.util.math;
|
package electrosphere.util.math;
|
||||||
|
|
||||||
|
import org.joml.Intersectiond;
|
||||||
import org.joml.Vector3d;
|
import org.joml.Vector3d;
|
||||||
import org.joml.Vector3i;
|
import org.joml.Vector3i;
|
||||||
|
|
||||||
@ -649,5 +650,18 @@ public class GeomUtils {
|
|||||||
public static boolean pointIntersectsSpline(Vector3d point, Spline3d spline, double radius) {
|
public static boolean pointIntersectsSpline(Vector3d point, Spline3d spline, double radius) {
|
||||||
return GeomUtils.pointIntersectsSpline(point, spline, radius, SPLINE_SAMPLE_RATE);
|
return GeomUtils.pointIntersectsSpline(point, spline, radius, SPLINE_SAMPLE_RATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a point intersects a tube defined by a line
|
||||||
|
* @param point The point
|
||||||
|
* @param lineStart The start of the line segment
|
||||||
|
* @param lineEnd The end of the line segment
|
||||||
|
* @param radius The radius of the tube around the line
|
||||||
|
* @return true if they intersect, false otherwise
|
||||||
|
*/
|
||||||
|
public static boolean pointIntersectsLineSegment(Vector3d point, Vector3d lineStart, Vector3d lineEnd, double radius) {
|
||||||
|
double dist = Intersectiond.distancePointLine(point.x, point.y, point.z, lineStart.x, lineStart.y, lineStart.z, lineEnd.x, lineEnd.y, lineEnd.z);
|
||||||
|
return dist < radius;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
84
src/main/java/electrosphere/util/math/VoronoiUtils.java
Normal file
84
src/main/java/electrosphere/util/math/VoronoiUtils.java
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package electrosphere.util.math;
|
||||||
|
|
||||||
|
import org.joml.Vector3d;
|
||||||
|
|
||||||
|
import io.github.studiorailgun.RandUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilities for dealing with voronoi noise
|
||||||
|
*/
|
||||||
|
public class VoronoiUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* x offsets for a 3x3x3 kernel
|
||||||
|
*/
|
||||||
|
static final int[] KERNEL_3_3_3_X = new int[]{
|
||||||
|
1, 0, -1,
|
||||||
|
1, 0, -1,
|
||||||
|
1, 0, -1,
|
||||||
|
1, 0, -1,
|
||||||
|
1, 0, -1,
|
||||||
|
1, 0, -1,
|
||||||
|
1, 0, -1,
|
||||||
|
1, 0, -1,
|
||||||
|
1, 0, -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* y offsets for a 3x3x3 kernel
|
||||||
|
*/
|
||||||
|
static final int[] KERNEL_3_3_3_Y = new int[]{
|
||||||
|
1, 1, 1,
|
||||||
|
0, 0, 0,
|
||||||
|
-1, -1, -1,
|
||||||
|
1, 1, 1,
|
||||||
|
0, 0, 0,
|
||||||
|
-1, -1, -1,
|
||||||
|
1, 1, 1,
|
||||||
|
0, 0, 0,
|
||||||
|
-1, -1, -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* z offsets for a 3x3x3 kernel
|
||||||
|
*/
|
||||||
|
static final int[] KERNEL_3_3_3_Z = new int[]{
|
||||||
|
1, 1, 1,
|
||||||
|
1, 1, 1,
|
||||||
|
1, 1, 1,
|
||||||
|
0, 0, 0,
|
||||||
|
0, 0, 0,
|
||||||
|
0, 0, 0,
|
||||||
|
-1, -1, -1,
|
||||||
|
-1, -1, -1,
|
||||||
|
-1, -1, -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates voronoi noise within a cube
|
||||||
|
* @param x The x coordinate
|
||||||
|
* @param y The y coordinate
|
||||||
|
* @param z The z coordinate
|
||||||
|
* @param relaxationFactor The relaxation factor
|
||||||
|
* @return The voronoi value
|
||||||
|
*/
|
||||||
|
public static Vector3d solveClosestVoronoiNode(double x, double y, double z, double relaxationFactor){
|
||||||
|
//integer of the point coordinates
|
||||||
|
double x_i = Math.floor(x);
|
||||||
|
double y_i = Math.floor(y);
|
||||||
|
double z_i = Math.floor(z);
|
||||||
|
|
||||||
|
//get the point
|
||||||
|
double p_x = RandUtils.rand(x_i, y_i, z_i, 0);
|
||||||
|
double p_y = RandUtils.rand(x_i, y_i, z_i, 1);
|
||||||
|
double p_z = RandUtils.rand(x_i, y_i, z_i, 2);
|
||||||
|
|
||||||
|
//relax the point based on relaxation factor
|
||||||
|
double x_relaxed = p_x * (1.0 - relaxationFactor) + (relaxationFactor / 2.0);
|
||||||
|
double y_relaxed = p_y * (1.0 - relaxationFactor) + (relaxationFactor / 2.0);
|
||||||
|
double z_relaxed = p_z * (1.0 - relaxationFactor) + (relaxationFactor / 2.0);
|
||||||
|
|
||||||
|
return new Vector3d(x_i + x_relaxed,y_i + y_relaxed,z_i + z_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user