From 8e48250013ea1a7f3105c766a874d5248c03b6b3 Mon Sep 17 00:00:00 2001 From: austin Date: Mon, 12 Aug 2024 09:41:00 -0400 Subject: [PATCH] server anim time scale fix --- .../creatures/creatureideas.md | 10 ++- docs/src/progress/renderertodo.md | 3 + .../menu/debug/ImGuiEntityMacros.java | 83 ++++++++++++++++++ .../server/poseactor/PoseActor.java | 86 +++++++++++++++---- .../server/poseactor/PoseModel.java | 23 +++++ .../server/simulation/MicroSimulation.java | 3 +- 6 files changed, 190 insertions(+), 18 deletions(-) diff --git a/docs/src/highlevel-design/creatures/creatureideas.md b/docs/src/highlevel-design/creatures/creatureideas.md index 5dfc2456..522550f9 100644 --- a/docs/src/highlevel-design/creatures/creatureideas.md +++ b/docs/src/highlevel-design/creatures/creatureideas.md @@ -1,4 +1,12 @@ @page creatureideas Creature Ideas Leaf Sheep -Jellyfish \ No newline at end of file +Jellyfish +Mushroom-men + + +Ogres +Troll-men +Goblins +Demons/Devils + diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index b05777ef..202e5f97 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -537,6 +537,9 @@ Ability to serialize/deserialize a creature with equipped items Sending initial synchronized state on player connect to chunk Pass at client-server physics synchronization +(08/12/2024) +Fix server animation playing at reduced timescale + # TODO diff --git a/src/main/java/electrosphere/menu/debug/ImGuiEntityMacros.java b/src/main/java/electrosphere/menu/debug/ImGuiEntityMacros.java index 42e09006..e2cf86f3 100644 --- a/src/main/java/electrosphere/menu/debug/ImGuiEntityMacros.java +++ b/src/main/java/electrosphere/menu/debug/ImGuiEntityMacros.java @@ -33,6 +33,8 @@ import electrosphere.renderer.model.Model; import electrosphere.renderer.ui.imgui.ImGuiWindow; import electrosphere.renderer.ui.imgui.ImGuiWindow.ImGuiWindowCallback; import electrosphere.server.datacell.utils.EntityLookupUtils; +import electrosphere.server.poseactor.PoseActor; +import electrosphere.server.poseactor.PoseModel; import imgui.ImGui; /** @@ -50,6 +52,7 @@ public class ImGuiEntityMacros { //tree node values private static boolean showActorTab = false; //show the actor tab + private static boolean showPoseActorTab = false; //show the pose actor tab private static boolean showEquipStateTab = false; //actor details private static boolean showFirstPersonTab = false; //first person tab private static boolean showLinkedEntitiesTab = false;//show linked entities @@ -111,6 +114,9 @@ public class ImGuiEntityMacros { if(EntityUtils.getActor(detailViewEntity) != null && ImGui.checkbox("Actor Details", showActorTab)){ showActorTab = !showActorTab; } + if(EntityUtils.getPoseActor(detailViewEntity) != null && ImGui.checkbox("Pose Actor Details", showPoseActorTab)){ + showPoseActorTab = !showPoseActorTab; + } if(ClientEquipState.hasEquipState(detailViewEntity) && ImGui.checkbox("Equip State", showEquipStateTab)){ showEquipStateTab = !showEquipStateTab; } @@ -133,6 +139,7 @@ public class ImGuiEntityMacros { } ImGui.nextColumn(); drawActorView(); + drawPoseActor(); drawEquipState(); drawFirstPersonView(); drawLinkedEntities(); @@ -242,6 +249,82 @@ public class ImGuiEntityMacros { } } + /** + * Draws pose actor + */ + protected static void drawPoseActor(){ + if(showPoseActorTab && ImGui.collapsingHeader("Pose Actor Details")){ + ImGui.indent(); + if(detailViewEntity != null && EntityUtils.getPoseActor(detailViewEntity) != null){ + PoseActor poseActor = EntityUtils.getPoseActor(detailViewEntity); + + //animation queue + if(ImGui.collapsingHeader("Animation Queue")){ + Set animationQueue = poseActor.getAnimationQueue(); + for(ActorAnimationMask mask : animationQueue){ + ImGui.text(mask.getAnimationName() + " - " + mask.getPriority()); + ImGui.text(mask.getDuration() + " " + mask.getTime()); + } + } + + //bone values + if(ImGui.collapsingHeader("Bone Values")){ + for(Bone bone : poseActor.getBoneValues()){ + ImGui.text(bone.boneID); + ImGui.text("Position: " + poseActor.getBonePosition(bone.boneID)); + ImGui.text("Rotation: " + poseActor.getBoneRotation(bone.boneID)); + ImGui.text(bone.getFinalTransform() + ""); + } + } + + //Draws all the bones in the world + if(ImGui.button("Draw Bones")){ + Globals.renderingEngine.getDebugContentPipeline().getDebugBonesPipeline().setEntity(detailViewEntity); + } + + //Browsable list of all animations with their data + if(ImGui.collapsingHeader("Animation Channel Data")){ + Model model = Globals.assetManager.fetchModel(poseActor.getModelPath()); + ImGui.indent(); + for(Animation animation : model.getAnimations()){ + if(ImGui.collapsingHeader(animation.name)){ + for(AnimChannel channel : animation.channels){ + ImGui.pushID(channel.getNodeID()); + if(ImGui.button("Fully describe")){ + channel.fullDescribeChannel(); + } + ImGui.text("=" + channel.getNodeID() + "="); + ImGui.text("" + channel.getCurrentPosition()); + ImGui.text("" + channel.getCurrentRotation()); + ImGui.text("" + channel.getCurrentScale()); + ImGui.popID(); + } + } + } + ImGui.unindent(); + } + + //print data macros + if(ImGui.collapsingHeader("Print Data")){ + //print bone values + if(ImGui.button("Print current bone values")){ + for(Bone bone : poseActor.getBoneValues()){ + LoggerInterface.loggerRenderer.DEBUG(bone.boneID); + LoggerInterface.loggerRenderer.DEBUG("" + bone.getFinalTransform()); + } + } + + //print animation keys + if(ImGui.button("Print animation keys")){ + PoseModel model = Globals.assetManager.fetchPoseModel(poseActor.getModelPath()); + model.describeAllAnimations(); + } + } + } + ImGui.unindent(); + } + } + /** * First person data */ diff --git a/src/main/java/electrosphere/server/poseactor/PoseActor.java b/src/main/java/electrosphere/server/poseactor/PoseActor.java index 956cf2f9..c51fb9d0 100644 --- a/src/main/java/electrosphere/server/poseactor/PoseActor.java +++ b/src/main/java/electrosphere/server/poseactor/PoseActor.java @@ -4,7 +4,8 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.PriorityQueue; +import java.util.Set; +import java.util.TreeSet; import org.joml.AxisAngle4f; import org.joml.Matrix4d; @@ -32,7 +33,7 @@ public class PoseActor { //scalar on the speed of animation playback float animationScalar = 1.0f; //priority queue of animations to play. Allows masking a higher priority animation over a lower priority one. - PriorityQueue animationQueue = new PriorityQueue(); + Set animationQueue = new TreeSet(); //bone rotation map. Used to apply rotator functionality to bones (think hair, cloth, and camera rotation on looking) Map boneRotators = new HashMap(); //static morph used to apply an initial, static modification to the layout of bones in the pose model @@ -332,6 +333,14 @@ public class PoseActor { } } + /** + * Gets the animation queue + * @return The animation queue + */ + public Set getAnimationQueue(){ + return animationQueue; + } + /** * Calculates all node transforms for the PoseModel based on bone rotators and the static morph of this PoseActor * @param model The PoseModel to calculate transforms for @@ -396,6 +405,14 @@ public class PoseActor { return rVal; } + /** + * Gets the model path associated with the pose actor + * @return The model path + */ + public String getModelPath(){ + return modelPath; + } + /** * Gets the position of a bone currently * @param boneName @@ -407,23 +424,62 @@ public class PoseActor { if(model != null){ applyAnimationMasks(model); calculateNodeTransforms(model); - // if(animation != null){ - // model.playAnimation(animation); - // model.incrementTime(animationTime); - // model.updateNodeTransform(); - Bone currentBone = model.boneMap.get(boneName); - if(currentBone != null){ - Vector4d result = new Matrix4d(currentBone.getFinalTransform()).transform(currentBone.getMOffset().invert().transform(new Vector4d(rVal.x,rVal.y,rVal.z,1))); -// currentBone.inverseBindPoseMatrix - rVal.x = (float)result.x; - rVal.y = (float)result.y; - rVal.z = (float)result.z; - } - // } + Bone currentBone = model.boneMap.get(boneName); + if(currentBone != null){ + Vector4d result = new Matrix4d(currentBone.getFinalTransform()).transform(currentBone.getMOffset().invert().transform(new Vector4d(rVal.x,rVal.y,rVal.z,1))); + rVal.x = (float)result.x; + rVal.y = (float)result.y; + rVal.z = (float)result.z; + } else { + String message = "Trying to get position of bone that does not exist on model!\n" + + "boneName: " + boneName; + throw new IllegalArgumentException(message); + } + } + if(!Double.isFinite(rVal.x)){ + throw new IllegalStateException("Bone position that is not finite!"); } return rVal; } + /** + * Gets the transform of a given bone + * @param boneName The name of the bone + * @return The transform + */ + public Matrix4d getBoneTransform(String boneName){ + Matrix4d rVal = new Matrix4d(); + PoseModel model = Globals.assetManager.fetchPoseModel(modelPath); + if(model != null){ + applyAnimationMasks(model); + calculateNodeTransforms(model); + Bone currentBone = model.getBoneMap().get(boneName); + if(currentBone != null){ + rVal = currentBone.getFinalTransform(); + } else { + throw new IllegalArgumentException("Trying to get rotation of bone that does not exist on model!"); + } + } + if(!Double.isFinite(rVal.m00())){ + throw new IllegalStateException("Bone rotation that is not finite!"); + } + return rVal; + } + + /** + * Gets the list of all bones + * @return the list of all bones + */ + public List getBoneValues(){ + PoseModel model = Globals.assetManager.fetchPoseModel(modelPath); + if(model != null){ + applyAnimationMasks(model); + calculateNodeTransforms(model); + return model.getBones(); + } + return null; + } + /** * Sets the bone groups * @param boneGroups The bone groups diff --git a/src/main/java/electrosphere/server/poseactor/PoseModel.java b/src/main/java/electrosphere/server/poseactor/PoseModel.java index d50238dd..8935dea9 100644 --- a/src/main/java/electrosphere/server/poseactor/PoseModel.java +++ b/src/main/java/electrosphere/server/poseactor/PoseModel.java @@ -161,6 +161,14 @@ public class PoseModel { } } + /** + * Gets the map of bone name -> bone + * @return the map + */ + public Map getBoneMap(){ + return boneMap; + } + /** * Internal recursive method behind the public updateNodeTransform * @param boneRotators The bone rotators @@ -211,6 +219,21 @@ public class PoseModel { return animMap.get(animName); } + /** + * Logs all animations for a given model + */ + public void describeAllAnimations(){ + if(animations.size() > 0){ + LoggerInterface.loggerRenderer.DEBUG("====================="); + LoggerInterface.loggerRenderer.DEBUG(animations.size() + " animations available in model!"); + Iterator animIterator = animations.iterator(); + while(animIterator.hasNext()){ + Animation currentAnim = animIterator.next(); + currentAnim.describeAnimation(); + } + } + } + /** * Gets the list of bones in this pose model * @return The list of bones diff --git a/src/main/java/electrosphere/server/simulation/MicroSimulation.java b/src/main/java/electrosphere/server/simulation/MicroSimulation.java index c3a97245..1688d441 100644 --- a/src/main/java/electrosphere/server/simulation/MicroSimulation.java +++ b/src/main/java/electrosphere/server/simulation/MicroSimulation.java @@ -5,7 +5,6 @@ import electrosphere.entity.types.attach.AttachUtils; import java.util.Set; import electrosphere.engine.Globals; -import electrosphere.engine.Main; import electrosphere.entity.Entity; import electrosphere.entity.EntityTags; import electrosphere.entity.EntityUtils; @@ -42,7 +41,7 @@ public class MicroSimulation { PoseActor currentPoseActor = EntityUtils.getPoseActor(currentEntity); //increment animations if(currentPoseActor.isPlayingAnimation()){ - currentPoseActor.incrementAnimationTime(Globals.timekeeper.getSimFrameTime() / Main.targetFrameRate); + currentPoseActor.incrementAnimationTime(Globals.timekeeper.getSimFrameTime()); } } }