pathfinding work
Some checks reported errors
studiorailgun/Renderer/pipeline/head Something is wrong with the build of this commit
Some checks reported errors
studiorailgun/Renderer/pipeline/head Something is wrong with the build of this commit
This commit is contained in:
parent
d7868d0ccc
commit
e33c57f983
@ -1655,6 +1655,8 @@ Refactor recast pathfinding classes
|
||||
(05/03/2025)
|
||||
Fix voxel pathfinding logic
|
||||
Remove several usages of concurrent datastructures
|
||||
Fix block chunk data allocation explosion
|
||||
Fix pathfinding voxel hashing calculating
|
||||
|
||||
|
||||
|
||||
|
||||
@ -118,7 +118,28 @@ public class ImGuiAI {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -534,6 +534,192 @@ public class VoxelPathfinder {
|
||||
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
|
||||
* @param voxelCellManager The voxel cell manager
|
||||
|
||||
@ -60,7 +60,14 @@ public class HashUtils {
|
||||
* @return The resultant hashed long
|
||||
*/
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
Reference in New Issue
Block a user