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", "navier_stokes.h": "c",
"electrosphere_client_fluid_cache_fluidchunkdata.h": "c", "electrosphere_client_fluid_cache_fluidchunkdata.h": "c",
"normalization.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 #maven.buildNumber.plugin properties file
#Wed Apr 02 13:31:25 EDT 2025 #Wed Apr 16 16:24:00 EDT 2025
buildNumber=617 buildNumber=619

View File

@ -1510,6 +1510,11 @@ Refactoring server classes under physics package
Refactoring server classes under entity package Refactoring server classes under entity package
Refactoring server macro data classes Refactoring server macro data classes
Refactoring some macro 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); 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, JNIEnv * env,
jclass fluidSimClass, jclass fluidSimClass,
jobject chunkList, jobject chunkList,
@ -74,7 +74,7 @@ JNIEXPORT void JNICALL Java_electrosphere_server_fluid_simulator_FluidAccelerate
/** /**
* Should clean up all native allocations and state * 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, JNIEnv * env,
jclass fluidSimClass jclass fluidSimClass
){ ){
@ -94,7 +94,7 @@ JNIEXPORT void JNICALL Java_electrosphere_server_fluid_simulator_FluidAccelerate
/** /**
* Initializes the library * 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, JNIEnv * env,
jclass fluidSimClass, jclass fluidSimClass,
jfloat gravity jfloat gravity
@ -114,7 +114,7 @@ JNIEXPORT void JNICALL Java_electrosphere_server_fluid_simulator_FluidAccelerate
//store jni lookup tables //store jni lookup tables
jclass listClass = (*env)->FindClass(env,"java/util/List"); 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; environment->lookupTable.serverFluidChunkClass = fluidSimStorageClass;
//JNIEnv *env, jclass clazz, const char *name, const char *sig //JNIEnv *env, jclass clazz, const char *name, const char *sig
environment->lookupTable.listTable.jListSize = (*env)->GetMethodID(env, listClass, "size", "()I"); 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.worldXId = (*env)->GetFieldID(env,fluidSimStorageClass,"worldX","I");
environment->lookupTable.serverFluidChunkTable.worldYId = (*env)->GetFieldID(env,fluidSimStorageClass,"worldY","I"); environment->lookupTable.serverFluidChunkTable.worldYId = (*env)->GetFieldID(env,fluidSimStorageClass,"worldY","I");
environment->lookupTable.serverFluidChunkTable.worldZId = (*env)->GetFieldID(env,fluidSimStorageClass,"worldZ","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.chunkmaskJId = (*env)->GetFieldID(env,fluidSimStorageClass,"chunkMask","I");
environment->lookupTable.serverFluidChunkTable.totalDensityId = (*env)->GetFieldID(env,fluidSimStorageClass,"totalDensity","F"); environment->lookupTable.serverFluidChunkTable.totalDensityId = (*env)->GetFieldID(env,fluidSimStorageClass,"totalDensity","F");
environment->lookupTable.serverFluidChunkTable.updatedId = (*env)->GetFieldID(env,fluidSimStorageClass,"updated","Z"); 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){ int readInChunks(JNIEnv * env, jobject chunkList, Environment * environment){
jclass listClass = (*env)->FindClass(env,"java/util/List"); 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 //JNIEnv *env, jclass clazz, const char *name, const char *sig
jmethodID jListSize = environment->lookupTable.listTable.jListSize; jmethodID jListSize = environment->lookupTable.listTable.jListSize;
jmethodID jListGet = environment->lookupTable.listTable.jListGet; jmethodID jListGet = environment->lookupTable.listTable.jListGet;

View File

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

View File

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

View File

@ -1,7 +1,7 @@
#include <jni.h> #include <jni.h>
#include <stdlib.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 "../../includes/native/electrosphere_client_fluid_cache_FluidChunkData.h"
#include "fluid/queue/chunk.h" #include "fluid/queue/chunk.h"
@ -65,7 +65,7 @@ void freeField(JNIEnv * env, jobject fluidObj, jfieldID arrFieldId);
* @param env The JNI env * @param env The JNI env
* @param fluidObj The fluid object * @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, JNIEnv * env,
jobject fluidObj jobject fluidObj
){ ){
@ -136,7 +136,7 @@ void allocateCenterField(JNIEnv * env, jobject fluidObj, jfieldID arrFieldId){
* @param env The JNI env * @param env The JNI env
* @param fluidObj The fluid object * @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, JNIEnv * env,
jobject fluidObj jobject fluidObj
){ ){

View File

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

View File

@ -107,16 +107,23 @@ public class TerrainChunk {
} }
/** /**
* Creates a terrain chunk entity on the server * Generates terrain chunk data from a set of weights and values
* @param entity The entity to populate * @param weights The weights
* @param weights The weights for the terrain chunk * @param values The values
* @param values The values of each voxel in the chunk * @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); TransvoxelChunkData chunkData = new TransvoxelChunkData(weights, values, ClientDrawCellManager.FULL_RES_LOD);
TerrainChunkData data = TransvoxelModelGeneration.generateTerrainChunkData(chunkData); 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){ if(data.getVertices().length > 0){
PhysicsEntityUtils.serverAttachTriGeomRigidBody(entity, data); PhysicsEntityUtils.serverAttachTriGeomRigidBody(entity, data);
Realm realm = Globals.realmManager.getEntityRealm(entity); Realm realm = Globals.realmManager.getEntityRealm(entity);

View File

@ -5,7 +5,6 @@ import java.util.List;
import org.joml.Matrix4d; import org.joml.Matrix4d;
import org.joml.Quaterniond; import org.joml.Quaterniond;
import org.joml.Quaternionf;
import org.joml.Vector3d; import org.joml.Vector3d;
import org.lwjgl.opengl.GL40; import org.lwjgl.opengl.GL40;
import org.ode4j.ode.DCapsule; import org.ode4j.ode.DCapsule;
@ -32,9 +31,6 @@ import electrosphere.renderer.pipelines.RenderPipeline;
import electrosphere.renderer.texture.Texture; import electrosphere.renderer.texture.Texture;
import electrosphere.server.datacell.Realm; import electrosphere.server.datacell.Realm;
import electrosphere.server.datacell.utils.EntityLookupUtils; 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 * Pipeline for rendering content to assist debugging
@ -236,28 +232,7 @@ public class DebugContentPipeline implements RenderPipeline {
renderPipelineState.setUseMaterial(true); renderPipelineState.setUseMaterial(true);
if(Globals.userSettings.graphicsDebugDrawNavmesh()){ if(Globals.userSettings.graphicsDebugDrawNavmesh()){
Model shapeGraphicsModel; throw new Error("Not yet implemented!");
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);
}
}
}
}
} }
debugBonesPipeline.render(openGLState, renderPipelineState); debugBonesPipeline.render(openGLState, renderPipelineState);

View File

@ -9,6 +9,7 @@ import electrosphere.entity.scene.Scene;
import electrosphere.net.parser.net.message.NetworkMessage; import electrosphere.net.parser.net.message.NetworkMessage;
import electrosphere.script.ScriptEngine; import electrosphere.script.ScriptEngine;
import electrosphere.server.datacell.interfaces.DataCellManager; import electrosphere.server.datacell.interfaces.DataCellManager;
import electrosphere.server.datacell.interfaces.PathfindingManager;
import electrosphere.server.entity.ServerContentManager; import electrosphere.server.entity.ServerContentManager;
import java.util.HashSet; import java.util.HashSet;
@ -29,10 +30,14 @@ public class Realm {
*/ */
public static final int NO_SCENE_INSTANCE = -1; 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>(); 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()); ServerDataCell loadingCell = new ServerDataCell(new Scene());
/** /**
@ -40,13 +45,24 @@ public class Realm {
*/ */
ServerDataCell inventoryCell = new ServerDataCell(new Scene()); ServerDataCell inventoryCell = new ServerDataCell(new Scene());
//resolver for entity -> data cell within this realm /**
* resolver for entity -> data cell within this realm
*/
EntityDataCellMapper entityDataCellMapper; 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; DataCellManager dataCellManager;
//Main entity physics collision checking engine /**
* The pathfinding manager
*/
PathfindingManager pathfindingManager;
/**
* Main entity physics collision checking engine
*/
CollisionEngine collisionEngine; CollisionEngine collisionEngine;
/** /**
@ -54,7 +70,9 @@ public class Realm {
*/ */
CollisionEngine chemistryEngine; CollisionEngine chemistryEngine;
//Hitbox manager for the realm /**
* Hitbox manager for the realm
*/
HitboxManager hitboxManager; HitboxManager hitboxManager;
/** /**
@ -172,6 +190,22 @@ public class Realm {
this.dataCellManager = dataCellManager; 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 * Gets the collision engine for physics collision checking in this realm
* @return The collision engine * @return The collision engine
@ -208,11 +242,6 @@ public class Realm {
//main simulation //main simulation
dataCellManager.simulate(); dataCellManager.simulate();
// //
//data cell manager update misc variables (player positions, unload not-in-use cells)
if(dataCellManager != null){
// dataCellManager.unloadPlayerlessChunks();
}
//
//clear collidable impulse lists //clear collidable impulse lists
collisionEngine.clearCollidableImpulseLists(); collisionEngine.clearCollidableImpulseLists();
chemistryEngine.clearCollidableImpulseLists(); chemistryEngine.clearCollidableImpulseLists();

View File

@ -94,6 +94,7 @@ public class RealmManager {
GriddedDataCellManager griddedDataCellManager = new GriddedDataCellManager(realm); GriddedDataCellManager griddedDataCellManager = new GriddedDataCellManager(realm);
//add function classes to realm //add function classes to realm
realm.setDataCellManager(griddedDataCellManager); realm.setDataCellManager(griddedDataCellManager);
realm.setPathfindingManager(griddedDataCellManager);
//register within the manager //register within the manager
realms.add(realm); realms.add(realm);
return 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.parser.net.message.NetworkMessage;
import electrosphere.net.server.player.Player; import electrosphere.net.server.player.Player;
import electrosphere.server.macro.character.Character; import electrosphere.server.macro.character.Character;
import electrosphere.server.pathfinding.navmesh.NavMesh;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -32,29 +31,24 @@ public class ServerDataCell {
/** /**
* All players attached to this server data cell * All players attached to this server data cell
*/ */
Set<Player> activePlayers = new HashSet<Player>(); private Set<Player> activePlayers = new HashSet<Player>();
/**
* The navmesh for the data cell
*/
NavMesh navMesh;
/** /**
* The scene backing the server data cell * The scene backing the server data cell
*/ */
Scene scene; private Scene scene;
/** /**
* Controls whether the server data cell simulates its entities or not * 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 * Constructs a datacell based on a virtual cell. Should be used when a player
* first comes into range of the cell. * first comes into range of the cell.
* @param virtualCell * @param virtualCell
*/ */
ServerDataCell(Scene scene){ protected ServerDataCell(Scene scene){
this.scene = scene; this.scene = scene;
} }
@ -247,14 +241,6 @@ public class ServerDataCell {
return scene; 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 * Gets the simulation ready status of the server data cell
* @return True if ready, false otherwise * @return True if ready, false otherwise

View File

@ -17,6 +17,7 @@ import org.joml.Vector3d;
import org.joml.Vector3i; import org.joml.Vector3i;
import electrosphere.client.block.BlockChunkData; import electrosphere.client.block.BlockChunkData;
import electrosphere.client.terrain.data.TerrainChunkData;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
import electrosphere.entity.EntityCreationUtils; import electrosphere.entity.EntityCreationUtils;
@ -32,10 +33,12 @@ import electrosphere.server.datacell.Realm;
import electrosphere.server.datacell.ServerDataCell; import electrosphere.server.datacell.ServerDataCell;
import electrosphere.server.datacell.ServerWorldData; import electrosphere.server.datacell.ServerWorldData;
import electrosphere.server.datacell.interfaces.DataCellManager; import electrosphere.server.datacell.interfaces.DataCellManager;
import electrosphere.server.datacell.interfaces.PathfindingManager;
import electrosphere.server.datacell.interfaces.VoxelCellManager; import electrosphere.server.datacell.interfaces.VoxelCellManager;
import electrosphere.server.datacell.physics.PhysicsDataCell; import electrosphere.server.datacell.physics.PhysicsDataCell;
import electrosphere.server.entity.ServerContentManager; import electrosphere.server.entity.ServerContentManager;
import electrosphere.server.entity.serialization.ContentSerialization; import electrosphere.server.entity.serialization.ContentSerialization;
import electrosphere.server.pathfinding.NavMeshConstructor;
import electrosphere.server.physics.block.manager.ServerBlockManager; import electrosphere.server.physics.block.manager.ServerBlockManager;
import electrosphere.server.physics.fluid.manager.ServerFluidChunk; import electrosphere.server.physics.fluid.manager.ServerFluidChunk;
import electrosphere.server.physics.fluid.manager.ServerFluidManager; 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. * 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 * The minimum grid size allowed
@ -349,6 +352,13 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
cell.setTerrainChunk(terrainChunk); cell.setTerrainChunk(terrainChunk);
cell.setBlockChunk(blockChunkData); cell.setBlockChunk(blockChunkData);
cell.generatePhysics(); 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(); loadedCellsLock.lock();
posPhysicsMap.put(key, cell); posPhysicsMap.put(key, cell);
loadedCellsLock.unlock(); loadedCellsLock.unlock();
@ -716,6 +726,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
PhysicsDataCell cell, PhysicsDataCell cell,
Map<Long, PhysicsDataCell> posPhysicsMap, Map<Long, PhysicsDataCell> posPhysicsMap,
Map<Long, ServerDataCell> groundDataCells, Map<Long, ServerDataCell> groundDataCells,
Map<ServerDataCell,GriddedDataCellTrackingData> cellTrackingMap,
Realm realm Realm realm
){ ){
//get data to generate with //get data to generate with
@ -746,18 +757,29 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
ServerEntityUtils.destroyEntity(blockEntity); ServerEntityUtils.destroyEntity(blockEntity);
} }
//get advanced tracking data for the data cell
GriddedDataCellTrackingData trackingData = cellTrackingMap.get(dataCell);
generationService.submit(() -> { generationService.submit(() -> {
BlockChunkData blockChunkData = realm.getServerWorldData().getServerBlockManager().getChunk(worldPos.x, worldPos.y, worldPos.z); BlockChunkData blockChunkData = realm.getServerWorldData().getServerBlockManager().getChunk(worldPos.x, worldPos.y, worldPos.z);
ServerTerrainChunk terrainChunk = realm.getServerWorldData().getServerTerrainManager().getChunk(worldPos.x, worldPos.y, worldPos.z); ServerTerrainChunk terrainChunk = realm.getServerWorldData().getServerTerrainManager().getChunk(worldPos.x, worldPos.y, worldPos.z);
targetCell.setTerrainChunk(terrainChunk); targetCell.setTerrainChunk(terrainChunk);
targetCell.setBlockChunk(blockChunkData); targetCell.setBlockChunk(blockChunkData);
TerrainChunkData terrainMeshData;
//create physics entities //create physics entities
if(cell != null){ if(cell != null){
cell.retireCell(); cell.retireCell();
cell.generatePhysics(); cell.generatePhysics();
terrainMeshData = cell.getTerrainChunkData();
} else { } else {
targetCell.generatePhysics(); targetCell.generatePhysics();
terrainMeshData = targetCell.getTerrainChunkData();
} }
//create pathfinding mesh
trackingData.setNavMeshData(NavMeshConstructor.constructNavmesh(terrainMeshData));
//set ready //set ready
dataCell.setReady(true); 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 //generates physics for the cell in a dedicated thread then finally registers
loadedCellsLock.lock(); loadedCellsLock.lock();
PhysicsDataCell cell = posPhysicsMap.get(key); 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(); loadedCellsLock.unlock();
@ -1047,4 +1069,9 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
return cellPlayerlessFrameMap; 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; package electrosphere.server.datacell.gridded;
import org.recast4j.detour.MeshData;
/** /**
* Data associated with a ServerDataCell by the GriddedDataCellManager * Data associated with a ServerDataCell by the GriddedDataCellManager
*/ */
@ -15,6 +17,11 @@ public class GriddedDataCellTrackingData {
*/ */
double closestPlayer; double closestPlayer;
/**
* The nav mesh data
*/
MeshData navMeshData;
/** /**
* Gets the distance from the cell to the closest player * Gets the distance from the cell to the closest player
* @return The distance * @return The distance
@ -31,6 +38,20 @@ public class GriddedDataCellTrackingData {
this.closestPlayer = closestPlayer; 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.block.BlockChunkData;
import electrosphere.client.terrain.cache.ChunkData; import electrosphere.client.terrain.cache.ChunkData;
import electrosphere.client.terrain.data.TerrainChunkData;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityDataStrings;
@ -33,11 +34,16 @@ public class PhysicsDataCell {
float[][][] weights = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE]; 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]; int[][][] types = new int[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];
/**
PhysicsDataCell(){ * The terrain vertex data
*/
} TerrainChunkData terrainChunkData;
/**
* The block vertex data
*/
BlockMeshData blockData;
/** /**
* Creates a physics cell * Creates a physics cell
@ -79,11 +85,12 @@ public class PhysicsDataCell {
// //
this.fillInData(); this.fillInData();
TerrainChunk.serverCreateTerrainChunkEntity(physicsEntity, weights, types); this.terrainChunkData = TerrainChunk.serverGenerateTerrainChunkData(weights, types);
physicsEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true); TerrainChunk.serverCreateTerrainChunkEntity(this.physicsEntity, this.terrainChunkData);
BlockMeshData meshData = BlockMeshgen.rasterize(blockChunk); this.physicsEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
BlockChunkEntity.serverCreateBlockChunkEntity(blockPhysicsEntity, meshData); this.blockData = BlockMeshgen.rasterize(this.blockChunk);
blockPhysicsEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true); BlockChunkEntity.serverCreateBlockChunkEntity(this.blockPhysicsEntity, this.blockData);
this.blockPhysicsEntity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
// //then actually perform the attach // //then actually perform the attach
// physicsObject = PhysicsUtils.attachTerrainRigidBody(physicsEntity,heightmap,true); // physicsObject = PhysicsUtils.attachTerrainRigidBody(physicsEntity,heightmap,true);
// Realm realm = Globals.realmManager.getEntityRealm(physicsEntity); // Realm realm = Globals.realmManager.getEntityRealm(physicsEntity);
@ -244,6 +251,20 @@ public class PhysicsDataCell {
this.blockChunk = blockChunk; 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()); FileUtils.saveBinaryToSavePath(saveName, "./biome.dat", shortBuff.array());
} }
//for each chunk, save via disk map //for each chunk, save via disk map
for(ServerTerrainChunk chunk : this.chunkCache.getFullRes()){ if(this.chunkCache != null){
chunkDiskMap.saveToDisk(chunk); for(ServerTerrainChunk chunk : this.chunkCache.getFullRes()){
chunkDiskMap.saveToDisk(chunk);
}
} }
//save disk map itself //save disk map itself
if(chunkDiskMap != null){ if(chunkDiskMap != null){