Zwischenstand - animationen passen weitesgehend

This commit is contained in:
2026-06-22 22:36:44 +02:00
parent b3b943e588
commit 914bf6e673

View File

@@ -258,6 +258,10 @@ public class AnimPreviewState extends BaseAppState {
// Alle Clips in-place snappen (verhindert Drift im Preview)
AnimComposer previewAC = findControl(model, AnimComposer.class);
SkinningControl previewSC = findControl(model, SkinningControl.class);
LOG.info("[AnimPreview] Modell-Controls: AnimComposer={}, SkinningControl={}, EmbeddedClips={}",
previewAC != null ? "gefunden" : "NULL",
previewSC != null ? "gefunden" : "NULL",
previewAC != null ? previewAC.getAnimClips().size() : "n/a");
if (previewAC != null && previewSC != null) {
// Eingebettete Clips snappen
for (AnimClip c : new java.util.ArrayList<>(previewAC.getAnimClips())) {
@@ -275,29 +279,33 @@ public class AnimPreviewState extends BaseAppState {
}
saveModelStripped(model, assetPath, embedCount);
}
// T-Pose: AnimClip mit einem Einzel-Frame-Track für den Root-Joint.
// T-Pose: AnimClip für alle Joints in Bind-Pose.
// Leerer Clip → NPE in ClipAction.doInterpolate (JME3 erwartet tracks != null).
// Ein Frame am Root-Joint in Bind-Pose → alle anderen Joints bleiben an
// ihren lokalen Bind-Pose-Transforms → SC-Matrix = Bind × Bind⁻¹ = I
// getInitialTransform() liefert die Bind-Pose → SC-Matrix = Bind × Bind⁻¹ = I
// → Vertices in Y-up = stehender Charakter.
AnimClip tpose = buildTPoseClip(previewSC.getArmature());
LOG.info("[AnimPreview] T-Pose Clip: {}", tpose != null ? "erstellt" : "NULL (keine Root-Joints?)");
if (tpose != null) {
previewAC.addAnimClip(tpose);
setSkinningEnabled(model, true);
previewAC.setCurrentAction("__tpose__");
LOG.info("[AnimPreview] T-Pose aktiviert");
}
} else {
LOG.warn("[AnimPreview] T-Pose NICHT möglich: previewAC={}, previewSC={}",
previewAC != null ? "ok" : "NULL",
previewSC != null ? "ok" : "NULL");
}
// Kamera auf Bounding Box ausrichten
// Kamera: immer auf Hüfthöhe (0, 1, 0) zielen; Distanz aus BoundingBox
model.updateGeometricState();
if (model.getWorldBound() instanceof BoundingBox bb) {
float ext = Math.max(bb.getXExtent(), Math.max(bb.getYExtent(), bb.getZExtent()));
previewCamDist = ext * 2.8f;
previewTarget.set(bb.getCenter());
} else {
previewCamDist = 3f;
previewTarget.set(0, 1, 0);
}
previewTarget.set(0, 1, 0);
input.animPreviewZoom = 1.0f;
// Clips sammeln und melden
@@ -1002,18 +1010,20 @@ public class AnimPreviewState extends BaseAppState {
* Gibt null zurück wenn das Armature keine Root-Joints hat.
*/
private static AnimClip buildTPoseClip(com.jme3.anim.Armature armature) {
com.jme3.anim.Joint[] roots = armature.getRoots();
if (roots == null || roots.length == 0) return null;
int jointCount = armature.getJointCount();
if (jointCount == 0) return null;
AnimClip clip = new AnimClip("__tpose__");
com.jme3.anim.AnimTrack[] tracks = new com.jme3.anim.AnimTrack[roots.length];
for (int i = 0; i < roots.length; i++) {
com.jme3.anim.Joint root = roots[i];
com.jme3.anim.AnimTrack[] tracks = new com.jme3.anim.AnimTrack[jointCount];
for (int i = 0; i < jointCount; i++) {
com.jme3.anim.Joint joint = armature.getJoint(i);
// getInitialTransform() liefert die echte Bind-Pose (nicht den aktuellen Zustand)
com.jme3.math.Transform bt = joint.getInitialTransform();
tracks[i] = new com.jme3.anim.TransformTrack(
root,
joint,
new float[]{0f},
new com.jme3.math.Vector3f[]{root.getLocalTranslation().clone()},
new com.jme3.math.Quaternion[]{root.getLocalRotation().clone()},
new com.jme3.math.Vector3f[]{root.getLocalScale().clone()});
new com.jme3.math.Vector3f[]{bt.getTranslation().clone()},
new com.jme3.math.Quaternion[]{bt.getRotation().clone()},
new com.jme3.math.Vector3f[]{bt.getScale().clone()});
}
clip.setTracks(tracks);
return clip;