Animations neu importiert, SLF4J-Format-Bugs gefixt, stripRootXZ als tote Methode für später

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-21 09:58:15 +02:00
parent c8f1dd9432
commit 07a4c6a323
8 changed files with 59 additions and 2 deletions

View File

@@ -45,6 +45,7 @@ import java.nio.ByteBuffer;
import java.nio.file.*;
import java.util.Comparator;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

View File

@@ -259,7 +259,7 @@ public class AnimationLibrary extends BaseAppState {
/** 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={:.3f}s tracks={}",
log.info("[ClipDump] '{}' length={}s tracks={}",
clip.getName(), clip.getLength(), clip.getTracks().length);
int tracksWithTranslation = 0;
for (com.jme3.anim.AnimTrack<?> t : clip.getTracks()) {
@@ -272,7 +272,7 @@ public class AnimationLibrary extends BaseAppState {
tracksWithTranslation++;
com.jme3.math.Vector3f t0 = tt.getTranslations()[0];
com.jme3.math.Vector3f tN = tt.getTranslations()[tt.getTranslations().length - 1];
log.info("[ClipDump] TRANSLATE '{}' frames={} start=({:.3f},{:.3f},{:.3f}) end=({:.3f},{:.3f},{:.3f}) deltaY={:.4f}",
log.info("[ClipDump] TRANSLATE '{}' frames={} start=({},{},{}) end=({},{},{}) deltaY={}",
target, tt.getTranslations().length,
t0.x, t0.y, t0.z, tN.x, tN.y, tN.z, tN.y - t0.y);
} else {

View File

@@ -451,4 +451,60 @@ public final class RetargetingSystem {
}
return null;
}
/**
* Entfernt Root-Motion aus dem Wurzel-Knochen (Tiefe 0) des Clips.
*
* Koordinatensystem dieses Rigs (Blender Z-Up-Export):
* bone-local X → Welt-X (seitliche Drift) → auf Frame-0-Wert fixieren
* bone-local Y → Welt-Z (Vorwärtsbewegung) → auf Frame-0-Wert fixieren
* bone-local Z → Welt-Y (Höhe / sit_down / Jump) → NIEMALS anfassen
*
* Erstellt neue TransformTrack-Objekte, damit BinaryExporter die Änderung serialisiert.
*/
public static AnimClip stripRootXZTranslations(AnimClip clip, Armature armature) {
if (clip == null || armature == null) return clip;
log.info("[Strip] Clip '{}' {} Tracks", clip.getName(), clip.getTracks().length);
List<AnimTrack<?>> newTracks = new ArrayList<>();
for (AnimTrack<?> track : clip.getTracks()) {
if (!(track instanceof TransformTrack tt) || !(tt.getTarget() instanceof Joint j)) {
newTracks.add(track);
continue;
}
if (jointDepth(j) != 0) { newTracks.add(track); continue; }
Vector3f[] translations = tt.getTranslations();
if (translations == null || translations.length == 0) { newTracks.add(track); continue; }
// Ersten Frame als Ruheposition verwenden (verhindert Mesh-Sprünge)
float fx = translations[0].x;
float fy = translations[0].y;
float maxDriftX = 0, maxDriftY = 0, maxDriftZ = 0;
for (Vector3f v : translations) {
maxDriftX = Math.max(maxDriftX, Math.abs(v.x - fx));
maxDriftY = Math.max(maxDriftY, Math.abs(v.y - fy));
maxDriftZ = Math.max(maxDriftZ, Math.abs(v.z - translations[0].z));
}
log.info("[Strip] '{}' depth=0 driftX={} driftY={} driftZ(Höhe,unberührt)={}",
j.getName(), maxDriftX, maxDriftY, maxDriftZ);
Vector3f[] stripped = new Vector3f[translations.length];
for (int i = 0; i < translations.length; i++) {
// X und Y auf Frame-0 fixieren; Z (= Welt-Höhe) komplett frei lassen
stripped[i] = new Vector3f(fx, fy, translations[i].z);
}
newTracks.add(new TransformTrack(j, tt.getTimes(), stripped, tt.getRotations(), tt.getScales()));
log.info("[Strip] ✓ '{}' X+Y fixiert auf ({},{}), Z frei", j.getName(), fx, fy);
}
AnimClip result = new AnimClip(clip.getName());
result.setTracks(newTracks.toArray(new AnimTrack[0]));
return result;
}
private static int jointDepth(Joint j) {
int d = 0;
Joint p = j.getParent();
while (p != null) { d++; p = p.getParent(); }
return d;
}
}