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 dae3098..f6ba3ed 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 @@ -9,6 +9,8 @@ import com.jme3.scene.Spatial; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.jme3.math.Vector3f; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -115,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")) { @@ -257,6 +260,46 @@ public class AnimationLibrary extends BaseAppState { return null; } + /** + * Friert X und Z des Root-Knochens (kein Eltern-Joint) auf den Wert von Frame 0 ein. + * Y (Höhenachse, JME3 Y-Up) bleibt vollständig erhalten — sit_down / Jump / Bounce laufen korrekt. + * Erstellt einen neuen in-memory-Clip; J3O-Dateien bleiben unverändert. + */ + private static AnimClip snapRootBoneXZ(AnimClip clip, Armature armature) { + if (clip == null || armature == null) return clip; + List> newTracks = new ArrayList<>(); + boolean modified = false; + for (AnimTrack track : clip.getTracks()) { + if (!(track instanceof TransformTrack tt) || !(tt.getTarget() instanceof Joint j)) { + newTracks.add(track); + continue; + } + if (j.getParent() != null) { + newTracks.add(track); + continue; + } + Vector3f[] translations = tt.getTranslations(); + if (translations == null || translations.length == 0) { + newTracks.add(track); + continue; + } + float f0x = translations[0].x; + float f0z = translations[0].z; + Vector3f[] snapped = new Vector3f[translations.length]; + for (int i = 0; i < translations.length; i++) { + snapped[i] = new Vector3f(f0x, translations[i].y, f0z); + } + newTracks.add(new TransformTrack(j, tt.getTimes(), snapped, tt.getRotations(), tt.getScales())); + modified = true; + log.debug("[AnimLib] '{}': Root-Joint '{}' XZ={},{} eingefroren, Y frei", + clip.getName(), j.getName(), f0x, f0z); + } + if (!modified) return clip; + AnimClip result = new AnimClip(clip.getName()); + result.setTracks(newTracks.toArray(new AnimTrack[0])); + return result; + } + /** Loggt alle Tracks eines Clips: Bone-Name, hat Translation (T), Rotation (R), Scale (S). */ private void dumpClipTracks(com.jme3.anim.AnimClip clip) { log.info("[ClipDump] '{}' length={}s tracks={}",