From 52c1fb1fe80d6921babae8d02a1ac0b916ea85ba Mon Sep 17 00:00:00 2001 From: Mario Date: Sun, 21 Jun 2026 23:01:29 +0200 Subject: [PATCH] =?UTF-8?q?Revert=20Variante=20B=20=E2=80=93=20zur=C3=BCck?= =?UTF-8?q?=20zu=20snapRootBoneXZ=20mit=20frame0-XZ?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RootDriftCompensatorControl entfernt, AnimationLibrary auf Stand 7dcf16f zurückgesetzt (snapRootBoneXZ friert XZ auf frame0-Wert ein, Early-Return bleibt, kein Clip-Clearing in applyAllTo). Co-Authored-By: Claude Sonnet 4.6 --- .../game/animation/AnimationLibrary.java | 1 + .../RootDriftCompensatorControl.java | 103 ------------------ .../java/de/blight/game/scene/WorldScene.java | 4 - 3 files changed, 1 insertion(+), 107 deletions(-) delete mode 100644 blight-game/src/main/java/de/blight/game/animation/RootDriftCompensatorControl.java diff --git a/blight-game/src/main/java/de/blight/game/animation/AnimationLibrary.java b/blight-game/src/main/java/de/blight/game/animation/AnimationLibrary.java index 844ff31..0155f1a 100644 --- a/blight-game/src/main/java/de/blight/game/animation/AnimationLibrary.java +++ b/blight-game/src/main/java/de/blight/game/animation/AnimationLibrary.java @@ -117,6 +117,7 @@ public class AnimationLibrary extends BaseAppState { return false; } + target = snapRootBoneXZ(target, sc.getArmature()); ac.addAnimClip(target); log.info("[AnimLib] Clip '{}' zu AnimComposer von '{}' hinzugefügt", clipName, model.getName()); if (clipName.equals("sit_down")) { diff --git a/blight-game/src/main/java/de/blight/game/animation/RootDriftCompensatorControl.java b/blight-game/src/main/java/de/blight/game/animation/RootDriftCompensatorControl.java deleted file mode 100644 index 3d8c7fe..0000000 --- a/blight-game/src/main/java/de/blight/game/animation/RootDriftCompensatorControl.java +++ /dev/null @@ -1,103 +0,0 @@ -package de.blight.game.animation; - -import com.jme3.anim.Armature; -import com.jme3.anim.Joint; -import com.jme3.anim.SkinningControl; -import com.jme3.math.Vector3f; -import com.jme3.renderer.RenderManager; -import com.jme3.renderer.ViewPort; -import com.jme3.scene.control.AbstractControl; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Verhindert visuellen Root-Motion-Drift, ohne Animations-Daten zu verändern. - * - * Dieses Control muss auf dem selben Spatial wie AnimComposer liegen und NACH - * AnimComposer hinzugefügt werden. Die Update-Reihenfolge ist dann: - * 1. AnimComposer – setzt Joint-Locals und propagiert Model-Transforms - * 2. RootDriftCompensator – liest aktuellen Frame-Knochen-XZ, korrigiert Spatial-XZ - * 3. SkinningControl (Kind) – skinnt mit korrekten Model-Transforms - * - * Mechanismus: Wenn der "Hüft-Knochen" (tiefster sinnvoller Root) im Model-Space - * in XZ driftet, verschiebt dieses Control den Spatial in die entgegengesetzte - * Richtung. Netto bleibt das Mesh immer über der Physik-Kapsel. - * Y (Höhenachse für sit_down/Jump) wird NICHT angefasst – applyBoneAnchorOffset - * in PlayerInputControl ist weiterhin allein zuständig. - */ -public class RootDriftCompensatorControl extends AbstractControl { - - private static final Logger log = LoggerFactory.getLogger(RootDriftCompensatorControl.class); - - private final float offsetY; // Y-Versatz des Modells (Füße auf Kapsel-Unterkante) - private Armature armature; - private Joint locomotionRoot; - - /** @param offsetY Wert aus {@code loaded.setLocalTranslation(0, offsetY, 0)} */ - public RootDriftCompensatorControl(float offsetY) { - this.offsetY = offsetY; - } - - @Override - protected void controlUpdate(float tpf) { - if (!ensureArmature()) return; - - Joint root = getOrFindLocomotionRoot(); - if (root == null) return; - - // Model-Transform enthält die akkumulierte Position des Knochens im Model-Space. - // AnimComposer hat diesen bereits für den aktuellen Frame gesetzt (läuft vor uns). - Vector3f modelXZ = root.getModelTransform().getTranslation(); - float scale = spatial.getLocalScale().x; // uniform scale - - // Spatial (= das geladene Modell) in die entgegengesetzte Richtung verschieben: - // mesh_world_X = capsule_X + spatial_local_X + bone_model_X * scale - // = capsule_X + (−bone_model_X*scale) + bone_model_X*scale = capsule_X ✓ - spatial.setLocalTranslation(-modelXZ.x * scale, offsetY, -modelXZ.z * scale); - } - - @Override - protected void controlRender(RenderManager rm, ViewPort vp) {} - - private boolean ensureArmature() { - if (armature != null) return true; - SkinningControl sc = RetargetingSystem.findSkinningControl(spatial); - if (sc == null) return false; - armature = sc.getArmature(); - return armature != null; - } - - /** - * Sucht den flachsten Joint, dessen Model-Transform-XZ von 0 abweicht. - * Dieser Joint ist der "Hüft-Knochen", der den Gesamtdrift verursacht. - * Wird gecacht sobald gefunden; bis dahin jeden Frame erneut versucht. - */ - private Joint getOrFindLocomotionRoot() { - if (locomotionRoot != null) return locomotionRoot; - - int minDepth = Integer.MAX_VALUE; - for (int i = 0; i < armature.getJointCount(); i++) { - Joint j = armature.getJoint(i); - Vector3f mt = j.getModelTransform().getTranslation(); - if (Math.abs(mt.x) > 0.005f || Math.abs(mt.z) > 0.005f) { - int d = depth(j); - if (d < minDepth) { - minDepth = d; - locomotionRoot = j; - } - } - } - if (locomotionRoot != null) { - log.info("[RootDrift] Locomotion-Root: '{}' Tiefe={}", - locomotionRoot.getName(), minDepth); - } - return locomotionRoot; - } - - private static int depth(Joint j) { - int d = 0; - Joint p = j.getParent(); - while (p != null) { d++; p = p.getParent(); } - return d; - } -} diff --git a/blight-game/src/main/java/de/blight/game/scene/WorldScene.java b/blight-game/src/main/java/de/blight/game/scene/WorldScene.java index 56a62f3..9d6cd91 100644 --- a/blight-game/src/main/java/de/blight/game/scene/WorldScene.java +++ b/blight-game/src/main/java/de/blight/game/scene/WorldScene.java @@ -338,10 +338,6 @@ public class WorldScene extends BaseAppState { log.info("[WorldScene] Kein Scale möglich (height={}), Fallback-Offset", modelHeight); } - // RootDriftCompensator nach dem Laden hinzufügen: läuft nach AnimComposer - // (gleicher Spatial, später hinzugefügt) und korrigiert XZ-Drift des Meshes. - loaded.addControl(new de.blight.game.animation.RootDriftCompensatorControl(offsetY)); - // rotationNode als Drehpunkt (CharacterControl überschreibt wrapper-Rotation jeden Frame) Node rotNode = new Node("charRot"); loaded.setLocalTranslation(0, offsetY, 0);