Renderer/src/main/java/electrosphere/entity/state/movement/AirplaneMovementTree.java
2024-03-21 20:37:47 -04:00

320 lines
12 KiB
Java

package electrosphere.entity.state.movement;
import java.util.concurrent.CopyOnWriteArrayList;
import org.joml.Quaterniond;
import org.joml.Quaternionf;
import org.joml.Vector3d;
import org.joml.Vector3f;
import electrosphere.collision.collidable.Collidable;
import electrosphere.engine.Globals;
import electrosphere.engine.Main;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.btree.BehaviorTree;
import electrosphere.entity.state.collidable.Impulse;
import electrosphere.entity.types.camera.CameraEntityUtils;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.net.parser.net.message.EntityMessage;
import electrosphere.renderer.actor.Actor;
import electrosphere.renderer.anim.Animation;
import electrosphere.server.datacell.utils.DataCellSearchUtils;
public class AirplaneMovementTree implements BehaviorTree {
public static enum AirplaneMovementTreeState {
ACCELERATING,
DECELERATING,
}
float minVelocity = 0;
float maxRotationSpeed = 1.0f;
//The yaw value last simulation frame
float previousYaw = 270;
//how much we're rolling currently
float rollVal = 0;
// the factor to increment rollVal by while swinging the camera around
float rollFactor = 0.05f;
static final double STATE_DIFFERENCE_HARD_UPDATE_THRESHOLD = 1.0;
static final double STATE_DIFFERENCE_SOFT_UPDATE_THRESHOLD = 0.2;
static final double SOFT_UPDATE_MULTIPLIER = 0.1;
AirplaneMovementTreeState state;
Entity parent;
Collidable collidable;
CopyOnWriteArrayList<EntityMessage> networkMessageQueue = new CopyOnWriteArrayList<EntityMessage>();
//when the latest network message for this tree was received
//used to filter out packets before the most recent one
long lastUpdateTime = 0;
float pitchCalculationTolerance = 0.99f;
/**
* Constructs an airplane movement tree
* @param e The entity this tree will be attached to
* @param collidable The collidable of the entity that parents this tree
*/
public AirplaneMovementTree(Entity e, Collidable collidable){
state = AirplaneMovementTreeState.ACCELERATING;
parent = e;
this.collidable = collidable;
}
/**
* Simulates a step of the behavior tree
*/
public void simulate(float deltaTime){
//
//Get important initial values
//
float velocity = CreatureUtils.getVelocity(parent);
float acceleration = CreatureUtils.getAcceleration(parent);
float maxNaturalVelocity = CreatureUtils.getMaxNaturalVelocity(parent);
Actor entityActor = EntityUtils.getActor(parent);
Vector3d position = EntityUtils.getPosition(parent);
Vector3d facingVector = CreatureUtils.getFacingVector(parent);
Quaterniond movementQuaternion = new Quaterniond().rotationTo(new Vector3d(0,0,1), new Vector3d(facingVector.x,0,facingVector.z)).normalize();
Quaterniond rotation = EntityUtils.getRotation(parent);
//
//handle network messages
//
for(EntityMessage message : networkMessageQueue){
networkMessageQueue.remove(message);
long updateTime = message.gettime();
switch(message.getMessageSubtype()){
case MOVE: {
if(Globals.RUN_CLIENT){
position.set(message.getpositionX(), message.getpositionY(), message.getpositionZ());
}
} break;
//received a message to update the tree
case MOVEUPDATE: {
if(updateTime > lastUpdateTime){
lastUpdateTime = updateTime;
//update the behavior tree state
switch(message.gettreeState()){
case 0:
state = AirplaneMovementTreeState.ACCELERATING;
break;
case 1:
state = AirplaneMovementTreeState.DECELERATING;
break;
}
//if we're the client snap to the reported position as appropriate
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));
}
}
//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()));
}
} break;
case SETBEHAVIORTREE: {
} break;
case SETFACING: {
} break;
case SETPOSITION: {
} break;
case ATTACHENTITYTOENTITY:
case ATTACKUPDATE:
case CREATE:
case DESTROY:
case KILL:
case SETPROPERTY:
case SPAWNCREATURE:
case SPAWNITEM:
//do nothing
break;
}
}
//
// Actual simulation
//
switch(state){
case ACCELERATING: {
//velocity calculation
velocity = velocity + acceleration;
if(velocity > maxNaturalVelocity){
velocity = maxNaturalVelocity;
}
CreatureUtils.setVelocity(parent, velocity);
//update rotation
updateRotation(rotation,facingVector);
//add movement impulse
addMovementForce(velocity,rotation,collidable);
//if server, update all clients to simulation changes
serverUpdateTree(position,rotation,velocity);
} break;
case DECELERATING: {
//velocity calculation
velocity = velocity - acceleration;
if(velocity < minVelocity){
velocity = minVelocity;
}
CreatureUtils.setVelocity(parent, velocity);
//update rotation
updateRotation(rotation,facingVector);
//add movement impulse
addMovementForce(velocity,rotation,collidable);
//if server, update all clients to simulation changes
serverUpdateTree(position,rotation,velocity);
} break;
}
}
/**
* Updates the rotation of the airplane
* @param rotation Rotation quaternion
* @param rotationVector Rotation vector
*/
void updateRotation(Quaterniond rotation, Vector3d rotationVector){
if(Globals.RUN_CLIENT && this.parent == Globals.playerEntity){
Vector3f cameraEyeVector = CameraEntityUtils.getCameraEye(Globals.playerCamera);
float pitch = CameraEntityUtils.getCameraPitch(Globals.playerCamera) / 180 * (float)Math.PI;
float yaw = -(CameraEntityUtils.getCameraYaw(Globals.playerCamera) + 180) / 180 * (float)Math.PI;
float deltaYaw = yaw - previousYaw;
if(deltaYaw > 0){
rollVal += -rollFactor;
}
if(deltaYaw < 0){
rollVal += rollFactor;
}
Quaterniond yawQuat = new Quaterniond().fromAxisAngleRad(new Vector3d(0,1,0), yaw);
Quaterniond pitchQuat = new Quaterniond().fromAxisAngleRad(new Vector3d(1,0,0), pitch);
Quaterniond rollQuat = new Quaterniond().fromAxisAngleRad(new Vector3d(0,0,1), rollVal);
rotation.slerp(yawQuat.mul(pitchQuat).mul(rollQuat),0.1);
//rotate thrust vector
rotationVector.set(rotation.transform(new Vector3d(0,0,1)));
// rotationVector.set(new Vector3f((float)rotationVector.x,(float)rotationVector.y,(float)rotationVector.z).mul(1.0f - this.maxRotationSpeed).add(new Vector3f(cameraEyeVector).mul(-this.maxRotationSpeed)));
rollVal = rollVal * 0.9f;
previousYaw = yaw;
}
}
/**
* Adds the force to actually move the airplane entity
* @param velocity The current velocity
* @param facingVector The current facing vector
* @param collidable The collidable of the entity
*/
void addMovementForce(float velocity, Quaterniond rotation, Collidable collidable){
Vector3d impulseDir = rotation.transform(new Vector3d(0,0,1));
collidable.addImpulse(new Impulse(new Vector3d(impulseDir), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Globals.timekeeper.getSimFrameTime(), "movement"));
}
/**
* If the instance is a server, update all clients within the chunk as appropriate
* @param position The position of the airplane
* @param facingVector The facing vector of the airplane
* @param velocity The velocity of the airplane
*/
void serverUpdateTree(Vector3d position, Quaterniond rotation, float velocity){
if(Globals.RUN_SERVER){
int stateNumber = 0;
switch(this.state){
case ACCELERATING:
stateNumber = 0;
break;
case DECELERATING:
stateNumber = 1;
break;
}
DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(
EntityMessage.constructmoveUpdateMessage(
parent.getId(),
Globals.timekeeper.getNumberOfSimFramesElapsed(),
position.x,
position.y,
position.z,
rotation.x,
rotation.y,
rotation.z,
rotation.w,
velocity,
stateNumber
)
);
}
}
/**
* Register a network message relavent to this tree
* @param networkMessage The network message to register
*/
public void addNetworkMessage(EntityMessage networkMessage) {
networkMessageQueue.add(networkMessage);
}
/**
* Determines the animation to play at a given point in the simulation loop
* @return The animation to play as a string
*/
public String determineCorrectAnimation(){
String rVal = "";
switch(state){
case ACCELERATING:
rVal = Animation.ANIMATION_IDLE_1;
break;
case DECELERATING:
rVal = Animation.ANIMATION_IDLE_1;
break;
}
return rVal;
}
/**
* Gets the current state of the behavior tree
* @return The current state of the behavior tree
*/
public AirplaneMovementTreeState getState(){
return state;
}
/**
* Sets the minimum velocity of this airplane tree
* @param minVelocity The minimum velocity
*/
public void setMinimumVelocity(float minVelocity){
this.minVelocity = minVelocity;
}
/**
* Sets the max rotation speed of this airplane movement tree
* @param maxRotationSpeed The max rotation speed
*/
public void setMaxRotationSpeed(float maxRotationSpeed){
this.maxRotationSpeed = maxRotationSpeed;
}
}