Pathfinding!
This commit is contained in:
parent
3185866fa4
commit
5f4eb22fa9
@ -14,6 +14,7 @@
|
||||
|
||||
"graphicsDebugDrawCollisionSpheres" : false,
|
||||
"graphicsDebugDrawPhysicsObjects" : false,
|
||||
"graphicsDebugDrawMovementVectors" : false
|
||||
"graphicsDebugDrawMovementVectors" : false,
|
||||
"graphicsDebugDrawNavmesh" : true
|
||||
|
||||
}
|
||||
BIN
assets/Models/waypoint1.fbx
Normal file
BIN
assets/Models/waypoint1.fbx
Normal file
Binary file not shown.
@ -29,7 +29,7 @@ import electrosphere.game.server.saves.SaveUtils;
|
||||
import electrosphere.game.server.terrain.models.TerrainModification;
|
||||
import electrosphere.game.server.town.Town;
|
||||
import electrosphere.game.server.world.MacroData;
|
||||
import electrosphere.game.server.world.datacell.DataCellManager;
|
||||
import electrosphere.game.server.datacell.DataCellManager;
|
||||
import electrosphere.game.state.MicroSimulation;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.main.Globals;
|
||||
@ -42,6 +42,9 @@ import electrosphere.renderer.ActorUtils;
|
||||
import electrosphere.renderer.Model;
|
||||
import electrosphere.renderer.RenderUtils;
|
||||
import electrosphere.engine.assetmanager.AssetDataStrings;
|
||||
import electrosphere.game.server.pathfinding.NavMeshPathfinder;
|
||||
import electrosphere.game.server.pathfinding.navmesh.NavCube;
|
||||
import electrosphere.game.server.pathfinding.navmesh.NavMesh;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -591,6 +594,13 @@ public class LoadingThread extends Thread {
|
||||
Entity bow = ItemUtils.spawnBasicItem("Bow");
|
||||
EntityUtils.getPosition(bow).set(1, 1, 2);
|
||||
|
||||
NavMeshPathfinder.navigatePointToPointInMesh(Globals.navMeshManager.getMeshes().get(0), new Vector3d(10,0,5), new Vector3d(5,0,10));
|
||||
|
||||
// NavMesh mesh = new NavMesh();
|
||||
// NavCube cube = new NavCube(5,0,0,10,5,5);
|
||||
// mesh.addNode(cube);
|
||||
// Globals.navMeshManager.addMesh(mesh);
|
||||
|
||||
// Entity fallOak = FoliageUtils.spawnBasicFoliage("FallOak1");
|
||||
// EntityUtils.getPosition(fallOak).set(1,0,3);
|
||||
//
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
package electrosphere.entity.types.waypoint;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author amaterasu
|
||||
*/
|
||||
public class WaypointUtils {
|
||||
|
||||
}
|
||||
@ -2,7 +2,7 @@ package electrosphere.game.client.world;
|
||||
|
||||
import electrosphere.game.server.world.*;
|
||||
import electrosphere.game.server.terrain.manager.ServerTerrainManager;
|
||||
import electrosphere.game.server.world.datacell.ServerDataCell;
|
||||
import electrosphere.game.server.datacell.ServerDataCell;
|
||||
import java.util.List;
|
||||
import org.joml.Vector2f;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
@ -37,6 +37,7 @@ public class UserSettings {
|
||||
boolean graphicsDebugDrawCollisionSpheres;
|
||||
boolean graphicsDebugDrawPhysicsObjects;
|
||||
boolean graphicsDebugDrawMovementVectors;
|
||||
boolean graphicsDebugDrawNavmesh;
|
||||
|
||||
|
||||
|
||||
@ -88,6 +89,10 @@ public class UserSettings {
|
||||
public int getGraphicsPerformanceLODChunkRadius() {
|
||||
return graphicsPerformanceLODChunkRadius;
|
||||
}
|
||||
|
||||
public boolean graphicsDebugDrawNavmesh() {
|
||||
return graphicsDebugDrawNavmesh;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -116,6 +121,7 @@ public class UserSettings {
|
||||
rVal.graphicsDebugDrawCollisionSpheres = false;
|
||||
rVal.graphicsDebugDrawMovementVectors = false;
|
||||
rVal.graphicsDebugDrawPhysicsObjects = false;
|
||||
rVal.graphicsDebugDrawNavmesh = false;
|
||||
rVal.graphicsPerformanceLODChunkRadius = 5;
|
||||
rVal.graphicsFOV = 90.0f;
|
||||
rVal.graphicsPerformanceDrawShadows = true;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package electrosphere.game.server.world.datacell;
|
||||
package electrosphere.game.server.datacell;
|
||||
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
@ -1,4 +1,4 @@
|
||||
package electrosphere.game.server.world.datacell;
|
||||
package electrosphere.game.server.datacell;
|
||||
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
@ -1,4 +1,4 @@
|
||||
package electrosphere.game.server.world.datacell;
|
||||
package electrosphere.game.server.datacell;
|
||||
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.types.creature.CreatureUtils;
|
||||
@ -6,6 +6,8 @@ import electrosphere.entity.types.item.ItemUtils;
|
||||
import electrosphere.entity.types.structure.StructureUtils;
|
||||
import electrosphere.net.server.Player;
|
||||
import electrosphere.game.server.character.Character;
|
||||
import electrosphere.game.server.pathfinding.NavMeshUtils;
|
||||
import electrosphere.game.server.pathfinding.navmesh.NavMesh;
|
||||
import electrosphere.main.Globals;
|
||||
import electrosphere.net.parser.net.message.NetworkMessage;
|
||||
import java.util.LinkedList;
|
||||
@ -22,6 +24,7 @@ public class ServerDataCell {
|
||||
|
||||
List<Entity> loadedEntities = new LinkedList();
|
||||
List<Player> activePlayers = new LinkedList();
|
||||
NavMesh navMesh;
|
||||
|
||||
|
||||
/**
|
||||
@ -41,6 +44,7 @@ public class ServerDataCell {
|
||||
//else create from scratch
|
||||
EnvironmentGenerator.generatePlains(rVal.loadedEntities, worldX, worldY, Globals.serverTerrainManager.getRandomizerAtPoint(worldX, worldY));
|
||||
}
|
||||
rVal.navMesh = NavMeshUtils.createMeshFromChunk(Globals.serverTerrainManager.getChunk(worldX, worldY),Globals.navMeshManager.getBlockerCache().getBlocker(worldX, worldY));
|
||||
return rVal;
|
||||
}
|
||||
|
||||
@ -1,11 +1,16 @@
|
||||
package electrosphere.game.server.pathfinding;
|
||||
|
||||
import electrosphere.game.server.pathfinding.blocker.NavBlocker;
|
||||
import electrosphere.game.server.pathfinding.blocker.NavTerrainBlockerCache;
|
||||
import electrosphere.game.server.pathfinding.navmesh.NavMesh;
|
||||
import electrosphere.game.server.pathfinding.navmesh.NavShape;
|
||||
import electrosphere.game.server.pathfinding.path.Waypoint;
|
||||
import electrosphere.game.server.terrain.manager.ServerTerrainChunk;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.joml.Vector3d;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -15,6 +20,34 @@ public class NavMeshManager {
|
||||
|
||||
List<NavMesh> meshes = new LinkedList();
|
||||
Map<ServerTerrainChunk,ChunkMeshList> chunkToMeshListMap = new HashMap();
|
||||
NavTerrainBlockerCache blockerCache = new NavTerrainBlockerCache();
|
||||
|
||||
public NavMesh createNavMesh(){
|
||||
NavMesh rVal = new NavMesh();
|
||||
meshes.add(rVal);
|
||||
return rVal;
|
||||
}
|
||||
|
||||
public void addMesh(NavMesh mesh){
|
||||
meshes.add(mesh);
|
||||
}
|
||||
|
||||
public List<NavMesh> getMeshes(){
|
||||
return meshes;
|
||||
}
|
||||
|
||||
public List<NavMesh> navigateMeshToMesh(NavMesh startMesh, NavMesh endMesh){
|
||||
List<NavMesh> rVal = null;
|
||||
return rVal;
|
||||
}
|
||||
|
||||
public NavBlocker getNavBlockerForChunk(int x, int y){
|
||||
NavBlocker rVal = null;
|
||||
return rVal;
|
||||
}
|
||||
|
||||
public NavTerrainBlockerCache getBlockerCache(){
|
||||
return blockerCache;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,450 @@
|
||||
package electrosphere.game.server.pathfinding;
|
||||
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.game.server.pathfinding.navmesh.NavCube;
|
||||
import electrosphere.game.server.pathfinding.navmesh.NavMesh;
|
||||
import electrosphere.game.server.pathfinding.navmesh.NavShape;
|
||||
import electrosphere.game.server.pathfinding.path.Waypoint;
|
||||
import java.awt.geom.Line2D;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.PriorityQueue;
|
||||
import org.joml.Vector3d;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author amaterasu
|
||||
*/
|
||||
public class NavMeshPathfinder {
|
||||
|
||||
//TODO: add movement type mask to this function
|
||||
public static List<Waypoint> navigatePointToPointInMesh(NavMesh mesh, Vector3d start, Vector3d end){
|
||||
|
||||
List<Waypoint> rVal = new LinkedList();
|
||||
|
||||
NavShape startNode = null;
|
||||
NavShape endNode = null;
|
||||
|
||||
for(NavShape node : mesh.getNodes()){
|
||||
if(node.containsPoint(start.x, start.y, start.z)){
|
||||
startNode = node;
|
||||
}
|
||||
if(node.containsPoint(end.x, end.y, end.z)){
|
||||
endNode = node;
|
||||
}
|
||||
if(startNode != null && endNode != null){
|
||||
break;
|
||||
}
|
||||
}
|
||||
//if the start and end nodes aren't both present, exit
|
||||
//need to do broadphase still
|
||||
if(startNode == null || endNode == null){
|
||||
return null;
|
||||
}
|
||||
|
||||
//theta *
|
||||
List<NavShape> openSet = new LinkedList();
|
||||
List<NavShape> closedSet = new LinkedList();
|
||||
PriorityQueue<SetItem> pathItemsQueue = new PriorityQueue();
|
||||
Map<NavShape,SetItem> pathItems = new HashMap();
|
||||
|
||||
openSet.add(startNode);
|
||||
SetItem startSetItem = new SetItem(null,startNode,0,start);
|
||||
startSetItem.currentPos = start;
|
||||
pathItems.put(startNode, startSetItem);
|
||||
pathItemsQueue.add(startSetItem);
|
||||
|
||||
while(!openSet.isEmpty()){
|
||||
//get lowest cost item
|
||||
SetItem currentItem = pathItemsQueue.poll();
|
||||
NavShape currentNode = currentItem.getNode();
|
||||
openSet.remove(currentNode);
|
||||
|
||||
//return path if found end
|
||||
if(currentNode == endNode){
|
||||
System.out.println("Found path!");
|
||||
//TODO: return path
|
||||
while(currentItem != null){
|
||||
if(currentItem.node == endNode){
|
||||
Entity waypoint = EntityUtils.spawnDrawableEntity("Models/waypoint1.fbx");
|
||||
System.out.println(end);
|
||||
EntityUtils.getPosition(waypoint).set(end.x,end.y + 1,end.z);
|
||||
EntityUtils.getRotation(waypoint).rotateLocalX(-(float)Math.PI/2.0f);
|
||||
} else {
|
||||
Entity waypoint = EntityUtils.spawnDrawableEntity("Models/waypoint1.fbx");
|
||||
System.out.println(currentItem.currentPos);
|
||||
EntityUtils.getPosition(waypoint).set(currentItem.currentPos.x,currentItem.currentPos.y + 1,currentItem.currentPos.z);
|
||||
EntityUtils.getRotation(waypoint).rotateLocalX(-(float)Math.PI/2.0f);
|
||||
}
|
||||
currentItem = currentItem.parent;
|
||||
}
|
||||
break;
|
||||
}
|
||||
closedSet.add(currentNode);
|
||||
for(NavShape neighbor : currentNode.getNodeNeighbors()){
|
||||
if(!closedSet.contains(neighbor)){
|
||||
if(!openSet.contains(neighbor)){
|
||||
Vector3d centerPoint = calculateCenterOfShape(neighbor);
|
||||
SetItem newSetItem = new SetItem(currentItem,neighbor,currentItem.getCost() + (float)currentItem.currentPos.distance(centerPoint),centerPoint);
|
||||
pathItems.put(neighbor, newSetItem);
|
||||
pathItemsQueue.add(newSetItem);
|
||||
openSet.add(neighbor);
|
||||
}
|
||||
//update vertex
|
||||
updateVertices(currentItem, pathItems.get(neighbor),pathItemsQueue);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static void updateVertices(SetItem current, SetItem neighbor, PriorityQueue<SetItem> queue){
|
||||
// This part of the algorithm is the main difference between A* and Theta*
|
||||
if(current.getParent() != null){
|
||||
LOSResult losResult = null;
|
||||
Vector3d neighborCenter = calculateCenterOfShape(neighbor.node);
|
||||
if(current.getParent().currentPos == null){
|
||||
current.getParent().currentPos = calculateCenterOfShape(current.getParent().node);
|
||||
}
|
||||
Vector3d parentPos = current.getParent().currentPos;
|
||||
if((losResult = lineOfSight(current.getParent(), current, neighbor))==null){
|
||||
// Vector3d neighborCenter = calculateCenterOfShape(neighbor.node);
|
||||
// Vector3d parentPos = current.getParent().currentPos;
|
||||
float dist = (float)neighborCenter.distance(parentPos);
|
||||
// If there is line-of-sight between parent(s) and neighbor
|
||||
// then ignore s and use the path from parent(s) to neighbor
|
||||
if(current.getParent().cost + dist < neighbor.cost){
|
||||
// c(s, neighbor) is the Euclidean distance from s to neighbor
|
||||
neighbor.cost = current.getParent().cost + dist;
|
||||
neighbor.parent = current.getParent();
|
||||
|
||||
//update prio q
|
||||
queue.remove(neighbor);
|
||||
queue.add(neighbor);
|
||||
// if(neighbor in open){
|
||||
// open.remove(neighbor);
|
||||
// }
|
||||
// open.insert(neighbor, gScore(neighbor) + heuristic(neighbor));
|
||||
}
|
||||
} else {
|
||||
//find midpoint
|
||||
// Vector3d neighborCenter = calculateCenterOfShape(neighbor.node);
|
||||
// Vector3d parentPos = current.getParent().currentPos;
|
||||
if(losResult.hit){
|
||||
float distToMidpoint = (float)neighborCenter.distance(losResult.currentPos);
|
||||
float distToParent = (float)losResult.currentPos.distance(losResult.parentPos);
|
||||
float dist = distToMidpoint + distToParent;
|
||||
// If the length of the path from start to s and from s to
|
||||
// neighbor is shorter than the shortest currently known distance
|
||||
// from start to neighbor, then update node with the new distance
|
||||
if(current.cost + dist < neighbor.cost){
|
||||
neighbor.cost = current.cost + dist;
|
||||
neighbor.parent = current;
|
||||
|
||||
//update prio q
|
||||
queue.remove(neighbor);
|
||||
queue.add(neighbor);
|
||||
|
||||
current.getParent().currentPos = losResult.parentPos;
|
||||
current.currentPos = losResult.currentPos;
|
||||
|
||||
}
|
||||
} else {
|
||||
float dist = (float)neighborCenter.distance(parentPos);
|
||||
// If there is line-of-sight between parent(s) and neighbor
|
||||
// then ignore s and use the path from parent(s) to neighbor
|
||||
if(current.getParent().cost + dist < neighbor.cost){
|
||||
// c(s, neighbor) is the Euclidean distance from s to neighbor
|
||||
neighbor.cost = current.getParent().cost + dist;
|
||||
neighbor.parent = current.getParent();
|
||||
|
||||
//update prio q
|
||||
queue.remove(neighbor);
|
||||
queue.add(neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class LOSResult{
|
||||
Vector3d parentPos;
|
||||
Vector3d currentPos;
|
||||
boolean hit;
|
||||
}
|
||||
|
||||
//calculates the line of sight ACROSS current FROM PARENT TO NEIGHBOR
|
||||
static LOSResult lineOfSight(SetItem parent, SetItem current, SetItem neighbor){
|
||||
if(parent.node instanceof NavCube && current.node instanceof NavCube && neighbor.node instanceof NavCube){
|
||||
//get points on border of current and neighbor, this is one of the lines
|
||||
double borderMinX = 0;
|
||||
double borderMinZ = 0;
|
||||
double borderMaxX = 0;
|
||||
double borderMaxZ = 0;
|
||||
NavCube parentCube = (NavCube)parent.node;
|
||||
NavCube currentCube = (NavCube)current.node;
|
||||
NavCube neighborCube = (NavCube)neighbor.node;
|
||||
//resolve min/max pos
|
||||
// Vector3d currentMin = currentCube.getMinPoint();
|
||||
// Vector3d currentMax = currentCube.getMaxPoint();
|
||||
// Vector3d neighborMin = neighborCube.getMinPoint();
|
||||
// Vector3d neighborMax = neighborCube.getMaxPoint();
|
||||
|
||||
BoxInternalBorder parentCurrentBorder = getBoxInternalBorder(parentCube.getMinPoint(),parentCube.getMaxPoint(),currentCube.getMinPoint(),currentCube.getMaxPoint());
|
||||
BoxInternalBorder currentChildBorder = getBoxInternalBorder(currentCube.getMinPoint(),currentCube.getMaxPoint(),neighborCube.getMinPoint(),neighborCube.getMaxPoint());
|
||||
|
||||
boolean intersectsParentBorder = Line2D.linesIntersect(
|
||||
parentCurrentBorder.minPoint.x, parentCurrentBorder.minPoint.z,
|
||||
parentCurrentBorder.maxPoint.x, parentCurrentBorder.maxPoint.z,
|
||||
neighbor.currentPos.x, neighbor.currentPos.z,
|
||||
parent.currentPos.x, parent.currentPos.z
|
||||
);
|
||||
boolean intersectsChildBorder = Line2D.linesIntersect(
|
||||
currentChildBorder.minPoint.x, currentChildBorder.minPoint.z,
|
||||
currentChildBorder.maxPoint.x, currentChildBorder.maxPoint.z,
|
||||
neighbor.currentPos.x, neighbor.currentPos.z,
|
||||
parent.currentPos.x, parent.currentPos.z
|
||||
);
|
||||
|
||||
LOSResult rVal = new LOSResult();
|
||||
|
||||
if(intersectsParentBorder && intersectsChildBorder){
|
||||
rVal.hit = false;
|
||||
} else {
|
||||
rVal.hit = true;
|
||||
// rVal.currentPos = new Vector3d();
|
||||
if(neighbor.currentPos.distance(currentChildBorder.minPoint) < neighbor.currentPos.distance(currentChildBorder.maxPoint)){
|
||||
rVal.currentPos = currentChildBorder.minPoint;
|
||||
} else {
|
||||
rVal.currentPos = currentChildBorder.maxPoint;
|
||||
}
|
||||
if(parent.currentPos.distance(parentCurrentBorder.minPoint) < parent.currentPos.distance(parentCurrentBorder.maxPoint)){
|
||||
rVal.parentPos = parentCurrentBorder.minPoint;
|
||||
} else {
|
||||
rVal.parentPos = parentCurrentBorder.maxPoint;
|
||||
}
|
||||
}
|
||||
|
||||
return rVal;
|
||||
|
||||
//the other line comes from earlier
|
||||
// if(Line2D.linesIntersect(borderMinX, borderMinZ, borderMaxX, borderMaxZ, rayStart.x, rayStart.z, rayEnd.x, rayEnd.z)){
|
||||
// return null;
|
||||
// } else {
|
||||
// double distMin = Line2D.ptLineDist(rayStart.x, rayStart.z, rayEnd.x, rayEnd.z, borderMinX, borderMinZ);
|
||||
// double distMax = Line2D.ptLineDist(rayStart.x, rayStart.z, rayEnd.x, rayEnd.z, borderMaxX, borderMaxZ);
|
||||
// if(distMin < distMax){
|
||||
// return new Vector3d(borderMinX, 0, borderMinZ);
|
||||
// } else {
|
||||
// return new Vector3d(borderMaxX, 0, borderMaxZ);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static class BoxInternalBorder {
|
||||
Vector3d minPoint = new Vector3d();
|
||||
Vector3d maxPoint = new Vector3d();
|
||||
}
|
||||
|
||||
static BoxInternalBorder getBoxInternalBorder(Vector3d currentMin, Vector3d currentMax, Vector3d neighborMin, Vector3d neighborMax){
|
||||
BoxInternalBorder rVal = new BoxInternalBorder();
|
||||
if(currentMin.x == neighborMax.x){
|
||||
double targetX = currentMin.x;
|
||||
if(currentMin.z >= neighborMin.z && currentMax.z <= neighborMax.z){
|
||||
rVal.minPoint.x = targetX;
|
||||
rVal.minPoint.z = currentMin.z;
|
||||
rVal.maxPoint.x = targetX;
|
||||
rVal.maxPoint.z = currentMax.z;
|
||||
} else if(neighborMin.z >= currentMin.z && neighborMax.z <= currentMax.z){
|
||||
rVal.minPoint.x = targetX;
|
||||
rVal.minPoint.z = neighborMin.z;
|
||||
rVal.maxPoint.x = targetX;
|
||||
rVal.maxPoint.z = neighborMax.z;
|
||||
} else if(currentMin.z >= neighborMin.z){
|
||||
rVal.minPoint.x = targetX;
|
||||
rVal.minPoint.z = currentMin.z;
|
||||
rVal.maxPoint.x = targetX;
|
||||
rVal.maxPoint.z = neighborMax.z;
|
||||
} else if(neighborMin.z >= currentMin.z){
|
||||
rVal.minPoint.x = targetX;
|
||||
rVal.minPoint.z = neighborMin.z;
|
||||
rVal.maxPoint.x = targetX;
|
||||
rVal.maxPoint.z = currentMax.z;
|
||||
} else {
|
||||
//they all line up or something
|
||||
rVal.minPoint.x = targetX;
|
||||
rVal.minPoint.z = currentMin.z;
|
||||
rVal.maxPoint.x = targetX;
|
||||
rVal.maxPoint.z = currentMax.z;
|
||||
}
|
||||
}
|
||||
if(currentMax.x == neighborMin.x){
|
||||
double targetX = currentMax.x;
|
||||
if(currentMin.z >= neighborMin.z && currentMax.z <= neighborMax.z){
|
||||
rVal.minPoint.x = targetX;
|
||||
rVal.minPoint.z = currentMin.z;
|
||||
rVal.maxPoint.x = targetX;
|
||||
rVal.maxPoint.z = currentMax.z;
|
||||
} else if(neighborMin.z >= currentMin.z && neighborMax.z <= currentMax.z){
|
||||
rVal.minPoint.x = targetX;
|
||||
rVal.minPoint.z = neighborMin.z;
|
||||
rVal.maxPoint.x = targetX;
|
||||
rVal.maxPoint.z = neighborMax.z;
|
||||
} else if(currentMin.z >= neighborMin.z){
|
||||
rVal.minPoint.x = targetX;
|
||||
rVal.minPoint.z = currentMin.z;
|
||||
rVal.maxPoint.x = targetX;
|
||||
rVal.maxPoint.z = neighborMax.z;
|
||||
} else if(neighborMin.z >= currentMin.z){
|
||||
rVal.minPoint.x = targetX;
|
||||
rVal.minPoint.z = neighborMin.z;
|
||||
rVal.maxPoint.x = targetX;
|
||||
rVal.maxPoint.z = currentMax.z;
|
||||
} else {
|
||||
//they all line up or something
|
||||
rVal.minPoint.x = targetX;
|
||||
rVal.minPoint.z = currentMin.z;
|
||||
rVal.maxPoint.x = targetX;
|
||||
rVal.maxPoint.z = currentMax.z;
|
||||
}
|
||||
}
|
||||
if(currentMin.z == neighborMax.z){
|
||||
double targetZ = currentMin.x;
|
||||
if(currentMin.x >= neighborMin.x && currentMax.x <= neighborMax.x){
|
||||
rVal.minPoint.x = currentMin.x;
|
||||
rVal.minPoint.z = targetZ;
|
||||
rVal.maxPoint.x = currentMax.x;
|
||||
rVal.maxPoint.z = targetZ;
|
||||
} else if(neighborMin.x >= currentMin.x && neighborMax.x <= currentMax.x){
|
||||
rVal.minPoint.x = neighborMin.x;
|
||||
rVal.minPoint.z = targetZ;
|
||||
rVal.maxPoint.x = neighborMax.x;
|
||||
rVal.maxPoint.z = targetZ;
|
||||
} else if(currentMin.x >= neighborMin.x){
|
||||
rVal.minPoint.x = currentMin.x;
|
||||
rVal.minPoint.z = targetZ;
|
||||
rVal.maxPoint.x = neighborMax.x;
|
||||
rVal.maxPoint.z = targetZ;
|
||||
} else if(neighborMin.x >= currentMin.x){
|
||||
rVal.minPoint.x = neighborMin.x;
|
||||
rVal.minPoint.z = targetZ;
|
||||
rVal.maxPoint.x = currentMax.x;
|
||||
rVal.maxPoint.z = targetZ;
|
||||
} else {
|
||||
//they all line up or something
|
||||
rVal.minPoint.x = currentMin.x;
|
||||
rVal.minPoint.z = targetZ;
|
||||
rVal.maxPoint.x = currentMax.x;
|
||||
rVal.maxPoint.z = targetZ;
|
||||
}
|
||||
}
|
||||
if(currentMax.z == neighborMin.z){
|
||||
double targetZ = currentMax.x;
|
||||
if(currentMin.x >= neighborMin.x && currentMax.x <= neighborMax.x){
|
||||
rVal.minPoint.x = currentMin.x;
|
||||
rVal.minPoint.z = targetZ;
|
||||
rVal.maxPoint.x = currentMax.x;
|
||||
rVal.maxPoint.z = targetZ;
|
||||
} else if(neighborMin.x >= currentMin.x && neighborMax.x <= currentMax.x){
|
||||
rVal.minPoint.x = neighborMin.x;
|
||||
rVal.minPoint.z = targetZ;
|
||||
rVal.maxPoint.x = neighborMax.x;
|
||||
rVal.maxPoint.z = targetZ;
|
||||
} else if(currentMin.x >= neighborMin.x){
|
||||
rVal.minPoint.x = currentMin.x;
|
||||
rVal.minPoint.z = targetZ;
|
||||
rVal.maxPoint.x = neighborMax.x;
|
||||
rVal.maxPoint.z = targetZ;
|
||||
} else if(neighborMin.x >= currentMin.x){
|
||||
rVal.minPoint.x = neighborMin.x;
|
||||
rVal.minPoint.z = targetZ;
|
||||
rVal.maxPoint.x = currentMax.x;
|
||||
rVal.maxPoint.z = targetZ;
|
||||
} else {
|
||||
//they all line up or something
|
||||
rVal.minPoint.x = currentMin.x;
|
||||
rVal.minPoint.z = targetZ;
|
||||
rVal.maxPoint.x = currentMax.x;
|
||||
rVal.maxPoint.z = targetZ;
|
||||
}
|
||||
}
|
||||
return rVal;
|
||||
}
|
||||
|
||||
// static float lineDistCalc(SetItem current, SetItem target){
|
||||
// float rVal = -1.0f;
|
||||
// return rVal;
|
||||
// }
|
||||
|
||||
static Vector3d calculateCenterOfShape(NavShape shape){
|
||||
Vector3d rVal = new Vector3d();
|
||||
if(shape instanceof NavCube){
|
||||
NavCube cube = (NavCube)shape;
|
||||
rVal.add(cube.getMaxPoint()).add(cube.getMinPoint());
|
||||
rVal.mul(0.5);
|
||||
}
|
||||
return rVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static class SetItem implements Comparable {
|
||||
SetItem parent;
|
||||
NavShape node;
|
||||
float cost;
|
||||
//closest point from parent's parent
|
||||
Vector3d currentPos;
|
||||
|
||||
public SetItem(SetItem parent, NavShape node, float cost, Vector3d currentPos){
|
||||
this.parent = parent;
|
||||
this.node = node;
|
||||
this.cost = cost;
|
||||
this.currentPos = currentPos;
|
||||
}
|
||||
|
||||
public SetItem getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public NavShape getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
public float getCost() {
|
||||
return cost;
|
||||
}
|
||||
|
||||
public void setCost(float cost) {
|
||||
this.cost = cost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Object o) {
|
||||
SetItem target = (SetItem)o;
|
||||
if(this.cost < target.cost){
|
||||
return -1;
|
||||
}
|
||||
if(this.cost > target.cost){
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Vector3d getCurrentPos() {
|
||||
return currentPos;
|
||||
}
|
||||
|
||||
public void setCurrentPos(Vector3d currentPos) {
|
||||
this.currentPos = currentPos;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,16 @@
|
||||
package electrosphere.game.server.pathfinding;
|
||||
|
||||
import electrosphere.game.server.pathfinding.blocker.NavBlocker;
|
||||
import electrosphere.game.server.pathfinding.navmesh.NavCube;
|
||||
import electrosphere.game.server.pathfinding.navmesh.NavMesh;
|
||||
import electrosphere.game.server.pathfinding.navmesh.NavShape;
|
||||
import electrosphere.game.server.terrain.manager.ServerTerrainChunk;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.main.Globals;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import org.joml.Vector2i;
|
||||
import org.joml.Vector4i;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -10,22 +18,341 @@ import electrosphere.main.Globals;
|
||||
*/
|
||||
public class NavMeshUtils {
|
||||
|
||||
public static NavMesh createMeshFromChunk(ServerTerrainChunk chunk){
|
||||
NavMesh rVal = new NavMesh();
|
||||
static float NAVMESH_PHASE_ONE_DISPARITY_TOLERANCE = 0.5f;
|
||||
static float NAVMESH_PHASE_TWO_DISPARITY_TOLERANCE = 0.5f;
|
||||
public static NavMesh createMeshFromChunk(ServerTerrainChunk chunk, NavBlocker navBlocker){
|
||||
NavMesh rVal = Globals.navMeshManager.createNavMesh();
|
||||
float[][] heightMap = chunk.getHeightMap();
|
||||
for(int x = 0; x < Globals.serverTerrainManager.getChunkWidth(); x++){
|
||||
for(int y = 0; y < Globals.serverTerrainManager.getChunkWidth(); y++){
|
||||
boolean[][] navMeshGeneratorMask = navBlocker.getHeightfieldBlocker();
|
||||
List<FirstPhaseBox> firstPassBoxes = new LinkedList();
|
||||
int numInCurrent = 0;
|
||||
float currentMin = 0;
|
||||
float currentMax = 0;
|
||||
int startPos = 0;
|
||||
int endPos = 0;
|
||||
for(int x = 0; x < Globals.serverTerrainManager.getAugmentedChunkWidth() - 1; x++){
|
||||
numInCurrent = 0;
|
||||
currentMin = 0;
|
||||
currentMax = 0;
|
||||
for(int y = 0; y < Globals.serverTerrainManager.getAugmentedChunkWidth(); y++){
|
||||
//create node
|
||||
|
||||
if(x > 0){
|
||||
//add back neighbor
|
||||
if(navMeshGeneratorMask[x][y]){
|
||||
if(numInCurrent > 0){
|
||||
firstPassBoxes.add(new FirstPhaseBox(x,startPos,endPos,currentMin,currentMax));
|
||||
}
|
||||
numInCurrent = 0;
|
||||
} else {
|
||||
if(numInCurrent == 0){
|
||||
currentMin = heightMap[x][y];
|
||||
currentMax = heightMap[x][y];
|
||||
startPos = y;
|
||||
endPos = y+1;
|
||||
numInCurrent = 1;
|
||||
} else {
|
||||
if(currentMin > heightMap[x][y]){
|
||||
if(currentMax - heightMap[x][y] < NAVMESH_PHASE_ONE_DISPARITY_TOLERANCE){
|
||||
currentMin = heightMap[x][y];
|
||||
endPos = y;
|
||||
numInCurrent++;
|
||||
} else {
|
||||
//expel previous rectangle
|
||||
firstPassBoxes.add(new FirstPhaseBox(x,startPos,endPos,currentMin,currentMax));
|
||||
//start new one
|
||||
currentMin = heightMap[x][y];
|
||||
currentMax = heightMap[x][y];
|
||||
startPos = y;
|
||||
endPos = y+1;
|
||||
numInCurrent = 1;
|
||||
}
|
||||
} else if(currentMax < heightMap[x][y]){
|
||||
if(heightMap[x][y] - currentMin < NAVMESH_PHASE_ONE_DISPARITY_TOLERANCE){
|
||||
currentMin = heightMap[x][y];
|
||||
endPos = y;
|
||||
numInCurrent++;
|
||||
} else {
|
||||
//expel previous rectangle
|
||||
firstPassBoxes.add(new FirstPhaseBox(x,startPos,endPos,currentMin,currentMax));
|
||||
//start new one
|
||||
currentMin = heightMap[x][y];
|
||||
currentMax = heightMap[x][y];
|
||||
startPos = y;
|
||||
endPos = y+1;
|
||||
numInCurrent = 1;
|
||||
}
|
||||
} else {
|
||||
endPos = y;
|
||||
numInCurrent++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(y > 0){
|
||||
//add back neighbor
|
||||
// if(x > 0){
|
||||
// //add back neighbor
|
||||
// }
|
||||
// if(y > 0){
|
||||
// //add back neighbor
|
||||
// }
|
||||
}
|
||||
|
||||
//close off last box on row
|
||||
// FirstPhaseBox boxy = new FirstPhaseBox(1, 1, 1, 1, 1);
|
||||
firstPassBoxes.add(new FirstPhaseBox(x,startPos,endPos,currentMin,currentMax));
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
//phase two
|
||||
//???
|
||||
List<SecondPhaseBox> secondPhaseBoxes = new LinkedList();
|
||||
List<SecondPhaseBox> toRemove = new LinkedList();
|
||||
for(FirstPhaseBox firstPhaseBox : firstPassBoxes){
|
||||
SecondPhaseBox newBox = new SecondPhaseBox(
|
||||
firstPhaseBox.x,firstPhaseBox.yStart,
|
||||
firstPhaseBox.x+1,firstPhaseBox.yEnd,
|
||||
firstPhaseBox.minHeight,firstPhaseBox.maxHeight);
|
||||
// System.out.println(
|
||||
// firstPhaseBox.x + " " +
|
||||
// firstPhaseBox.yStart + " " +
|
||||
// (firstPhaseBox.x+1) + " " +
|
||||
// firstPhaseBox.yEnd + " " +
|
||||
// firstPhaseBox.minHeight + " " +
|
||||
// firstPhaseBox.maxHeight
|
||||
// );
|
||||
// System.out.println(
|
||||
// "(" + (newBox.boundMinX + chunk.getWorldX() * Globals.serverTerrainManager.getChunkWidth()) + ", " +
|
||||
// (newBox.minHeight - 0.5f) + ", " +
|
||||
// (newBox.boundMinY + chunk.getWorldY() * Globals.serverTerrainManager.getChunkWidth()) + ") (" +
|
||||
// (newBox.boundMaxX + chunk.getWorldX() * Globals.serverTerrainManager.getChunkWidth()) + ", " +
|
||||
// (newBox.maxHeight + 0.5f) + ", " +
|
||||
// (newBox.boundMaxY + chunk.getWorldY() * Globals.serverTerrainManager.getChunkWidth()) + ")"
|
||||
// );
|
||||
toRemove.clear();
|
||||
//TODO: iterate this backwards and check for x adjacency, if no adjacency end loop early
|
||||
for(SecondPhaseBox interim : secondPhaseBoxes){
|
||||
if(firstPhaseBox.yStart == interim.boundMinY && firstPhaseBox.yEnd == interim.boundMaxY && firstPhaseBox.x == interim.boundMaxX){
|
||||
for(SecondPhaseBox neighbor : interim.getNeighbors()){
|
||||
neighbor.removeNeighbor(interim);
|
||||
interim.removeNeighbor(neighbor);
|
||||
neighbor.addNeighbor(newBox);
|
||||
newBox.addNeighbor(neighbor);
|
||||
// System.out.println("ADD NEIGHBOR: " + neighbor.boundMaxX + " " + neighbor.boundMaxY + " " + neighbor.boundMinX + " " + neighbor.boundMinY);
|
||||
}
|
||||
toRemove.add(interim);
|
||||
newBox.setBoundMinX(interim.getBoundMinX());
|
||||
//TODO: calculate new min/max height
|
||||
} else {
|
||||
//because of the way the rectangles are constructed, we can never have a neighbor along y axis
|
||||
//only neighbors will be behind us
|
||||
if(newBox.boundMinX == interim.boundMaxX && !toRemove.contains(interim)){
|
||||
if(
|
||||
(interim.boundMaxY < newBox.boundMaxY && interim.boundMaxY > newBox.boundMinY) ||
|
||||
(interim.boundMinY < newBox.boundMaxY && interim.boundMinY > newBox.boundMinY) ||
|
||||
(newBox.boundMaxY < interim.boundMaxY && newBox.boundMaxY > interim.boundMinY) ||
|
||||
(newBox.boundMinY < interim.boundMaxY && newBox.boundMinY > interim.boundMinY)
|
||||
){
|
||||
// System.out.println("ADD INTERIM: " + interim.boundMaxX + " " + interim.boundMaxY + " " + interim.boundMinX + " " + interim.boundMinY);
|
||||
newBox.addNeighbor(interim);
|
||||
interim.addNeighbor(newBox);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
secondPhaseBoxes.removeAll(toRemove);
|
||||
secondPhaseBoxes.add(newBox);
|
||||
}
|
||||
|
||||
int id = 0;
|
||||
for(SecondPhaseBox box : secondPhaseBoxes){
|
||||
box.setId(id);
|
||||
// System.out.println("getId:" + box.getId());
|
||||
id++;
|
||||
// System.out.println(
|
||||
// "(" + (box.boundMinX + chunk.getWorldX() * Globals.serverTerrainManager.getChunkWidth()) + ", " +
|
||||
// (box.minHeight - 0.5f) + ", " +
|
||||
// (box.boundMinY + chunk.getWorldY() * Globals.serverTerrainManager.getChunkWidth()) + ") (" +
|
||||
// (box.boundMaxX + chunk.getWorldX() * Globals.serverTerrainManager.getChunkWidth() - 1) + ", " +
|
||||
// (box.maxHeight + 0.5f) + ", " +
|
||||
// (box.boundMaxY + chunk.getWorldY() * Globals.serverTerrainManager.getChunkWidth()) + ")"
|
||||
// );
|
||||
// System.out.println(box.neighbors.size());
|
||||
//why the -1? I think the array fiddling above is causing the bounds to be off normally
|
||||
//this fixes that
|
||||
NavCube cube = new NavCube(
|
||||
box.boundMinX + chunk.getWorldX() * Globals.serverTerrainManager.getChunkWidth(),
|
||||
box.minHeight - 0.5f,
|
||||
box.boundMinY + chunk.getWorldY() * Globals.serverTerrainManager.getChunkWidth(),
|
||||
box.boundMaxX + chunk.getWorldX() * Globals.serverTerrainManager.getChunkWidth(),
|
||||
box.maxHeight + 0.5f,
|
||||
box.boundMaxY + chunk.getWorldY() * Globals.serverTerrainManager.getChunkWidth()
|
||||
);
|
||||
rVal.addNode(cube);
|
||||
}
|
||||
|
||||
id = 0;
|
||||
for(NavShape shape : rVal.getNodes()){
|
||||
NavCube cube = (NavCube)shape;
|
||||
|
||||
SecondPhaseBox currentBox = secondPhaseBoxes.get(id);
|
||||
// System.out.println("getId:" + currentBox.getId());
|
||||
id++;
|
||||
for(SecondPhaseBox neighbor : currentBox.getNeighbors()){
|
||||
//TODO: solve bug where items are getting picked up from not in second phase boxes
|
||||
if(!secondPhaseBoxes.contains(neighbor)){
|
||||
LoggerInterface.loggerGameLogic.WARNING("Found bad neighbor adjacency in navmesh generation");
|
||||
for(SecondPhaseBox newNeighbor : secondPhaseBoxes){
|
||||
if(newNeighbor.boundMaxX >= neighbor.boundMaxX &&
|
||||
newNeighbor.boundMaxY >= neighbor.boundMaxY &&
|
||||
newNeighbor.boundMinX <= neighbor.boundMinX &&
|
||||
newNeighbor.boundMinY <= neighbor.boundMinY
|
||||
){
|
||||
cube.addNodeNeighbor(rVal.getNodes().get(newNeighbor.getId()));
|
||||
LoggerInterface.loggerGameLogic.WARNING("Managed to replace bad neighbor");
|
||||
break;
|
||||
}
|
||||
}
|
||||
// System.out.println("ERR!");
|
||||
// System.out.println(neighbor.boundMaxX + " " + neighbor.boundMaxY + " " + neighbor.boundMinX + " " + neighbor.boundMinY);
|
||||
} else {
|
||||
cube.addNodeNeighbor(rVal.getNodes().get(neighbor.getId()));
|
||||
}
|
||||
}
|
||||
// System.out.println(cube.getNodeNeighbors().size());
|
||||
}
|
||||
// System.out.println();
|
||||
// System.out.println();
|
||||
// System.out.println(secondPhaseBoxes.size());
|
||||
|
||||
return rVal;
|
||||
}
|
||||
|
||||
static class FirstPhaseBox {
|
||||
int x;
|
||||
int yStart;
|
||||
int yEnd;
|
||||
float minHeight;
|
||||
float maxHeight;
|
||||
|
||||
public FirstPhaseBox(int x, int yStart, int yEnd, float minHeight, float maxHeight) {
|
||||
this.x = x;
|
||||
this.yStart = yStart;
|
||||
this.yEnd = yEnd;
|
||||
this.minHeight = minHeight;
|
||||
this.maxHeight = maxHeight;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getyStart() {
|
||||
return yStart;
|
||||
}
|
||||
|
||||
public int getyEnd() {
|
||||
return yEnd;
|
||||
}
|
||||
|
||||
public float getMinHeight() {
|
||||
return minHeight;
|
||||
}
|
||||
|
||||
public float getMaxHeight() {
|
||||
return maxHeight;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
static class SecondPhaseBox {
|
||||
|
||||
float minHeight;
|
||||
float maxHeight;
|
||||
int boundMinX;
|
||||
int boundMinY;
|
||||
int boundMaxX;
|
||||
int boundMaxY;
|
||||
List<SecondPhaseBox> neighbors = new LinkedList();
|
||||
int id = -1;
|
||||
|
||||
SecondPhaseBox(int boundMinX, int boundMinY, int boundMaxX, int boundMaxY, float minHeight, float maxHeight){
|
||||
this.boundMinX = boundMinX;
|
||||
this.boundMinY = boundMinY;
|
||||
|
||||
this.boundMaxX = boundMaxX;
|
||||
this.boundMaxY = boundMaxY;
|
||||
|
||||
this.minHeight = minHeight;
|
||||
this.maxHeight = maxHeight;
|
||||
}
|
||||
|
||||
float getMinHeight(){
|
||||
return minHeight;
|
||||
}
|
||||
float getMaxHeight(){
|
||||
return maxHeight;
|
||||
}
|
||||
|
||||
public int getBoundMinX() {
|
||||
return boundMinX;
|
||||
}
|
||||
|
||||
public int getBoundMinY() {
|
||||
return boundMinY;
|
||||
}
|
||||
|
||||
public int getBoundMaxX() {
|
||||
return boundMaxX;
|
||||
}
|
||||
|
||||
public int getBoundMaxY() {
|
||||
return boundMaxY;
|
||||
}
|
||||
|
||||
public void setMinHeight(float minHeight) {
|
||||
this.minHeight = minHeight;
|
||||
}
|
||||
|
||||
public void setMaxHeight(float maxHeight) {
|
||||
this.maxHeight = maxHeight;
|
||||
}
|
||||
|
||||
public void setBoundMinX(int boundMinX) {
|
||||
this.boundMinX = boundMinX;
|
||||
}
|
||||
|
||||
public void setBoundMinY(int boundMinY) {
|
||||
this.boundMinY = boundMinY;
|
||||
}
|
||||
|
||||
public void setBoundMaxX(int boundMaxX) {
|
||||
this.boundMaxX = boundMaxX;
|
||||
}
|
||||
|
||||
public void setBoundMaxY(int boundMaxY) {
|
||||
this.boundMaxY = boundMaxY;
|
||||
}
|
||||
|
||||
public List<SecondPhaseBox> getNeighbors() {
|
||||
return neighbors;
|
||||
}
|
||||
|
||||
public void addNeighbor(SecondPhaseBox neighbor){
|
||||
neighbors.add(neighbor);
|
||||
}
|
||||
|
||||
public void removeNeighbor(SecondPhaseBox neighbor){
|
||||
neighbors.remove(neighbor);
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
package electrosphere.game.server.pathfinding.blocker;
|
||||
|
||||
import electrosphere.main.Globals;
|
||||
|
||||
/**
|
||||
*
|
||||
* Why it's own class? in case we want to use separate logic for non-heightfield chunks later (dungeons mby)
|
||||
*
|
||||
* @author amaterasu
|
||||
*/
|
||||
public class NavBlocker {
|
||||
|
||||
boolean[][] heightfieldBlocker;
|
||||
|
||||
public NavBlocker(){
|
||||
heightfieldBlocker = new boolean[Globals.serverTerrainManager.getAugmentedChunkWidth()][Globals.serverTerrainManager.getAugmentedChunkWidth()];
|
||||
}
|
||||
|
||||
public NavBlocker(boolean[][] field){
|
||||
heightfieldBlocker = field;
|
||||
}
|
||||
|
||||
public boolean[][] getHeightfieldBlocker(){
|
||||
return heightfieldBlocker;
|
||||
}
|
||||
|
||||
public void setHeightfieldBlockerValue(int x, int y, boolean value){
|
||||
heightfieldBlocker[x][y] = value;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
package electrosphere.game.server.pathfinding.blocker;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author amaterasu
|
||||
*/
|
||||
/**
|
||||
* A cache of the blocker fields for terrain meshes
|
||||
*/
|
||||
public class NavTerrainBlockerCache {
|
||||
//Basic idea is we associate string that contains chunk x&y with elevation
|
||||
//While we incur a penalty with converting ints -> string, think this will
|
||||
//offset regenerating the array every time we want a new one
|
||||
int cacheSize = 50;
|
||||
HashMap<String, NavBlocker> navBlockerMapCache = new HashMap();
|
||||
ArrayList<String> navBlockerMapCacheContents = new ArrayList();
|
||||
|
||||
|
||||
public String getKey(int x, int y){
|
||||
return x + "-" + y;
|
||||
}
|
||||
|
||||
public NavBlocker getBlocker(int x, int y){
|
||||
NavBlocker rVal;
|
||||
String key = getKey(x,y);
|
||||
//if in cache
|
||||
if(navBlockerMapCache.containsKey(key)){
|
||||
navBlockerMapCacheContents.remove(key);
|
||||
navBlockerMapCacheContents.add(0, key);
|
||||
rVal = navBlockerMapCache.get(key);
|
||||
return rVal;
|
||||
} else {
|
||||
//else fetch from sql if available
|
||||
|
||||
}
|
||||
//else, create an empty one I guess
|
||||
rVal = new NavBlocker();
|
||||
rVal.setHeightfieldBlockerValue(5,5,true);
|
||||
rVal.setHeightfieldBlockerValue(6,5,true);
|
||||
rVal.setHeightfieldBlockerValue(5,6,true);
|
||||
rVal.setHeightfieldBlockerValue(6,6,true);
|
||||
rVal.setHeightfieldBlockerValue(7,5,true);
|
||||
rVal.setHeightfieldBlockerValue(11,5,true);
|
||||
rVal.setHeightfieldBlockerValue(5,8,true);
|
||||
rVal.setHeightfieldBlockerValue(5,22,true);
|
||||
rVal.setHeightfieldBlockerValue(5,50,true);
|
||||
if(navBlockerMapCache.size() > cacheSize){
|
||||
String oldChunk = navBlockerMapCacheContents.remove(navBlockerMapCacheContents.size() - 1);
|
||||
navBlockerMapCache.remove(oldChunk);
|
||||
}
|
||||
navBlockerMapCacheContents.add(0, key);
|
||||
navBlockerMapCache.put(key, rVal);
|
||||
return rVal;
|
||||
}
|
||||
|
||||
}
|
||||
@ -19,7 +19,7 @@ public class NavCube extends NavShape {
|
||||
Vector3d minPoint;
|
||||
Vector3d maxPoint;
|
||||
|
||||
List<NavCube> neighbors = new LinkedList();
|
||||
List<NavShape> neighbors = new LinkedList();
|
||||
|
||||
|
||||
public NavCube(double minX, double minY, double minZ, double maxX, double maxY, double maxZ){
|
||||
@ -28,12 +28,12 @@ public class NavCube extends NavShape {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNeighbor(NavCube neighbor){
|
||||
public void addNodeNeighbor(NavShape neighbor){
|
||||
neighbors.add(neighbor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NavCube> getNeighbors(){
|
||||
public List<NavShape> getNodeNeighbors(){
|
||||
return neighbors;
|
||||
}
|
||||
|
||||
@ -42,6 +42,13 @@ public class NavCube extends NavShape {
|
||||
return x >= minPoint.x && y >= minPoint.y && z >= minPoint.z && x <= maxPoint.x && y <= maxPoint.y && z <= maxPoint.z;
|
||||
}
|
||||
|
||||
public Vector3d getMinPoint(){
|
||||
return minPoint;
|
||||
}
|
||||
|
||||
public Vector3d getMaxPoint(){
|
||||
return maxPoint;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -13,14 +13,14 @@ public class NavMesh {
|
||||
String method;
|
||||
|
||||
List<NavShape> navNodes = new LinkedList();
|
||||
List<NavMesh> neighbors = new LinkedList();
|
||||
List<NavMesh> meshNeighbors = new LinkedList();
|
||||
|
||||
public void addNeighbor(NavMesh neighbor){
|
||||
neighbors.add(neighbor);
|
||||
public void addMeshNeighbor(NavMesh neighbor){
|
||||
meshNeighbors.add(neighbor);
|
||||
}
|
||||
|
||||
public List<NavMesh> getNeighbors(){
|
||||
return neighbors;
|
||||
public List<NavMesh> getMeshNeighbors(){
|
||||
return meshNeighbors;
|
||||
}
|
||||
|
||||
public void addNode(NavShape node){
|
||||
|
||||
@ -8,9 +8,9 @@ import java.util.List;
|
||||
*/
|
||||
public abstract class NavShape {
|
||||
|
||||
public abstract void addNeighbor(NavCube neighbor);
|
||||
public abstract void addNodeNeighbor(NavShape neighbor);
|
||||
|
||||
public abstract List<NavCube> getNeighbors();
|
||||
public abstract List<NavShape> getNodeNeighbors();
|
||||
|
||||
public abstract boolean containsPoint(double x, double y, double z);
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ package electrosphere.game.server.saves;
|
||||
import electrosphere.game.server.db.DatabaseUtils;
|
||||
import electrosphere.game.server.terrain.manager.ServerTerrainManager;
|
||||
import electrosphere.game.server.world.ServerWorldData;
|
||||
import electrosphere.game.server.world.datacell.DataCellManager;
|
||||
import electrosphere.game.server.datacell.DataCellManager;
|
||||
import electrosphere.main.Globals;
|
||||
import electrosphere.util.FileUtils;
|
||||
import java.util.LinkedList;
|
||||
|
||||
@ -24,11 +24,11 @@ public class ServerTerrainChunk {
|
||||
this.randomizer = randomizer;
|
||||
}
|
||||
|
||||
public static ServerTerrainChunk getArenaChunk(int width){
|
||||
public static ServerTerrainChunk getArenaChunk(int width, int x, int y){
|
||||
float[][] macroValues = new float[5][5];
|
||||
long[][] randomizer = new long[5][5];
|
||||
float[][] heightmap = new float[width + 1][width + 1];
|
||||
ServerTerrainChunk rVal = new ServerTerrainChunk(0, 0, heightmap, macroValues, randomizer);
|
||||
ServerTerrainChunk rVal = new ServerTerrainChunk(x, y, heightmap, macroValues, randomizer);
|
||||
return rVal;
|
||||
}
|
||||
|
||||
|
||||
@ -296,7 +296,7 @@ public class ServerTerrainManager {
|
||||
returnedChunk = elevationMapCache.get(key);
|
||||
return returnedChunk;
|
||||
} else {
|
||||
returnedChunk = ServerTerrainChunk.getArenaChunk(dynamicInterpolationRatio + 1);
|
||||
returnedChunk = ServerTerrainChunk.getArenaChunk(dynamicInterpolationRatio + 1, x, y);
|
||||
elevationMapCache.put(key, returnedChunk);
|
||||
elevationMapCacheContents.add(key);
|
||||
return returnedChunk;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package electrosphere.game.server.world;
|
||||
|
||||
import electrosphere.game.server.terrain.manager.ServerTerrainManager;
|
||||
import electrosphere.game.server.world.datacell.ServerDataCell;
|
||||
import electrosphere.game.server.datacell.ServerDataCell;
|
||||
import java.util.List;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
|
||||
@ -33,7 +33,7 @@ import electrosphere.game.server.terrain.manager.ServerTerrainManager;
|
||||
import electrosphere.game.server.town.Town;
|
||||
import electrosphere.game.server.world.ServerWorldData;
|
||||
import electrosphere.game.server.world.MacroData;
|
||||
import electrosphere.game.server.world.datacell.DataCellManager;
|
||||
import electrosphere.game.server.datacell.DataCellManager;
|
||||
import electrosphere.game.state.MicroSimulation;
|
||||
import electrosphere.menu.Menu;
|
||||
import electrosphere.net.client.ClientNetworking;
|
||||
@ -46,6 +46,7 @@ import electrosphere.renderer.RenderingEngine;
|
||||
import electrosphere.renderer.ShaderProgram;
|
||||
import electrosphere.engine.assetmanager.AssetDataStrings;
|
||||
import electrosphere.engine.assetmanager.AssetManager;
|
||||
import electrosphere.game.server.pathfinding.NavMeshManager;
|
||||
import electrosphere.renderer.ui.WidgetManager;
|
||||
import electrosphere.renderer.ui.WidgetUtils;
|
||||
import electrosphere.renderer.ui.font.FontUtils;
|
||||
@ -242,6 +243,8 @@ public class Globals {
|
||||
//constant for how far in game units you have to move to load chunks
|
||||
public static DrawCellManager drawCellManager;
|
||||
|
||||
//navmesh manager
|
||||
public static NavMeshManager navMeshManager;
|
||||
|
||||
//famous fuckin last words, but temporary solution
|
||||
//global arraylist of values for the skybox colors
|
||||
@ -320,6 +323,8 @@ public class Globals {
|
||||
aiManager = new AIManager();
|
||||
//collision engine
|
||||
collisionEngine = new CollisionEngine();
|
||||
//nav mesh manager
|
||||
navMeshManager = new NavMeshManager();
|
||||
//game config
|
||||
gameConfigDefault = electrosphere.game.config.Config.loadDefaultConfig();
|
||||
gameConfigCurrent = gameConfigDefault;
|
||||
|
||||
@ -7,6 +7,9 @@ import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.entity.types.hitbox.HitboxData;
|
||||
import electrosphere.entity.types.hitbox.HitboxUtils;
|
||||
import electrosphere.game.config.creature.type.CollidableTemplate;
|
||||
import electrosphere.game.server.pathfinding.navmesh.NavCube;
|
||||
import electrosphere.game.server.pathfinding.navmesh.NavMesh;
|
||||
import electrosphere.game.server.pathfinding.navmesh.NavShape;
|
||||
import electrosphere.logger.LoggerInterface;
|
||||
import electrosphere.main.Globals;
|
||||
import static electrosphere.main.Main.deltaTime;
|
||||
@ -545,6 +548,31 @@ public class RenderingEngine {
|
||||
}
|
||||
}
|
||||
|
||||
if(Globals.userSettings.graphicsDebugDrawNavmesh()){
|
||||
Model shapeGraphicsModel;
|
||||
for(NavMesh mesh : Globals.navMeshManager.getMeshes()){
|
||||
for(NavShape shape : mesh.getNodes()){
|
||||
if(shape instanceof NavCube){
|
||||
if((shapeGraphicsModel = Globals.assetManager.fetchModel("Models/unitcube.fbx")) != null){
|
||||
NavCube cube = (NavCube)shape;
|
||||
Vector3d position = new Vector3d(cube.getMinPoint()).add(cube.getMaxPoint()).mul(0.5);
|
||||
Vector3f scale = new Vector3f((float)(cube.getMaxPoint().x-cube.getMinPoint().x)/2,(float)(cube.getMaxPoint().y-cube.getMinPoint().y)/2,(float)(cube.getMaxPoint().z-cube.getMinPoint().z)/2);
|
||||
Quaternionf rotation = new Quaternionf();
|
||||
//calculate camera-modified vector3f
|
||||
Vector3f cameraModifiedPosition = new Vector3f((float)position.x,(float)position.y,(float)position.z).sub(CameraEntityUtils.getCameraCenter(Globals.playerCamera));
|
||||
modelTransformMatrix.identity();
|
||||
modelTransformMatrix.translate(cameraModifiedPosition);
|
||||
modelTransformMatrix.rotate(rotation);
|
||||
// modelTransformMatrix.translate(template.getOffsetX(),template.getOffsetY(),template.getOffsetZ()); //center sphere
|
||||
modelTransformMatrix.scale(scale);
|
||||
shapeGraphicsModel.modelMatrix = modelTransformMatrix;
|
||||
shapeGraphicsModel.draw(true, true, false, true, true, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// glBindVertexArray(0);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user