town road nav graph construction
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2025-05-30 13:27:54 -04:00
parent cde8f35887
commit 4b1efb2bcf
5 changed files with 127 additions and 59 deletions

View File

@ -2074,6 +2074,7 @@ Sprinting/physics work
(05/30/2025)
Reorganizing macro classes
Start work on macro pathfinding storage
Town constructs nav graph of road nodes

View File

@ -19,6 +19,7 @@ import electrosphere.server.macro.civilization.town.Town;
import electrosphere.server.macro.region.MacroRegion;
import electrosphere.server.macro.spatial.MacroAreaObject;
import electrosphere.server.macro.spatial.MacroObject;
import electrosphere.server.macro.spatial.path.MacroPathCache;
import electrosphere.server.macro.structure.VirtualStructure;
import electrosphere.util.FileUtils;
import electrosphere.util.annotation.Exclude;
@ -34,35 +35,30 @@ import org.joml.Vector3d;
*/
public class MacroData {
/**
* The maximum number of attempts to try placing something
*/
static final int MAX_PLACEMENT_ATTEMPTS = 50;
/**
* List of races
*/
List<Race> races = new LinkedList<Race>();
private List<Race> races = new LinkedList<Race>();
/**
* List of civilizations
*/
List<Civilization> civilizations = new LinkedList<Civilization>();
private List<Civilization> civilizations = new LinkedList<Civilization>();
/**
* List of towns
*/
List<Town> towns = new LinkedList<Town>();
private List<Town> towns = new LinkedList<Town>();
/**
* List of structures
*/
List<VirtualStructure> structures = new LinkedList<VirtualStructure>();
private List<VirtualStructure> structures = new LinkedList<VirtualStructure>();
/**
* The list of all regions
*/
List<MacroRegion> regions = new LinkedList<MacroRegion>();
private List<MacroRegion> regions = new LinkedList<MacroRegion>();
/**
* Maps structure id -> structure
@ -79,7 +75,12 @@ public class MacroData {
/**
* List of roads
*/
List<Road> roads = new LinkedList<Road>();
private List<Road> roads = new LinkedList<Road>();
/**
* The pathing cache
*/
private MacroPathCache pathingCache = new MacroPathCache();
/**
* Generates a world
@ -89,15 +90,6 @@ public class MacroData {
public static MacroData generateWorld(long seed, ServerWorldData serverWorldData){
Random random = new Random(seed);
MacroData rVal = new MacroData();
//generate initial dieties
// int numDieties = 3 + Math.abs(random.nextInt()) % 7;
// for(int i = 0; i < numDieties; i++){
// Character diety = generateInitialDiety(random.nextLong());
// rVal.initialDieties.add(diety);
// rVal.characters.add(diety);
// CharacterService.createCharacter(null, i);
// }
//generate initial races
@ -120,19 +112,6 @@ public class MacroData {
//init civilizations
CivilizationGenerator.generate(serverWorldData, rVal, Globals.gameConfigCurrent);
//add a test character
// Character testChar = new Character();
// testChar.setPos(new Vector3d(ServerWorldData.convertChunkToRealSpace(new Vector3i(32774, 3, 32769))));
// Race.setRace(testChar, Race.create("human", "human"));
// rVal.characters.add(testChar);
//add a test character
// Vector3d structPos = ServerWorldData.convertChunkToRealSpace(new Vector3i(32774, 0, 32770));
// double elevationAtStruct = serverWorldData.getServerTerrainManager().getElevation(32774, 32770, 0, 0);
// structPos.y = elevationAtStruct;
// Structure struct = Structure.createStructure(Globals.gameConfigCurrent.getStructureData().getType("test1"),structPos);
// rVal.structures.add(struct);
//spawn initial characters in each race
//find initial positions to place characters at per race
//generate initial characters
@ -188,6 +167,9 @@ public class MacroData {
for(MacroRegion region : this.regions){
this.idRegionMap.put(region.getId(),region);
}
if(this.pathingCache != null){
this.pathingCache.reconstruct();
}
}
/**
@ -197,22 +179,6 @@ public class MacroData {
public void save(String saveName){
FileUtils.serializeObjectToSavePath(saveName, "./macro.json", this);
}
// /**
// * Generates an initial diety
// * @param seed The seed
// * @return The character for the diety
// */
// static Character generateInitialDiety(long seed){
// Character rVal = new Character();
// Diety diety = Diety.generateDiety(seed);
// CharacterUtils.addDiety(rVal, diety);
// return rVal;
// }
/**
* Gets the list of civilizations
@ -422,5 +388,13 @@ public class MacroData {
blockers.addAll(this.regions);
return blockers;
}
/**
* Gets the path cache in the macro data
* @return The macro pathing cache
*/
public MacroPathCache getPathCache(){
return this.pathingCache;
}
}

View File

@ -1,7 +1,9 @@
package electrosphere.server.macro.civilization.town;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.joml.AABBd;
@ -19,6 +21,8 @@ import electrosphere.server.macro.character.race.Race;
import electrosphere.server.macro.civilization.Civilization;
import electrosphere.server.macro.civilization.road.Road;
import electrosphere.server.macro.region.MacroRegion;
import electrosphere.server.macro.spatial.path.MacroPathCache;
import electrosphere.server.macro.spatial.path.MacroPathNode;
import electrosphere.server.macro.structure.VirtualStructure;
import electrosphere.util.math.HashUtils;
import electrosphere.util.math.VoronoiUtils;
@ -80,6 +84,8 @@ public class TownLayout {
*/
public static void layoutTown(Realm realm, MacroData macroData, Town town){
MacroPathCache pathCache = macroData.getPathCache();
//
//figure out what structures we're allowed to place
Civilization parentCiv = town.getParent(macroData);
@ -183,6 +189,76 @@ public class TownLayout {
closedSet.add(openHash);
}
//
// Construct pathfinding nodes for roads and link them
//
//
//sets for breadth search
Map<Long,MacroPathNode> positionNodeMap = new HashMap<Long,MacroPathNode>();
openSet.clear();
closedSet.clear();
closedSet.add(HashUtils.hashIVec(HASH_OFFSET, 0, HASH_OFFSET));
openSet.add(HashUtils.hashIVec(HASH_OFFSET - 1, 0, HASH_OFFSET));
openSet.add(HashUtils.hashIVec(HASH_OFFSET + 1, 0, HASH_OFFSET));
openSet.add(HashUtils.hashIVec(HASH_OFFSET, 0, HASH_OFFSET - 1));
openSet.add(HashUtils.hashIVec(HASH_OFFSET, 0, HASH_OFFSET + 1));
//add pathing nodes for initial entries
MacroPathNode currentPathingNode = MacroPathNode.createRoadNode(pathCache, new Vector3d(centerNodeLoc));
positionNodeMap.put(HashUtils.hashIVec(HASH_OFFSET, 0, HASH_OFFSET), currentPathingNode);
//up
currentPathingNode = MacroPathNode.createRoadNode(pathCache, new Vector3d(upNodeLoc));
positionNodeMap.put(HashUtils.hashIVec(HASH_OFFSET, 0, HASH_OFFSET + 1), currentPathingNode);
//left
currentPathingNode = MacroPathNode.createRoadNode(pathCache, new Vector3d(leftNodeLoc));
positionNodeMap.put(HashUtils.hashIVec(HASH_OFFSET - 1, 0, HASH_OFFSET), currentPathingNode);
//right
currentPathingNode = MacroPathNode.createRoadNode(pathCache, new Vector3d(rightNodeLoc));
positionNodeMap.put(HashUtils.hashIVec(HASH_OFFSET + 1, 0, HASH_OFFSET), currentPathingNode);
//down
currentPathingNode = MacroPathNode.createRoadNode(pathCache, new Vector3d(downNodeLoc));
positionNodeMap.put(HashUtils.hashIVec(HASH_OFFSET, 0, HASH_OFFSET - 1), currentPathingNode);
while(openSet.size() > 0){
long openHash = openSet.poll();
int x = HashUtils.unhashIVec(openHash, HashUtils.UNHASH_COMPONENT_X) - HASH_OFFSET;
int z = HashUtils.unhashIVec(openHash, HashUtils.UNHASH_COMPONENT_Z) - HASH_OFFSET;
scanPoint.set(townCenter).add(TOWN_LAYOUT_SCALER * x,0,TOWN_LAYOUT_SCALER * z);
currPoint = TownLayout.getTownCenter(realm, scanPoint);
currentPathingNode = positionNodeMap.get(openHash);
if(currentPathingNode == null){
throw new Error("Failed to find pathing node for hash");
}
//check below
for(int i = 0; i < 4; i++){
int oX = x + offsetX[i];
int oZ = z + offsetZ[i];
scanPoint.set(townCenter).add(TOWN_LAYOUT_SCALER * oX,0,TOWN_LAYOUT_SCALER * oZ);
nearPoint = TownLayout.getTownCenter(realm, scanPoint);
long newHash = HashUtils.hashIVec(HASH_OFFSET + oX, 0, HASH_OFFSET + oZ);
if(nearPoint.distance(townCenter) < TOWN_MAX_RADIUS){
if(!openSet.contains(newHash) && !closedSet.contains(newHash)){
openSet.add(newHash);
}
//link the neighbor to this node
MacroPathNode neighborPathingNode;
if(positionNodeMap.containsKey(newHash)){
neighborPathingNode = positionNodeMap.get(newHash);
} else {
neighborPathingNode = MacroPathNode.createRoadNode(pathCache, new Vector3d(nearPoint));
positionNodeMap.put(newHash,neighborPathingNode);
}
currentPathingNode.addNeighbor(neighborPathingNode);
}
}
closedSet.add(openHash);
}
//
//Breadth search other nodes to branch outwards

View File

@ -22,7 +22,7 @@ public class MacroPathCache {
* Map of node id -> node
*/
@Exclude
private Map<Integer,MacroPathNode> idNodeMap = new HashMap<Integer,MacroPathNode>();
private Map<Long,MacroPathNode> idNodeMap = new HashMap<Long,MacroPathNode>();
/**
* Reconstructs the datastructures for this cache after deserialization
@ -46,7 +46,7 @@ public class MacroPathCache {
* @param id The id
* @return The corresponding node if it exists, null otherwise
*/
public MacroPathNode getNodeById(int id){
public MacroPathNode getNodeById(long id){
return idNodeMap.get(id);
}

View File

@ -6,6 +6,7 @@ import java.util.stream.Collectors;
import org.joml.Vector3d;
import electrosphere.server.macro.region.MacroRegion;
import electrosphere.server.macro.structure.VirtualStructure;
/**
@ -27,6 +28,11 @@ public class MacroPathNode {
*/
private static final int TYPE_VIRTUAL_STRUCTURE = 1;
/**
* A region
*/
private static final int TYPE_REGION = 2;
//
// cost values
@ -37,6 +43,11 @@ public class MacroPathNode {
*/
private static final int COST_VIRTUAL_STRUCTURE = 10;
/**
* Cost of a region object
*/
private static final int COST_REGION = 8;
/**
* Cost of a road node
*/
@ -50,12 +61,12 @@ public class MacroPathNode {
/**
* The id of this node
*/
private int id;
private long id;
/**
* The id of the object that this node corresponds to
*/
private int objectId;
private long objectId;
/**
* The type of object at the node
@ -65,7 +76,7 @@ public class MacroPathNode {
/**
* The ids of neighboring path nodes
*/
private List<Integer> neighborNodes = new LinkedList<Integer>();
private List<Long> neighborNodes = new LinkedList<Long>();
/**
* The position of the node
@ -102,6 +113,10 @@ public class MacroPathNode {
rVal.cost = COST_VIRTUAL_STRUCTURE;
rVal.objectId = structObj.getId();
rVal.objectType = TYPE_VIRTUAL_STRUCTURE;
} else if(correspondingObject instanceof MacroRegion regionObj){
rVal.cost = COST_REGION;
rVal.objectId = regionObj.getId();
rVal.objectType = TYPE_REGION;
} else {
throw new Error("Unsupported object type! " + correspondingObject);
}
@ -134,7 +149,7 @@ public class MacroPathNode {
* Gets the associated id
* @return
*/
public int getId() {
public long getId() {
return id;
}
@ -142,7 +157,7 @@ public class MacroPathNode {
* Sets the id of this path node
* @param id The id of this path node
*/
public void setId(int id) {
protected void setId(int id) {
this.id = id;
}
@ -166,7 +181,7 @@ public class MacroPathNode {
* Gets the id of the object associated with this node
* @return The id of the object
*/
public int getObjectId() {
public long getObjectId() {
return objectId;
}
@ -184,7 +199,7 @@ public class MacroPathNode {
* @return The list of node ids
*/
public List<MacroPathNode> getNeighborNodes(MacroPathCache cache) {
return this.neighborNodes.stream().map((Integer id) -> cache.getNodeById(objectId)).collect(Collectors.toList());
return this.neighborNodes.stream().map((Long id) -> cache.getNodeById(objectId)).collect(Collectors.toList());
}
/**
@ -192,7 +207,9 @@ public class MacroPathNode {
* @param neighbor The neighbor
*/
public void addNeighbor(MacroPathNode neighbor){
this.neighborNodes.add(neighbor.getId());
if(!this.neighborNodes.contains(neighbor.getId())){
this.neighborNodes.add(neighbor.getId());
}
}
/**