Renderer/src/main/java/electrosphere/client/sim/ClientSimulation.java
2025-06-08 18:41:32 -04:00

297 lines
11 KiB
Java

package electrosphere.client.sim;
import org.joml.Vector3d;
import electrosphere.client.entity.camera.CameraEntityUtils;
import electrosphere.client.entity.crosshair.Crosshair;
import electrosphere.client.entity.instance.InstanceUpdater;
import electrosphere.client.fluid.manager.ClientFluidManager;
import electrosphere.client.interact.ClientInteractionEngine;
import electrosphere.client.terrain.manager.ClientTerrainManager;
import electrosphere.client.ui.menu.ingame.ToolbarPreviewWindow;
import electrosphere.engine.EngineState;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityTags;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.state.attach.AttachUtils;
import electrosphere.entity.state.collidable.ClientCollidableTree;
import electrosphere.entity.state.equip.ClientEquipState;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.entity.types.item.ItemUtils;
import electrosphere.renderer.actor.Actor;
public class ClientSimulation {
//if true, is ready to simulate
boolean isReady = false;
//if true, should load terrain
boolean loadTerrain = false;
//used for tracking different in player position between frames (principally for draw cell manager)
Vector3d newPlayerCharacterPosition = new Vector3d();
/**
* Constructor
*/
public ClientSimulation(){
isReady = false;
}
/**
* Main simulation function
*/
public void simulate(){
//
//check for dependencies
if(Globals.clientState.clientSceneWrapper == null){
return;
}
Globals.profiler.beginCpuSample("simulate");
//
//load terrain
if(isLoadingTerrain()){
loadTerrain();
}
//process all server synchronization messages
Globals.profiler.beginCpuSample("clientSynchronizationManager.processMessages");
Globals.clientState.clientSynchronizationManager.processMessages();
Globals.profiler.endCpuSample();
//
//simulate bullet physics engine step
if(EngineState.EngineFlags.RUN_PHYSICS){
Globals.clientState.clientSceneWrapper.getCollisionEngine().simulatePhysics();
Globals.clientState.clientSceneWrapper.getCollisionEngine().updateDynamicObjectTransforms();
}
//update actor animations
Globals.profiler.beginCpuSample("update actor animations");
for(Entity currentEntity : Globals.clientState.clientSceneWrapper.getScene().getEntitiesWithTag(EntityTags.DRAWABLE)){
Actor currentActor = EntityUtils.getActor(currentEntity);
if(currentActor.getLodLevel() == Actor.LOD_LEVEL_STATIC){
continue;
}
if(currentActor.getAnimationData().isPlayingAnimation()){
currentActor.getAnimationData().incrementAnimationTime((float)Globals.engineState.timekeeper.getSimFrameTime());
}
}
Globals.profiler.endCpuSample();
//
//make items play idle animation
Globals.profiler.beginCpuSample("item animations");
for(Entity item : Globals.clientState.clientSceneWrapper.getScene().getEntitiesWithTag(EntityTags.ITEM)){
ItemUtils.updateItemActorAnimation(item);
}
Globals.profiler.endCpuSample();
//
//update attached entity positions
AttachUtils.clientUpdateAttachedEntityPositions();
Globals.particleService.handleAllSignals();
//
//Hitbox stuff
Globals.profiler.beginCpuSample("update hitboxes");
Globals.clientState.clientSceneWrapper.getHitboxManager().simulate();
Globals.profiler.endCpuSample();
//
//update foliage
if(Globals.clientState.foliageCellManager != null){
Globals.clientState.foliageCellManager.update();
}
//
//targeting crosshair
Globals.profiler.beginCpuSample("crosshair update");
Crosshair.checkTargetable();
Crosshair.updateTargetCrosshairPosition();
Globals.profiler.endCpuSample();
//
//simulate behavior trees
Globals.clientState.clientSceneWrapper.getScene().simulateBehaviorTrees((float)Globals.engineState.timekeeper.getSimFrameTime());
//
//sum collidable impulses
Globals.profiler.beginCpuSample("collidable logic");
for(Entity collidable : Globals.clientState.clientSceneWrapper.getScene().getEntitiesWithTag(EntityTags.COLLIDABLE)){
if(ClientCollidableTree.hasClientCollidableTree(collidable)){
ClientCollidableTree.getClientCollidableTree(collidable).simulate((float)Globals.engineState.timekeeper.getSimFrameTime());
}
}
//
//clear collidable impulse lists
Globals.clientState.clientSceneWrapper.getCollisionEngine().clearCollidableImpulseLists();
Globals.clientState.clientSceneWrapper.getChemistryEngine().clearCollidableImpulseLists();
Globals.profiler.endCpuSample();
//
//Rebase world origin
Globals.clientState.clientSceneWrapper.getCollisionEngine().rebaseWorldOrigin();
//
//wrap up functions
this.runClientFunctions();
Globals.profiler.endCpuSample();
}
/**
* Client functions
*/
public void runClientFunctions(){
Globals.profiler.beginCpuSample("client functions");
ClientTerrainManager.generateTerrainChunkGeometry();
ClientFluidManager.generateFluidChunkGeometry();
this.updateSkyboxPos();
Globals.clientState.clientSceneWrapper.destroyEntitiesOutsideSimRange();
InstanceUpdater.updateInstancedActorPriority();
Globals.cameraHandler.updateGlobalCamera();
this.updateFirstPersonAttachments();
//bones have potenitally moved, so need to update where attached entities actually are before drawing
AttachUtils.clientUpdateAttachedEntityPositions();
ClientInteractionEngine.updateInteractionTargetLabel();
ToolbarPreviewWindow.checkVisibility();
this.runServices();
// updateCellManager();
Globals.profiler.endCpuSample();
}
/**
* Runs the client services
*/
private void runServices(){
Globals.profiler.beginCpuSample("ClientSimulation.runServices");
Globals.clientState.clientTemporalService.simulate();
Globals.profiler.endCpuSample();
}
/**
* Updates the skybox position to center on the player
*/
private void updateSkyboxPos(){
Globals.profiler.beginCpuSample("updateSkyboxPos");
if(Globals.clientState.skybox != null && Globals.clientState.playerEntity != null){
EntityUtils.setPosition(Globals.clientState.skybox, EntityUtils.getPosition(Globals.clientState.playerEntity));
}
Globals.profiler.endCpuSample();
}
/**
* If in first person, update the spatial transforms of things attached to the player viewmodel
*/
private void updateFirstPersonAttachments(){
Globals.profiler.beginCpuSample("updateFirstPersonAttachments");
//update the facing vector when camera moves in first person
if(!Globals.controlHandler.cameraIsThirdPerson() && Globals.clientState.playerCamera != null && Globals.clientState.playerEntity != null){
CreatureUtils.setFacingVector(Globals.clientState.playerEntity, CameraEntityUtils.getFacingVec(Globals.clientState.playerCamera));
//flush equipped item state
if(ClientEquipState.hasEquipState(Globals.clientState.playerEntity)){
ClientEquipState equipState = ClientEquipState.getClientEquipState(Globals.clientState.playerEntity);
equipState.evaluatePlayerAttachments();
}
}
Globals.profiler.endCpuSample();
}
/**
* Loads terrain that is in queue
*/
public void loadTerrain(){
Globals.profiler.beginCpuSample("ClientSimulation.loadTerrain");
if(Globals.clientState.clientTerrainManager != null){
Globals.clientState.clientTerrainManager.handleMessages();
this.updateTerrainCellManager();
}
if(Globals.clientState.clientFluidManager != null && EngineState.EngineFlags.RUN_FLUIDS){
Globals.clientState.clientFluidManager.handleMessages();
this.updateFluidCellManager();
}
if(Globals.clientState.clientBlockManager != null){
Globals.clientState.clientBlockManager.handleMessages();
this.updateBlockCellManager();
}
Globals.profiler.endCpuSample();
}
/**
* Updates the terrain cell manager (specifically position handling)
*/
private void updateTerrainCellManager(){
///
/// C L I E N T C E L L M A N A G E R
///
if(Globals.clientState.clientDrawCellManager != null && Globals.clientState.clientWorldData != null){
//Cell manager do your things
Globals.clientState.clientDrawCellManager.update();
}
}
/**
* Updates the fluid cell manager (specifically position handling)
*/
private void updateFluidCellManager(){
//fluid work
if(Globals.clientState.fluidCellManager != null && Globals.clientState.clientWorldData != null && EngineState.EngineFlags.RUN_FLUIDS){
Globals.clientState.fluidCellManager.update();
}
}
/**
* Updates the block cell manager
*/
private void updateBlockCellManager(){
if(Globals.clientState.clientBlockCellManager != null && Globals.clientState.clientWorldData != null){
Globals.clientState.clientBlockCellManager.update();
}
}
/**
* Gets whether the client simulation is ready to execute
* @return True if ready to execute, false otherwise
*/
public boolean isReady(){
return isReady;
}
/**
* Sets the ready status of the client simulation
* @param ready True if ready to simulate, false otherwise
*/
public void setReady(boolean ready){
isReady = ready;
}
/**
* Freezes simulation (sets ready to false)
*/
public void freeze(){
isReady = false;
}
/**
* Unfreezes simulation (sets ready to true)
*/
public void unfreeze(){
isReady = true;
}
/**
* Gets whether the client simulation is loading terrain
* @return True if loading terrain, false otherwise
*/
public boolean isLoadingTerrain(){
return this.loadTerrain;
}
/**
* Sets whether the client should load terrain or not
* @param loadTerrain True if should load terrain, false otherwise
*/
public void setLoadingTerrain(boolean loadTerrain){
this.loadTerrain = loadTerrain;
}
}