Zwischenstand - animationen passen weitesgehend
This commit is contained in:
@@ -258,6 +258,10 @@ public class AnimPreviewState extends BaseAppState {
|
|||||||
// Alle Clips in-place snappen (verhindert Drift im Preview)
|
// Alle Clips in-place snappen (verhindert Drift im Preview)
|
||||||
AnimComposer previewAC = findControl(model, AnimComposer.class);
|
AnimComposer previewAC = findControl(model, AnimComposer.class);
|
||||||
SkinningControl previewSC = findControl(model, SkinningControl.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) {
|
if (previewAC != null && previewSC != null) {
|
||||||
// Eingebettete Clips snappen
|
// Eingebettete Clips snappen
|
||||||
for (AnimClip c : new java.util.ArrayList<>(previewAC.getAnimClips())) {
|
for (AnimClip c : new java.util.ArrayList<>(previewAC.getAnimClips())) {
|
||||||
@@ -275,29 +279,33 @@ public class AnimPreviewState extends BaseAppState {
|
|||||||
}
|
}
|
||||||
saveModelStripped(model, assetPath, embedCount);
|
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).
|
// Leerer Clip → NPE in ClipAction.doInterpolate (JME3 erwartet tracks != null).
|
||||||
// Ein Frame am Root-Joint in Bind-Pose → alle anderen Joints bleiben an
|
// getInitialTransform() liefert die Bind-Pose → SC-Matrix = Bind × Bind⁻¹ = I
|
||||||
// ihren lokalen Bind-Pose-Transforms → SC-Matrix = Bind × Bind⁻¹ = I
|
|
||||||
// → Vertices in Y-up = stehender Charakter.
|
// → Vertices in Y-up = stehender Charakter.
|
||||||
AnimClip tpose = buildTPoseClip(previewSC.getArmature());
|
AnimClip tpose = buildTPoseClip(previewSC.getArmature());
|
||||||
|
LOG.info("[AnimPreview] T-Pose Clip: {}", tpose != null ? "erstellt" : "NULL (keine Root-Joints?)");
|
||||||
if (tpose != null) {
|
if (tpose != null) {
|
||||||
previewAC.addAnimClip(tpose);
|
previewAC.addAnimClip(tpose);
|
||||||
setSkinningEnabled(model, true);
|
setSkinningEnabled(model, true);
|
||||||
previewAC.setCurrentAction("__tpose__");
|
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();
|
model.updateGeometricState();
|
||||||
if (model.getWorldBound() instanceof BoundingBox bb) {
|
if (model.getWorldBound() instanceof BoundingBox bb) {
|
||||||
float ext = Math.max(bb.getXExtent(), Math.max(bb.getYExtent(), bb.getZExtent()));
|
float ext = Math.max(bb.getXExtent(), Math.max(bb.getYExtent(), bb.getZExtent()));
|
||||||
previewCamDist = ext * 2.8f;
|
previewCamDist = ext * 2.8f;
|
||||||
previewTarget.set(bb.getCenter());
|
|
||||||
} else {
|
} else {
|
||||||
previewCamDist = 3f;
|
previewCamDist = 3f;
|
||||||
previewTarget.set(0, 1, 0);
|
|
||||||
}
|
}
|
||||||
|
previewTarget.set(0, 1, 0);
|
||||||
input.animPreviewZoom = 1.0f;
|
input.animPreviewZoom = 1.0f;
|
||||||
|
|
||||||
// Clips sammeln und melden
|
// Clips sammeln und melden
|
||||||
@@ -1002,18 +1010,20 @@ public class AnimPreviewState extends BaseAppState {
|
|||||||
* Gibt null zurück wenn das Armature keine Root-Joints hat.
|
* Gibt null zurück wenn das Armature keine Root-Joints hat.
|
||||||
*/
|
*/
|
||||||
private static AnimClip buildTPoseClip(com.jme3.anim.Armature armature) {
|
private static AnimClip buildTPoseClip(com.jme3.anim.Armature armature) {
|
||||||
com.jme3.anim.Joint[] roots = armature.getRoots();
|
int jointCount = armature.getJointCount();
|
||||||
if (roots == null || roots.length == 0) return null;
|
if (jointCount == 0) return null;
|
||||||
AnimClip clip = new AnimClip("__tpose__");
|
AnimClip clip = new AnimClip("__tpose__");
|
||||||
com.jme3.anim.AnimTrack[] tracks = new com.jme3.anim.AnimTrack[roots.length];
|
com.jme3.anim.AnimTrack[] tracks = new com.jme3.anim.AnimTrack[jointCount];
|
||||||
for (int i = 0; i < roots.length; i++) {
|
for (int i = 0; i < jointCount; i++) {
|
||||||
com.jme3.anim.Joint root = roots[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(
|
tracks[i] = new com.jme3.anim.TransformTrack(
|
||||||
root,
|
joint,
|
||||||
new float[]{0f},
|
new float[]{0f},
|
||||||
new com.jme3.math.Vector3f[]{root.getLocalTranslation().clone()},
|
new com.jme3.math.Vector3f[]{bt.getTranslation().clone()},
|
||||||
new com.jme3.math.Quaternion[]{root.getLocalRotation().clone()},
|
new com.jme3.math.Quaternion[]{bt.getRotation().clone()},
|
||||||
new com.jme3.math.Vector3f[]{root.getLocalScale().clone()});
|
new com.jme3.math.Vector3f[]{bt.getScale().clone()});
|
||||||
}
|
}
|
||||||
clip.setTracks(tracks);
|
clip.setTracks(tracks);
|
||||||
return clip;
|
return clip;
|
||||||
|
|||||||
Reference in New Issue
Block a user