pathfinding work
Some checks reported errors
studiorailgun/Renderer/pipeline/head Something is wrong with the build of this commit

This commit is contained in:
austin 2025-05-03 19:44:11 -04:00
parent d7868d0ccc
commit e33c57f983
4 changed files with 218 additions and 2 deletions

View File

@ -1655,6 +1655,8 @@ Refactor recast pathfinding classes
(05/03/2025) (05/03/2025)
Fix voxel pathfinding logic Fix voxel pathfinding logic
Remove several usages of concurrent datastructures Remove several usages of concurrent datastructures
Fix block chunk data allocation explosion
Fix pathfinding voxel hashing calculating

View File

@ -118,7 +118,28 @@ public class ImGuiAI {
displayEntity.add(newEnt); displayEntity.add(newEnt);
} }
} }
ImGui.text("Current pos: " + solvedPosition); if(ImGui.button("Draw Open Set (" + numIterations + ")")){
numIterations = numIterations + 1;
VoxelPathfinder voxelPathfinder = new VoxelPathfinder();
GriddedDataCellManager griddedDataCellManager = (GriddedDataCellManager)Globals.realmManager.getEntityRealm(serverPlayerEntity).getDataCellManager();
Vector3d playerPos = new Vector3d(EntityUtils.getPosition(serverPlayerEntity));
List<PathfinderNode> closedSet = voxelPathfinder.aStarStepOpen(griddedDataCellManager, playerPos, new Vector3d(playerPos).add(10,0,0), 1000, numIterations);
if(displayEntity.size() > 0){
for(Entity entity : displayEntity){
ClientEntityUtils.destroyEntity(entity);
}
}
for(PathfinderNode node : closedSet){
Entity newEnt = EntityCreationUtils.createClientSpatialEntity();
EntityCreationUtils.makeEntityDrawable(newEnt, AssetDataStrings.UNITCUBE);
Actor blockCursorActor = EntityUtils.getActor(newEnt);
blockCursorActor.addTextureMask(new ActorTextureMask("cube", Arrays.asList(new String[]{AssetDataStrings.TEXTURE_RED_TRANSPARENT})));
ClientEntityUtils.initiallyPositionEntity(newEnt, node.getPosition(), new Quaterniond());
EntityUtils.getScale(newEnt).set(0.4f);
displayEntity.add(newEnt);
}
}
} }
} }

View File

@ -534,6 +534,192 @@ public class VoxelPathfinder {
return rVal; return rVal;
} }
/**
* Steps through the A* solver to a given number of closet set items
* @param voxelCellManager The voxel manager
* @param startPoint The start point
* @param endPoint The end point
* @param maxCost The max allowable cost
* @param closetSetSize The size of the closed set to stop at
* @return The closed set from iteration through A*
*/
public List<PathfinderNode> aStarStepOpen(VoxelCellManager voxelCellManager, Vector3d startPoint, Vector3d endPoint, long maxCost, int closetSetSize){
if(startPoint.distance(endPoint) > MAX_DIST){
throw new Error("Distance is outside range provided! " + startPoint.distance(endPoint) + " vs " + MAX_DIST);
}
List<PathfinderNode> rVal = new LinkedList<PathfinderNode>();
//create sets
PriorityQueue<PathfinderNode> openSet = new PriorityQueue<PathfinderNode>();
Map<Long,PathfinderNode> openSetLookup = new HashMap<Long,PathfinderNode>();
Map<Long,PathfinderNode> closetSet = new HashMap<Long,PathfinderNode>();
//add starting node
PathfinderNode startingNode = new PathfinderNode(
ServerWorldData.convertRealToChunkSpace(startPoint.x), ServerWorldData.convertRealToChunkSpace(startPoint.y), ServerWorldData.convertRealToChunkSpace(startPoint.z),
ServerWorldData.convertRealToVoxelSpace(startPoint.x), ServerWorldData.convertRealToVoxelSpace(startPoint.y), ServerWorldData.convertRealToVoxelSpace(startPoint.z),
0, 0, 0
);
openSet.add(startingNode);
//structures used throughout iteration
int chunkPosX = 0;
int chunkPosY = 0;
int chunkPosZ = 0;
int voxelPosX = 0;
int voxelPosY = 0;
int voxelPosZ = 0;
Vector3i endWorldPos = ServerWorldData.convertRealToChunkSpace(endPoint);
Vector3i endVoxelPos = ServerWorldData.convertRealToVoxelSpace(endPoint);
long goalHash = HashUtils.hashVoxel(
endWorldPos.x,endWorldPos.y,endWorldPos.z,
endVoxelPos.x,endVoxelPos.y,endVoxelPos.z
);
//set heuristic
this.setHeuristic(startPoint, endPoint);
//tracks whether we've found the goal or not
boolean foundGoal = false;
int countConsidered = 0;
int iteration = 0;
while(openSet.size() > 0 && !foundGoal && iteration < closetSetSize){
//pull from open set
PathfinderNode currentNode = openSet.poll();
long currentCost = currentNode.cost;
openSetLookup.remove(currentNode.hash);
closetSet.put(currentNode.hash, currentNode);
countConsidered++;
//scan all neighbors
for(int x = -1; x <= 1; x++){
if(foundGoal){
continue;
}
for(int y = -1; y <= 1; y++){
if(foundGoal){
continue;
}
for(int z = -1; z <= 1; z++){
if(foundGoal){
continue;
}
if(x == 0 && y == 0 && z == 0){
continue;
}
//calculate chunk offsets
voxelPosX = (currentNode.voxelX + x);
voxelPosY = (currentNode.voxelY + y);
voxelPosZ = (currentNode.voxelZ + z);
if(voxelPosX < 0){
voxelPosX = -1;
} else {
voxelPosX = voxelPosX / ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET;
}
if(voxelPosY < 0){
voxelPosY = -1;
} else {
voxelPosY = voxelPosY / ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET;
}
if(voxelPosZ < 0){
voxelPosZ = -1;
} else {
voxelPosZ = voxelPosZ / ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET;
}
//update world position
chunkPosX = currentNode.worldX + voxelPosX;
chunkPosY = currentNode.worldY + voxelPosY;
chunkPosZ = currentNode.worldZ + voxelPosZ;
voxelPosX = (currentNode.voxelX + x + ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET) % ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET;
voxelPosY = (currentNode.voxelY + y + ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET) % ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET;
voxelPosZ = (currentNode.voxelZ + z + ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET) % ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET;
//error/bounds check
if(chunkPosX < 0 || chunkPosY < 0 || chunkPosZ < 0){
continue;
}
//
//checking if this is the goal
//
//calculte hash for neighbor pos
long newHash = HashUtils.hashVoxel(
chunkPosX,chunkPosY,chunkPosZ,
voxelPosX,voxelPosY,voxelPosZ
);
//check if found goal
if(newHash == goalHash){
foundGoal = true;
PathfinderNode newNode = new PathfinderNode(
chunkPosX, chunkPosY, chunkPosZ,
voxelPosX, voxelPosY, voxelPosZ,
0, newHash, currentNode.hash
);
closetSet.put(goalHash, newNode);
continue;
}
//
//creating a new node
//
//it's a solid block
if(!this.isWalkable(voxelCellManager, new Vector3i(chunkPosX,chunkPosY,chunkPosZ), new Vector3i(voxelPosX,voxelPosY,voxelPosZ))){
continue;
}
//calculate new cost
//TODO: apply heuristic here
Vector3d currPosReal = new Vector3d(
ServerWorldData.convertVoxelToRealSpace(voxelPosX, chunkPosX),
ServerWorldData.convertVoxelToRealSpace(voxelPosY, chunkPosY),
ServerWorldData.convertVoxelToRealSpace(voxelPosZ, chunkPosZ)
);
long newCost = currentCost + (int)currPosReal.distance(endPoint);
//check cost boundary
if(newCost > maxCost){
continue;
}
//push to open set
if(!closetSet.containsKey(newHash) && !openSetLookup.containsKey(newHash)){
PathfinderNode newNode = new PathfinderNode(
chunkPosX, chunkPosY, chunkPosZ,
voxelPosX, voxelPosY, voxelPosZ,
newCost, newHash, currentNode.hash
);
openSet.add(newNode);
openSetLookup.put(newHash, newNode);
}
}
}
}
if(openSet.size() < 1){
throw new Error("Open set ran out of nodes! " + countConsidered);
}
iteration++;
}
rVal.addAll(openSet);
return rVal;
}
/** /**
* Performs string pulling on the closed set to get the optimized path * Performs string pulling on the closed set to get the optimized path
* @param voxelCellManager The voxel cell manager * @param voxelCellManager The voxel cell manager

View File

@ -60,7 +60,14 @@ public class HashUtils {
* @return The resultant hashed long * @return The resultant hashed long
*/ */
public static long hashVoxel(int chunkX, int chunkY, int chunkZ, int voxelX, int voxelY, int voxelZ){ public static long hashVoxel(int chunkX, int chunkY, int chunkZ, int voxelX, int voxelY, int voxelZ){
return ((long)chunkX | ((long) chunkY << 8) | ((long) chunkZ << 16) | ((long) voxelX << 24) | ((long) voxelY << 32) | ((long) voxelZ << 40)); return (
((long)chunkX & 0xFFl) |
((((long) chunkY) << 8 ) & 0xFF00l) |
((((long) chunkZ) << 16) & 0xFF0000l) |
((((long) voxelX) << 24) & 0xFF000000l) |
((((long) voxelY) << 32) & 0xFF00000000l) |
((((long) voxelZ) << 40) & 0xFF0000000000l)
);
} }
/** /**