diff --git a/docs/src/highlevel-design/controls/controlDesign.md b/docs/src/highlevel-design/controls/controlDesign.md new file mode 100644 index 00000000..21477d64 --- /dev/null +++ b/docs/src/highlevel-design/controls/controlDesign.md @@ -0,0 +1,24 @@ +@page controlDesign Controls Design + +==First person control scheme== +How to move - WASD, Space +Crouch - Ctrl +Sprint - Shift +Cursor interaction - E +Radial menu for various important submenus (ie inventories) - Tab +Quick switch equipped item (Radial Menu) - Hold 4 key +Item interaction 1 (ie swapping weapon stance, swapping spell, etc) - X +Item interaction 2 (ie swapping weapon stance, swapping spell, etc) - C +Item interaction 3 (ie swapping weapon stance, swapping spell, etc) - V + +==Third person control scheme== +How to move - WASD, Space +Crouch - Ctrl +Sprint - Shift +Cursor interaction - E +Radial menu for various important submenus (ie inventories) - Tab +Quick switch equipped item (Radial Menu) - Hold 4 key radial menu +Item interaction 1 (ie swapping weapon stance, swapping spell, etc) - X +Item interaction 2 (ie swapping weapon stance, swapping spell, etc) - C +Item interaction 3 (ie swapping weapon stance, swapping spell, etc) - V + diff --git a/docs/src/highlevel-design/highleveldesignindex.md b/docs/src/highlevel-design/highleveldesignindex.md index d121e9ec..71b3738f 100644 --- a/docs/src/highlevel-design/highleveldesignindex.md +++ b/docs/src/highlevel-design/highleveldesignindex.md @@ -18,4 +18,5 @@ Discussion of, at a high game-design level, how everything should work and conne - @subpage locomotion - @subpage economicsindex - @subpage structuresandbuildings -- @subpage dungeonsindex \ No newline at end of file +- @subpage dungeonsindex +- @subpage controlDesign \ No newline at end of file diff --git a/docs/src/progress/currenttarget.md b/docs/src/progress/currenttarget.md index b600109c..8507bcef 100644 --- a/docs/src/progress/currenttarget.md +++ b/docs/src/progress/currenttarget.md @@ -8,3 +8,6 @@ review combat code (lifestate, damage calculation, etc) audio fx for everything fix rendering pipelines (black when looking at character from angle with item, shadows are not darker color, etc) + ++ bug fixes + fix client-attached models drawing on previous frame diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index f187e402..4e741b20 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -452,6 +452,9 @@ Attacker ai tree Switching between first and third person Devtools for updating first person attachment rotations +(07/26/2024) +Viewmodel equipped item rotates inverted to bone rotation + # TODO @@ -497,6 +500,9 @@ Fix being able to walk off far side of the world (ie in level editor) Grass System properly LOD - Have foliage dynamically time out cells to be reconsidered based on distance from player (if close, short cooldown, if far long cooldown) +Debug + - Draw all bones with orientations + Would be nice to be able to cut clients that stream their logs to my server Data Cleanup diff --git a/src/main/java/electrosphere/client/scene/ClientSceneWrapper.java b/src/main/java/electrosphere/client/scene/ClientSceneWrapper.java index 30ad425d..9e8bc3be 100644 --- a/src/main/java/electrosphere/client/scene/ClientSceneWrapper.java +++ b/src/main/java/electrosphere/client/scene/ClientSceneWrapper.java @@ -104,6 +104,17 @@ public class ClientSceneWrapper { return scene.getEntityFromId(mapServerToClientId(id)); } + /** + * Dumps the status of the network translation layer + */ + public void dumpTranslationLayerStatus(){ + LoggerInterface.loggerNetworking.WARNING("Client -> Server keys"); + LoggerInterface.loggerNetworking.WARNING(clientToServerIdMap.keySet() + ""); + LoggerInterface.loggerNetworking.WARNING("Server -> Client keys"); + LoggerInterface.loggerNetworking.WARNING(serverToClientIdMap.keySet() + ""); + LoggerInterface.loggerNetworking.WARNING("Debug here"); + } + /** * Gets the scene backing this client scene wrapper * @return The scene diff --git a/src/main/java/electrosphere/client/sim/ClientSimulation.java b/src/main/java/electrosphere/client/sim/ClientSimulation.java index ac5704db..645d71fa 100644 --- a/src/main/java/electrosphere/client/sim/ClientSimulation.java +++ b/src/main/java/electrosphere/client/sim/ClientSimulation.java @@ -1,6 +1,7 @@ package electrosphere.client.sim; import org.joml.Vector3d; +import org.joml.Vector3f; import electrosphere.client.fluid.manager.ClientFluidManager; import electrosphere.client.instancing.InstanceUpdater; @@ -11,7 +12,10 @@ import electrosphere.entity.Entity; import electrosphere.entity.EntityTags; import electrosphere.entity.EntityUtils; import electrosphere.entity.state.collidable.ClientCollidableTree; +import electrosphere.entity.state.equip.ClientEquipState; import electrosphere.entity.types.attach.AttachUtils; +import electrosphere.entity.types.camera.CameraEntityUtils; +import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.entity.types.item.ItemUtils; import electrosphere.entity.types.particle.ParticleUtils; import electrosphere.renderer.actor.Actor; @@ -133,6 +137,7 @@ public class ClientSimulation { Globals.clientSceneWrapper.destroyEntitiesOutsideSimRange(); InstanceUpdater.updateInstancedActorPriority(); Globals.cameraHandler.updateGlobalCamera(); + updateFirstPersonAttachments(); // updateCellManager(); Globals.profiler.endCpuSample(); } @@ -141,7 +146,7 @@ public class ClientSimulation { /** * Updates the skybox position to center on the player */ - void updateSkyboxPos(){ + private void updateSkyboxPos(){ Globals.profiler.beginCpuSample("updateSkyboxPos"); if(Globals.skybox != null && Globals.playerEntity != null){ EntityUtils.getPosition(Globals.skybox).set(EntityUtils.getPosition(Globals.playerEntity)); @@ -149,6 +154,24 @@ public class ClientSimulation { 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.playerCamera != null && Globals.playerEntity != null){ + Vector3f cameraEyeVec = CameraEntityUtils.getCameraEye(Globals.playerCamera); + CreatureUtils.setFacingVector(Globals.playerEntity, new Vector3d(cameraEyeVec.x,0,cameraEyeVec.z).normalize()); + //flush equipped item state + if(ClientEquipState.hasEquipState(Globals.playerEntity)){ + ClientEquipState equipState = ClientEquipState.getClientEquipState(Globals.playerEntity); + equipState.evaluatePlayerAttachments(); + } + } + Globals.profiler.endCpuSample(); + } + /** * Loads terrain that is in queue */ diff --git a/src/main/java/electrosphere/controls/CameraHandler.java b/src/main/java/electrosphere/controls/CameraHandler.java index 27bfc4b9..6f5f9cfe 100644 --- a/src/main/java/electrosphere/controls/CameraHandler.java +++ b/src/main/java/electrosphere/controls/CameraHandler.java @@ -127,7 +127,6 @@ public class CameraHandler { Vector3f radialOffset = CameraEntityUtils.getOrbitalCameraRadialOffset(Globals.playerCamera); Vector3f trueOffset = new Vector3f(radialOffset).mul(xFactor,1.0f,yFactor); CameraEntityUtils.setOrbitalCameraRadialOffset(Globals.playerCamera, trueOffset); - // float cam_Player_Orbit_Magnitude = CameraEntityUtils.getCameraOrbitRadius(Globals.playerCamera); cameraRotationVector.mul(CameraEntityUtils.getOrbitalCameraDistance(Globals.playerCamera)); CameraEntityUtils.setCameraEye(Globals.playerCamera, cameraRotationVector); diff --git a/src/main/java/electrosphere/controls/ControlHandler.java b/src/main/java/electrosphere/controls/ControlHandler.java index 8419e019..4e33e6b9 100644 --- a/src/main/java/electrosphere/controls/ControlHandler.java +++ b/src/main/java/electrosphere/controls/ControlHandler.java @@ -92,7 +92,6 @@ import electrosphere.entity.state.movement.SprintTree; import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree; import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree.MovementRelativeFacing; import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree.MovementTreeState; -import electrosphere.entity.types.attach.AttachUtils; import electrosphere.entity.types.camera.CameraEntityUtils; import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.logger.LoggerInterface; diff --git a/src/main/java/electrosphere/entity/state/equip/ClientEquipState.java b/src/main/java/electrosphere/entity/state/equip/ClientEquipState.java index 6f519ac6..57cbd70c 100644 --- a/src/main/java/electrosphere/entity/state/equip/ClientEquipState.java +++ b/src/main/java/electrosphere/entity/state/equip/ClientEquipState.java @@ -340,15 +340,23 @@ public class ClientEquipState implements BehaviorTree { for(String occupiedPoint : this.getEquippedPoints()){ EquipPoint point = this.getEquipPoint(occupiedPoint); Entity toEquip = this.equipMap.get(point.getEquipPointId()); - AttachUtils.clientDetatchEntityFromEntityAtBone(Globals.firstPersonEntity, toEquip); - AttachUtils.clientAttachEntityToEntityAtBone(Globals.playerEntity, toEquip, point.getBone(), AttachUtils.getEquipPointRotationOffset(point.getOffsetRotationThirdPerson())); + if(AttachUtils.getParent(toEquip) != Globals.playerEntity){ + AttachUtils.clientDetatchEntityFromEntityAtBone(Globals.firstPersonEntity, toEquip); + AttachUtils.clientAttachEntityToEntityAtBone(Globals.playerEntity, toEquip, point.getBone(), AttachUtils.getEquipPointRotationOffset(point.getOffsetRotationThirdPerson())); + } else { + AttachUtils.clientUpdateEntityTransforms(toEquip, Globals.playerEntity); + } } } else { for(String occupiedPoint : this.getEquippedPoints()){ EquipPoint point = this.getEquipPoint(occupiedPoint); Entity toEquip = this.equipMap.get(point.getEquipPointId()); - AttachUtils.clientDetatchEntityFromEntityAtBone(Globals.playerEntity, toEquip); - AttachUtils.clientAttachEntityToEntityAtBone(Globals.firstPersonEntity, toEquip, point.getFirstPersonBone(), AttachUtils.getEquipPointRotationOffset(point.getOffsetRotationFirstPerson())); + if(AttachUtils.getParent(toEquip) != Globals.firstPersonEntity){ + AttachUtils.clientDetatchEntityFromEntityAtBone(Globals.playerEntity, toEquip); + AttachUtils.clientAttachEntityToEntityAtBone(Globals.firstPersonEntity, toEquip, point.getFirstPersonBone(), AttachUtils.getEquipPointRotationOffset(point.getOffsetRotationFirstPerson())); + } else { + AttachUtils.clientUpdateEntityTransforms(toEquip, Globals.firstPersonEntity); + } } } } diff --git a/src/main/java/electrosphere/entity/types/attach/AttachUtils.java b/src/main/java/electrosphere/entity/types/attach/AttachUtils.java index 69f5e010..9369a1d6 100644 --- a/src/main/java/electrosphere/entity/types/attach/AttachUtils.java +++ b/src/main/java/electrosphere/entity/types/attach/AttachUtils.java @@ -182,34 +182,7 @@ public class AttachUtils { for(Entity currentEntity : Globals.clientSceneWrapper.getScene().getEntitiesWithTag(EntityTags.BONE_ATTACHED)){ Entity parent; if((parent = (Entity)currentEntity.getData(EntityDataStrings.ATTACH_PARENT))!=null){ - String targetBone; - if((targetBone = (String)currentEntity.getData(EntityDataStrings.ATTACH_TARGET_BONE))!=null){ - Actor parentActor = EntityUtils.getActor(parent); - //get offset rotation - Quaterniond offsetRotation = getRotationOffset(currentEntity); - //transform bone space - Vector3d position = new Vector3d(parentActor.getBonePosition(targetBone)); - position = position.mul(((Vector3f)EntityUtils.getScale(parent))); - Quaterniond rotation = EntityUtils.getRotation(parent); - position = position.rotate(new Quaterniond(rotation.x,rotation.y,rotation.z,rotation.w)); - //transform worldspace - position.add(new Vector3d(EntityUtils.getPosition(parent))); - //set - EntityUtils.getPosition(currentEntity).set(position); - //set rotation -// Quaternionf rotation = parentActor.getBoneRotation(targetBone); -// EntityUtils.getRotation(currentEntity).set(rotation).normalize(); - Vector3d facingAngle = CreatureUtils.getFacingVector(parent); - if(facingAngle == null){ - facingAngle = MathUtils.getOriginVector(); - } - //calculate rotation of model - EntityUtils.getRotation(currentEntity) - .rotationTo(MathUtils.getOriginVector(), new Vector3d(facingAngle.x,facingAngle.y,facingAngle.z)) - .mul(parentActor.getBoneRotation(targetBone)) - .mul(offsetRotation) - .normalize(); - } + clientUpdateEntityTransforms(currentEntity,parent); } else if(currentEntity.getData(EntityDataStrings.ATTACH_TARGET_BASE)!=null){ Vector3d positionOffset = getAttachPositionOffset(currentEntity); Vector3d parentPosition = EntityUtils.getPosition(parent); @@ -219,6 +192,47 @@ public class AttachUtils { Globals.profiler.endCpuSample(); } + /** + * Updates the spatial data for the attached entity + * @param child The entity that is attached to a parent + * @param parent The parent entity that has a child attached to it + */ + public static void clientUpdateEntityTransforms(Entity child, Entity parent){ + String targetBone; + if((targetBone = (String)child.getData(EntityDataStrings.ATTACH_TARGET_BONE))!=null){ + Actor parentActor = EntityUtils.getActor(parent); + //get offset rotation + Quaterniond offsetRotation = getRotationOffset(child); + //transform bone space + Vector3d position = new Vector3d(parentActor.getBonePosition(targetBone)); + position = position.mul(((Vector3f)EntityUtils.getScale(parent))); + Quaterniond rotation = EntityUtils.getRotation(parent); + position = position.rotate(new Quaterniond(rotation.x,rotation.y,rotation.z,rotation.w)); + //transform worldspace + position.add(new Vector3d(EntityUtils.getPosition(parent))); + //set + EntityUtils.getPosition(child).set(position); + //set rotation +// Quaternionf rotation = parentActor.getBoneRotation(targetBone); +// EntityUtils.getRotation(currentEntity).set(rotation).normalize(); + Vector3d facingAngle; + if(parent == Globals.firstPersonEntity){ + facingAngle = CreatureUtils.getFacingVector(Globals.playerEntity); + } else { + facingAngle = CreatureUtils.getFacingVector(parent); + } + if(facingAngle == null){ + facingAngle = MathUtils.getOriginVector(); + } + //calculate rotation of model + EntityUtils.getRotation(child) + .rotationTo(MathUtils.getOriginVector(), new Vector3d(facingAngle.x,facingAngle.y,facingAngle.z)) + .mul(parentActor.getBoneRotation(targetBone)) + .mul(offsetRotation) + .normalize(); + } + } + /** * Updates entities that aren't attached to a bone directly */ diff --git a/src/main/java/electrosphere/menu/debug/ImGuiEntityMacros.java b/src/main/java/electrosphere/menu/debug/ImGuiEntityMacros.java index 91534477..ce24cfbb 100644 --- a/src/main/java/electrosphere/menu/debug/ImGuiEntityMacros.java +++ b/src/main/java/electrosphere/menu/debug/ImGuiEntityMacros.java @@ -71,6 +71,7 @@ public class ImGuiEntityMacros { continue; } ImGui.beginGroup(); + ImGui.pushID(entity.getId()); ImGui.text("Id: " + entity.getId() + " (" + getEntityName(entity) + ")"); if(CreatureUtils.isCreature(entity)){ if(EntityUtils.getActor(entity) != null){ @@ -86,6 +87,7 @@ public class ImGuiEntityMacros { } } } + ImGui.popID(); ImGui.endGroup(); } } diff --git a/src/main/java/electrosphere/net/synchronization/ClientSynchronizationManager.java b/src/main/java/electrosphere/net/synchronization/ClientSynchronizationManager.java index f2a7f616..802cc1f5 100644 --- a/src/main/java/electrosphere/net/synchronization/ClientSynchronizationManager.java +++ b/src/main/java/electrosphere/net/synchronization/ClientSynchronizationManager.java @@ -76,7 +76,8 @@ public class ClientSynchronizationManager { "Client received synchronization packet for entity that does not exists on client!\n" + "Entity id in network message: " + message.getentityId() ; - LoggerInterface.loggerNetworking.ERROR(new IllegalStateException(errorMessage)); + Globals.clientSceneWrapper.dumpTranslationLayerStatus(); + throw new IllegalStateException(errorMessage); } } for(SynchronizationMessage message : messagesToClear){