snapRootBoneXZ: flachsten Joint mit Translations snappen statt nur Tiefe-0
Rigs wo Hips auf Tiefe 1 liegt (Kind des Root) wurden bisher nicht gesnappt weil nur Tiefe-0-Joints gefunden wurden. Jetzt wird die kleinste Tiefe unter allen Joints mit Translation-Track gesucht und nur diese Ebene eingefroren. Passt zu beiden Rig-Strukturen (Root-Translation und Hips-Translation). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -261,12 +261,29 @@ public class AnimationLibrary extends BaseAppState {
|
||||
}
|
||||
|
||||
/**
|
||||
* Friert X und Z des Root-Knochens (kein Eltern-Joint) auf den Wert von Frame 0 ein.
|
||||
* Friert X und Z des "Hüft-Knochens" auf den Wert von Frame 0 ein.
|
||||
* Y (Höhenachse, JME3 Y-Up) bleibt vollständig erhalten — sit_down / Jump / Bounce laufen korrekt.
|
||||
*
|
||||
* Strategie: findet die kleinste Tiefe unter allen Joints die einen Translation-Track haben.
|
||||
* Bei Rigs wo Root (Tiefe 0) selbst Translations hat, wird Root gesnappt.
|
||||
* Bei Rigs wo Hips (Tiefe 1) die erste Ebene mit Translations ist (Root hat nur Rotation),
|
||||
* wird Hips gesnappt. So passt der Snap zu beiden Rig-Strukturen.
|
||||
* 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;
|
||||
|
||||
// Tiefe des flachsten Joints mit Translation-Track bestimmen
|
||||
int minDepth = Integer.MAX_VALUE;
|
||||
for (AnimTrack<?> track : clip.getTracks()) {
|
||||
if (!(track instanceof TransformTrack tt) || !(tt.getTarget() instanceof Joint j)) continue;
|
||||
Vector3f[] tr = tt.getTranslations();
|
||||
if (tr != null && tr.length > 0) {
|
||||
minDepth = Math.min(minDepth, jointDepth(j));
|
||||
}
|
||||
}
|
||||
if (minDepth == Integer.MAX_VALUE) return clip; // keine Translation-Tracks vorhanden
|
||||
|
||||
List<AnimTrack<?>> newTracks = new ArrayList<>();
|
||||
boolean modified = false;
|
||||
for (AnimTrack<?> track : clip.getTracks()) {
|
||||
@@ -274,12 +291,8 @@ public class AnimationLibrary extends BaseAppState {
|
||||
newTracks.add(track);
|
||||
continue;
|
||||
}
|
||||
if (j.getParent() != null) {
|
||||
newTracks.add(track);
|
||||
continue;
|
||||
}
|
||||
Vector3f[] translations = tt.getTranslations();
|
||||
if (translations == null || translations.length == 0) {
|
||||
if (translations == null || translations.length == 0 || jointDepth(j) != minDepth) {
|
||||
newTracks.add(track);
|
||||
continue;
|
||||
}
|
||||
@@ -291,8 +304,8 @@ public class AnimationLibrary extends BaseAppState {
|
||||
}
|
||||
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);
|
||||
log.info("[AnimLib] '{}': Tiefe-{}-Joint '{}' XZ={},{} eingefroren, Y frei",
|
||||
clip.getName(), minDepth, j.getName(), f0x, f0z);
|
||||
}
|
||||
if (!modified) return clip;
|
||||
AnimClip result = new AnimClip(clip.getName());
|
||||
@@ -300,6 +313,13 @@ public class AnimationLibrary extends BaseAppState {
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int jointDepth(Joint j) {
|
||||
int d = 0;
|
||||
Joint p = j.getParent();
|
||||
while (p != null) { d++; p = p.getParent(); }
|
||||
return d;
|
||||
}
|
||||
|
||||
/** 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={}",
|
||||
|
||||
Reference in New Issue
Block a user