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

This commit is contained in:
austin 2025-04-16 16:47:05 -04:00
parent 277c9303ca
commit f64d360d14
34 changed files with 519 additions and 1302 deletions

View File

@ -57,6 +57,8 @@
"navier_stokes.h": "c",
"electrosphere_client_fluid_cache_fluidchunkdata.h": "c",
"normalization.h": "c",
"bounds.h": "c"
"bounds.h": "c",
"electrosphere_server_physics_fluid_manager_serverfluidchunk.h": "c",
"electrosphere_server_physics_fluid_simulator_fluidacceleratedsimulator.h": "c"
}
}

View File

@ -1,3 +1,3 @@
#maven.buildNumber.plugin properties file
#Wed Apr 02 13:31:25 EDT 2025
buildNumber=617
#Wed Apr 16 16:24:00 EDT 2025
buildNumber=619

View File

@ -1510,6 +1510,11 @@ Refactoring server classes under physics package
Refactoring server classes under entity package
Refactoring server macro data classes
Refactoring some macro classes
Fix native code linking after refactoring
Obliterate old navmesh/pathfinding code
Integrate recast4j as pathfinding source
GriddedDataCellManager pathfinding solution
ServerTerrainManager nullcheck fix

View File

@ -1,49 +0,0 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class electrosphere_server_fluid_manager_ServerFluidChunk */
#ifndef _Included_electrosphere_server_fluid_manager_ServerFluidChunk
#define _Included_electrosphere_server_fluid_manager_ServerFluidChunk
#ifdef __cplusplus
extern "C" {
#endif
#undef electrosphere_server_fluid_manager_ServerFluidChunk_ARRAY_CT
#define electrosphere_server_fluid_manager_ServerFluidChunk_ARRAY_CT 27L
#undef electrosphere_server_fluid_manager_ServerFluidChunk_CENTER_BUFF
#define electrosphere_server_fluid_manager_ServerFluidChunk_CENTER_BUFF 13L
#undef electrosphere_server_fluid_manager_ServerFluidChunk_TRUE_DATA_DIM
#define electrosphere_server_fluid_manager_ServerFluidChunk_TRUE_DATA_DIM 16L
#undef electrosphere_server_fluid_manager_ServerFluidChunk_TRUE_DATA_GENERATOR_SIZE
#define electrosphere_server_fluid_manager_ServerFluidChunk_TRUE_DATA_GENERATOR_SIZE 17L
#undef electrosphere_server_fluid_manager_ServerFluidChunk_TRUE_DATA_OFFSET
#define electrosphere_server_fluid_manager_ServerFluidChunk_TRUE_DATA_OFFSET 1L
#undef electrosphere_server_fluid_manager_ServerFluidChunk_BUFFER_DIM
#define electrosphere_server_fluid_manager_ServerFluidChunk_BUFFER_DIM 18L
#undef electrosphere_server_fluid_manager_ServerFluidChunk_BUFFER_SIZE
#define electrosphere_server_fluid_manager_ServerFluidChunk_BUFFER_SIZE 5832L
#undef electrosphere_server_fluid_manager_ServerFluidChunk_IS_HOMOGENOUS
#define electrosphere_server_fluid_manager_ServerFluidChunk_IS_HOMOGENOUS 1.0f
#undef electrosphere_server_fluid_manager_ServerFluidChunk_IS_NOT_HOMOGENOUS
#define electrosphere_server_fluid_manager_ServerFluidChunk_IS_NOT_HOMOGENOUS 0.0f
#undef electrosphere_server_fluid_manager_ServerFluidChunk_HOMOGENOUS_BUFFER_SIZE
#define electrosphere_server_fluid_manager_ServerFluidChunk_HOMOGENOUS_BUFFER_SIZE 4L
/*
* Class: electrosphere_server_fluid_manager_ServerFluidChunk
* Method: allocate
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_electrosphere_server_fluid_manager_ServerFluidChunk_allocate
(JNIEnv *, jobject);
/*
* Class: electrosphere_server_fluid_manager_ServerFluidChunk
* Method: free
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_electrosphere_server_fluid_manager_ServerFluidChunk_free
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,41 +0,0 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class electrosphere_server_fluid_simulator_FluidAcceleratedSimulator */
#ifndef _Included_electrosphere_server_fluid_simulator_FluidAcceleratedSimulator
#define _Included_electrosphere_server_fluid_simulator_FluidAcceleratedSimulator
#ifdef __cplusplus
extern "C" {
#endif
#undef electrosphere_server_fluid_simulator_FluidAcceleratedSimulator_SIMULATE_TIMESTEP
#define electrosphere_server_fluid_simulator_FluidAcceleratedSimulator_SIMULATE_TIMESTEP 0.1f
#undef electrosphere_server_fluid_simulator_FluidAcceleratedSimulator_GRAVITY_CONST
#define electrosphere_server_fluid_simulator_FluidAcceleratedSimulator_GRAVITY_CONST -100.0f
/*
* Class: electrosphere_server_fluid_simulator_FluidAcceleratedSimulator
* Method: init
* Signature: (F)V
*/
JNIEXPORT void JNICALL Java_electrosphere_server_fluid_simulator_FluidAcceleratedSimulator_init
(JNIEnv *, jclass, jfloat);
/*
* Class: electrosphere_server_fluid_simulator_FluidAcceleratedSimulator
* Method: simulate
* Signature: (Ljava/util/List;F)V
*/
JNIEXPORT void JNICALL Java_electrosphere_server_fluid_simulator_FluidAcceleratedSimulator_simulate
(JNIEnv *, jclass, jobject, jfloat);
/*
* Class: electrosphere_server_fluid_simulator_FluidAcceleratedSimulator
* Method: free
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_electrosphere_server_fluid_simulator_FluidAcceleratedSimulator_free
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,49 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class electrosphere_server_physics_fluid_manager_ServerFluidChunk */
#ifndef _Included_electrosphere_server_physics_fluid_manager_ServerFluidChunk
#define _Included_electrosphere_server_physics_fluid_manager_ServerFluidChunk
#ifdef __cplusplus
extern "C" {
#endif
#undef electrosphere_server_physics_fluid_manager_ServerFluidChunk_ARRAY_CT
#define electrosphere_server_physics_fluid_manager_ServerFluidChunk_ARRAY_CT 27L
#undef electrosphere_server_physics_fluid_manager_ServerFluidChunk_CENTER_BUFF
#define electrosphere_server_physics_fluid_manager_ServerFluidChunk_CENTER_BUFF 13L
#undef electrosphere_server_physics_fluid_manager_ServerFluidChunk_TRUE_DATA_DIM
#define electrosphere_server_physics_fluid_manager_ServerFluidChunk_TRUE_DATA_DIM 16L
#undef electrosphere_server_physics_fluid_manager_ServerFluidChunk_TRUE_DATA_GENERATOR_SIZE
#define electrosphere_server_physics_fluid_manager_ServerFluidChunk_TRUE_DATA_GENERATOR_SIZE 17L
#undef electrosphere_server_physics_fluid_manager_ServerFluidChunk_TRUE_DATA_OFFSET
#define electrosphere_server_physics_fluid_manager_ServerFluidChunk_TRUE_DATA_OFFSET 1L
#undef electrosphere_server_physics_fluid_manager_ServerFluidChunk_BUFFER_DIM
#define electrosphere_server_physics_fluid_manager_ServerFluidChunk_BUFFER_DIM 18L
#undef electrosphere_server_physics_fluid_manager_ServerFluidChunk_BUFFER_SIZE
#define electrosphere_server_physics_fluid_manager_ServerFluidChunk_BUFFER_SIZE 5832L
#undef electrosphere_server_physics_fluid_manager_ServerFluidChunk_IS_HOMOGENOUS
#define electrosphere_server_physics_fluid_manager_ServerFluidChunk_IS_HOMOGENOUS 1.0f
#undef electrosphere_server_physics_fluid_manager_ServerFluidChunk_IS_NOT_HOMOGENOUS
#define electrosphere_server_physics_fluid_manager_ServerFluidChunk_IS_NOT_HOMOGENOUS 0.0f
#undef electrosphere_server_physics_fluid_manager_ServerFluidChunk_HOMOGENOUS_BUFFER_SIZE
#define electrosphere_server_physics_fluid_manager_ServerFluidChunk_HOMOGENOUS_BUFFER_SIZE 4L
/*
* Class: electrosphere_server_physics_fluid_manager_ServerFluidChunk
* Method: allocate
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_electrosphere_server_physics_fluid_manager_ServerFluidChunk_allocate
(JNIEnv *, jobject);
/*
* Class: electrosphere_server_physics_fluid_manager_ServerFluidChunk
* Method: free
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_electrosphere_server_physics_fluid_manager_ServerFluidChunk_free
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,41 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class electrosphere_server_physics_fluid_simulator_FluidAcceleratedSimulator */
#ifndef _Included_electrosphere_server_physics_fluid_simulator_FluidAcceleratedSimulator
#define _Included_electrosphere_server_physics_fluid_simulator_FluidAcceleratedSimulator
#ifdef __cplusplus
extern "C" {
#endif
#undef electrosphere_server_physics_fluid_simulator_FluidAcceleratedSimulator_SIMULATE_TIMESTEP
#define electrosphere_server_physics_fluid_simulator_FluidAcceleratedSimulator_SIMULATE_TIMESTEP 0.1f
#undef electrosphere_server_physics_fluid_simulator_FluidAcceleratedSimulator_GRAVITY_CONST
#define electrosphere_server_physics_fluid_simulator_FluidAcceleratedSimulator_GRAVITY_CONST -100.0f
/*
* Class: electrosphere_server_physics_fluid_simulator_FluidAcceleratedSimulator
* Method: init
* Signature: (F)V
*/
JNIEXPORT void JNICALL Java_electrosphere_server_physics_fluid_simulator_FluidAcceleratedSimulator_init
(JNIEnv *, jclass, jfloat);
/*
* Class: electrosphere_server_physics_fluid_simulator_FluidAcceleratedSimulator
* Method: simulate
* Signature: (Ljava/util/List;F)V
*/
JNIEXPORT void JNICALL Java_electrosphere_server_physics_fluid_simulator_FluidAcceleratedSimulator_simulate
(JNIEnv *, jclass, jobject, jfloat);
/*
* Class: electrosphere_server_physics_fluid_simulator_FluidAcceleratedSimulator
* Method: free
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_electrosphere_server_physics_fluid_simulator_FluidAcceleratedSimulator_free
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -51,7 +51,7 @@ Environment * environment = NULL;
void * getArray(JNIEnv * env, jobjectArray arr, int index);
JNIEXPORT void JNICALL Java_electrosphere_server_fluid_simulator_FluidAcceleratedSimulator_simulate(
JNIEXPORT void JNICALL Java_electrosphere_server_physics_fluid_simulator_FluidAcceleratedSimulator_simulate(
JNIEnv * env,
jclass fluidSimClass,
jobject chunkList,
@ -74,7 +74,7 @@ JNIEXPORT void JNICALL Java_electrosphere_server_fluid_simulator_FluidAccelerate
/**
* Should clean up all native allocations and state
*/
JNIEXPORT void JNICALL Java_electrosphere_server_fluid_simulator_FluidAcceleratedSimulator_free(
JNIEXPORT void JNICALL Java_electrosphere_server_physics_fluid_simulator_FluidAcceleratedSimulator_free(
JNIEnv * env,
jclass fluidSimClass
){
@ -94,7 +94,7 @@ JNIEXPORT void JNICALL Java_electrosphere_server_fluid_simulator_FluidAccelerate
/**
* Initializes the library
*/
JNIEXPORT void JNICALL Java_electrosphere_server_fluid_simulator_FluidAcceleratedSimulator_init(
JNIEXPORT void JNICALL Java_electrosphere_server_physics_fluid_simulator_FluidAcceleratedSimulator_init(
JNIEnv * env,
jclass fluidSimClass,
jfloat gravity
@ -114,7 +114,7 @@ JNIEXPORT void JNICALL Java_electrosphere_server_fluid_simulator_FluidAccelerate
//store jni lookup tables
jclass listClass = (*env)->FindClass(env,"java/util/List");
jclass fluidSimStorageClass = (*env)->FindClass(env,"electrosphere/server/fluid/manager/ServerFluidChunk");
jclass fluidSimStorageClass = (*env)->FindClass(env,"electrosphere/server/physics/fluid/manager/ServerFluidChunk");
environment->lookupTable.serverFluidChunkClass = fluidSimStorageClass;
//JNIEnv *env, jclass clazz, const char *name, const char *sig
environment->lookupTable.listTable.jListSize = (*env)->GetMethodID(env, listClass, "size", "()I");
@ -135,7 +135,7 @@ JNIEXPORT void JNICALL Java_electrosphere_server_fluid_simulator_FluidAccelerate
environment->lookupTable.serverFluidChunkTable.worldXId = (*env)->GetFieldID(env,fluidSimStorageClass,"worldX","I");
environment->lookupTable.serverFluidChunkTable.worldYId = (*env)->GetFieldID(env,fluidSimStorageClass,"worldY","I");
environment->lookupTable.serverFluidChunkTable.worldZId = (*env)->GetFieldID(env,fluidSimStorageClass,"worldZ","I");
environment->lookupTable.serverFluidChunkTable.neighborsId = (*env)->GetFieldID(env,fluidSimStorageClass,"neighbors","[Lelectrosphere/server/fluid/manager/ServerFluidChunk;");
environment->lookupTable.serverFluidChunkTable.neighborsId = (*env)->GetFieldID(env,fluidSimStorageClass,"neighbors","[Lelectrosphere/server/physics/fluid/manager/ServerFluidChunk;");
environment->lookupTable.serverFluidChunkTable.chunkmaskJId = (*env)->GetFieldID(env,fluidSimStorageClass,"chunkMask","I");
environment->lookupTable.serverFluidChunkTable.totalDensityId = (*env)->GetFieldID(env,fluidSimStorageClass,"totalDensity","F");
environment->lookupTable.serverFluidChunkTable.updatedId = (*env)->GetFieldID(env,fluidSimStorageClass,"updated","Z");
@ -157,7 +157,7 @@ JNIEXPORT void JNICALL Java_electrosphere_server_fluid_simulator_FluidAccelerate
*/
int readInChunks(JNIEnv * env, jobject chunkList, Environment * environment){
jclass listClass = (*env)->FindClass(env,"java/util/List");
jclass fluidSimClass = (*env)->FindClass(env,"electrosphere/server/fluid/manager/ServerFluidChunk");
jclass fluidSimClass = (*env)->FindClass(env,"electrosphere/server/physics/fluid/manager/ServerFluidChunk");
//JNIEnv *env, jclass clazz, const char *name, const char *sig
jmethodID jListSize = environment->lookupTable.listTable.jListSize;
jmethodID jListGet = environment->lookupTable.listTable.jListGet;

View File

@ -1,7 +1,7 @@
#include <stdint.h>
//native interfaces
#include "native/electrosphere_server_fluid_simulator_FluidAcceleratedSimulator.h"
#include "native/electrosphere_server_physics_fluid_simulator_FluidAcceleratedSimulator.h"
//fluid lib
#include "fluid/env/utilities.h"

View File

@ -4,7 +4,7 @@
#include <sys/time.h>
//native interfaces
#include "native/electrosphere_server_fluid_simulator_FluidAcceleratedSimulator.h"
#include "native/electrosphere_server_physics_fluid_simulator_FluidAcceleratedSimulator.h"
//fluid lib
#include "fluid/env/utilities.h"

View File

@ -1,7 +1,7 @@
#include <jni.h>
#include <stdlib.h>
#include "../../includes/native/electrosphere_server_fluid_manager_ServerFluidChunk.h"
#include "../../includes/native/electrosphere_server_physics_fluid_manager_ServerFluidChunk.h"
#include "../../includes/native/electrosphere_client_fluid_cache_FluidChunkData.h"
#include "fluid/queue/chunk.h"
@ -65,7 +65,7 @@ void freeField(JNIEnv * env, jobject fluidObj, jfieldID arrFieldId);
* @param env The JNI env
* @param fluidObj The fluid object
*/
JNIEXPORT void JNICALL Java_electrosphere_server_fluid_manager_ServerFluidChunk_allocate(
JNIEXPORT void JNICALL Java_electrosphere_server_physics_fluid_manager_ServerFluidChunk_allocate(
JNIEnv * env,
jobject fluidObj
){
@ -136,7 +136,7 @@ void allocateCenterField(JNIEnv * env, jobject fluidObj, jfieldID arrFieldId){
* @param env The JNI env
* @param fluidObj The fluid object
*/
JNIEXPORT void JNICALL Java_electrosphere_server_fluid_manager_ServerFluidChunk_free(
JNIEXPORT void JNICALL Java_electrosphere_server_physics_fluid_manager_ServerFluidChunk_free(
JNIEnv * env,
jobject fluidObj
){

View File

@ -84,7 +84,6 @@ import electrosphere.server.db.DatabaseController;
import electrosphere.server.entity.poseactor.PoseModel;
import electrosphere.server.macro.MacroData;
import electrosphere.server.macro.structure.StructureManager;
import electrosphere.server.pathfinding.NavMeshManager;
import electrosphere.server.saves.Save;
import electrosphere.server.simulation.MacroSimulation;
import electrosphere.server.simulation.MicroSimulation;
@ -378,9 +377,6 @@ public class Globals {
//fluid cell manager
public static FluidCellManager fluidCellManager;
//navmesh manager
public static NavMeshManager navMeshManager;
//famous fuckin last words, but temporary solution
//global arraylist of values for the skybox colors
//edit(6/1/21): :upside_down_smile:
@ -515,8 +511,6 @@ public class Globals {
//realm & data cell manager
realmManager = new RealmManager();
entityDataCellMapper = new EntityDataCellMapper();
//nav mesh manager
navMeshManager = new NavMeshManager();
//gridded managers
Globals.clientTerrainManager = new ClientTerrainManager();
Globals.clientFluidManager = new ClientFluidManager();

View File

@ -107,16 +107,23 @@ public class TerrainChunk {
}
/**
* Creates a terrain chunk entity on the server
* @param entity The entity to populate
* @param weights The weights for the terrain chunk
* @param values The values of each voxel in the chunk
* Generates terrain chunk data from a set of weights and values
* @param weights The weights
* @param values The values
* @return The terrain chunk data
*/
public static void serverCreateTerrainChunkEntity(Entity entity, float[][][] weights, int[][][] values){
public static TerrainChunkData serverGenerateTerrainChunkData(float[][][] weights, int[][][] values){
TransvoxelChunkData chunkData = new TransvoxelChunkData(weights, values, ClientDrawCellManager.FULL_RES_LOD);
TerrainChunkData data = TransvoxelModelGeneration.generateTerrainChunkData(chunkData);
return data;
}
/**
* Creates a terrain chunk entity on the server
* @param entity The entity to populate
* @param data The terrain chunk data
*/
public static void serverCreateTerrainChunkEntity(Entity entity, TerrainChunkData data){
if(data.getVertices().length > 0){
PhysicsEntityUtils.serverAttachTriGeomRigidBody(entity, data);
Realm realm = Globals.realmManager.getEntityRealm(entity);

View File

@ -5,7 +5,6 @@ import java.util.List;
import org.joml.Matrix4d;
import org.joml.Quaterniond;
import org.joml.Quaternionf;
import org.joml.Vector3d;
import org.lwjgl.opengl.GL40;
import org.ode4j.ode.DCapsule;
@ -32,9 +31,6 @@ import electrosphere.renderer.pipelines.RenderPipeline;
import electrosphere.renderer.texture.Texture;
import electrosphere.server.datacell.Realm;
import electrosphere.server.datacell.utils.EntityLookupUtils;
import electrosphere.server.pathfinding.navmesh.NavCube;
import electrosphere.server.pathfinding.navmesh.NavMesh;
import electrosphere.server.pathfinding.navmesh.NavShape;
/**
* Pipeline for rendering content to assist debugging
@ -236,28 +232,7 @@ public class DebugContentPipeline implements RenderPipeline {
renderPipelineState.setUseMaterial(true);
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(AssetDataStrings.UNITCUBE)) != null){
NavCube cube = (NavCube)shape;
Vector3d position = new Vector3d(cube.getMinPoint()).add(cube.getMaxPoint()).mul(0.5);
Vector3d scale = new Vector3d((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 vector3d
Vector3d cameraModifiedPosition = new Vector3d(position).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(new Vector3d(scale));
shapeGraphicsModel.setModelMatrix(modelTransformMatrix);
shapeGraphicsModel.draw(renderPipelineState,openGLState);
}
}
}
}
throw new Error("Not yet implemented!");
}
debugBonesPipeline.render(openGLState, renderPipelineState);

View File

@ -9,6 +9,7 @@ import electrosphere.entity.scene.Scene;
import electrosphere.net.parser.net.message.NetworkMessage;
import electrosphere.script.ScriptEngine;
import electrosphere.server.datacell.interfaces.DataCellManager;
import electrosphere.server.datacell.interfaces.PathfindingManager;
import electrosphere.server.entity.ServerContentManager;
import java.util.HashSet;
@ -29,10 +30,14 @@ public class Realm {
*/
public static final int NO_SCENE_INSTANCE = -1;
//The set containing all data cells loaded into this realm
/**
* The set containing all data cells loaded into this realm
*/
Set<ServerDataCell> loadedDataCells = new HashSet<ServerDataCell>();
//this is the cell that all players loading into the game (via connection startup, death, etc) reside in
/**
* this is the cell that all players loading into the game (via connection startup, death, etc) reside in
*/
ServerDataCell loadingCell = new ServerDataCell(new Scene());
/**
@ -40,13 +45,24 @@ public class Realm {
*/
ServerDataCell inventoryCell = new ServerDataCell(new Scene());
//resolver for entity -> data cell within this realm
/**
* resolver for entity -> data cell within this realm
*/
EntityDataCellMapper entityDataCellMapper;
//provides functions for relating data cells to physical locations (eg creating cells, deleting cells, etc)
/**
* provides functions for relating data cells to physical locations (eg creating cells, deleting cells, etc)
*/
DataCellManager dataCellManager;
//Main entity physics collision checking engine
/**
* The pathfinding manager
*/
PathfindingManager pathfindingManager;
/**
* Main entity physics collision checking engine
*/
CollisionEngine collisionEngine;
/**
@ -54,7 +70,9 @@ public class Realm {
*/
CollisionEngine chemistryEngine;
//Hitbox manager for the realm
/**
* Hitbox manager for the realm
*/
HitboxManager hitboxManager;
/**
@ -172,6 +190,22 @@ public class Realm {
this.dataCellManager = dataCellManager;
}
/**
* Gets the pathfinding manager
* @return The pathfinding manager
*/
public PathfindingManager getPathfindingManager(){
return pathfindingManager;
}
/**
* Sets the pathfinding manager
* @param pathfindingManager The pathfinding manager
*/
protected void setPathfindingManager(PathfindingManager pathfindingManager){
this.pathfindingManager = pathfindingManager;
}
/**
* Gets the collision engine for physics collision checking in this realm
* @return The collision engine
@ -208,11 +242,6 @@ public class Realm {
//main simulation
dataCellManager.simulate();
//
//data cell manager update misc variables (player positions, unload not-in-use cells)
if(dataCellManager != null){
// dataCellManager.unloadPlayerlessChunks();
}
//
//clear collidable impulse lists
collisionEngine.clearCollidableImpulseLists();
chemistryEngine.clearCollidableImpulseLists();

View File

@ -94,6 +94,7 @@ public class RealmManager {
GriddedDataCellManager griddedDataCellManager = new GriddedDataCellManager(realm);
//add function classes to realm
realm.setDataCellManager(griddedDataCellManager);
realm.setPathfindingManager(griddedDataCellManager);
//register within the manager
realms.add(realm);
return realm;

View File

@ -12,7 +12,6 @@ import electrosphere.net.parser.net.message.EntityMessage;
import electrosphere.net.parser.net.message.NetworkMessage;
import electrosphere.net.server.player.Player;
import electrosphere.server.macro.character.Character;
import electrosphere.server.pathfinding.navmesh.NavMesh;
import java.util.HashSet;
import java.util.List;
@ -32,29 +31,24 @@ public class ServerDataCell {
/**
* All players attached to this server data cell
*/
Set<Player> activePlayers = new HashSet<Player>();
/**
* The navmesh for the data cell
*/
NavMesh navMesh;
private Set<Player> activePlayers = new HashSet<Player>();
/**
* The scene backing the server data cell
*/
Scene scene;
private Scene scene;
/**
* Controls whether the server data cell simulates its entities or not
*/
boolean ready = false;
private boolean ready = false;
/**
* Constructs a datacell based on a virtual cell. Should be used when a player
* first comes into range of the cell.
* @param virtualCell
*/
ServerDataCell(Scene scene){
protected ServerDataCell(Scene scene){
this.scene = scene;
}
@ -247,14 +241,6 @@ public class ServerDataCell {
return scene;
}
/**
* Sets the nav mesh of this server data cell
* @param navMesh The nav mesh to store
*/
public void setNavMesh(NavMesh navMesh){
this.navMesh = navMesh;
}
/**
* Gets the simulation ready status of the server data cell
* @return True if ready, false otherwise

View File

@ -17,6 +17,7 @@ import org.joml.Vector3d;
import org.joml.Vector3i;
import electrosphere.client.block.BlockChunkData;
import electrosphere.client.terrain.data.TerrainChunkData;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityCreationUtils;
@ -32,10 +33,12 @@ import electrosphere.server.datacell.Realm;
import electrosphere.server.datacell.ServerDataCell;
import electrosphere.server.datacell.ServerWorldData;
import electrosphere.server.datacell.interfaces.DataCellManager;
import electrosphere.server.datacell.interfaces.PathfindingManager;
import electrosphere.server.datacell.interfaces.VoxelCellManager;
import electrosphere.server.datacell.physics.PhysicsDataCell;
import electrosphere.server.entity.ServerContentManager;
import electrosphere.server.entity.serialization.ContentSerialization;
import electrosphere.server.pathfinding.NavMeshConstructor;
import electrosphere.server.physics.block.manager.ServerBlockManager;
import electrosphere.server.physics.fluid.manager.ServerFluidChunk;
import electrosphere.server.physics.fluid.manager.ServerFluidManager;
@ -47,7 +50,7 @@ import electrosphere.util.math.HashUtils;
/**
* Implementation of DataCellManager that lays out cells in a logical grid (array). Useful for eg 3d terrain gridded world.
*/
public class GriddedDataCellManager implements DataCellManager, VoxelCellManager {
public class GriddedDataCellManager implements DataCellManager, VoxelCellManager, PathfindingManager {
/**
* The minimum grid size allowed
@ -349,6 +352,13 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
cell.setTerrainChunk(terrainChunk);
cell.setBlockChunk(blockChunkData);
cell.generatePhysics();
//create pathfinding mesh
TerrainChunkData terrainMeshData = cell.getTerrainChunkData();
ServerDataCell serverDataCell = this.groundDataCells.get(key);
GriddedDataCellTrackingData trackingData = this.cellTrackingMap.get(serverDataCell);
trackingData.setNavMeshData(NavMeshConstructor.constructNavmesh(terrainMeshData));
loadedCellsLock.lock();
posPhysicsMap.put(key, cell);
loadedCellsLock.unlock();
@ -716,6 +726,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
PhysicsDataCell cell,
Map<Long, PhysicsDataCell> posPhysicsMap,
Map<Long, ServerDataCell> groundDataCells,
Map<ServerDataCell,GriddedDataCellTrackingData> cellTrackingMap,
Realm realm
){
//get data to generate with
@ -746,18 +757,29 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
ServerEntityUtils.destroyEntity(blockEntity);
}
//get advanced tracking data for the data cell
GriddedDataCellTrackingData trackingData = cellTrackingMap.get(dataCell);
generationService.submit(() -> {
BlockChunkData blockChunkData = realm.getServerWorldData().getServerBlockManager().getChunk(worldPos.x, worldPos.y, worldPos.z);
ServerTerrainChunk terrainChunk = realm.getServerWorldData().getServerTerrainManager().getChunk(worldPos.x, worldPos.y, worldPos.z);
targetCell.setTerrainChunk(terrainChunk);
targetCell.setBlockChunk(blockChunkData);
TerrainChunkData terrainMeshData;
//create physics entities
if(cell != null){
cell.retireCell();
cell.generatePhysics();
terrainMeshData = cell.getTerrainChunkData();
} else {
targetCell.generatePhysics();
terrainMeshData = targetCell.getTerrainChunkData();
}
//create pathfinding mesh
trackingData.setNavMeshData(NavMeshConstructor.constructNavmesh(terrainMeshData));
//set ready
dataCell.setReady(true);
});
@ -800,7 +822,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
//generates physics for the cell in a dedicated thread then finally registers
loadedCellsLock.lock();
PhysicsDataCell cell = posPhysicsMap.get(key);
GriddedDataCellManager.runPhysicsGenerationThread(localWorldPos,key,cell,this.posPhysicsMap,this.groundDataCells,this.parent);
GriddedDataCellManager.runPhysicsGenerationThread(localWorldPos,key,cell,this.posPhysicsMap,this.groundDataCells,this.cellTrackingMap,this.parent);
loadedCellsLock.unlock();
@ -1047,4 +1069,9 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
return cellPlayerlessFrameMap;
}
@Override
public List<Vector3d> findPath(Vector3d start, Vector3d end) {
throw new UnsupportedOperationException("Unimplemented method 'findPath'");
}
}

View File

@ -1,5 +1,7 @@
package electrosphere.server.datacell.gridded;
import org.recast4j.detour.MeshData;
/**
* Data associated with a ServerDataCell by the GriddedDataCellManager
*/
@ -15,6 +17,11 @@ public class GriddedDataCellTrackingData {
*/
double closestPlayer;
/**
* The nav mesh data
*/
MeshData navMeshData;
/**
* Gets the distance from the cell to the closest player
* @return The distance
@ -31,6 +38,20 @@ public class GriddedDataCellTrackingData {
this.closestPlayer = closestPlayer;
}
/**
* Gets the nav mesh data
* @return The nav mesh data
*/
public MeshData getNavMeshData() {
return navMeshData;
}
/**
* Sets the nav mesh data
* @param navMeshData The nav mesh data
*/
public void setNavMeshData(MeshData navMeshData) {
this.navMeshData = navMeshData;
}
}

View File

@ -0,0 +1,20 @@
package electrosphere.server.datacell.interfaces;
import java.util.List;
import org.joml.Vector3d;
/**
* Performs pathfinding
*/
public interface PathfindingManager {
/**
* Solves a path
* @param start The start point
* @param end The end point
* @return The path if it exists, null otherwise
*/
public List<Vector3d> findPath(Vector3d start, Vector3d end);
}

View File

@ -2,6 +2,7 @@ package electrosphere.server.datacell.physics;
import electrosphere.client.block.BlockChunkData;
import electrosphere.client.terrain.cache.ChunkData;
import electrosphere.client.terrain.data.TerrainChunkData;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings;
@ -34,10 +35,15 @@ public class PhysicsDataCell {
float[][][] weights = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];
int[][][] types = new int[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];
/**
* The terrain vertex data
*/
TerrainChunkData terrainChunkData;
PhysicsDataCell(){
}
/**
* The block vertex data
*/
BlockMeshData blockData;
/**
* Creates a physics cell
@ -79,11 +85,12 @@ public class PhysicsDataCell {
//
this.fillInData();
TerrainChunk.serverCreateTerrainChunkEntity(physicsEntity, weights, types);
physicsEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
BlockMeshData meshData = BlockMeshgen.rasterize(blockChunk);
BlockChunkEntity.serverCreateBlockChunkEntity(blockPhysicsEntity, meshData);
blockPhysicsEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
this.terrainChunkData = TerrainChunk.serverGenerateTerrainChunkData(weights, types);
TerrainChunk.serverCreateTerrainChunkEntity(this.physicsEntity, this.terrainChunkData);
this.physicsEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
this.blockData = BlockMeshgen.rasterize(this.blockChunk);
BlockChunkEntity.serverCreateBlockChunkEntity(this.blockPhysicsEntity, this.blockData);
this.blockPhysicsEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
// //then actually perform the attach
// physicsObject = PhysicsUtils.attachTerrainRigidBody(physicsEntity,heightmap,true);
// Realm realm = Globals.realmManager.getEntityRealm(physicsEntity);
@ -244,6 +251,20 @@ public class PhysicsDataCell {
this.blockChunk = blockChunk;
}
/**
* Gets the terrain vertex data
* @return The terrain vertex data
*/
public TerrainChunkData getTerrainChunkData() {
return terrainChunkData;
}
/**
* Gets the block vertex data
* @return The block vertex data
*/
public BlockMeshData getBlockData() {
return blockData;
}
}

View File

@ -1,36 +0,0 @@
package electrosphere.server.pathfinding;
import electrosphere.server.pathfinding.navmesh.NavMesh;
import electrosphere.server.physics.terrain.manager.ServerTerrainChunk;
import java.util.LinkedList;
import java.util.List;
/**
*
* @author satellite
*/
public class ChunkMeshList {
ServerTerrainChunk chunk;
List<NavMesh> meshes = new LinkedList<NavMesh>();
public ChunkMeshList(ServerTerrainChunk parent){
chunk = parent;
}
public void addMesh(NavMesh mesh){
meshes.add(mesh);
}
public List<NavMesh> getMeshes(){
return meshes;
}
public ServerTerrainChunk getChunk(){
return chunk;
}
}

View File

@ -0,0 +1,150 @@
package electrosphere.server.pathfinding;
import org.recast4j.detour.MeshData;
import org.recast4j.detour.NavMeshBuilder;
import org.recast4j.detour.NavMeshDataCreateParams;
import org.recast4j.recast.AreaModification;
import org.recast4j.recast.PolyMesh;
import org.recast4j.recast.PolyMeshDetail;
import org.recast4j.recast.RecastBuilder;
import org.recast4j.recast.RecastBuilder.RecastBuilderResult;
import org.recast4j.recast.RecastConstants.PartitionType;
import org.recast4j.recast.RecastBuilderConfig;
import org.recast4j.recast.RecastConfig;
import org.recast4j.recast.geom.SingleTrimeshInputGeomProvider;
import electrosphere.client.terrain.data.TerrainChunkData;
/**
* Constructor methods for nav meshes
*/
public class NavMeshConstructor {
/**
* Size of a recast cell
*/
static final float RECAST_CELL_SIZE = 1.0f;
/**
* Height of a recast cell
*/
static final float RECAST_CELL_HEIGHT = 1.0f;
/**
* Size of a recast agent
*/
static final float RECAST_AGENT_SIZE = 1.0f;
/**
* Height of a recast agent
*/
static final float RECAST_AGENT_HEIGHT = 1.0f;
/**
* Maximum height a recast agent can climb
*/
static final float RECAST_AGENT_MAX_CLIMB = 0.1f;
/**
* Maximum slope a recast agent can handle
*/
static final float RECAST_AGENT_MAX_SLOPE = 0.4f;
/**
* Minimum size of a recast region
*/
static final int RECAST_MIN_REGION_SIZE = 1;
/**
* Merge size of a recast region
*/
static final int RECAST_REGION_MERGE_SIZE = 1;
static final float RECAST_REGION_EDGE_MAX_LEN = 1.0f;
static final float RECAST_REGION_EDGE_MAX_ERROR = 1.0f;
static final int RECAST_VERTS_PER_POLY = 3;
static final float RECAST_DETAIL_SAMPLE_DIST = 0.1f;
static final float RECAST_DETAIL_SAMPLE_MAX_ERROR = 0.1f;
/**
* Constructs a navmesh
* @param terrainChunk The terrain chunk
* @return the MeshData
*/
public static MeshData constructNavmesh(TerrainChunkData terrainChunkData){
RecastConfig recastConfig = new RecastConfig(
PartitionType.MONOTONE,
RECAST_CELL_SIZE,
RECAST_CELL_HEIGHT,
RECAST_AGENT_HEIGHT,
RECAST_AGENT_SIZE,
RECAST_AGENT_MAX_CLIMB,
RECAST_AGENT_MAX_SLOPE,
RECAST_MIN_REGION_SIZE,
RECAST_REGION_MERGE_SIZE,
RECAST_REGION_EDGE_MAX_LEN,
RECAST_REGION_EDGE_MAX_ERROR,
RECAST_VERTS_PER_POLY,
RECAST_DETAIL_SAMPLE_DIST,
RECAST_DETAIL_SAMPLE_MAX_ERROR,
new AreaModification(0)
);
SingleTrimeshInputGeomProvider geomProvider = new SingleTrimeshInputGeomProvider(terrainChunkData.getVertices(), terrainChunkData.getFaceElements());
RecastBuilderConfig recastBuilderConfig = new RecastBuilderConfig(recastConfig, geomProvider.getMeshBoundsMin(), geomProvider.getMeshBoundsMax());
RecastBuilder recastBuilder = new RecastBuilder();
RecastBuilderResult recastBuilderResult = recastBuilder.build(geomProvider, recastBuilderConfig);
PolyMesh polyMesh = recastBuilderResult.getMesh();
for(int i = 0; i < polyMesh.npolys; i++){
polyMesh.flags[i] = 1;
}
PolyMeshDetail polyMeshDetail = recastBuilderResult.getMeshDetail();
NavMeshDataCreateParams params = new NavMeshDataCreateParams();
params.verts = polyMesh.verts;
params.vertCount = polyMesh.nverts;
params.polys = polyMesh.polys;
params.polyAreas = polyMesh.areas;
params.polyFlags = polyMesh.flags;
params.polyCount = polyMesh.npolys;
params.nvp = polyMesh.nvp;
params.detailMeshes = polyMeshDetail.meshes;
params.detailVerts = polyMeshDetail.verts;
params.detailVertsCount = polyMeshDetail.nverts;
params.detailTris = polyMeshDetail.tris;
params.detailTriCount = polyMeshDetail.ntris;
params.walkableHeight = RECAST_AGENT_HEIGHT;
params.walkableRadius = RECAST_AGENT_SIZE;
params.walkableClimb = RECAST_AGENT_MAX_CLIMB;
params.bmin = polyMesh.bmin;
params.bmax = polyMesh.bmax;
params.cs = RECAST_CELL_SIZE;
params.ch = RECAST_CELL_HEIGHT;
params.buildBvTree = true;
// params.offMeshConVerts = new float[6];
// params.offMeshConVerts[0] = 0.1f;
// params.offMeshConVerts[1] = 0.2f;
// params.offMeshConVerts[2] = 0.3f;
// params.offMeshConVerts[3] = 0.4f;
// params.offMeshConVerts[4] = 0.5f;
// params.offMeshConVerts[5] = 0.6f;
// params.offMeshConRad = new float[1];
// params.offMeshConRad[0] = 0.1f;
// params.offMeshConDir = new int[1];
// params.offMeshConDir[0] = 1;
// params.offMeshConAreas = new int[1];
// params.offMeshConAreas[0] = 2;
// params.offMeshConFlags = new int[1];
// params.offMeshConFlags[0] = 12;
// params.offMeshConUserID = new int[1];
// params.offMeshConUserID[0] = 0x4567;
// params.offMeshConCount = 1;
MeshData meshData = NavMeshBuilder.createNavMeshData(params);
return meshData;
}
}

View File

@ -1,51 +0,0 @@
package electrosphere.server.pathfinding;
import electrosphere.server.pathfinding.blocker.NavBlocker;
import electrosphere.server.pathfinding.blocker.NavTerrainBlockerCache;
import electrosphere.server.pathfinding.navmesh.NavMesh;
import electrosphere.server.physics.terrain.manager.ServerTerrainChunk;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
*
* @author satellite
*/
public class NavMeshManager {
List<NavMesh> meshes = new LinkedList<NavMesh>();
Map<ServerTerrainChunk,ChunkMeshList> chunkToMeshListMap = new HashMap<ServerTerrainChunk,ChunkMeshList>();
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;
}
}

View File

@ -1,452 +0,0 @@
package electrosphere.server.pathfinding;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityCreationUtils;
import electrosphere.entity.EntityUtils;
import electrosphere.server.pathfinding.navmesh.NavCube;
import electrosphere.server.pathfinding.navmesh.NavMesh;
import electrosphere.server.pathfinding.navmesh.NavShape;
import electrosphere.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;
/**
* Navmesh pathfinding functions
*/
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<Waypoint>();
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<NavShape>();
List<NavShape> closedSet = new LinkedList<NavShape>();
PriorityQueue<SetItem> pathItemsQueue = new PriorityQueue<SetItem>();
Map<NavShape,SetItem> pathItems = new HashMap<NavShape,SetItem>();
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 = EntityCreationUtils.createClientSpatialEntity();
EntityCreationUtils.makeEntityDrawable(waypoint, "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 = EntityCreationUtils.createClientSpatialEntity();
EntityCreationUtils.makeEntityDrawable(waypoint, "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> {
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(SetItem target) {
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;
}
}
}

View File

@ -1,361 +0,0 @@
package electrosphere.server.pathfinding;
import electrosphere.engine.Globals;
import electrosphere.logger.LoggerInterface;
import electrosphere.server.pathfinding.blocker.NavBlocker;
import electrosphere.server.pathfinding.navmesh.NavCube;
import electrosphere.server.pathfinding.navmesh.NavMesh;
import electrosphere.server.pathfinding.navmesh.NavShape;
import electrosphere.server.physics.terrain.manager.ServerTerrainChunk;
import java.util.LinkedList;
import java.util.List;
/**
*
* @author satellite
*/
public class NavMeshUtils {
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.getWeights()[0];
boolean[][] navMeshGeneratorMask = navBlocker.getHeightfieldBlocker();
List<FirstPhaseBox> firstPassBoxes = new LinkedList<FirstPhaseBox>();
int numInCurrent = 0;
float currentMin = 0;
float currentMax = 0;
int startPos = 0;
int endPos = 0;
for(int x = 0; x < ServerTerrainChunk.CHUNK_DIMENSION - 1; x++){
numInCurrent = 0;
currentMin = 0;
currentMax = 0;
for(int y = 0; y < ServerTerrainChunk.CHUNK_DIMENSION; y++){
//create node
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(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<SecondPhaseBox>();
List<SecondPhaseBox> toRemove = new LinkedList<SecondPhaseBox>();
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
List<SecondPhaseBox> neighborUpdateList = new LinkedList<SecondPhaseBox>();
for(SecondPhaseBox interim : secondPhaseBoxes){
if(firstPhaseBox.yStart == interim.boundMinY && firstPhaseBox.yEnd == interim.boundMaxY && firstPhaseBox.x == interim.boundMaxX){
for(SecondPhaseBox neighbor : interim.getNeighbors()){
neighborUpdateList.add(neighbor);
// System.out.println("ADD NEIGHBOR: " + neighbor.boundMaxX + " " + neighbor.boundMaxY + " " + neighbor.boundMinX + " " + neighbor.boundMinY);
}
for(SecondPhaseBox update : neighborUpdateList){
update.removeNeighbor(interim);
interim.removeNeighbor(update);
update.addNeighbor(newBox);
newBox.addNeighbor(update);
}
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() * ServerTerrainChunk.CHUNK_DIMENSION,
box.minHeight - 0.5f,
box.boundMinY + chunk.getWorldY() * ServerTerrainChunk.CHUNK_DIMENSION,
box.boundMaxX + chunk.getWorldX() * ServerTerrainChunk.CHUNK_DIMENSION,
box.maxHeight + 0.5f,
box.boundMaxY + chunk.getWorldY() * ServerTerrainChunk.CHUNK_DIMENSION
);
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<SecondPhaseBox>();
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;
}
}
}

View File

@ -0,0 +1,92 @@
package electrosphere.server.pathfinding;
import java.util.LinkedList;
import java.util.List;
import org.joml.Vector3d;
import org.recast4j.detour.DefaultQueryFilter;
import org.recast4j.detour.FindNearestPolyResult;
import org.recast4j.detour.MeshData;
import org.recast4j.detour.NavMesh;
import org.recast4j.detour.NavMeshQuery;
import org.recast4j.detour.QueryFilter;
import org.recast4j.detour.Result;
import org.recast4j.detour.StraightPathItem;
/**
* Performs pathfinding
*/
public class Pathfinder {
/**
* Maximum points in a straight path
*/
static final int MAX_STRAIGHT_PATH_POINTS = 100;
/**
* Solves for a path
* @param mesh The navmesh
* @param startPos The start point
* @param endPos The end point
* @return The set of points to path along
*/
public List<Vector3d> solve(MeshData mesh, Vector3d startPos, Vector3d endPos){
List<Vector3d> rVal = new LinkedList<Vector3d>();
//construct objects
NavMesh navMesh = new NavMesh(mesh,6,0);
NavMeshQuery query = new NavMeshQuery(navMesh);
QueryFilter filter = new DefaultQueryFilter();
//convert points to correct datatypes
float[] startArr = new float[]{(float)startPos.x, (float)startPos.y, (float)startPos.z};
float[] endArr = new float[]{(float)endPos.x, (float)endPos.y, (float)endPos.z};
float[] polySearchBounds = new float[]{10,10,10};
//find start poly
Result<FindNearestPolyResult> startPolyResult = query.findNearestPoly(startArr, polySearchBounds, filter);
if(startPolyResult.failed()){
String message = "Failed to solve for start polygon!\n" +
startPolyResult.message
;
throw new Error(message);
}
long startRef = startPolyResult.result.getNearestRef();
//find end poly
Result<FindNearestPolyResult> endPolyResult = query.findNearestPoly(endArr, polySearchBounds, filter);
if(endPolyResult.failed()){
String message = "Failed to solve for end polygon!\n" +
endPolyResult.message
;
throw new Error(message);
}
long endRef = endPolyResult.result.getNearestRef();
//solve path
Result<List<Long>> pathResult = query.findPath(startRef, endRef, startArr, endArr, filter);
if(pathResult.failed()){
String message = "Failed to solve for path!\n" +
pathResult.message
;
throw new Error(message);
}
//straighten the path ("string pull")
Result<List<StraightPathItem>> straightPathResult = query.findStraightPath(startArr, endArr, pathResult.result, MAX_STRAIGHT_PATH_POINTS, 0);
if(straightPathResult.failed()){
String message = "Failed to straighten path!\n" +
straightPathResult.message
;
throw new Error(message);
}
//convert to usable structures
for(StraightPathItem pathItem : straightPathResult.result){
rVal.add(new Vector3d(pathItem.getPos()[0],pathItem.getPos()[1],pathItem.getPos()[2]));
}
return rVal;
}
}

View File

@ -1,32 +0,0 @@
package electrosphere.server.pathfinding.blocker;
import electrosphere.server.physics.terrain.manager.ServerTerrainChunk;
/**
*
* Blocks navigation calculations for a defined area
*
* Why it's own class? in case we want to use separate logic for non-heightfield chunks later (dungeons mby)
*
*/
public class NavBlocker {
boolean[][] heightfieldBlocker;
public NavBlocker(){
heightfieldBlocker = new boolean[ServerTerrainChunk.CHUNK_DIMENSION][ServerTerrainChunk.CHUNK_DIMENSION];
}
public NavBlocker(boolean[][] field){
heightfieldBlocker = field;
}
public boolean[][] getHeightfieldBlocker(){
return heightfieldBlocker;
}
public void setHeightfieldBlockerValue(int x, int y, boolean value){
heightfieldBlocker[x][y] = value;
}
}

View File

@ -1,57 +0,0 @@
package electrosphere.server.pathfinding.blocker;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 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;
Map<String, NavBlocker> navBlockerMapCache = new HashMap<String, NavBlocker>();
List<String> navBlockerMapCacheContents = new ArrayList<String>();
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;
}
}

View File

@ -1,54 +0,0 @@
package electrosphere.server.pathfinding.navmesh;
import java.util.LinkedList;
import java.util.List;
import org.joml.Vector3d;
/**
*
* @author satellite
*/
/**
* Axis aligned bounding boxes used as nodes in pathfinding navmeshes
* Inclusive of boundary points on both minimum and maximum
*/
public class NavCube extends NavShape {
Vector3d minPoint;
Vector3d maxPoint;
List<NavShape> neighbors = new LinkedList<NavShape>();
public NavCube(double minX, double minY, double minZ, double maxX, double maxY, double maxZ){
minPoint = new Vector3d(minX, minY, minZ);
maxPoint = new Vector3d(maxX, maxY, maxZ);
}
@Override
public void addNodeNeighbor(NavShape neighbor){
neighbors.add(neighbor);
}
@Override
public List<NavShape> getNodeNeighbors(){
return neighbors;
}
@Override
public boolean containsPoint(double x, double y, double z){
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;
}
}

View File

@ -1,43 +0,0 @@
package electrosphere.server.pathfinding.navmesh;
import java.util.LinkedList;
import java.util.List;
/**
*
* @author satellite
*/
public class NavMesh {
//nav method, ie walking, flying, climbing, swimming, etc
String method;
List<NavShape> navNodes = new LinkedList<NavShape>();
List<NavMesh> meshNeighbors = new LinkedList<NavMesh>();
public void addMeshNeighbor(NavMesh neighbor){
meshNeighbors.add(neighbor);
}
public List<NavMesh> getMeshNeighbors(){
return meshNeighbors;
}
public void addNode(NavShape node){
navNodes.add(node);
}
public List<NavShape> getNodes(){
return navNodes;
}
public boolean containsPoint(double x, double y, double z){
for(NavShape shape : navNodes){
if(shape.containsPoint(x, y, z)){
return true;
}
}
return false;
}
}

View File

@ -1,17 +0,0 @@
package electrosphere.server.pathfinding.navmesh;
import java.util.List;
/**
*
* @author satellite
*/
public abstract class NavShape {
public abstract void addNodeNeighbor(NavShape neighbor);
public abstract List<NavShape> getNodeNeighbors();
public abstract boolean containsPoint(double x, double y, double z);
}

View File

@ -1,12 +0,0 @@
package electrosphere.server.pathfinding.path;
import org.joml.Vector3d;
/**
*
* @author satellite
*/
public class Waypoint {
String method; //ie walking, flying, climbing, swimming, etc
Vector3d position;
}

View File

@ -169,8 +169,10 @@ public class ServerTerrainManager {
FileUtils.saveBinaryToSavePath(saveName, "./biome.dat", shortBuff.array());
}
//for each chunk, save via disk map
for(ServerTerrainChunk chunk : this.chunkCache.getFullRes()){
chunkDiskMap.saveToDisk(chunk);
if(this.chunkCache != null){
for(ServerTerrainChunk chunk : this.chunkCache.getFullRes()){
chunkDiskMap.saveToDisk(chunk);
}
}
//save disk map itself
if(chunkDiskMap != null){