pathfinding work + ai work
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
This commit is contained in:
parent
e59ab56e3f
commit
6339181aec
@ -1661,6 +1661,9 @@ Fix pathfinding voxel hashing calculating
|
||||
(05/04/2025)
|
||||
Path to nearest valid voxel instead of a non-walkable voxel
|
||||
Blocks factor into voxel pathfinding
|
||||
Debugging pathfinding code
|
||||
New AI behaviors
|
||||
- Will explore for resources if local ones aren't available
|
||||
|
||||
|
||||
|
||||
@ -1728,6 +1731,8 @@ Rearchitecting
|
||||
- Main render is a ui element (that we can have multiple of)
|
||||
- Shader injecting consts from the engine itself (ie max lights is dynamically injected, that way never have to worry about .glsl and .java not aligning)
|
||||
- Cache busting for particle atlas cache
|
||||
- Convert behavior tree nodes to use static evaluation methods instead of constructing objects
|
||||
- This will make stepping through the logic for a tree SIGNIFICANTLY more legible with debugger
|
||||
|
||||
Code cleanup
|
||||
- Rename "BehaviorTree" to be "Component" (what it actually is)
|
||||
|
||||
@ -22,9 +22,14 @@ public class PathfindingNode implements AITreeNode {
|
||||
|
||||
|
||||
/**
|
||||
* The value used to check if the entity is close to a pathing point
|
||||
* The value used to check if the entity is close to a pathing point horizontally
|
||||
*/
|
||||
public static final double CLOSENESS_CHECK_BOUND = 0.3f;
|
||||
public static final double CLOSENESS_CHECK_BOUND_HORIZONTAL = 0.3f;
|
||||
|
||||
/**
|
||||
* The value used to check if the entity is close to a pathing point vertically
|
||||
*/
|
||||
public static final double CLOSENESS_CHECK_BOUND_VERTICAL = 0.5f;
|
||||
|
||||
/**
|
||||
* The blackboard key to lookup the target entity under
|
||||
@ -43,7 +48,32 @@ public class PathfindingNode implements AITreeNode {
|
||||
|
||||
|
||||
@Override
|
||||
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard) {
|
||||
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard){
|
||||
//make sure that the solved pathfinding data is for the point we want
|
||||
if(PathfindingNode.hasPathfindingData(blackboard)){
|
||||
PathingProgressiveData pathingProgressiveData = PathfindingNode.getPathfindingData(blackboard);
|
||||
Vector3d actualPoint = pathingProgressiveData.getPoints().get(pathingProgressiveData.getPoints().size() - 1);
|
||||
Object targetRaw = blackboard.get(this.targetEntityKey);
|
||||
Vector3d targetPos = null;
|
||||
if(targetRaw == null){
|
||||
throw new Error("Target undefined!");
|
||||
}
|
||||
if(targetRaw instanceof Vector3d){
|
||||
targetPos = (Vector3d)targetRaw;
|
||||
} else if(targetRaw instanceof Entity){
|
||||
targetPos = EntityUtils.getPosition((Entity)targetRaw);
|
||||
} else if(targetRaw instanceof Structure){
|
||||
targetPos = ((Structure)targetRaw).getPos();
|
||||
} else {
|
||||
throw new Error("Unsupported target type " + targetRaw);
|
||||
}
|
||||
if(actualPoint.distance(targetPos) > CLOSENESS_CHECK_BOUND_HORIZONTAL){
|
||||
PathfindingNode.clearPathfindingData(blackboard);
|
||||
PathfindingNode.clearPathfindingPoint(blackboard);
|
||||
}
|
||||
}
|
||||
|
||||
//create a path if we don't already have one
|
||||
if(!PathfindingNode.hasPathfindingData(blackboard)){
|
||||
Object targetRaw = blackboard.get(this.targetEntityKey);
|
||||
Vector3d targetPos = null;
|
||||
@ -82,16 +112,41 @@ public class PathfindingNode implements AITreeNode {
|
||||
if(pathingProgressiveData.getCurrentPoint() < pathingProgressiveData.getPoints().size()){
|
||||
currentPathPos = pathingProgressiveData.getPoints().get(pathingProgressiveData.getCurrentPoint());
|
||||
}
|
||||
double dist = currentPathPos.distance(entityPos);
|
||||
double vertDist = Math.abs(currentPathPos.y - entityPos.y);
|
||||
double horizontalDist = Math.sqrt((currentPathPos.x - entityPos.x) * (currentPathPos.x - entityPos.x) + (currentPathPos.z - entityPos.z) * (currentPathPos.z - entityPos.z));
|
||||
while(
|
||||
currentPathPos != null &&
|
||||
dist < CLOSENESS_CHECK_BOUND &&
|
||||
vertDist < CLOSENESS_CHECK_BOUND_VERTICAL &&
|
||||
horizontalDist < CLOSENESS_CHECK_BOUND_HORIZONTAL &&
|
||||
pathingProgressiveData.getCurrentPoint() < pathingProgressiveData.getPoints().size() - 1
|
||||
){
|
||||
pathingProgressiveData.setCurrentPoint(pathingProgressiveData.getCurrentPoint() + 1);
|
||||
currentPathPos = pathingProgressiveData.getPoints().get(pathingProgressiveData.getCurrentPoint());
|
||||
dist = currentPathPos.distance(entityPos);
|
||||
}
|
||||
|
||||
//if we're close enough to the final pathing point, always path to actual final point
|
||||
if(
|
||||
vertDist < CLOSENESS_CHECK_BOUND_VERTICAL &&
|
||||
horizontalDist < CLOSENESS_CHECK_BOUND_HORIZONTAL &&
|
||||
pathingProgressiveData.getCurrentPoint() == pathingProgressiveData.getPoints().size() - 1
|
||||
){
|
||||
Object targetRaw = blackboard.get(this.targetEntityKey);
|
||||
Vector3d targetPos = null;
|
||||
if(targetRaw == null){
|
||||
throw new Error("Target undefined!");
|
||||
}
|
||||
if(targetRaw instanceof Vector3d){
|
||||
targetPos = (Vector3d)targetRaw;
|
||||
} else if(targetRaw instanceof Entity){
|
||||
targetPos = EntityUtils.getPosition((Entity)targetRaw);
|
||||
} else if(targetRaw instanceof Structure){
|
||||
targetPos = ((Structure)targetRaw).getPos();
|
||||
} else {
|
||||
throw new Error("Unsupported target type " + targetRaw);
|
||||
}
|
||||
currentPathPos = targetPos;
|
||||
}
|
||||
|
||||
PathfindingNode.setPathfindingPoint(blackboard, currentPathPos);
|
||||
|
||||
return AITreeNodeResult.SUCCESS;
|
||||
|
||||
@ -0,0 +1,61 @@
|
||||
package electrosphere.server.ai.nodes.plan;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.joml.Vector3d;
|
||||
import org.joml.Vector3i;
|
||||
|
||||
import electrosphere.engine.Globals;
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.server.ai.blackboard.Blackboard;
|
||||
import electrosphere.server.ai.nodes.AITreeNode;
|
||||
import electrosphere.server.datacell.Realm;
|
||||
import electrosphere.server.datacell.ServerWorldData;
|
||||
|
||||
/**
|
||||
* Generates a point to explore towards
|
||||
*/
|
||||
public class TargetExploreNode implements AITreeNode {
|
||||
|
||||
/**
|
||||
* The key to store the point under
|
||||
*/
|
||||
String targetKey;
|
||||
|
||||
/**
|
||||
* Distance to travel in whatever direction
|
||||
*/
|
||||
static final double OFFSET_DIST = 50;
|
||||
|
||||
/**
|
||||
* constructor
|
||||
* @param targetKey The key to store the point under
|
||||
*/
|
||||
public TargetExploreNode(String targetKey){
|
||||
this.targetKey = targetKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard){
|
||||
Vector3d targetPos = null;
|
||||
|
||||
if(!blackboard.has(targetKey)){
|
||||
Vector3d entPos = new Vector3d(EntityUtils.getPosition(entity));
|
||||
Random rand = new Random();
|
||||
Realm realm = Globals.realmManager.getEntityRealm(entity);
|
||||
Vector3d offsetVec = new Vector3d(rand.nextDouble(),0,rand.nextDouble()).normalize().mul(OFFSET_DIST);
|
||||
targetPos = entPos.add(offsetVec);
|
||||
//solve for height via world data
|
||||
Vector3i voxelPos = ServerWorldData.convertRealToVoxelSpace(targetPos);
|
||||
Vector3i chunkPos = ServerWorldData.convertRealToChunkSpace(targetPos);
|
||||
double height = realm.getServerWorldData().getServerTerrainManager().getElevation(chunkPos.x, chunkPos.z, voxelPos.x, voxelPos.z);
|
||||
targetPos.y = height;
|
||||
|
||||
//store
|
||||
blackboard.put(targetKey, targetPos);
|
||||
}
|
||||
return AITreeNodeResult.SUCCESS;
|
||||
}
|
||||
|
||||
}
|
||||
@ -24,6 +24,36 @@ public class MoveToTree {
|
||||
*/
|
||||
public static final String TREE_NAME = "MoveTo";
|
||||
|
||||
/**
|
||||
* Default distance to be within
|
||||
*/
|
||||
static final double DEFAULT_DIST = 0.5f;
|
||||
|
||||
/**
|
||||
* Creates a move-to-target tree
|
||||
* @param targetKey The key to lookup the target under
|
||||
* @return The root node of the move-to-target tree
|
||||
*/
|
||||
public static AITreeNode create(String targetKey){
|
||||
return new SelectorNode(
|
||||
new SequenceNode(
|
||||
//check if in range of target
|
||||
new TargetRangeCheckNode(DEFAULT_DIST, targetKey),
|
||||
new DataDeleteNode(BlackboardKeys.PATHFINDING_POINT),
|
||||
new DataDeleteNode(BlackboardKeys.PATHFINDING_DATA),
|
||||
//if in range, stop moving fowards and return SUCCESS
|
||||
new SucceederNode(new MoveStopNode())
|
||||
),
|
||||
|
||||
//not in range of target, keep moving towards it
|
||||
new SequenceNode(
|
||||
PathfindingNode.createPathEntity(targetKey),
|
||||
new FaceTargetNode(BlackboardKeys.PATHFINDING_POINT),
|
||||
new RunnerNode(new MoveStartNode(MovementRelativeFacing.FORWARD))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a move-to-target tree
|
||||
* @param dist The target distance to be within
|
||||
@ -31,7 +61,7 @@ public class MoveToTree {
|
||||
* @return The root node of the move-to-target tree
|
||||
*/
|
||||
public static AITreeNode create(double dist, String targetKey){
|
||||
if(dist < PathfindingNode.CLOSENESS_CHECK_BOUND){
|
||||
if(dist < PathfindingNode.CLOSENESS_CHECK_BOUND_HORIZONTAL){
|
||||
throw new Error("Dist less than minimal amount! " + dist);
|
||||
}
|
||||
return new SelectorNode(
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
package electrosphere.server.ai.trees.creature.explore;
|
||||
|
||||
import electrosphere.server.ai.blackboard.BlackboardKeys;
|
||||
import electrosphere.server.ai.nodes.AITreeNode;
|
||||
import electrosphere.server.ai.nodes.meta.DataDeleteNode;
|
||||
import electrosphere.server.ai.nodes.meta.collections.SequenceNode;
|
||||
import electrosphere.server.ai.nodes.meta.debug.PublishStatusNode;
|
||||
import electrosphere.server.ai.nodes.plan.TargetExploreNode;
|
||||
import electrosphere.server.ai.trees.creature.MoveToTree;
|
||||
|
||||
/**
|
||||
* A tree for exploring new chunks
|
||||
*/
|
||||
public class ExploreTree {
|
||||
|
||||
/**
|
||||
* Name of the tree
|
||||
*/
|
||||
public static final String TREE_NAME = "ExploreTree";
|
||||
|
||||
/**
|
||||
* Creates an explore tree
|
||||
* @return The root node of the explore tree
|
||||
*/
|
||||
public static AITreeNode create(){
|
||||
return new SequenceNode(
|
||||
new PublishStatusNode("Explore"),
|
||||
//resolve point to explore towards
|
||||
new TargetExploreNode(BlackboardKeys.MOVE_TO_TARGET),
|
||||
//move towards the point
|
||||
MoveToTree.create(BlackboardKeys.MOVE_TO_TARGET),
|
||||
//clear position after moving towards it
|
||||
new DataDeleteNode(BlackboardKeys.MOVE_TO_TARGET)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@ -16,6 +16,7 @@ import electrosphere.server.ai.nodes.meta.decorators.SucceederNode;
|
||||
import electrosphere.server.ai.nodes.plan.SolveSourcingTreeNode;
|
||||
import electrosphere.server.ai.nodes.plan.TargetEntityCategoryNode;
|
||||
import electrosphere.server.ai.trees.creature.MoveToTree;
|
||||
import electrosphere.server.ai.trees.creature.explore.ExploreTree;
|
||||
|
||||
/**
|
||||
* A tree to acquire an item
|
||||
@ -71,6 +72,11 @@ public class AcquireItemTree {
|
||||
new TargetEntityCategoryNode(BlackboardKeys.HARVEST_TARGET_TYPE),
|
||||
FellTree.create(BlackboardKeys.ENTITY_TARGET),
|
||||
new RunnerNode(null)
|
||||
),
|
||||
new SequenceNode(
|
||||
new PublishStatusNode("Explore new chunks for resources"),
|
||||
//Failed to find sources of material in existing chunks, must move for new chunks
|
||||
ExploreTree.create()
|
||||
)
|
||||
),
|
||||
new SucceederNode(null)
|
||||
|
||||
@ -1097,6 +1097,10 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
|
||||
throw new Error("Failed to find tracking data for " + start);
|
||||
}
|
||||
Vector3d nearestValidGoal = this.pathfinder.scanNearestWalkable(this, end, VoxelPathfinder.DEFAULT_MAX_TARGET_SCAN_DIST);
|
||||
if(nearestValidGoal == null){
|
||||
nearestValidGoal = this.pathfinder.scanNearestWalkable(this, end, VoxelPathfinder.DEFAULT_MAX_TARGET_SCAN_DIST);
|
||||
throw new Error("Failed to resolve valid point near " + end.x + "," + end.y + "," + end.z);
|
||||
}
|
||||
List<Vector3d> points = this.pathfinder.findPath(this, start, nearestValidGoal, VoxelPathfinder.DEFAULT_MAX_COST);
|
||||
return points;
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ public class VoxelPathfinder {
|
||||
/**
|
||||
* Maximum distance to scan for a walkable position
|
||||
*/
|
||||
public static final double DEFAULT_MAX_TARGET_SCAN_DIST = 3;
|
||||
public static final double DEFAULT_MAX_TARGET_SCAN_DIST = 5;
|
||||
|
||||
/**
|
||||
* The heuristic lookup table
|
||||
@ -53,6 +53,9 @@ public class VoxelPathfinder {
|
||||
public List<Vector3d> findPath(VoxelCellManager voxelCellManager, Vector3d startPoint, Vector3d endPoint, long maxCost){
|
||||
List<Vector3d> rVal = null;
|
||||
|
||||
if(startPoint == null || endPoint == null){
|
||||
throw new Error("Points undefined! " + startPoint + " " + endPoint);
|
||||
}
|
||||
if(startPoint.distance(endPoint) > MAX_DIST){
|
||||
throw new Error("Distance is outside range provided! " + startPoint.distance(endPoint) + " vs " + MAX_DIST);
|
||||
}
|
||||
@ -905,7 +908,7 @@ public class VoxelPathfinder {
|
||||
for(int x = 0; x < 4; x++){
|
||||
for(int y = 0; y < 4; y++){
|
||||
for(int z = 0; z < 4; z++){
|
||||
blockPos.set(voxelPos);
|
||||
blockPos.set(voxelPos).mul(BlockChunkData.BLOCKS_PER_UNIT_DISTANCE);
|
||||
currChunk.set(chunkPos);
|
||||
offsets.set(
|
||||
x,
|
||||
@ -953,6 +956,7 @@ public class VoxelPathfinder {
|
||||
Vector3d realPos;
|
||||
Vector3i offsets = new Vector3i();
|
||||
|
||||
|
||||
while(true){
|
||||
scanned = 0;
|
||||
for(int x = -radius; x <= radius; x++){
|
||||
@ -961,7 +965,7 @@ public class VoxelPathfinder {
|
||||
currChunk.set(originChunk);
|
||||
offsets.set(-radius,x,y);
|
||||
VoxelPathfinder.clampVoxelOffsets(currVoxel, currChunk, offsets);
|
||||
realPos = ServerWorldData.convertVoxelToRealSpace(currVoxel.set(originVoxel).add(-radius,x,y), currChunk);
|
||||
realPos = ServerWorldData.convertVoxelToRealSpace(currVoxel, currChunk);
|
||||
if(realPos.distance(targetPoint) < maxScanRadius){
|
||||
scanned++;
|
||||
if(this.isWalkable(voxelCellManager, currChunk, currVoxel)){
|
||||
@ -973,7 +977,7 @@ public class VoxelPathfinder {
|
||||
currChunk.set(originChunk);
|
||||
offsets.set(radius,x,y);
|
||||
VoxelPathfinder.clampVoxelOffsets(currVoxel, currChunk, offsets);
|
||||
realPos = ServerWorldData.convertVoxelToRealSpace(currVoxel.set(originVoxel).add(-radius,x,y), currChunk);
|
||||
realPos = ServerWorldData.convertVoxelToRealSpace(currVoxel, currChunk);
|
||||
if(realPos.distance(targetPoint) < maxScanRadius){
|
||||
scanned++;
|
||||
if(this.isWalkable(voxelCellManager, currChunk, currVoxel)){
|
||||
@ -985,7 +989,7 @@ public class VoxelPathfinder {
|
||||
currChunk.set(originChunk);
|
||||
offsets.set(x,-radius,y);
|
||||
VoxelPathfinder.clampVoxelOffsets(currVoxel, currChunk, offsets);
|
||||
realPos = ServerWorldData.convertVoxelToRealSpace(currVoxel.set(originVoxel).add(-radius,x,y), currChunk);
|
||||
realPos = ServerWorldData.convertVoxelToRealSpace(currVoxel, currChunk);
|
||||
if(realPos.distance(targetPoint) < maxScanRadius){
|
||||
scanned++;
|
||||
if(this.isWalkable(voxelCellManager, currChunk, currVoxel)){
|
||||
@ -997,7 +1001,7 @@ public class VoxelPathfinder {
|
||||
currChunk.set(originChunk);
|
||||
offsets.set(x,radius,y);
|
||||
VoxelPathfinder.clampVoxelOffsets(currVoxel, currChunk, offsets);
|
||||
realPos = ServerWorldData.convertVoxelToRealSpace(currVoxel.set(originVoxel).add(-radius,x,y), currChunk);
|
||||
realPos = ServerWorldData.convertVoxelToRealSpace(currVoxel, currChunk);
|
||||
if(realPos.distance(targetPoint) < maxScanRadius){
|
||||
scanned++;
|
||||
if(this.isWalkable(voxelCellManager, currChunk, currVoxel)){
|
||||
@ -1009,7 +1013,7 @@ public class VoxelPathfinder {
|
||||
currChunk.set(originChunk);
|
||||
offsets.set(x,y,-radius);
|
||||
VoxelPathfinder.clampVoxelOffsets(currVoxel, currChunk, offsets);
|
||||
realPos = ServerWorldData.convertVoxelToRealSpace(currVoxel.set(originVoxel).add(-radius,x,y), currChunk);
|
||||
realPos = ServerWorldData.convertVoxelToRealSpace(currVoxel, currChunk);
|
||||
if(realPos.distance(targetPoint) < maxScanRadius){
|
||||
scanned++;
|
||||
if(this.isWalkable(voxelCellManager, currChunk, currVoxel)){
|
||||
@ -1021,7 +1025,7 @@ public class VoxelPathfinder {
|
||||
currChunk.set(originChunk);
|
||||
offsets.set(x,y,radius);
|
||||
VoxelPathfinder.clampVoxelOffsets(currVoxel, currChunk, offsets);
|
||||
realPos = ServerWorldData.convertVoxelToRealSpace(currVoxel.set(originVoxel).add(-radius,x,y), currChunk);
|
||||
realPos = ServerWorldData.convertVoxelToRealSpace(currVoxel, currChunk);
|
||||
if(realPos.distance(targetPoint) < maxScanRadius){
|
||||
scanned++;
|
||||
if(this.isWalkable(voxelCellManager, currChunk, currVoxel)){
|
||||
@ -1047,31 +1051,45 @@ public class VoxelPathfinder {
|
||||
*/
|
||||
private static void clampVoxelOffsets(Vector3i voxelPos, Vector3i chunkPos, Vector3i offsets){
|
||||
//calculate chunk offsets
|
||||
voxelPos.x = (voxelPos.x + offsets.x);
|
||||
voxelPos.y = (voxelPos.y + offsets.y);
|
||||
voxelPos.z = (voxelPos.z + offsets.z);
|
||||
if(voxelPos.x < 0){
|
||||
voxelPos.x = -1;
|
||||
int storageX = (voxelPos.x + offsets.x);
|
||||
int storageY = (voxelPos.y + offsets.y);
|
||||
int storageZ = (voxelPos.z + offsets.z);
|
||||
if(storageX < 0){
|
||||
storageX = -1;
|
||||
} else {
|
||||
voxelPos.x = voxelPos.x / ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET;
|
||||
storageX = storageX / ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET;
|
||||
}
|
||||
if(voxelPos.y < 0){
|
||||
voxelPos.y = -1;
|
||||
if(storageY < 0){
|
||||
storageY = -1;
|
||||
} else {
|
||||
voxelPos.y = voxelPos.y / ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET;
|
||||
storageY = storageY / ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET;
|
||||
}
|
||||
if(voxelPos.z < 0){
|
||||
voxelPos.z = -1;
|
||||
if(storageZ < 0){
|
||||
storageZ = -1;
|
||||
} else {
|
||||
voxelPos.z = voxelPos.z / ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET;
|
||||
storageZ = storageZ / ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET;
|
||||
}
|
||||
//update world position
|
||||
chunkPos.x = chunkPos.x + voxelPos.x;
|
||||
chunkPos.y = chunkPos.y + voxelPos.y;
|
||||
chunkPos.z = chunkPos.z + voxelPos.z;
|
||||
chunkPos.x = chunkPos.x + storageX;
|
||||
chunkPos.y = chunkPos.y + storageY;
|
||||
chunkPos.z = chunkPos.z + storageZ;
|
||||
voxelPos.x = (voxelPos.x + offsets.x + ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET) % ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET;
|
||||
voxelPos.y = (voxelPos.y + offsets.y + ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET) % ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET;
|
||||
voxelPos.z = (voxelPos.z + offsets.z + ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET) % ServerTerrainChunk.CHUNK_PLACEMENT_OFFSET;
|
||||
|
||||
if(
|
||||
voxelPos.x < 0 || voxelPos.y < 0 || voxelPos.z < 0 ||
|
||||
chunkPos.x < 0 || chunkPos.y < 0 || chunkPos.z < 0 ||
|
||||
chunkPos.x > 65536 || chunkPos.y > 65536 || chunkPos.z > 65536
|
||||
){
|
||||
String message = "Failed to clamp \n" +
|
||||
"voxelPos: " + voxelPos.x + "," + voxelPos.y + "," + voxelPos.z + "\n" +
|
||||
"chunkPos: " + chunkPos.x + "," + chunkPos.y + "," + chunkPos.z + "\n" +
|
||||
"offsets: " + offsets.x + "," + offsets.y + "," + offsets.z + "\n" +
|
||||
"storage: " + storageX + "," + storageY + "," + storageZ + "\n" +
|
||||
"";
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1083,28 +1101,28 @@ public class VoxelPathfinder {
|
||||
*/
|
||||
private static void clampBlockOffsets(Vector3i blockPos, Vector3i chunkPos, Vector3i offsets){
|
||||
//calculate chunk offsets
|
||||
blockPos.x = (blockPos.x + offsets.x);
|
||||
blockPos.y = (blockPos.y + offsets.y);
|
||||
blockPos.z = (blockPos.z + offsets.z);
|
||||
if(blockPos.x < 0){
|
||||
blockPos.x = -1;
|
||||
int storageX = (blockPos.x + offsets.x);
|
||||
int storageY = (blockPos.y + offsets.y);
|
||||
int storageZ = (blockPos.z + offsets.z);
|
||||
if(storageX < 0){
|
||||
storageX = -1;
|
||||
} else {
|
||||
blockPos.x = blockPos.x / BlockChunkData.CHUNK_DATA_WIDTH;
|
||||
storageX = storageX / BlockChunkData.CHUNK_DATA_WIDTH;
|
||||
}
|
||||
if(blockPos.y < 0){
|
||||
blockPos.y = -1;
|
||||
if(storageY < 0){
|
||||
storageY = -1;
|
||||
} else {
|
||||
blockPos.y = blockPos.y / BlockChunkData.CHUNK_DATA_WIDTH;
|
||||
storageY = storageY / BlockChunkData.CHUNK_DATA_WIDTH;
|
||||
}
|
||||
if(blockPos.z < 0){
|
||||
blockPos.z = -1;
|
||||
if(storageZ < 0){
|
||||
storageZ = -1;
|
||||
} else {
|
||||
blockPos.z = blockPos.z / BlockChunkData.CHUNK_DATA_WIDTH;
|
||||
storageZ = storageZ / BlockChunkData.CHUNK_DATA_WIDTH;
|
||||
}
|
||||
//update world position
|
||||
chunkPos.x = chunkPos.x + blockPos.x;
|
||||
chunkPos.y = chunkPos.y + blockPos.y;
|
||||
chunkPos.z = chunkPos.z + blockPos.z;
|
||||
chunkPos.x = chunkPos.x + storageX;
|
||||
chunkPos.y = chunkPos.y + storageY;
|
||||
chunkPos.z = chunkPos.z + storageZ;
|
||||
blockPos.x = (blockPos.x + offsets.x + BlockChunkData.CHUNK_DATA_WIDTH) % BlockChunkData.CHUNK_DATA_WIDTH;
|
||||
blockPos.y = (blockPos.y + offsets.y + BlockChunkData.CHUNK_DATA_WIDTH) % BlockChunkData.CHUNK_DATA_WIDTH;
|
||||
blockPos.z = (blockPos.z + offsets.z + BlockChunkData.CHUNK_DATA_WIDTH) % BlockChunkData.CHUNK_DATA_WIDTH;
|
||||
|
||||
@ -44,7 +44,7 @@ public class HashUtils {
|
||||
*/
|
||||
public static long hashIVec(int x, int y, int z){
|
||||
if (x < 0 || x > 65536 || y < 0 || y > 65536 || z < 0 || z > 65536) {
|
||||
throw new IllegalArgumentException("Values must be in range [0, 65536]");
|
||||
throw new IllegalArgumentException("Values must be in range [0, 65536] " + x + "," + y + "," + z);
|
||||
}
|
||||
return ((long) x) | ((long) y << SHIFT_Y) | ((long) z << SHIFT_Z);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user