diff --git a/assets/Config/settings.json b/assets/Config/settings.json index e9af34a2..2141ac80 100644 --- a/assets/Config/settings.json +++ b/assets/Config/settings.json @@ -14,6 +14,7 @@ "graphicsDebugDrawCollisionSpheres" : false, "graphicsDebugDrawPhysicsObjects" : false, - "graphicsDebugDrawMovementVectors" : false + "graphicsDebugDrawMovementVectors" : false, + "graphicsDebugDrawNavmesh" : true } \ No newline at end of file diff --git a/assets/Models/waypoint1.fbx b/assets/Models/waypoint1.fbx new file mode 100644 index 00000000..954ca699 Binary files /dev/null and b/assets/Models/waypoint1.fbx differ diff --git a/src/main/java/electrosphere/engine/LoadingThread.java b/src/main/java/electrosphere/engine/LoadingThread.java index 3fa0274f..588cef54 100644 --- a/src/main/java/electrosphere/engine/LoadingThread.java +++ b/src/main/java/electrosphere/engine/LoadingThread.java @@ -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); // diff --git a/src/main/java/electrosphere/entity/types/waypoint/WaypointUtils.java b/src/main/java/electrosphere/entity/types/waypoint/WaypointUtils.java new file mode 100644 index 00000000..a4350ae3 --- /dev/null +++ b/src/main/java/electrosphere/entity/types/waypoint/WaypointUtils.java @@ -0,0 +1,9 @@ +package electrosphere.entity.types.waypoint; + +/** + * + * @author amaterasu + */ +public class WaypointUtils { + +} diff --git a/src/main/java/electrosphere/game/client/world/ClientWorldData.java b/src/main/java/electrosphere/game/client/world/ClientWorldData.java index 61654174..0e926a15 100644 --- a/src/main/java/electrosphere/game/client/world/ClientWorldData.java +++ b/src/main/java/electrosphere/game/client/world/ClientWorldData.java @@ -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; diff --git a/src/main/java/electrosphere/game/config/UserSettings.java b/src/main/java/electrosphere/game/config/UserSettings.java index e07add89..1facd865 100644 --- a/src/main/java/electrosphere/game/config/UserSettings.java +++ b/src/main/java/electrosphere/game/config/UserSettings.java @@ -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; diff --git a/src/main/java/electrosphere/game/server/world/datacell/DataCellManager.java b/src/main/java/electrosphere/game/server/datacell/DataCellManager.java similarity index 99% rename from src/main/java/electrosphere/game/server/world/datacell/DataCellManager.java rename to src/main/java/electrosphere/game/server/datacell/DataCellManager.java index 5742929d..4eb487ae 100644 --- a/src/main/java/electrosphere/game/server/world/datacell/DataCellManager.java +++ b/src/main/java/electrosphere/game/server/datacell/DataCellManager.java @@ -1,4 +1,4 @@ -package electrosphere.game.server.world.datacell; +package electrosphere.game.server.datacell; import electrosphere.entity.Entity; import electrosphere.entity.EntityUtils; diff --git a/src/main/java/electrosphere/game/server/world/datacell/EnvironmentGenerator.java b/src/main/java/electrosphere/game/server/datacell/EnvironmentGenerator.java similarity index 96% rename from src/main/java/electrosphere/game/server/world/datacell/EnvironmentGenerator.java rename to src/main/java/electrosphere/game/server/datacell/EnvironmentGenerator.java index 640b588c..ba62ec25 100644 --- a/src/main/java/electrosphere/game/server/world/datacell/EnvironmentGenerator.java +++ b/src/main/java/electrosphere/game/server/datacell/EnvironmentGenerator.java @@ -1,4 +1,4 @@ -package electrosphere.game.server.world.datacell; +package electrosphere.game.server.datacell; import electrosphere.entity.Entity; import electrosphere.entity.EntityUtils; diff --git a/src/main/java/electrosphere/game/server/world/datacell/ServerDataCell.java b/src/main/java/electrosphere/game/server/datacell/ServerDataCell.java similarity index 92% rename from src/main/java/electrosphere/game/server/world/datacell/ServerDataCell.java rename to src/main/java/electrosphere/game/server/datacell/ServerDataCell.java index 05b14d2c..3277679f 100644 --- a/src/main/java/electrosphere/game/server/world/datacell/ServerDataCell.java +++ b/src/main/java/electrosphere/game/server/datacell/ServerDataCell.java @@ -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 loadedEntities = new LinkedList(); List 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; } diff --git a/src/main/java/electrosphere/game/server/pathfinding/NavMeshManager.java b/src/main/java/electrosphere/game/server/pathfinding/NavMeshManager.java index 2d25a231..aa195bc5 100644 --- a/src/main/java/electrosphere/game/server/pathfinding/NavMeshManager.java +++ b/src/main/java/electrosphere/game/server/pathfinding/NavMeshManager.java @@ -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 meshes = new LinkedList(); Map 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 getMeshes(){ + return meshes; + } + + public List navigateMeshToMesh(NavMesh startMesh, NavMesh endMesh){ + List rVal = null; + return rVal; + } + + public NavBlocker getNavBlockerForChunk(int x, int y){ + NavBlocker rVal = null; + return rVal; + } + + public NavTerrainBlockerCache getBlockerCache(){ + return blockerCache; + } } diff --git a/src/main/java/electrosphere/game/server/pathfinding/NavMeshPathfinder.java b/src/main/java/electrosphere/game/server/pathfinding/NavMeshPathfinder.java new file mode 100644 index 00000000..d061ff8e --- /dev/null +++ b/src/main/java/electrosphere/game/server/pathfinding/NavMeshPathfinder.java @@ -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 navigatePointToPointInMesh(NavMesh mesh, Vector3d start, Vector3d end){ + + List 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 openSet = new LinkedList(); + List closedSet = new LinkedList(); + PriorityQueue pathItemsQueue = new PriorityQueue(); + Map 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 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; + } + + + + + } +} diff --git a/src/main/java/electrosphere/game/server/pathfinding/NavMeshUtils.java b/src/main/java/electrosphere/game/server/pathfinding/NavMeshUtils.java index 7e227561..e7db855f 100644 --- a/src/main/java/electrosphere/game/server/pathfinding/NavMeshUtils.java +++ b/src/main/java/electrosphere/game/server/pathfinding/NavMeshUtils.java @@ -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 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 secondPhaseBoxes = new LinkedList(); + List 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 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 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; + } + + } + } diff --git a/src/main/java/electrosphere/game/server/pathfinding/blocker/NavBlocker.java b/src/main/java/electrosphere/game/server/pathfinding/blocker/NavBlocker.java new file mode 100644 index 00000000..f04eeb58 --- /dev/null +++ b/src/main/java/electrosphere/game/server/pathfinding/blocker/NavBlocker.java @@ -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; + } + +} diff --git a/src/main/java/electrosphere/game/server/pathfinding/blocker/NavTerrainBlockerCache.java b/src/main/java/electrosphere/game/server/pathfinding/blocker/NavTerrainBlockerCache.java new file mode 100644 index 00000000..4be1bef0 --- /dev/null +++ b/src/main/java/electrosphere/game/server/pathfinding/blocker/NavTerrainBlockerCache.java @@ -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 navBlockerMapCache = new HashMap(); + ArrayList 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; + } + +} diff --git a/src/main/java/electrosphere/game/server/pathfinding/navmesh/NavCube.java b/src/main/java/electrosphere/game/server/pathfinding/navmesh/NavCube.java index 291ea175..0bbe79f7 100644 --- a/src/main/java/electrosphere/game/server/pathfinding/navmesh/NavCube.java +++ b/src/main/java/electrosphere/game/server/pathfinding/navmesh/NavCube.java @@ -19,7 +19,7 @@ public class NavCube extends NavShape { Vector3d minPoint; Vector3d maxPoint; - List neighbors = new LinkedList(); + List 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 getNeighbors(){ + public List 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; + } } diff --git a/src/main/java/electrosphere/game/server/pathfinding/navmesh/NavMesh.java b/src/main/java/electrosphere/game/server/pathfinding/navmesh/NavMesh.java index 893c998a..19b59dce 100644 --- a/src/main/java/electrosphere/game/server/pathfinding/navmesh/NavMesh.java +++ b/src/main/java/electrosphere/game/server/pathfinding/navmesh/NavMesh.java @@ -13,14 +13,14 @@ public class NavMesh { String method; List navNodes = new LinkedList(); - List neighbors = new LinkedList(); + List meshNeighbors = new LinkedList(); - public void addNeighbor(NavMesh neighbor){ - neighbors.add(neighbor); + public void addMeshNeighbor(NavMesh neighbor){ + meshNeighbors.add(neighbor); } - public List getNeighbors(){ - return neighbors; + public List getMeshNeighbors(){ + return meshNeighbors; } public void addNode(NavShape node){ diff --git a/src/main/java/electrosphere/game/server/pathfinding/navmesh/NavShape.java b/src/main/java/electrosphere/game/server/pathfinding/navmesh/NavShape.java index 18e867b9..8ba4d8c7 100644 --- a/src/main/java/electrosphere/game/server/pathfinding/navmesh/NavShape.java +++ b/src/main/java/electrosphere/game/server/pathfinding/navmesh/NavShape.java @@ -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 getNeighbors(); + public abstract List getNodeNeighbors(); public abstract boolean containsPoint(double x, double y, double z); diff --git a/src/main/java/electrosphere/game/server/saves/SaveUtils.java b/src/main/java/electrosphere/game/server/saves/SaveUtils.java index 7c879378..4f20cdd3 100644 --- a/src/main/java/electrosphere/game/server/saves/SaveUtils.java +++ b/src/main/java/electrosphere/game/server/saves/SaveUtils.java @@ -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; diff --git a/src/main/java/electrosphere/game/server/terrain/manager/ServerTerrainChunk.java b/src/main/java/electrosphere/game/server/terrain/manager/ServerTerrainChunk.java index fcdf1e12..e1a5e45e 100644 --- a/src/main/java/electrosphere/game/server/terrain/manager/ServerTerrainChunk.java +++ b/src/main/java/electrosphere/game/server/terrain/manager/ServerTerrainChunk.java @@ -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; } diff --git a/src/main/java/electrosphere/game/server/terrain/manager/ServerTerrainManager.java b/src/main/java/electrosphere/game/server/terrain/manager/ServerTerrainManager.java index a6b5620a..c6246bb0 100644 --- a/src/main/java/electrosphere/game/server/terrain/manager/ServerTerrainManager.java +++ b/src/main/java/electrosphere/game/server/terrain/manager/ServerTerrainManager.java @@ -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; diff --git a/src/main/java/electrosphere/game/server/world/ServerWorldData.java b/src/main/java/electrosphere/game/server/world/ServerWorldData.java index c16fa89d..47394ac9 100644 --- a/src/main/java/electrosphere/game/server/world/ServerWorldData.java +++ b/src/main/java/electrosphere/game/server/world/ServerWorldData.java @@ -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; diff --git a/src/main/java/electrosphere/main/Globals.java b/src/main/java/electrosphere/main/Globals.java index 020bf0fb..6b3a2b31 100644 --- a/src/main/java/electrosphere/main/Globals.java +++ b/src/main/java/electrosphere/main/Globals.java @@ -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; diff --git a/src/main/java/electrosphere/renderer/RenderingEngine.java b/src/main/java/electrosphere/renderer/RenderingEngine.java index 1d008e3b..282ab923 100644 --- a/src/main/java/electrosphere/renderer/RenderingEngine.java +++ b/src/main/java/electrosphere/renderer/RenderingEngine.java @@ -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);