Animationsübergänge weiter verbessert
This commit is contained in:
@@ -18,6 +18,9 @@ import com.jme3.anim.AnimClip;
|
|||||||
import com.jme3.anim.AnimComposer;
|
import com.jme3.anim.AnimComposer;
|
||||||
import com.jme3.anim.SkinningControl;
|
import com.jme3.anim.SkinningControl;
|
||||||
import com.jme3.anim.tween.action.Action;
|
import com.jme3.anim.tween.action.Action;
|
||||||
|
import com.jme3.anim.tween.action.BlendAction;
|
||||||
|
import com.jme3.anim.tween.action.BlendableAction;
|
||||||
|
import com.jme3.anim.tween.action.LinearBlendSpace;
|
||||||
import com.jme3.app.Application;
|
import com.jme3.app.Application;
|
||||||
import com.jme3.app.SimpleApplication;
|
import com.jme3.app.SimpleApplication;
|
||||||
import com.jme3.app.state.BaseAppState;
|
import com.jme3.app.state.BaseAppState;
|
||||||
@@ -86,6 +89,11 @@ public class AnimPreviewState extends BaseAppState {
|
|||||||
private float transitionElapsed = 0.15f;
|
private float transitionElapsed = 0.15f;
|
||||||
private static final float TRANSITION_DURATION = 0.15f;
|
private static final float TRANSITION_DURATION = 0.15f;
|
||||||
|
|
||||||
|
private LinearBlendSpace blendSpace = null;
|
||||||
|
private String pendingClipName = null;
|
||||||
|
private float blendElapsed = 0f;
|
||||||
|
private static final float BLEND_DURATION = TRANSITION_DURATION; // Pose- und Offset-Blend synchron
|
||||||
|
|
||||||
private Node axesNode;
|
private Node axesNode;
|
||||||
|
|
||||||
public AnimPreviewState(SharedInput input) { this.input = input; }
|
public AnimPreviewState(SharedInput input) { this.input = input; }
|
||||||
@@ -208,6 +216,14 @@ public class AnimPreviewState extends BaseAppState {
|
|||||||
try { currentAction.setSpeed(input.animPreviewSpeed); } catch (Exception ignored) {}
|
try { currentAction.setSpeed(input.animPreviewSpeed); } catch (Exception ignored) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Blend voranschreiten: BlendWeight 0→1 über BLEND_DURATION
|
||||||
|
if (pendingClipName != null && blendSpace != null && currentModel != null) {
|
||||||
|
blendElapsed += tpf;
|
||||||
|
float bt = Math.min(1f, blendElapsed / BLEND_DURATION);
|
||||||
|
blendSpace.setValue(bt);
|
||||||
|
if (bt >= 1f) finishBlend();
|
||||||
|
}
|
||||||
|
|
||||||
// Loop-Steuerung: AnimComposer spielt einmal ab – wir starten manuell neu
|
// Loop-Steuerung: AnimComposer spielt einmal ab – wir starten manuell neu
|
||||||
if (currentClipName != null && currentModel != null && currentAction != null) {
|
if (currentClipName != null && currentModel != null && currentAction != null) {
|
||||||
AnimComposer ac = findControl(currentModel, AnimComposer.class);
|
AnimComposer ac = findControl(currentModel, AnimComposer.class);
|
||||||
@@ -425,8 +441,6 @@ public class AnimPreviewState extends BaseAppState {
|
|||||||
|
|
||||||
private void playClip(String clipName) {
|
private void playClip(String clipName) {
|
||||||
if (currentModel == null) return;
|
if (currentModel == null) return;
|
||||||
currentAction = null;
|
|
||||||
currentClipName = null;
|
|
||||||
// Offset für diesen Clip aus der Map lesen (wird von EditorApp befüllt)
|
// Offset für diesen Clip aus der Map lesen (wird von EditorApp befüllt)
|
||||||
float[] off = input.animPreviewClipOffsets.get(clipName);
|
float[] off = input.animPreviewClipOffsets.get(clipName);
|
||||||
if (off != null && off.length >= 3) {
|
if (off != null && off.length >= 3) {
|
||||||
@@ -443,6 +457,45 @@ public class AnimPreviewState extends BaseAppState {
|
|||||||
// SkinningControls auf dem gesamten Modell aktivieren – AnimComposer und
|
// SkinningControls auf dem gesamten Modell aktivieren – AnimComposer und
|
||||||
// SkinningControl sitzen oft auf verschiedenen Geschwisterknoten.
|
// SkinningControl sitzen oft auf verschiedenen Geschwisterknoten.
|
||||||
setSkinningEnabled(currentModel, true);
|
setSkinningEnabled(currentModel, true);
|
||||||
|
|
||||||
|
// Wenn schon ein Clip läuft: Freeze-Pose einfrieren + BlendAction.
|
||||||
|
// blend.setTransitionLength(0) verhindert den internen 0.4s-Cross-Fade der
|
||||||
|
// BlendableAction, der sonst parallel zu unserem blendSpace.setValue läuft
|
||||||
|
// und ein "hin und her" erzeugt.
|
||||||
|
AnimComposer ac = findControl(currentModel, AnimComposer.class);
|
||||||
|
SkinningControl sc = findControl(currentModel, SkinningControl.class);
|
||||||
|
if (ac != null && sc != null && ac.getAnimClipsNames().contains(clipName)
|
||||||
|
&& (currentClipName != null || pendingClipName != null)) {
|
||||||
|
AnimClip freeze = buildFreezeClip(sc.getArmature());
|
||||||
|
if (freeze != null) {
|
||||||
|
try {
|
||||||
|
if (ac.hasAnimClip("__freeze__")) ac.removeAnimClip(ac.getAnimClip("__freeze__"));
|
||||||
|
ac.addAnimClip(freeze);
|
||||||
|
if (ac.hasAction("__blend__")) ac.removeAction("__blend__");
|
||||||
|
LinearBlendSpace space = new LinearBlendSpace(0f, 1f);
|
||||||
|
space.setValue(0f);
|
||||||
|
BlendAction blend = ac.actionBlended("__blend__", space, "__freeze__", clipName);
|
||||||
|
blend.clearSpeedFactors(); // N/0=Infinity für length=0 Freeze-Clip vermeiden
|
||||||
|
blend.setTransitionLength(0.0); // keinen 2. internen Cross-Fade doppelt laufen lassen
|
||||||
|
ac.getLayer(AnimComposer.DEFAULT_LAYER).setCurrentAction(blend);
|
||||||
|
blendSpace = space;
|
||||||
|
pendingClipName = clipName;
|
||||||
|
blendElapsed = 0f;
|
||||||
|
currentAction = blend;
|
||||||
|
currentClipName = null;
|
||||||
|
return;
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.warn("[AnimPreview] BlendAction fehlgeschlagen, direktes Wechseln: {}", e.getMessage());
|
||||||
|
blendSpace = null;
|
||||||
|
pendingClipName = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAction = null;
|
||||||
|
currentClipName = null;
|
||||||
|
pendingClipName = null;
|
||||||
|
blendSpace = null;
|
||||||
playOnSpatial(currentModel, clipName);
|
playOnSpatial(currentModel, clipName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1151,6 +1204,51 @@ public class AnimPreviewState extends BaseAppState {
|
|||||||
return clip;
|
return clip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private static AnimClip buildFreezeClip(com.jme3.anim.Armature arm) {
|
||||||
|
int n = arm.getJointCount();
|
||||||
|
if (n == 0) return null;
|
||||||
|
AnimClip clip = new AnimClip("__freeze__");
|
||||||
|
com.jme3.anim.AnimTrack[] tracks = new com.jme3.anim.AnimTrack[n];
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
com.jme3.anim.Joint j = arm.getJoint(i);
|
||||||
|
com.jme3.math.Transform lt = j.getLocalTransform();
|
||||||
|
tracks[i] = new com.jme3.anim.TransformTrack(
|
||||||
|
j, new float[]{0f},
|
||||||
|
new com.jme3.math.Vector3f[]{lt.getTranslation().clone()},
|
||||||
|
new com.jme3.math.Quaternion[]{lt.getRotation().clone()},
|
||||||
|
new com.jme3.math.Vector3f[]{lt.getScale().clone()});
|
||||||
|
}
|
||||||
|
clip.setTracks(tracks);
|
||||||
|
return clip;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void finishBlend() {
|
||||||
|
String clipName = pendingClipName;
|
||||||
|
float elapsed = blendElapsed;
|
||||||
|
pendingClipName = null;
|
||||||
|
blendSpace = null;
|
||||||
|
if (currentModel == null || clipName == null) return;
|
||||||
|
AnimComposer ac = findControl(currentModel, AnimComposer.class);
|
||||||
|
if (ac == null) return;
|
||||||
|
if (ac.hasAction("__blend__")) ac.removeAction("__blend__");
|
||||||
|
try {
|
||||||
|
// transitionLength=0 verhindert den Default-0.4s-Cross-Fade nach dem Blend
|
||||||
|
Action next = ac.action(clipName);
|
||||||
|
if (next instanceof BlendableAction ba) ba.setTransitionLength(0.0);
|
||||||
|
currentAction = ac.setCurrentAction(clipName);
|
||||||
|
currentClipName = clipName;
|
||||||
|
if (currentAction != null) {
|
||||||
|
currentAction.setSpeed(input.animPreviewSpeed);
|
||||||
|
input.animPreviewCurrentClipDuration = (float) currentAction.getLength();
|
||||||
|
double len = currentAction.getLength();
|
||||||
|
if (len > 0.01 && elapsed > 0) ac.setTime(elapsed % len);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.warn("[AnimPreview] Direkt-Wechsel nach Blend fehlgeschlagen: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final String[] MODEL_SAVE_ROOTS = {
|
private static final String[] MODEL_SAVE_ROOTS = {
|
||||||
null, // ASSET_ROOT (src) – wird durch ASSET_ROOT ersetzt
|
null, // ASSET_ROOT (src) – wird durch ASSET_ROOT ersetzt
|
||||||
"blight-assets/bin/main",
|
"blight-assets/bin/main",
|
||||||
|
|||||||
Reference in New Issue
Block a user