Physics fixes

This commit is contained in:
austin 2023-07-14 22:10:38 -04:00
parent ceb5a5aaf8
commit c71f4fecad
28 changed files with 636 additions and 501 deletions

3
buildNumber.properties Normal file
View File

@ -0,0 +1,3 @@
#maven.buildNumber.plugin properties file
#Fri Jul 14 20:48:01 EDT 2023
buildNumber=4

View File

@ -13,6 +13,14 @@
<joml.version>1.9.19</joml.version>
</properties>
<!-- Used for build number plugin because it LITERALLY WONT LET YOU NOT HAVE SCM-->
<scm>
<connection>scm:svn:http://127.0.0.1/dummy</connection>
<developerConnection>scm:svn:https://127.0.0.1/dummy</developerConnection>
<tag>HEAD</tag>
<url>http://127.0.0.1/dummy</url>
</scm>
<dependencies>
<!-- generic LWJGL runtimes -->
<!--License: BSD-->

1
saves/default/chunk.map Normal file
View File

@ -0,0 +1 @@
{}

View File

@ -49,10 +49,10 @@ import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.state.collidable.Impulse;
import electrosphere.entity.types.hitbox.HitboxData;
import electrosphere.logger.LoggerInterface;
/**
*
* TODO: https://stackoverflow.com/questions/32445679/3d-java-collision-detection-with-jbullet
*/
public class CollisionEngine {
@ -85,58 +85,6 @@ public class CollisionEngine {
world = OdeHelper.createWorld();
space = OdeHelper.createHashSpace();
contactgroup = OdeHelper.createJointGroup();
// callback = new InternalTickCallback(){
// @Override
// public void internalTick(DynamicsWorld dw, float f) {
// Dispatcher dispatcher = dw.getDispatcher();
// int manifoldCount = dispatcher.getNumManifolds();
// for (int i = 0; i < manifoldCount; i++) {
// PersistentManifold manifold = dispatcher.getManifoldByIndexInternal(i);
// // The following two lines are optional.
// CollisionObject object1 = (CollisionObject)manifold.getBody0();
// CollisionObject object2 = (CollisionObject)manifold.getBody1();
// Collidable physicsObject1 = (Collidable)object1.getUserPointer();
// Collidable physicsObject2 = (Collidable)object2.getUserPointer();
// boolean hit = false;
// Vector3d normal = null;
// Vector3d localPosition1 = null;
// Vector3d localPosition2 = null;
// Vector3d worldPosA = null;
// Vector3d worldPosB = null;
// float magnitude = 0.0f;
// for (int j = 0; j < manifold.getNumContacts(); j++) {
// ManifoldPoint contactPoint = manifold.getContactPoint(j);
// if (contactPoint.getDistance() < 0.0f) {
// magnitude = -contactPoint.getDistance();
// //linear dampen
// // magnitude = magnitude;// * (float)Math.pow(1.0f - linearDamping,deltaTime * 2);
// hit = true;
// // System.out.println(contactPoint.positionWorldOnA + " " + contactPoint.positionWorldOnB);
// normal = new Vector3d(contactPoint.normalWorldOnB.x,contactPoint.normalWorldOnB.y,contactPoint.normalWorldOnB.z);
// localPosition1 = new Vector3d(contactPoint.localPointA.x,contactPoint.localPointA.y,contactPoint.localPointA.z);
// localPosition2 = new Vector3d(contactPoint.localPointB.x,contactPoint.localPointB.y,contactPoint.localPointB.z);
// worldPosA = new Vector3d(contactPoint.positionWorldOnA.x,contactPoint.positionWorldOnA.y,contactPoint.positionWorldOnA.z);
// worldPosB = new Vector3d(contactPoint.positionWorldOnB.x,contactPoint.positionWorldOnB.y,contactPoint.positionWorldOnB.z);
// break;
// }
// }
// if (hit) {
// resolveCollision(physicsObject1,physicsObject2, new Vector3d(normal).mul(-1.0), localPosition1, worldPosA, magnitude);
// resolveCollision(physicsObject2,physicsObject1, normal, localPosition2, worldPosB, magnitude);
// // System.out.println("HIT + " + normal);
// // Collision happened between physicsObject1 and physicsObject2. Collision normal is in variable 'normal'.
// }
// }
// }
// };
// world.setInternalTickCallback(callback, world);
//https://pybullet.org/Bullet/phpBB3/viewtopic.php?t=11507
//https://www.google.com/search?client=firefox-b-1-d&q=bullet+set+position+and+check+if+collision
//https://pybullet.org/Bullet/phpBB3/viewtopic.php?t=11399
}
@ -457,8 +405,8 @@ public class CollisionEngine {
public void listBodyPositions(){
for(DBody body : bodies){
System.out.println(body);
System.out.println(PhysicsUtils.getRigidBodyPosition(body));
LoggerInterface.loggerEngine.INFO("" + body);
LoggerInterface.loggerEngine.INFO("" + PhysicsUtils.getRigidBodyPosition(body));
}
}
@ -741,4 +689,30 @@ public class CollisionEngine {
spaceLock.release();
}
/**
* Gets the position of the body in a thread-safe way
* @param body The body to get the position of
* @return The position
*/
protected Vector3d getBodyPosition(DBody body){
Vector3d rVal = null;
spaceLock.acquireUninterruptibly();
rVal = PhysicsUtils.odeVecToJomlVec(body.getPosition());
spaceLock.release();
return rVal;
}
/**
* Gets the rotation of the body in a thread-safe way
* @param body The body to get the rotation of
* @return The rotation
*/
protected Quaterniond getBodyRotation(DBody body){
Quaterniond rVal = null;
spaceLock.acquireUninterruptibly();
rVal = PhysicsUtils.odeQuatToJomlQuat(body.getQuaternion());
spaceLock.release();
return rVal;
}
}

View File

@ -18,7 +18,6 @@ import org.ode4j.ode.DMass;
import org.ode4j.ode.DPlane;
import org.ode4j.ode.DSphere;
import org.ode4j.ode.DTriMesh;
import org.ode4j.ode.OdeHelper;
import electrosphere.collision.collidable.Collidable;
import electrosphere.engine.Globals;
@ -105,20 +104,13 @@ public class PhysicsUtils {
}
DBody body = OdeHelper.createBody(null);
DTriMesh triMesh = collisionEngine.createTrimeshGeom(vertices,indices);
triMesh.setBody(body);
// terrainRigidBody.setFriction(1f);
DBody body = collisionEngine.createDBody(triMesh);
Globals.clientSceneWrapper.getCollisionEngine().registerCollisionObject(body, new Collidable(terrain,Collidable.TYPE_TERRAIN));
// terrainRigidBody.getAabb(aabbMin, aabbMax);
//
// System.out.println("aabbMin: " + aabbMin + " aabbMax: " + aabbMax);
terrain.putData(EntityDataStrings.PHYSICS_COLLISION_BODY, body);
return body;
@ -200,7 +192,7 @@ public class PhysicsUtils {
*/
public static DBody generateRigidBodyFromAIScene(CollisionEngine collisionEngine, AIScene scene){
DBody body = OdeHelper.createBody(collisionEngine.getDWorld());
DBody body = collisionEngine.createDBody(null);
PointerBuffer meshesBuffer = scene.mMeshes();
while(meshesBuffer.hasRemaining()){
@ -244,14 +236,6 @@ public class PhysicsUtils {
return body;
}
/**
* Adds a test plane body to the engine scene
* @param engine The engine
*/
public static void addTestPlaneRigidBody(CollisionEngine engine){
DPlane plane = OdeHelper.createPlane(engine.getSpace(), 0, 1, 0, 0);
}
/**
* Gets the current position of a rigid body as a joml vector
* @param body The dbody
@ -308,17 +292,6 @@ public class PhysicsUtils {
collisionEngine.setBodyTransform(body, position, rotation);
}
// public static RigidBody getUnitCylinderRigidBody(float mass){
// CylinderShape cylinderShape = new CylinderShape(jomlToVecmathVector3f(new Vector3f(1.0f,1.0f,1.0f)));
// DefaultMotionState defaultMotionState = new DefaultMotionState(new Transform(new javax.vecmath.Matrix4f(new javax.vecmath.Quat4f(0,1,0,1),new javax.vecmath.Vector3f(0,0,0),1.0f)));
// RigidBodyConstructionInfo cylinderRigidBodyCI = new RigidBodyConstructionInfo(mass, defaultMotionState, cylinderShape);
// RigidBody cylinderRigidBody = new RigidBody(cylinderRigidBodyCI);
//// cylinderRigidBody.setMassProps(mass, PhysicsUtils.jomlToVecmathVector3f(new Vector3f(1.0f,1.0f,1.0f)));
// cylinderRigidBody.clearForces();
// return cylinderRigidBody;
// }
//The width of a plane rigid body
//It's really a box under the hood
static final double PLANE_WIDTH = 0.3;

View File

@ -1011,7 +1011,7 @@ public class ControlHandler {
void setAlwaysOnDebugControls(){
alwaysOnDebugControlList.add(controls.get(DEBUG_OPEN_DEBUG_MENU));
controls.get(DEBUG_OPEN_DEBUG_MENU).setOnPress(new ControlMethod(){public void execute(){
System.out.println("open debug menu");
LoggerInterface.loggerEngine.INFO("open debug menu");
// Window mainMenuWindow = new Window(0, 0, Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT);
Window mainMenuInGame = MenuGeneratorsDebug.createTopLevelDebugMenu();
// mainMenuWindow.addChild(mainMenuInGame);

View File

@ -17,6 +17,7 @@ import electrosphere.net.parser.net.message.TerrainMessage;
import electrosphere.net.server.ServerConnectionHandler;
import electrosphere.renderer.ui.Window;
import electrosphere.server.saves.SaveUtils;
import electrosphere.server.terrain.generation.OverworldChunkGenerator;
import electrosphere.server.terrain.manager.ServerTerrainManager;
import electrosphere.util.FileUtils;
import electrosphere.controls.ControlHandler;
@ -31,7 +32,7 @@ public class DebugSPWorldLoading {
WindowUtils.replaceMainMenuContents(MenuGenerators.createEmptyMainMenu());
loadingWindow.setVisible(true);
Globals.serverTerrainManager = new ServerTerrainManager(2000,50,0.0f,0);
Globals.serverTerrainManager = new ServerTerrainManager(2000,50,0.0f,0, new OverworldChunkGenerator());
if(!SaveUtils.getSaves().contains("random_sp_world")){
//
//the juicy server GENERATION part
@ -95,32 +96,4 @@ public class DebugSPWorldLoading {
ClientLoading.loadClientWorld();
}
private static void initWorldBaseGraphicalEntities(){
/*
Skybox
*/
// Model skyboxModel = Globals.assetManager.fetchModel(AssetDataStrings.ASSET_STRING_SKYBOX_BASIC);
// Globals.skybox = EntityUtils.spawnDrawableEntity(AssetDataStrings.ASSET_STRING_SKYBOX_BASIC);
/*
Player Camera
*/
Globals.playerCamera = CameraEntityUtils.spawnPlayerEntityTrackingCameraEntity(new Vector3f(1,0,1), new Vector3f(0,0,1));
// Globals.playerCamera = CameraEntityUtils.spawnPlayerEntityAirplaneTrackingCameraEntity(new Vector3f(1,0,1), new Vector3f(0,0,1));
/*
Targeting crosshair
*/
Crosshair.initCrossHairEntity();
}
}

View File

@ -41,6 +41,7 @@ import electrosphere.server.datacell.Realm;
import electrosphere.server.saves.SaveUtils;
import electrosphere.server.simulation.MacroSimulation;
import electrosphere.server.simulation.MicroSimulation;
import electrosphere.server.terrain.generation.OverworldChunkGenerator;
import electrosphere.server.terrain.manager.ServerTerrainManager;
/**
@ -60,7 +61,7 @@ public class LoadingUtils {
Actually initialize the terrain manager
*/
float randomDampener = 0.0f; //0.25f;
Globals.serverTerrainManager = new ServerTerrainManager(2000,50,randomDampener,0);
Globals.serverTerrainManager = new ServerTerrainManager(2000,50,randomDampener,0,new OverworldChunkGenerator());
if(Globals.RUN_SERVER){
if(Globals.userSettings.gameplayGenerateWorld()){
Globals.serverTerrainManager.generate();

View File

@ -3,6 +3,7 @@ package electrosphere.engine.loadingthreads;
import electrosphere.engine.Globals;
import electrosphere.logger.LoggerInterface;
import electrosphere.server.saves.SaveUtils;
import electrosphere.server.terrain.generation.OverworldChunkGenerator;
import electrosphere.server.terrain.manager.ServerTerrainManager;
public class ServerLoading {
@ -15,7 +16,7 @@ public class ServerLoading {
// }
//TODO: Globals.serverTerrainManager = new ServerTerrainManager(2000,50,100,randomDampener,0);
Globals.serverTerrainManager = new ServerTerrainManager(2000,50,0.0f,0);
Globals.serverTerrainManager = new ServerTerrainManager(2000,50,0.0f,0, new OverworldChunkGenerator());
SaveUtils.loadSave(Globals.currentSaveName);
// LoadingUtils.initTerrainDataCellManager();
//TODO: set spawnpoint

View File

@ -201,8 +201,12 @@ public class ClientCollidableTree implements BehaviorTree {
//update collision engine of this thing's position
CollidableTemplate template = (CollidableTemplate)parent.getData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE);
body.setPosition(position.x + template.getOffsetX(),position.y + template.getOffsetY(),position.z + template.getOffsetZ());
body.setQuaternion(PhysicsUtils.jomlQuatToOdeQuat(rotation));
PhysicsUtils.setRigidBodyTransform(
Globals.clientSceneWrapper.getCollisionEngine(),
new Vector3d(position.x + template.getOffsetX(),position.y + template.getOffsetY(),position.z + template.getOffsetZ()),
rotation,
body
);
}
/**

View File

@ -207,9 +207,12 @@ public class ServerCollidableTree implements BehaviorTree {
//update collision engine of this thing's position
CollidableTemplate template = (CollidableTemplate)parent.getData(EntityDataStrings.PHYSICS_MODEL_TEMPLATE);
body.setPosition(position.x + template.getOffsetX(),position.y + template.getOffsetY(),position.z + template.getOffsetZ());
body.setQuaternion(PhysicsUtils.jomlQuatToOdeQuat(rotation));
PhysicsUtils.setRigidBodyTransform(
parentRealm.getCollisionEngine(),
new Vector3d(position.x + template.getOffsetX(),position.y + template.getOffsetY(),position.z + template.getOffsetZ()),
rotation,
body
);
}

View File

@ -181,9 +181,6 @@ public class ClientGravityTree implements BehaviorTree {
// hitFraction = 1;
// }
collidable.addImpulse(new Impulse(new Vector3d(0,-1,0), new Vector3d(0,0,0), new Vector3d(position), gravityDif,"gravity"));
// System.out.println(hitFraction);
// bodyTransformMatrix = new javax.vecmath.Matrix4f(PhysicsUtils.jomlToVecmathQuaternionf(rotation),PhysicsUtils.jomlToVecmathVector3f(new Vector3f((float)position.x,(float)position.y,(float)position.z)),1.0f);
// body.setWorldTransform(new electrosphere.linearmath.Transform(bodyTransformMatrix));
}
break;
case NOT_ACTIVE:

View File

@ -175,9 +175,6 @@ public class ServerGravityTree implements BehaviorTree {
// hitFraction = 1;
// }
collidable.addImpulse(new Impulse(new Vector3d(0,-1,0), new Vector3d(0,0,0), new Vector3d(position), gravityDif,"gravity"));
// System.out.println(hitFraction);
// bodyTransformMatrix = new javax.vecmath.Matrix4f(PhysicsUtils.jomlToVecmathQuaternionf(rotation),PhysicsUtils.jomlToVecmathVector3f(new Vector3f((float)position.x,(float)position.y,(float)position.z)),1.0f);
// body.setWorldTransform(new electrosphere.linearmath.Transform(bodyTransformMatrix));
}
break;
case NOT_ACTIVE:

View File

@ -99,27 +99,25 @@ public class GroundMovementTree implements BehaviorTree {
this.facing = facing;
state = MovementTreeState.STARTUP;
//if we aren't the server, alert the server we intend to walk forward
if(!Globals.RUN_SERVER){
Vector3d position = EntityUtils.getPosition(parent);
Quaterniond rotation = EntityUtils.getRotation(parent);
Vector3d facingVector = CreatureUtils.getFacingVector(parent);
float velocity = CreatureUtils.getVelocity(parent);
Globals.clientConnection.queueOutgoingMessage(
EntityMessage.constructmoveUpdateMessage(
parent.getId(),
Main.getCurrentFrame(),
position.x,
position.y,
position.z,
rotation.x,
rotation.y,
rotation.z,
rotation.w,
velocity,
0 //magic number corresponding to state startup
)
);
}
Vector3d position = EntityUtils.getPosition(parent);
Quaterniond rotation = EntityUtils.getRotation(parent);
Vector3d facingVector = CreatureUtils.getFacingVector(parent);
float velocity = CreatureUtils.getVelocity(parent);
Globals.clientConnection.queueOutgoingMessage(
EntityMessage.constructmoveUpdateMessage(
Globals.clientSceneWrapper.mapClientToServerId(parent.getId()),
Main.getCurrentFrame(),
position.x,
position.y,
position.z,
rotation.x,
rotation.y,
rotation.z,
rotation.w,
velocity,
0 //magic number corresponding to state startup
)
);
}
}
@ -131,27 +129,25 @@ public class GroundMovementTree implements BehaviorTree {
public void slowdown(){
state = MovementTreeState.SLOWDOWN;
//if we aren't the server, alert the server we intend to slow down
if(!Globals.RUN_SERVER){
Vector3d position = EntityUtils.getPosition(parent);
Quaterniond rotation = EntityUtils.getRotation(parent);
Vector3d facingVector = CreatureUtils.getFacingVector(parent);
float velocity = CreatureUtils.getVelocity(parent);
Globals.clientConnection.queueOutgoingMessage(
EntityMessage.constructmoveUpdateMessage(
parent.getId(),
Main.getCurrentFrame(),
position.x,
position.y,
position.z,
rotation.x,
rotation.y,
rotation.z,
rotation.w,
velocity,
2 //magic number corresponding to state slowdown
)
);
}
Vector3d position = EntityUtils.getPosition(parent);
Quaterniond rotation = EntityUtils.getRotation(parent);
Vector3d facingVector = CreatureUtils.getFacingVector(parent);
float velocity = CreatureUtils.getVelocity(parent);
Globals.clientConnection.queueOutgoingMessage(
EntityMessage.constructmoveUpdateMessage(
Globals.clientSceneWrapper.mapClientToServerId(parent.getId()),
Main.getCurrentFrame(),
position.x,
position.y,
position.z,
rotation.x,
rotation.y,
rotation.z,
rotation.w,
velocity,
2 //magic number corresponding to state slowdown
)
);
}
public void simulate(float deltaTime){
@ -233,15 +229,14 @@ public class GroundMovementTree implements BehaviorTree {
// System.out.println(EntityUtils.getEntityPosition(parent));
// System.out.println(message.getpositionX() + " " + message.getpositionY() + " " + message.getpositionZ());
//this should only fire on the client, we don't want the server snap updating due to client position reporting
if(!Globals.RUN_SERVER){
if(position.distance(message.getpositionX(),message.getpositionY(),message.getpositionZ()) > STATE_DIFFERENCE_HARD_UPDATE_THRESHOLD){
EntityUtils.getPosition(parent).set(message.getpositionX(),message.getpositionY(),message.getpositionZ());
} else if(position.distance(message.getpositionX(),message.getpositionY(),message.getpositionZ()) > STATE_DIFFERENCE_SOFT_UPDATE_THRESHOLD){
EntityUtils.getPosition(parent).add(new Vector3d(message.getpositionX(),message.getpositionY(),message.getpositionZ()).mul(SOFT_UPDATE_MULTIPLIER));
}
if(position.distance(message.getpositionX(),message.getpositionY(),message.getpositionZ()) > STATE_DIFFERENCE_HARD_UPDATE_THRESHOLD){
EntityUtils.getPosition(parent).set(message.getpositionX(),message.getpositionY(),message.getpositionZ());
} else if(position.distance(message.getpositionX(),message.getpositionY(),message.getpositionZ()) > STATE_DIFFERENCE_SOFT_UPDATE_THRESHOLD){
EntityUtils.getPosition(parent).add(new Vector3d(message.getpositionX(),message.getpositionY(),message.getpositionZ()).mul(SOFT_UPDATE_MULTIPLIER));
}
//we want to always update the server facing vector with where the client says they're facing
CreatureUtils.setFacingVector(parent, new Vector3d(message.getrotationX(),message.getrotationY(),message.getrotationZ()));
EntityUtils.getRotation(parent).set(message.getrotationX(),message.getrotationY(),message.getrotationZ(),message.getrotationW());
// CreatureUtils.setFacingVector(parent, new Vector3d(message.getrotationX(),message.getrotationY(),message.getrotationZ()));
break;
}
break;
@ -274,14 +269,6 @@ public class GroundMovementTree implements BehaviorTree {
state = MovementTreeState.MOVE;
}
CreatureUtils.setVelocity(parent, velocity);
// body.applyCentralForce(PhysicsUtils.jomlToVecmathVector3f(new Vector3f(movementVector.x,0,movementVector.z).normalize().mul(velocity)));
// EntityUtils.getRotation(parent).set(movementQuaternion);
// //move the entity
// newPosition = new Vector3d(position).add(new Vector3d(movementVector).mul(velocity).mul(Main.deltaTime));
// //check/update if collision
// if(!Globals.collisionEngine.checkCanOccupyPosition(Globals.commonWorldData, parent, newPosition)){
// newPosition = Globals.collisionEngine.suggestMovementPosition(Globals.commonWorldData, parent, newPosition);
// }
// //actually update
collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement"));
// position.set(newPosition);
@ -308,14 +295,6 @@ public class GroundMovementTree implements BehaviorTree {
velocity = maxNaturalVelocity;
CreatureUtils.setVelocity(parent, velocity);
}
// body.applyCentralForce(PhysicsUtils.jomlToVecmathVector3f(force));
// EntityUtils.getRotation(parent).set(movementQuaternion);
//check if can move forward (collision engine)
//if can, move forward by entity movement stats
// newPosition = new Vector3d(position).add(new Vector3d(movementVector).mul(velocity).mul(Main.deltaTime));
// if(!Globals.collisionEngine.checkCanOccupyPosition(Globals.commonWorldData, parent, newPosition)){
// newPosition = Globals.collisionEngine.suggestMovementPosition(Globals.commonWorldData, parent, newPosition);
// }
collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement"));
// position.set(newPosition);
rotation.set(movementQuaternion);
@ -350,13 +329,6 @@ public class GroundMovementTree implements BehaviorTree {
}
}
CreatureUtils.setVelocity(parent, velocity);
// body.applyCentralForce(PhysicsUtils.jomlToVecmathVector3f(new Vector3f(movementVector).mul(-1.0f).normalize().mul(velocity)));
// EntityUtils.getRotation(parent).rotationTo(new Vector3f(0,0,1), new Vector3f((float)movementVector.x,(float)movementVector.y,(float)movementVector.z));
//move the entity
// newPosition = new Vector3d(position).add(new Vector3d(movementVector).mul(velocity).mul(Main.deltaTime));
// if(!Globals.collisionEngine.checkCanOccupyPosition(Globals.commonWorldData, parent, newPosition)){
// newPosition = Globals.collisionEngine.suggestMovementPosition(Globals.commonWorldData, parent, newPosition);
// }
collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement"));
// position.set(newPosition);
rotation.set(movementQuaternion);
@ -365,14 +337,6 @@ public class GroundMovementTree implements BehaviorTree {
break;
case IDLE:
// body.clearForces();
// if(Globals.collisionEngine.gravityCheck(Globals.commonWorldData, parent)){
// position.set(Globals.collisionEngine.suggestMovementPosition(Globals.commonWorldData,parent,new Vector3f(position.x,position.y - 9.8f,position.z)));
// }
// position.set(new Vector3f(position.x,position.y - 0.08f,position.z));
// bodyTransformMatrix = new javax.vecmath.Matrix4f(PhysicsUtils.jomlToVecmathQuaternionf(rotation),PhysicsUtils.jomlToVecmathVector3f(position),1.0f);
// body.setWorldTransform(new com.bulletphysics.linearmath.Transform(bodyTransformMatrix));
break;
}
}

View File

@ -99,44 +99,11 @@ public class ServerGroundMovementTree implements BehaviorTree {
this.facing = facing;
state = MovementTreeState.STARTUP;
//if we aren't the server, alert the server we intend to walk forward
if(!Globals.RUN_SERVER){
Vector3d position = EntityUtils.getPosition(parent);
Quaterniond rotation = EntityUtils.getRotation(parent);
Vector3d facingVector = CreatureUtils.getFacingVector(parent);
float velocity = CreatureUtils.getVelocity(parent);
Globals.clientConnection.queueOutgoingMessage(
EntityMessage.constructmoveUpdateMessage(
parent.getId(),
Main.getCurrentFrame(),
position.x,
position.y,
position.z,
rotation.x,
rotation.y,
rotation.z,
rotation.w,
velocity,
0 //magic number corresponding to state startup
)
);
}
}
}
public void interrupt(){
state = MovementTreeState.IDLE;
CreatureUtils.setVelocity(parent, 0);
}
public void slowdown(){
state = MovementTreeState.SLOWDOWN;
//if we aren't the server, alert the server we intend to slow down
if(!Globals.RUN_SERVER){
Vector3d position = EntityUtils.getPosition(parent);
Quaterniond rotation = EntityUtils.getRotation(parent);
Vector3d facingVector = CreatureUtils.getFacingVector(parent);
float velocity = CreatureUtils.getVelocity(parent);
Globals.clientConnection.queueOutgoingMessage(
DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(
EntityMessage.constructmoveUpdateMessage(
parent.getId(),
Main.getCurrentFrame(),
@ -148,12 +115,41 @@ public class ServerGroundMovementTree implements BehaviorTree {
rotation.z,
rotation.w,
velocity,
2 //magic number corresponding to state slowdown
0 //magic number corresponding to state startup
)
);
}
}
public void interrupt(){
state = MovementTreeState.IDLE;
CreatureUtils.setVelocity(parent, 0);
}
public void slowdown(){
state = MovementTreeState.SLOWDOWN;
//if we aren't the server, alert the server we intend to slow down
Vector3d position = EntityUtils.getPosition(parent);
Quaterniond rotation = EntityUtils.getRotation(parent);
Vector3d facingVector = CreatureUtils.getFacingVector(parent);
float velocity = CreatureUtils.getVelocity(parent);
DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(
EntityMessage.constructmoveUpdateMessage(
parent.getId(),
Main.getCurrentFrame(),
position.x,
position.y,
position.z,
rotation.x,
rotation.y,
rotation.z,
rotation.w,
velocity,
2 //magic number corresponding to state slowdown
)
);
}
public void simulate(float deltaTime){
float velocity = CreatureUtils.getVelocity(parent);
float acceleration = CreatureUtils.getAcceleration(parent);
@ -201,11 +197,11 @@ public class ServerGroundMovementTree implements BehaviorTree {
long updateTime = message.gettime();
// System.out.println("MOVE to " + message.getX() + " " + message.getY() + " " + message.getZ());
switch(message.getMessageSubtype()){
case MOVE:
break;
case MOVE: {
} break;
case SETFACING:
break;
case MOVEUPDATE:
case MOVEUPDATE: {
if(updateTime > lastUpdateTime){
lastUpdateTime = updateTime;
switch(message.gettreeState()){
@ -230,10 +226,11 @@ public class ServerGroundMovementTree implements BehaviorTree {
break;
}
//we want to always update the server facing vector with where the client says they're facing
CreatureUtils.setFacingVector(parent, new Vector3d(message.getrotationX(),message.getrotationY(),message.getrotationZ()));
EntityUtils.getRotation(parent).set(message.getrotationX(),message.getrotationY(),message.getrotationZ(),message.getrotationW());
CreatureUtils.setFacingVector(parent, new Vector3d(0,0,1).rotate(EntityUtils.getRotation(parent)));
break;
}
break;
} break;
default:
break;
}
@ -263,14 +260,6 @@ public class ServerGroundMovementTree implements BehaviorTree {
state = MovementTreeState.MOVE;
}
CreatureUtils.setVelocity(parent, velocity);
// body.applyCentralForce(PhysicsUtils.jomlToVecmathVector3f(new Vector3f(movementVector.x,0,movementVector.z).normalize().mul(velocity)));
// EntityUtils.getRotation(parent).set(movementQuaternion);
// //move the entity
// newPosition = new Vector3d(position).add(new Vector3d(movementVector).mul(velocity).mul(Main.deltaTime));
// //check/update if collision
// if(!Globals.collisionEngine.checkCanOccupyPosition(Globals.commonWorldData, parent, newPosition)){
// newPosition = Globals.collisionEngine.suggestMovementPosition(Globals.commonWorldData, parent, newPosition);
// }
// //actually update
collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement"));
// position.set(newPosition);
@ -312,14 +301,6 @@ public class ServerGroundMovementTree implements BehaviorTree {
velocity = maxNaturalVelocity;
CreatureUtils.setVelocity(parent, velocity);
}
// body.applyCentralForce(PhysicsUtils.jomlToVecmathVector3f(force));
// EntityUtils.getRotation(parent).set(movementQuaternion);
//check if can move forward (collision engine)
//if can, move forward by entity movement stats
// newPosition = new Vector3d(position).add(new Vector3d(movementVector).mul(velocity).mul(Main.deltaTime));
// if(!Globals.collisionEngine.checkCanOccupyPosition(Globals.commonWorldData, parent, newPosition)){
// newPosition = Globals.collisionEngine.suggestMovementPosition(Globals.commonWorldData, parent, newPosition);
// }
collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement"));
// position.set(newPosition);
rotation.set(movementQuaternion);
@ -369,13 +350,6 @@ public class ServerGroundMovementTree implements BehaviorTree {
}
}
CreatureUtils.setVelocity(parent, velocity);
// body.applyCentralForce(PhysicsUtils.jomlToVecmathVector3f(new Vector3f(movementVector).mul(-1.0f).normalize().mul(velocity)));
// EntityUtils.getRotation(parent).rotationTo(new Vector3f(0,0,1), new Vector3f((float)movementVector.x,(float)movementVector.y,(float)movementVector.z));
//move the entity
// newPosition = new Vector3d(position).add(new Vector3d(movementVector).mul(velocity).mul(Main.deltaTime));
// if(!Globals.collisionEngine.checkCanOccupyPosition(Globals.commonWorldData, parent, newPosition)){
// newPosition = Globals.collisionEngine.suggestMovementPosition(Globals.commonWorldData, parent, newPosition);
// }
collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement"));
// position.set(newPosition);
rotation.set(movementQuaternion);
@ -399,14 +373,6 @@ public class ServerGroundMovementTree implements BehaviorTree {
);
break;
case IDLE:
// body.clearForces();
// if(Globals.collisionEngine.gravityCheck(Globals.commonWorldData, parent)){
// position.set(Globals.collisionEngine.suggestMovementPosition(Globals.commonWorldData,parent,new Vector3f(position.x,position.y - 9.8f,position.z)));
// }
// position.set(new Vector3f(position.x,position.y - 0.08f,position.z));
// bodyTransformMatrix = new javax.vecmath.Matrix4f(PhysicsUtils.jomlToVecmathQuaternionf(rotation),PhysicsUtils.jomlToVecmathVector3f(position),1.0f);
// body.setWorldTransform(new com.bulletphysics.linearmath.Transform(bodyTransformMatrix));
break;
}
}

View File

@ -40,6 +40,7 @@ import electrosphere.renderer.ui.events.NavigationEvent;
import electrosphere.renderer.ui.events.ValueChangeEvent;
import electrosphere.renderer.ui.form.FormElement;
import electrosphere.server.saves.SaveUtils;
import electrosphere.server.terrain.generation.OverworldChunkGenerator;
import electrosphere.server.terrain.manager.ServerTerrainManager;
/**
@ -130,7 +131,7 @@ public class MenuGenerators {
//create save dir
SaveUtils.createOrOverwriteSave(saveName);
//create and save terrain
ServerTerrainManager terrainManager = new ServerTerrainManager(2000,50,0.0f,0);
ServerTerrainManager terrainManager = new ServerTerrainManager(2000,50,0.0f,0,new OverworldChunkGenerator());
terrainManager.generate();
terrainManager.save(SaveUtils.deriveSaveDirectoryPath(saveName));
WindowUtils.replaceMainMenuContents(MenuGenerators.createWorldSelectMenu());

View File

@ -4,6 +4,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Semaphore;
import org.joml.Vector3d;
@ -29,6 +30,10 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
//these are going to be the natural ground grid of data cells, but we're going to have more than this
Map<String,ServerDataCell> groundDataCells = new HashMap<String,ServerDataCell>();
Map<ServerDataCell,Vector3i> cellPositionMap = new HashMap<ServerDataCell,Vector3i>();
//Map of server data cell to the number of frames said cell has had no players
Map<ServerDataCell,Integer> cellPlayerlessFrameMap = new HashMap<ServerDataCell,Integer>();
//The number of frames without players that must pass before a server data cell is unloaded
static final int UNLOAD_FRAME_THRESHOLD = 100;
//loaded cells
Semaphore loadedCellsLock = new Semaphore(1);
Set<ServerDataCell> loadedCells;
@ -55,7 +60,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
*/
public void init(ServerWorldData data){
discreteWorldSize = data.getWorldSizeDiscrete();
loadedCells = new HashSet<ServerDataCell>();
loadedCells = new CopyOnWriteArraySet<ServerDataCell>();
}
/**
@ -87,6 +92,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
//add to loaded cells
loadedCellsLock.acquireUninterruptibly();
loadedCells.add(groundDataCells.get(getServerDataCellKey(targetPos)));
cellPlayerlessFrameMap.put(groundDataCells.get(getServerDataCellKey(targetPos)),0);
loadedCellsLock.release();
//generate/handle content for new server data cell
@ -108,9 +114,9 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
int playerSimulationRadius = player.getSimulationRadius();
Vector3i oldPosition = player.getWorldPos();
player.setWorldPos(newPosition);
// System.out.println("=======" + "SET" + newX + " " + newY + " FROM " + oldX + " " + oldY + "========");
// int removals = 0;
// int additions = 0;
// System.out.println("=======" + "SET" + newX + " " + newY + " FROM " + oldX + " " + oldY + "========");
int removals = 0;
int additions = 0;
for(int x = oldPosition.x - playerSimulationRadius; x < oldPosition.x + playerSimulationRadius + 1; x++){
for(int y = oldPosition.y - playerSimulationRadius; y < oldPosition.y + playerSimulationRadius + 1; y++){
for(int z = oldPosition.z - playerSimulationRadius; z < oldPosition.z + playerSimulationRadius + 1; z++){
@ -166,6 +172,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
//add to loaded cells
loadedCellsLock.acquireUninterruptibly();
loadedCells.add(groundDataCells.get(getServerDataCellKey(targetPos)));
cellPlayerlessFrameMap.put(groundDataCells.get(getServerDataCellKey(targetPos)),0);
loadedCellsLock.release();
//add player
groundDataCells.get(getServerDataCellKey(targetPos)).addPlayer(player);
@ -178,7 +185,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
}
}
}
// System.out.println("removals: " + removals + "\tadditions: " + additions);
// System.out.println("removals: " + removals + "\tadditions: " + additions);
}
/**
@ -221,12 +228,23 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
//TODO: improve to make have less performance impact
for(ServerDataCell cell : loadedCells){
if(cell.getPlayers().size() < 1){
System.out.println("Unload cell");
toCleanQueue.add(cell);
int frameCount = cellPlayerlessFrameMap.get(cell) + 1;
cellPlayerlessFrameMap.put(cell,frameCount);
if(frameCount > UNLOAD_FRAME_THRESHOLD){
System.out.println("Unload cell " + getCellWorldPosition(cell));
toCleanQueue.add(cell);
}
} else {
if(cellPlayerlessFrameMap.get(cell) > 0){
cellPlayerlessFrameMap.put(cell, 0);
}
}
}
for(ServerDataCell cell : toCleanQueue){
parent.deregisterCell(cell);
loadedCells.remove(cell);
String key = getServerDataCellKey(getCellWorldPosition(cell));
groundDataCells.remove(key);
}
toCleanQueue.clear();
}
@ -280,6 +298,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
//add to loaded cells
loadedCellsLock.acquireUninterruptibly();
loadedCells.add(groundDataCells.get(getServerDataCellKey(worldPos)));
cellPlayerlessFrameMap.put(groundDataCells.get(getServerDataCellKey(worldPos)),0);
loadedCellsLock.release();
}
return groundDataCells.get(getServerDataCellKey(worldPos));
@ -314,6 +333,7 @@ public class GriddedDataCellManager implements DataCellManager, VoxelCellManager
Globals.microSimulation.simulate(cell, parent.getHitboxManager());
}
loadedCellsLock.release();
updatePlayerPositions();
}

View File

@ -6,6 +6,8 @@ import electrosphere.engine.Globals;
import electrosphere.game.server.world.ServerWorldData;
import electrosphere.logger.LoggerInterface;
import electrosphere.server.db.DatabaseUtils;
import electrosphere.server.terrain.generation.OverworldChunkGenerator;
import electrosphere.server.terrain.generation.interfaces.ChunkGenerator;
import electrosphere.server.terrain.manager.ServerTerrainManager;
import electrosphere.util.FileUtils;
@ -106,7 +108,7 @@ public class SaveUtils {
}
public static boolean loadTerrainAndCreateWorldData(String currentSaveName){
Globals.serverTerrainManager = new ServerTerrainManager(2000,50,0.0f,0);
Globals.serverTerrainManager = new ServerTerrainManager(2000,50,0.0f,0,new OverworldChunkGenerator());
SaveUtils.loadTerrainAndDB(currentSaveName);
Globals.serverWorldData = ServerWorldData.createGameWorld(Globals.serverTerrainManager);
Globals.realmManager.createGriddedRealm(Globals.serverWorldData);

View File

@ -89,21 +89,12 @@ public class MicroSimulation {
// CollidableTree tree = CollidableTree.getCollidableTree(currentCollidable);
// tree.simulate(Main.deltaFrames);
// }
//targeting crosshair
if(Globals.RUN_CLIENT){
Crosshair.checkTargetable();
Crosshair.updateTargetCrosshairPosition();
}
//simulate behavior trees
dataCell.getScene().simulateBehaviorTrees(Main.deltaFrames);
//sum collidable impulses
for(Entity collidable : dataCell.getScene().getEntitiesWithTag(EntityTags.COLLIDABLE)){
ServerCollidableTree.getServerCollidableTree(collidable).simulate(Main.deltaFrames);
}
//delete all client side entities that aren't in visible chunks
if(Globals.clientEntityCullingManager != null){
Globals.clientEntityCullingManager.clearOutOfBoundsEntities();
}
}
}

View File

@ -1,92 +0,0 @@
package electrosphere.server.terrain.diskcache;
import java.util.Map;
import electrosphere.net.server.Server;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
import electrosphere.util.FileUtils;
/**
* An interface for accessing the disk cache of chunk information
*/
public class ChunkDiskCache {
//The map of world position+chunk type to the file that actually houses that information
Map<String,String> worldPosFileMap;
/**
* Constructor
*/
public ChunkDiskCache(){
}
/**
* Gets a key for a given chunk file based on a world coordinate
* @param worldX The x component
* @param worldY The y component
* @param worldZ The z component
* @return The key
*/
private static String getTerrainChunkKey(int worldX, int worldY, int worldZ){
return worldX + "_" + worldY + "_" + worldZ + "t";
}
/**
* Gets a key for a given chunk file based on a world coordinate
* @param worldX The x component
* @param worldY The y component
* @param worldZ The z component
* @return The key
*/
private static String getFluidChunkKey(int worldX, int worldY, int worldZ){
return worldX + "_" + worldY + "_" + worldZ + "f";
}
/**
* Initializes a diskcache based on a given save name
* @param saveName The save name
*/
public void init(String saveName){
worldPosFileMap = FileUtils.loadObjectFromSavePath(saveName, "ChunkCache.map", Map.class);
}
/**
* Checks if the cache contains a given chunk position
* @param worldX The x component
* @param worldY The y component
* @param worldZ The z component
* @return True if the cache contains the chunk, false otherwise
*/
public boolean containsTerrainAtPosition(int worldX, int worldY, int worldZ){
return worldPosFileMap.containsKey(getTerrainChunkKey(worldX, worldY, worldZ));
}
/**
* Checks if the cache contains a given chunk position
* @param worldX The x component
* @param worldY The y component
* @param worldZ The z component
* @return True if the cache contains the chunk, false otherwise
*/
public boolean containsFluidAtPosition(int worldX, int worldY, int worldZ){
return worldPosFileMap.containsKey(getFluidChunkKey(worldX, worldY, worldZ));
}
/**
* Gets the server terrain chunk from disk if it exists, otherwise returns null
* @param worldX The x coordinate
* @param worldY The y coordinate
* @param worldZ The z coordinate
* @return The server terrain chunk if it exists, null otherwise
*/
public ServerTerrainChunk getTerrainChunk(int worldX, int worldY, int worldZ){
ServerTerrainChunk rVal = null;
if(containsTerrainAtPosition(worldX, worldY, worldZ)){
String fileName = worldPosFileMap.get(getTerrainChunkKey(worldX, worldY, worldZ));
//TODO: implement
}
return rVal;
}
}

View File

@ -0,0 +1,205 @@
package electrosphere.server.terrain.diskmap;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.DeflaterInputStream;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterOutputStream;
import electrosphere.engine.Globals;
import electrosphere.net.server.Server;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
import electrosphere.util.FileUtils;
/**
* An interface for accessing the disk map of chunk information
*/
public class ChunkDiskMap {
//The map of world position+chunk type to the file that actually houses that information
Map<String,String> worldPosFileMap = new HashMap<String,String>();
/**
* Constructor
*/
public ChunkDiskMap(){
}
/**
* Gets a key for a given chunk file based on a world coordinate
* @param worldX The x component
* @param worldY The y component
* @param worldZ The z component
* @return The key
*/
private static String getTerrainChunkKey(int worldX, int worldY, int worldZ){
return worldX + "_" + worldY + "_" + worldZ + "t";
}
/**
* Gets a key for a given chunk file based on a world coordinate
* @param worldX The x component
* @param worldY The y component
* @param worldZ The z component
* @return The key
*/
private static String getFluidChunkKey(int worldX, int worldY, int worldZ){
return worldX + "_" + worldY + "_" + worldZ + "f";
}
/**
* Initializes a diskmap based on a given save name
* @param saveName The save name
*/
public void init(String saveName){
if(FileUtils.getSaveFile(saveName, "chunk.map").exists()){
worldPosFileMap = FileUtils.loadObjectFromSavePath(saveName, "chunk.map", Map.class);
} else {
worldPosFileMap = new HashMap<String,String>();
}
}
/**
* Saves the disk map to disk
*/
public void save(){
FileUtils.serializeObjectToSavePath(Globals.currentSaveName, "chunk.map", this);
}
/**
* Checks if the map contains a given chunk position
* @param worldX The x component
* @param worldY The y component
* @param worldZ The z component
* @return True if the map contains the chunk, false otherwise
*/
public boolean containsTerrainAtPosition(int worldX, int worldY, int worldZ){
return worldPosFileMap.containsKey(getTerrainChunkKey(worldX, worldY, worldZ));
}
/**
* Checks if the map contains a given chunk position
* @param worldX The x component
* @param worldY The y component
* @param worldZ The z component
* @return True if the map contains the chunk, false otherwise
*/
public boolean containsFluidAtPosition(int worldX, int worldY, int worldZ){
return worldPosFileMap.containsKey(getFluidChunkKey(worldX, worldY, worldZ));
}
/**
* Gets the server terrain chunk from disk if it exists, otherwise returns null
* @param worldX The x coordinate
* @param worldY The y coordinate
* @param worldZ The z coordinate
* @return The server terrain chunk if it exists, null otherwise
*/
public ServerTerrainChunk getTerrainChunk(int worldX, int worldY, int worldZ){
ServerTerrainChunk rVal = null;
if(containsTerrainAtPosition(worldX, worldY, worldZ)){
//read file
String fileName = worldPosFileMap.get(getTerrainChunkKey(worldX, worldY, worldZ));
byte[] rawDataCompressed = FileUtils.loadBinaryFromSavePath(Globals.currentSaveName, fileName);
//decompress
byte[] rawData = null;
ByteArrayOutputStream out = new ByteArrayOutputStream();
InflaterOutputStream inflaterInputStream = new InflaterOutputStream(out);
try {
inflaterInputStream.write(rawData);
inflaterInputStream.flush();
inflaterInputStream.close();
rawData = out.toByteArray();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//parse
if(rawData != null){
ByteBuffer buffer = ByteBuffer.wrap(rawData);
FloatBuffer floatView = buffer.asFloatBuffer();
int DIM = ServerTerrainChunk.CHUNK_DIMENSION;
float[][][] weights = new float[DIM][DIM][DIM];
int[][][] values = new int[DIM][DIM][DIM];
for(int x = 0; x < DIM; x++){
for(int y = 0; y < DIM; y++){
for(int z = 0; z < DIM; z++){
weights[x][y][z] = floatView.get();
}
}
}
IntBuffer intView = buffer.asIntBuffer();
intView.position(DIM * DIM * DIM * 4);
for(int x = 0; x < DIM; x++){
for(int y = 0; y < DIM; y++){
for(int z = 0; z < DIM; z++){
values[x][y][z] = intView.get();
}
}
}
rVal = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values);
}
}
return rVal;
}
/**
* Saves a terrain chunk to disk
* @param terrainChunk The terrain chunk
*/
public void saveToDisk(ServerTerrainChunk terrainChunk){
//get the file name for this chunk
String fileName = null;
String chunkKey = getTerrainChunkKey(terrainChunk.getWorldX(),terrainChunk.getWorldY(),terrainChunk.getWorldZ());
if(worldPosFileMap.containsKey(chunkKey)){
fileName = worldPosFileMap.get(chunkKey);
} else {
fileName = chunkKey + ".dat";
}
//generate binary for the file
float[][][] weights = terrainChunk.getWeights();
int[][][] values = terrainChunk.getValues();
int DIM = ServerTerrainChunk.CHUNK_DIMENSION;
ByteBuffer buffer = ByteBuffer.allocate(DIM * DIM * DIM * 4 + DIM * DIM * DIM * 4);
FloatBuffer floatView = buffer.asFloatBuffer();
for(int x = 0; x < DIM; x++){
for(int y = 0; y < DIM; y++){
for(int z = 0; z < DIM; z++){
floatView.put(weights[x][y][z]);
}
}
}
buffer.position(DIM * DIM * DIM * 4);
IntBuffer intView = buffer.asIntBuffer();
for(int x = 0; x < DIM; x++){
for(int y = 0; y < DIM; y++){
for(int z = 0; z < DIM; z++){
intView.put(values[x][y][z]);
}
}
}
//compress
ByteArrayOutputStream out = new ByteArrayOutputStream();
DeflaterOutputStream deflaterInputStream = new DeflaterOutputStream(out);
try {
deflaterInputStream.write(buffer.array());
deflaterInputStream.flush();
deflaterInputStream.close();
//write to disk
FileUtils.saveBinaryToSavePath(Globals.currentSaveName, fileName, out.toByteArray());
//save to the map of filenames
worldPosFileMap.put(chunkKey,fileName);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,43 @@
package electrosphere.server.terrain.generation;
import electrosphere.server.terrain.generation.interfaces.ChunkGenerator;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
import electrosphere.server.terrain.models.TerrainModel;
/**
* An arena terrain chunk generator
*/
public class ArenaChunkGenerator implements ChunkGenerator {
@Override
public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ) {
//Each chunk also needs custody of the next chunk's first values so that they can perfectly overlap.
//Hence, width should actually be chunk dimension + 1
float[][][] weights = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];
int[][][] values = new int[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];
for(int inc = 0; inc < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; inc++){
for(int weightX = 0; weightX < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightX++){
for(int weightZ = 0; weightZ < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightZ++){
weights[weightX][inc][weightZ] = -1;
values[weightX][inc][weightZ] = 0;
}
}
}
if(worldY < 1){
for(int weightX = 0; weightX < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightX++){
for(int weightZ = 0; weightZ < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightZ++){
weights[weightX][0][weightZ] = 0.1f;
values[weightX][0][weightZ] = 2;
}
}
}
ServerTerrainChunk rVal = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values);
return rVal;
}
@Override
public void setModel(TerrainModel model) {
//Does nothing as the arena is not based on a terrain model
}
}

View File

@ -0,0 +1,88 @@
package electrosphere.server.terrain.generation;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import electrosphere.game.terrain.processing.TerrainInterpolator;
import electrosphere.server.terrain.generation.interfaces.ChunkGenerator;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
import electrosphere.server.terrain.models.TerrainModel;
/**
* Chunk generator for overworld chunks
*/
public class OverworldChunkGenerator implements ChunkGenerator {
//The model of terrain for the overworld
TerrainModel model;
//cache for the bicubic interpolated chunks
//don't need to interpolate each time a new chunk is created
//This should eventually be removed as terrain generation becomes more complicated than a heightmap
Map<String, float[][]> heightmapCache = new ConcurrentHashMap<String, float[][]>();
/**
* Constructor
*/
public OverworldChunkGenerator(){
}
@Override
public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ) {
ServerTerrainChunk returnedChunk;
//Each chunk also needs custody of the next chunk's first values so that they can perfectly overlap.
//Hence, width should actually be chunk dimension + 1
float[][] heightmap = getHeightmap(worldX, worldZ);
float[][][] weights = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];
int[][][] values = new int[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];
for(int weightX = 0; weightX < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightX++){
for(int weightY = 0; weightY < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightY++){
for(int weightZ = 0; weightZ < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightZ++){
float height = heightmap[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE * worldX + weightX][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE * worldZ + weightZ];
if(weightY < height){
weights[weightX][weightY][weightZ] = 1;
values[weightX][weightY][weightZ] = 1;
} else if(height == 0 && weightY == 0 && worldY == 0) {
weights[weightX][weightY][weightZ] = 0.1f;
values[weightX][weightY][weightZ] = 1;
} else {
weights[weightX][weightY][weightZ] = -1;
values[weightX][weightY][weightZ] = 0;
}
}
}
}
returnedChunk = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values);
return returnedChunk;
}
@Override
/**
* Sets the terrain model for the overworld algo
*/
public void setModel(TerrainModel model){
this.model = model;
}
/**
* Gets a heightmap array. Either pulls it from cache if it exists or does the logic to generate it
* @param worldX The x position in world coordinates of the chunk
* @param worldZ THe z position in world coordinates of the chunk
* @return The heightmap array
*/
private float[][] getHeightmap(int worldX, int worldZ){
String key = worldX + "_" + worldZ;
if(heightmapCache.containsKey(key)){
return heightmapCache.get(key);
} else {
float[][] macroValues = model.getRad5MacroValuesAtPosition(worldX, worldZ);
float[][] heightmap = TerrainInterpolator.getBicubicInterpolatedChunk(
macroValues,
model.getDynamicInterpolationRatio()
);
heightmapCache.put(key,heightmap);
return heightmap;
}
}
}

View File

@ -0,0 +1,26 @@
package electrosphere.server.terrain.generation.interfaces;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
import electrosphere.server.terrain.models.TerrainModel;
/**
* An interface for generating chunks. Used to isolate different algorithms for getting chunks from one another.
*/
public interface ChunkGenerator {
/**
* Generates a chunk given an x, y, and z
* @param worldX The x component
* @param worldY The y component
* @param worldZ The z component
* @return The chunk
*/
public ServerTerrainChunk generateChunk(int worldX, int worldY, int worldZ);
/**
* Sets the terrain model for the generation algorithm
* @param model The terrain model
*/
public void setModel(TerrainModel model);
}

View File

@ -30,38 +30,6 @@ public class ServerTerrainChunk {
this.values = values;
}
/**
* Returns an arena chunk. Should be flat land if worldY=0, otherwise all air
* @param worldX The world x position
* @param worldY The world y position
* @param worldZ The world z position
* @return The ServerTerrainChunk
*/
public static ServerTerrainChunk getArenaChunk(int worldX, int worldY, int worldZ){
//Each chunk also needs custody of the next chunk's first values so that they can perfectly overlap.
//Hence, width should actually be chunk dimension + 1
float[][][] weights = new float[CHUNK_DATA_GENERATOR_SIZE][CHUNK_DATA_GENERATOR_SIZE][CHUNK_DATA_GENERATOR_SIZE];
int[][][] values = new int[CHUNK_DATA_GENERATOR_SIZE][CHUNK_DATA_GENERATOR_SIZE][CHUNK_DATA_GENERATOR_SIZE];
for(int inc = 0; inc < CHUNK_DATA_GENERATOR_SIZE; inc++){
for(int weightX = 0; weightX < CHUNK_DATA_GENERATOR_SIZE; weightX++){
for(int weightZ = 0; weightZ < CHUNK_DATA_GENERATOR_SIZE; weightZ++){
weights[weightX][inc][weightZ] = -1;
values[weightX][inc][weightZ] = 0;
}
}
}
if(worldY < 1){
for(int weightX = 0; weightX < CHUNK_DATA_GENERATOR_SIZE; weightX++){
for(int weightZ = 0; weightZ < CHUNK_DATA_GENERATOR_SIZE; weightZ++){
weights[weightX][0][weightZ] = 0.1f;
values[weightX][0][weightZ] = 2;
}
}
}
ServerTerrainChunk rVal = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values);
return rVal;
}
public int getWorldX() {
return worldX;
}

View File

@ -4,7 +4,10 @@ import com.google.gson.Gson;
import electrosphere.engine.Globals;
import electrosphere.game.terrain.processing.TerrainInterpolator;
import electrosphere.server.terrain.diskmap.ChunkDiskMap;
import electrosphere.server.terrain.generation.ArenaChunkGenerator;
import electrosphere.server.terrain.generation.continentphase.TerrainGenerator;
import electrosphere.server.terrain.generation.interfaces.ChunkGenerator;
import electrosphere.server.terrain.models.TerrainModel;
import electrosphere.server.terrain.models.TerrainModification;
import electrosphere.util.FileUtils;
@ -38,36 +41,52 @@ public class ServerTerrainManager {
long seed;
//The model of the terrain this manager is managing
TerrainModel model;
//Basic idea is we associate string that contains chunk x&y with elevation
//In memory cache of chunk data
//Basic idea is we associate string that contains chunk x&y&z 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 = 500;
Map<String, ServerTerrainChunk> chunkCache;
List<String> chunkCacheContents;
//cache for the bicubic interpolated chunks
//don't need to interpolate each time a new chunk is created
//This should eventually be removed as terrain generation becomes more complicated than a heightmap
Map<String, float[][]> heightmapCache = new ConcurrentHashMap<>();
//The map of chunk position <-> file on disk containing chunk data
ChunkDiskMap chunkDiskMap = null;
//The generation algorithm for this terrain manager
ChunkGenerator chunkGenerator;
public ServerTerrainManager(int worldSizeDiscrete, int verticalInterpolationRatio, float interpolationRandomDampener, long seed){
/**
* Constructor
*/
public ServerTerrainManager(
int worldSizeDiscrete,
int verticalInterpolationRatio,
float interpolationRandomDampener,
long seed,
ChunkGenerator chunkGenerator
){
this.worldSizeDiscrete = worldSizeDiscrete;
this.verticalInterpolationRatio = verticalInterpolationRatio;
this.chunkCache = new ConcurrentHashMap<String, ServerTerrainChunk>();
this.chunkCacheContents = new CopyOnWriteArrayList<String>();
this.interpolationRandomDampener = interpolationRandomDampener;
this.seed = seed;
this.chunkGenerator = chunkGenerator;
}
ServerTerrainManager(){
}
/**
* Constructs an arena terrain manager
* @return The arena terrain manager
*/
public static ServerTerrainManager constructArenaTerrainManager(){
ServerTerrainManager rVal = new ServerTerrainManager();
rVal.worldSizeDiscrete = 2;
@ -75,16 +94,22 @@ public class ServerTerrainManager {
rVal.chunkCache = new ConcurrentHashMap<String, ServerTerrainChunk>();
rVal.chunkCacheContents = new CopyOnWriteArrayList<String>();
rVal.interpolationRandomDampener = 0.0f;
rVal.chunkGenerator = new ArenaChunkGenerator();
return rVal;
}
/**
* Generates a terrain model for the manager
*/
public void generate(){
TerrainGenerator terrainGen = new TerrainGenerator();
terrainGen.setInterpolationRatio(worldSizeDiscrete/200);
terrainGen.setVerticalInterpolationRatio(verticalInterpolationRatio);
terrainGen.setRandomSeed(seed);
model = terrainGen.generateModel();
this.chunkGenerator.setModel(model);
model.setInterpolationRandomDampener(interpolationRandomDampener);
this.chunkDiskMap = new ChunkDiskMap();
}
/**
@ -100,10 +125,19 @@ public class ServerTerrainManager {
floatView.flip();
FileUtils.saveBinaryToSavePath(saveName, "./terrain.dat", buffer.array());
FileUtils.serializeObjectToSavePath(saveName, "./terrain.json", model);
if(chunkDiskMap != null){
chunkDiskMap.save();
}
}
/**
* Loads a terrain manager from a save file
* @param saveName The name of the save
*/
public void load(String saveName){
//load terrain model
model = FileUtils.loadObjectFromSavePath(saveName, "./terrain.json", TerrainModel.class);
chunkGenerator.setModel(model);
byte[] data = FileUtils.loadBinaryFromSavePath(saveName, "./terrain.dat");
ByteBuffer buffer = ByteBuffer.wrap(data);
FloatBuffer floatView = buffer.asFloatBuffer();
@ -114,6 +148,9 @@ public class ServerTerrainManager {
}
}
model.setElevationArray(elevation);
//load chunk disk map
chunkDiskMap = new ChunkDiskMap();
chunkDiskMap.init(saveName);
}
public float[][] getTerrainAtChunk(int x, int y){
@ -156,10 +193,21 @@ public class ServerTerrainManager {
}
}
/**
* Gets the terrain model backing this terrain manager
* @return The terrain model
*/
public TerrainModel getModel() {
return model;
}
/**
* Gets the key for a given world position
* @param worldX The x component
* @param worldY The y component
* @param worldZ The z component
* @return The key
*/
public String getKey(int worldX, int worldY, int worldZ){
return worldX + "_" + worldY + "_" + worldZ;
}
@ -172,77 +220,32 @@ public class ServerTerrainManager {
* @return The ServerTerrainChunk
*/
public ServerTerrainChunk getChunk(int worldX, int worldY, int worldZ){
if(model != null){
//THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING
String key = getKey(worldX,worldY,worldZ);
ServerTerrainChunk returnedChunk;
if(chunkCache.containsKey(key)){
chunkCacheContents.remove(key);
chunkCacheContents.add(0, key);
returnedChunk = chunkCache.get(key);
return returnedChunk;
} else {
//Each chunk also needs custody of the next chunk's first values so that they can perfectly overlap.
//Hence, width should actually be chunk dimension + 1
float[][] heightmap = getHeightmap(worldX, worldZ);
float[][][] weights = new float[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];
int[][][] values = new int[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE];
for(int weightX = 0; weightX < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightX++){
for(int weightY = 0; weightY < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightY++){
for(int weightZ = 0; weightZ < ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE; weightZ++){
float height = heightmap[ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE * worldX + weightX][ServerTerrainChunk.CHUNK_DATA_GENERATOR_SIZE * worldZ + weightZ];
if(weightY < height){
weights[weightX][weightY][weightZ] = 1;
values[weightX][weightY][weightZ] = 1;
} else if(height == 0 && weightY == 0 && worldY == 0) {
weights[weightX][weightY][weightZ] = 0.1f;
values[weightX][weightY][weightZ] = 1;
} else {
weights[weightX][weightY][weightZ] = -1;
values[weightX][weightY][weightZ] = 0;
}
}
}
}
if(chunkCacheContents.size() > cacheSize){
String oldChunk = chunkCacheContents.remove(chunkCacheContents.size() - 1);
chunkCache.remove(oldChunk);
}
returnedChunk = new ServerTerrainChunk(worldX, worldY, worldZ, weights, values);
chunkCache.put(key, returnedChunk);
chunkCacheContents.add(key);
return returnedChunk;
}
//THIS FIRES IF THERE IS A MAIN GAME WORLD RUNNING
String key = getKey(worldX,worldY,worldZ);
ServerTerrainChunk returnedChunk = null;
if(chunkCache.containsKey(key)){
chunkCacheContents.remove(key);
chunkCacheContents.add(0, key);
returnedChunk = chunkCache.get(key);
return returnedChunk;
} else {
//THIS FIRES IF THERE IS AN ARENA WORLD RUNNING
String key = getKey(worldX, worldY, worldZ);
ServerTerrainChunk returnedChunk;
if(chunkCache.containsKey(key)){
chunkCacheContents.remove("" + key);
chunkCacheContents.add(0, key);
returnedChunk = chunkCache.get(key);
return returnedChunk;
} else {
returnedChunk = ServerTerrainChunk.getArenaChunk(worldX, worldY, worldZ);
chunkCache.put(key, returnedChunk);
chunkCacheContents.add(key);
return returnedChunk;
if(chunkCacheContents.size() > cacheSize){
String oldChunk = chunkCacheContents.remove(chunkCacheContents.size() - 1);
chunkCache.remove(oldChunk);
}
}
}
private float[][] getHeightmap(int worldX, int worldZ){
String key = worldX + "_" + worldZ;
if(heightmapCache.containsKey(key)){
return heightmapCache.get(key);
} else {
float[][] macroValues = model.getRad5MacroValuesAtPosition(worldX, worldZ);
float[][] heightmap = TerrainInterpolator.getBicubicInterpolatedChunk(
macroValues,
model.getDynamicInterpolationRatio()
);
heightmapCache.put(key,heightmap);
return heightmap;
//pull from disk if it exists
if(chunkDiskMap != null){
if(chunkDiskMap.containsTerrainAtPosition(worldX, worldY, worldZ)){
returnedChunk = chunkDiskMap.getTerrainChunk(worldX, worldY, worldZ);
}
}
//generate if it does not exist
if(returnedChunk == null){
returnedChunk = chunkGenerator.generateChunk(worldX, worldY, worldZ);
}
chunkCache.put(key, returnedChunk);
chunkCacheContents.add(key);
return returnedChunk;
}
}

View File

@ -193,6 +193,14 @@ public class FileUtils {
return Files.readString(targetFile.toPath());
}
/**
* Loads an object from a save folder
* @param <T> The type of the object
* @param saveName The name of the save
* @param pathName The path of the file containing the object json
* @param className The class of the object
* @return The object
*/
public static <T>T loadObjectFromSavePath(String saveName, String pathName, Class<T> className){
T rVal = null;
String sanitizedFilePath = sanitizeFilePath(pathName);
@ -204,6 +212,12 @@ public class FileUtils {
return rVal;
}
/**
* Serializes an object to a save folder
* @param saveName The name of the save
* @param pathName The path within the save folder to the file
* @param object The object to save
*/
public static void serializeObjectToSavePath(String saveName, String pathName, Object object){
String sanitizedFilePath = sanitizeFilePath(pathName);
try {
@ -248,11 +262,11 @@ public class FileUtils {
/**
* Checks if a directory exists
* @param directoryName
* @param fileName
* @return true if directory exists, false otherwise
*/
public static boolean checkFileExists(String directoryName){
File targetDir = new File(sanitizeFilePath(directoryName));
public static boolean checkFileExists(String fileName){
File targetDir = new File(sanitizeFilePath(fileName));
if(targetDir.exists()){
return true;
} else {

View File

@ -1,6 +1,7 @@
package electrosphere.util.worldviewer;
import electrosphere.server.simulation.MacroSimulation;
import electrosphere.server.terrain.generation.OverworldChunkGenerator;
import electrosphere.server.terrain.manager.ServerTerrainManager;
import electrosphere.server.terrain.models.TerrainModel;
@ -19,7 +20,7 @@ public class TerrainViewer {
TerrainModel terrainModel;
ServerTerrainManager terrainManager = new ServerTerrainManager(2000, 1000, 0.05f, new Random().nextLong());
ServerTerrainManager terrainManager = new ServerTerrainManager(2000, 1000, 0.05f, new Random().nextLong(), new OverworldChunkGenerator());
terrainManager.generate();
terrainModel = terrainManager.getModel();