Commit vor Änderung Umstellung auf eine einzelne Animation
This commit is contained in:
@@ -157,12 +157,12 @@ public class EditorApp extends Application {
|
||||
private boolean animSetDirty = false;
|
||||
private String animSetCurrentName = null;
|
||||
private Path animSetCurrentDir = null;
|
||||
// Motion-Keyframe-Editor (innerhalb AnimSet-Editor)
|
||||
private javafx.scene.control.ListView<de.blight.game.animation.AnimKeyframe> animSetKfListView;
|
||||
private javafx.collections.ObservableList<de.blight.game.animation.AnimKeyframe> animSetKfList =
|
||||
// Anim-Offset-Editor (innerhalb AnimSet-Editor)
|
||||
private javafx.scene.control.ListView<de.blight.game.animation.AnimOffset> animSetOffsetListView;
|
||||
private javafx.collections.ObservableList<de.blight.game.animation.AnimOffset> animSetOffsetList =
|
||||
javafx.collections.FXCollections.observableArrayList();
|
||||
private java.util.Map<String, java.util.List<de.blight.game.animation.AnimKeyframe>>
|
||||
animSetMotionKeyframes = new java.util.LinkedHashMap<>();
|
||||
private java.util.Map<String, de.blight.game.animation.AnimOffset>
|
||||
animSetOffsets = new java.util.LinkedHashMap<>();
|
||||
|
||||
// Character-Editor-Zustand
|
||||
private de.blight.editor.ui.DialogEditorView dialogEditorView;
|
||||
@@ -8292,22 +8292,24 @@ public class EditorApp extends Application {
|
||||
animSetClipListView.getSelectionModel().selectedItemProperty()
|
||||
.addListener((obs, ov, nv) -> {
|
||||
removeClipBtn.setDisable(nv == null);
|
||||
// Keyframes des alten Clips sichern
|
||||
if (ov != null && animSetKfList != null) {
|
||||
animSetMotionKeyframes.put(ov, new java.util.ArrayList<>(animSetKfList));
|
||||
// Offset des alten Clips sichern
|
||||
if (ov != null && animSetOffsetList != null && !animSetOffsetList.isEmpty()) {
|
||||
animSetOffsets.put(ov, animSetOffsetList.get(0));
|
||||
} else if (ov != null) {
|
||||
animSetOffsets.remove(ov);
|
||||
}
|
||||
// Keyframes des neuen Clips laden
|
||||
if (animSetKfList != null) {
|
||||
animSetKfList.clear();
|
||||
// Offset des neuen Clips laden
|
||||
if (animSetOffsetList != null) {
|
||||
animSetOffsetList.clear();
|
||||
if (nv != null) {
|
||||
var kfs = animSetMotionKeyframes.getOrDefault(nv, new java.util.ArrayList<>());
|
||||
animSetKfList.setAll(kfs);
|
||||
de.blight.game.animation.AnimOffset off = animSetOffsets.get(nv);
|
||||
if (off != null) animSetOffsetList.setAll(off);
|
||||
// Clip direkt in Vorschau abspielen
|
||||
input.animPreviewPlayClip = nv;
|
||||
}
|
||||
}
|
||||
// KF-Bereich aktivieren/deaktivieren
|
||||
if (animSetKfListView != null) animSetKfListView.setDisable(nv == null);
|
||||
// Offset-Bereich aktivieren/deaktivieren
|
||||
if (animSetOffsetListView != null) animSetOffsetListView.setDisable(nv == null);
|
||||
});
|
||||
|
||||
addClipBtn.setOnAction(e -> {
|
||||
@@ -8427,64 +8429,61 @@ public class EditorApp extends Application {
|
||||
HBox.setHgrow(removeActionBtn, Priority.ALWAYS);
|
||||
inner.getChildren().addAll(animSetActionListView, actionBtns);
|
||||
|
||||
// ── Motion Keyframes ──────────────────────────────────────────────────
|
||||
inner.getChildren().addAll(new Separator(), sectionTitle("Motion Keyframes"), new Separator());
|
||||
// ── Anim-Offsets ──────────────────────────────────────────────────────
|
||||
inner.getChildren().addAll(new Separator(), sectionTitle("Anim-Offsets"), new Separator());
|
||||
|
||||
Label kfHint = new Label("TX/TZ = charakter-lokal (seitlich/vorwärts), TY = Welt-Y (hoch/runter). RX/RY/RZ in Grad, additiv zur Startrotation. Zeit ≤ Clip-Dauer (Clip vorab in Vorschau abspielen).");
|
||||
Label kfHint = new Label("TX/TZ = charakter-lokal (seitlich/vorwärts), TY = Welt-Y (hoch/runter). RX/RY/RZ in Grad, additiv zur Startrotation.");
|
||||
kfHint.setStyle("-fx-font-size: 10; -fx-text-fill: #888;");
|
||||
kfHint.setWrapText(true);
|
||||
|
||||
// Keyframes aus AnimSet laden
|
||||
animSetMotionKeyframes = new java.util.LinkedHashMap<>();
|
||||
for (var entry : animSet.getMotionKeyframes().entrySet()) {
|
||||
animSetMotionKeyframes.put(entry.getKey(), new java.util.ArrayList<>(entry.getValue()));
|
||||
}
|
||||
animSetKfList = javafx.collections.FXCollections.observableArrayList();
|
||||
animSetKfList.addListener((javafx.collections.ListChangeListener<de.blight.game.animation.AnimKeyframe>)
|
||||
change -> updateAnimPreviewKfOffset());
|
||||
// Offsets aus AnimSet laden
|
||||
animSetOffsets = new java.util.LinkedHashMap<>(animSet.getAnimOffsets());
|
||||
animSetOffsetList = javafx.collections.FXCollections.observableArrayList();
|
||||
animSetOffsetList.addListener((javafx.collections.ListChangeListener<de.blight.game.animation.AnimOffset>)
|
||||
change -> updateAnimPreviewOffset());
|
||||
|
||||
// ListView mit Zusammenfassung, Doppelklick öffnet Edit-Dialog
|
||||
animSetKfListView = new javafx.scene.control.ListView<>(animSetKfList);
|
||||
animSetKfListView.setPrefHeight(150);
|
||||
animSetKfListView.setPlaceholder(new Label("Keine Keyframes – [+ Keyframe] zum Hinzufügen"));
|
||||
animSetKfListView.setCellFactory(lv -> new javafx.scene.control.ListCell<>() {
|
||||
@Override protected void updateItem(de.blight.game.animation.AnimKeyframe kf, boolean empty) {
|
||||
super.updateItem(kf, empty);
|
||||
if (empty || kf == null) { setText(null); return; }
|
||||
setText(String.format("%.3fs | TX%+.3f TY%+.3f TZ%+.3f | RX%+.1f° RY%+.1f° RZ%+.1f°",
|
||||
kf.time, kf.tx, kf.ty, kf.tz, kf.rx, kf.ry, kf.rz));
|
||||
// ListView zeigt den einen Offset des gewählten Clips; Doppelklick öffnet Edit-Dialog
|
||||
animSetOffsetListView = new javafx.scene.control.ListView<>(animSetOffsetList);
|
||||
animSetOffsetListView.setPrefHeight(60);
|
||||
animSetOffsetListView.setPlaceholder(new Label("Kein Offset – [+ Offset] zum Setzen"));
|
||||
animSetOffsetListView.setCellFactory(lv -> new javafx.scene.control.ListCell<>() {
|
||||
@Override protected void updateItem(de.blight.game.animation.AnimOffset off, boolean empty) {
|
||||
super.updateItem(off, empty);
|
||||
if (empty || off == null) { setText(null); return; }
|
||||
setText(String.format("TX%+.3f TY%+.3f TZ%+.3f | RX%+.1f° RY%+.1f° RZ%+.1f°",
|
||||
off.tx, off.ty, off.tz, off.rx, off.ry, off.rz));
|
||||
}
|
||||
});
|
||||
animSetKfListView.setOnMouseClicked(ev -> {
|
||||
animSetOffsetListView.setOnMouseClicked(ev -> {
|
||||
if (ev.getClickCount() == 2) {
|
||||
de.blight.game.animation.AnimKeyframe sel =
|
||||
animSetKfListView.getSelectionModel().getSelectedItem();
|
||||
if (sel != null) showAnimKfDialog(sel);
|
||||
de.blight.game.animation.AnimOffset sel =
|
||||
animSetOffsetListView.getSelectionModel().getSelectedItem();
|
||||
if (sel != null) showAnimOffsetDialog(sel);
|
||||
}
|
||||
});
|
||||
|
||||
// initial deaktiviert – wird durch Clip-Selektion gesteuert
|
||||
animSetKfListView.setDisable(true);
|
||||
animSetOffsetListView.setDisable(true);
|
||||
|
||||
Button addKfBtn = new Button("+ Keyframe");
|
||||
Button addKfBtn = new Button("+ Offset");
|
||||
Button removeKfBtn = new Button("- Entfernen");
|
||||
addKfBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
removeKfBtn.setMaxWidth(Double.MAX_VALUE);
|
||||
addKfBtn.setDisable(true);
|
||||
removeKfBtn.setDisable(true);
|
||||
animSetKfListView.getSelectionModel().selectedItemProperty()
|
||||
animSetOffsetListView.getSelectionModel().selectedItemProperty()
|
||||
.addListener((obs, ov, nv) -> removeKfBtn.setDisable(nv == null));
|
||||
// addKfBtn folgt Clip-Selektion
|
||||
// addKfBtn folgt Clip-Selektion (nur wenn noch kein Offset gesetzt)
|
||||
animSetClipListView.getSelectionModel().selectedItemProperty()
|
||||
.addListener((obs, ov, nv) -> addKfBtn.setDisable(nv == null));
|
||||
|
||||
addKfBtn.setOnAction(e -> showAnimKfDialog(null));
|
||||
addKfBtn.setOnAction(e -> showAnimOffsetDialog(null));
|
||||
|
||||
removeKfBtn.setOnAction(e -> {
|
||||
de.blight.game.animation.AnimKeyframe sel =
|
||||
animSetKfListView.getSelectionModel().getSelectedItem();
|
||||
de.blight.game.animation.AnimOffset sel =
|
||||
animSetOffsetListView.getSelectionModel().getSelectedItem();
|
||||
if (sel != null) {
|
||||
animSetKfList.remove(sel);
|
||||
animSetOffsetList.remove(sel);
|
||||
animSetDirty = true;
|
||||
}
|
||||
});
|
||||
@@ -8492,7 +8491,7 @@ public class EditorApp extends Application {
|
||||
HBox kfBtns = new HBox(6, addKfBtn, removeKfBtn);
|
||||
HBox.setHgrow(addKfBtn, Priority.ALWAYS);
|
||||
HBox.setHgrow(removeKfBtn, Priority.ALWAYS);
|
||||
inner.getChildren().addAll(kfHint, animSetKfListView, kfBtns);
|
||||
inner.getChildren().addAll(kfHint, animSetOffsetListView, kfBtns);
|
||||
|
||||
// ── Vorschau ─────────────────────────────────────────────────────────
|
||||
inner.getChildren().addAll(new Separator(), sectionTitle("Vorschau"), new Separator());
|
||||
@@ -8552,16 +8551,10 @@ public class EditorApp extends Application {
|
||||
return panel;
|
||||
}
|
||||
|
||||
/** Öffnet den Keyframe-Dialog (null = Hinzufügen, non-null = Bearbeiten). */
|
||||
private void showAnimKfDialog(de.blight.game.animation.AnimKeyframe existing) {
|
||||
/** Öffnet den Offset-Dialog (null = Hinzufügen, non-null = Bearbeiten). */
|
||||
private void showAnimOffsetDialog(de.blight.game.animation.AnimOffset existing) {
|
||||
boolean isAdd = (existing == null);
|
||||
// Zeitlimit: zuletzt gemessene Clip-Dauer aus Preview, sonst 999
|
||||
float maxTime = input.animPreviewCurrentClipDuration > 0.01f
|
||||
? input.animPreviewCurrentClipDuration : 999.0f;
|
||||
|
||||
float initTime = isAdd
|
||||
? (animSetKfList.isEmpty() ? 0f : Math.min(animSetKfList.get(animSetKfList.size() - 1).time + 0.5f, maxTime))
|
||||
: existing.time;
|
||||
float initTX = isAdd ? 0f : existing.tx;
|
||||
float initTY = isAdd ? 0f : existing.ty;
|
||||
float initTZ = isAdd ? 0f : existing.tz;
|
||||
@@ -8569,25 +8562,19 @@ public class EditorApp extends Application {
|
||||
float initRY = isAdd ? 0f : existing.ry;
|
||||
float initRZ = isAdd ? 0f : existing.rz;
|
||||
|
||||
Spinner<Double> spTime = new Spinner<>(0.0, maxTime, initTime, 0.05);
|
||||
Spinner<Double> spTX = new Spinner<>(-10.0, 10.0, initTX, 0.05);
|
||||
Spinner<Double> spTY = new Spinner<>(-10.0, 10.0, initTY, 0.05);
|
||||
Spinner<Double> spTZ = new Spinner<>(-10.0, 10.0, initTZ, 0.05);
|
||||
Spinner<Double> spRX = new Spinner<>(-360.0, 360.0, initRX, 1.0);
|
||||
Spinner<Double> spRY = new Spinner<>(-360.0, 360.0, initRY, 1.0);
|
||||
Spinner<Double> spRZ = new Spinner<>(-360.0, 360.0, initRZ, 1.0);
|
||||
for (Spinner<Double> sp : new Spinner[]{spTime, spTX, spTY, spTZ, spRX, spRY, spRZ}) {
|
||||
Spinner<Double> spTX = new Spinner<>(-10.0, 10.0, initTX, 0.05);
|
||||
Spinner<Double> spTY = new Spinner<>(-10.0, 10.0, initTY, 0.05);
|
||||
Spinner<Double> spTZ = new Spinner<>(-10.0, 10.0, initTZ, 0.05);
|
||||
Spinner<Double> spRX = new Spinner<>(-360.0, 360.0, initRX, 1.0);
|
||||
Spinner<Double> spRY = new Spinner<>(-360.0, 360.0, initRY, 1.0);
|
||||
Spinner<Double> spRZ = new Spinner<>(-360.0, 360.0, initRZ, 1.0);
|
||||
for (Spinner<Double> sp : new Spinner[]{spTX, spTY, spTZ, spRX, spRY, spRZ}) {
|
||||
sp.setEditable(true);
|
||||
sp.setMaxWidth(Double.MAX_VALUE);
|
||||
}
|
||||
|
||||
String timeLabel = maxTime < 999f
|
||||
? String.format("Zeit (0 – %.2fs):", maxTime)
|
||||
: "Zeit (s):";
|
||||
String[][] rows = {
|
||||
{timeLabel}, {"TX (m):"}, {"TY (m):"}, {"TZ (m):"}, {"RX (°):"}, {"RY (°):"}, {"RZ (°):"}
|
||||
};
|
||||
Spinner<?>[] sps = {spTime, spTX, spTY, spTZ, spRX, spRY, spRZ};
|
||||
String[][] rows = {{"TX (m):"}, {"TY (m):"}, {"TZ (m):"}, {"RX (°):"}, {"RY (°):"}, {"RZ (°):"}};
|
||||
Spinner<?>[] sps = {spTX, spTY, spTZ, spRX, spRY, spRZ};
|
||||
javafx.scene.layout.GridPane kfGrid = new javafx.scene.layout.GridPane();
|
||||
kfGrid.setHgap(8); kfGrid.setVgap(5);
|
||||
for (int i = 0; i < sps.length; i++) {
|
||||
@@ -8600,7 +8587,7 @@ public class EditorApp extends Application {
|
||||
|
||||
javafx.scene.control.Dialog<javafx.scene.control.ButtonType> kfDlg =
|
||||
new javafx.scene.control.Dialog<>();
|
||||
kfDlg.setTitle(isAdd ? "Keyframe hinzufügen" : "Keyframe bearbeiten");
|
||||
kfDlg.setTitle(isAdd ? "Offset hinzufügen" : "Offset bearbeiten");
|
||||
javafx.scene.control.ButtonType okKf = new javafx.scene.control.ButtonType(
|
||||
isAdd ? "Hinzufügen" : "Übernehmen",
|
||||
javafx.scene.control.ButtonBar.ButtonData.OK_DONE);
|
||||
@@ -8608,15 +8595,12 @@ public class EditorApp extends Application {
|
||||
kfDlg.getDialogPane().setContent(kfGrid);
|
||||
kfDlg.showAndWait().ifPresent(bt -> {
|
||||
if (bt != okKf) return;
|
||||
float t = (float) Math.min(spTime.getValue(), maxTime);
|
||||
if (isAdd) {
|
||||
de.blight.game.animation.AnimKeyframe kf = new de.blight.game.animation.AnimKeyframe(
|
||||
t,
|
||||
de.blight.game.animation.AnimOffset off = new de.blight.game.animation.AnimOffset(
|
||||
spTX.getValue().floatValue(), spTY.getValue().floatValue(), spTZ.getValue().floatValue(),
|
||||
spRX.getValue().floatValue(), spRY.getValue().floatValue(), spRZ.getValue().floatValue());
|
||||
animSetKfList.add(kf);
|
||||
animSetOffsetList.setAll(off);
|
||||
} else {
|
||||
existing.time = t;
|
||||
existing.tx = spTX.getValue().floatValue();
|
||||
existing.ty = spTY.getValue().floatValue();
|
||||
existing.tz = spTZ.getValue().floatValue();
|
||||
@@ -8624,8 +8608,7 @@ public class EditorApp extends Application {
|
||||
existing.ry = spRY.getValue().floatValue();
|
||||
existing.rz = spRZ.getValue().floatValue();
|
||||
}
|
||||
animSetKfList.sort(java.util.Comparator.comparingDouble(k -> k.time));
|
||||
if (animSetKfListView != null) animSetKfListView.refresh();
|
||||
if (animSetOffsetListView != null) animSetOffsetListView.refresh();
|
||||
animSetDirty = true;
|
||||
});
|
||||
}
|
||||
@@ -8645,21 +8628,18 @@ public class EditorApp extends Application {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAnimPreviewKfOffset() {
|
||||
if (animSetKfList == null || animSetKfList.isEmpty()) {
|
||||
input.animPreviewKfTx = 0f;
|
||||
input.animPreviewKfTy = 0f;
|
||||
input.animPreviewKfTz = 0f;
|
||||
input.animPreviewKfActive = false;
|
||||
private void updateAnimPreviewOffset() {
|
||||
if (animSetOffsetList == null || animSetOffsetList.isEmpty()) {
|
||||
input.animPreviewOffsetTx = 0f;
|
||||
input.animPreviewOffsetTy = 0f;
|
||||
input.animPreviewOffsetTz = 0f;
|
||||
input.animPreviewOffsetActive = false;
|
||||
} else {
|
||||
de.blight.game.animation.AnimKeyframe first = animSetKfList.get(0);
|
||||
for (de.blight.game.animation.AnimKeyframe k : animSetKfList) {
|
||||
if (k.time < first.time) { first = k; }
|
||||
}
|
||||
input.animPreviewKfTx = first.tx;
|
||||
input.animPreviewKfTy = first.ty;
|
||||
input.animPreviewKfTz = first.tz;
|
||||
input.animPreviewKfActive = true;
|
||||
de.blight.game.animation.AnimOffset off = animSetOffsetList.get(0);
|
||||
input.animPreviewOffsetTx = off.tx;
|
||||
input.animPreviewOffsetTy = off.ty;
|
||||
input.animPreviewOffsetTz = off.tz;
|
||||
input.animPreviewOffsetActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8768,21 +8748,25 @@ public class EditorApp extends Application {
|
||||
}
|
||||
}
|
||||
animSet.setActionMap(actionMap);
|
||||
// Motion Keyframes: aktuell sichtbare Liste (des selektierten Clips) sichern, dann schreiben
|
||||
// Anim-Offsets: aktuell sichtbaren Offset (des selektierten Clips) sichern, dann schreiben
|
||||
if (animSetClipListView != null) {
|
||||
String selClip = animSetClipListView.getSelectionModel().getSelectedItem();
|
||||
if (selClip != null) {
|
||||
animSetMotionKeyframes.put(selClip, new java.util.ArrayList<>(animSetKfList));
|
||||
if (!animSetOffsetList.isEmpty()) {
|
||||
animSetOffsets.put(selClip, animSetOffsetList.get(0));
|
||||
} else {
|
||||
animSetOffsets.remove(selClip);
|
||||
}
|
||||
}
|
||||
}
|
||||
java.util.Map<String, java.util.List<de.blight.game.animation.AnimKeyframe>> kfFinal =
|
||||
java.util.Map<String, de.blight.game.animation.AnimOffset> offsetFinal =
|
||||
new java.util.LinkedHashMap<>();
|
||||
for (var kfEntry : animSetMotionKeyframes.entrySet()) {
|
||||
if (kfEntry.getValue() != null && !kfEntry.getValue().isEmpty()) {
|
||||
kfFinal.put(kfEntry.getKey(), kfEntry.getValue());
|
||||
for (var entry : animSetOffsets.entrySet()) {
|
||||
if (entry.getValue() != null) {
|
||||
offsetFinal.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
animSet.setMotionKeyframes(kfFinal);
|
||||
animSet.setAnimOffsets(offsetFinal);
|
||||
// Vorschau-Modell-Pfad beibehalten
|
||||
if (animSetModelCombo != null && animSetModelCombo.getValue() != null && !animSetModelCombo.getValue().isBlank()) {
|
||||
animSet.setPreviewModelPath(animSetModelCombo.getValue());
|
||||
|
||||
@@ -572,11 +572,11 @@ public class SharedInput {
|
||||
animPreviewJointNames = new java.util.concurrent.atomic.AtomicReference<>();
|
||||
/** JME3 → JavaFX: Länge des zuletzt gestarteten Clips in Sekunden (0 = unbekannt). */
|
||||
public volatile float animPreviewCurrentClipDuration = 0f;
|
||||
/** JavaFX → JME3: Motion-Keyframe-Vorschau-Versatz (erster KF, tx/ty/tz in Metern). */
|
||||
public volatile float animPreviewKfTx = 0f;
|
||||
public volatile float animPreviewKfTy = 0f;
|
||||
public volatile float animPreviewKfTz = 0f;
|
||||
public volatile boolean animPreviewKfActive = false;
|
||||
/** JavaFX → JME3: Anim-Offset-Vorschau (tx/ty/tz in Metern). */
|
||||
public volatile float animPreviewOffsetTx = 0f;
|
||||
public volatile float animPreviewOffsetTy = 0f;
|
||||
public volatile float animPreviewOffsetTz = 0f;
|
||||
public volatile boolean animPreviewOffsetActive = false;
|
||||
|
||||
/**
|
||||
* JME3 → JavaFX: Relativer Asset-Pfad des gerade geladenen Modells.
|
||||
|
||||
@@ -231,11 +231,11 @@ public class AnimPreviewState extends BaseAppState {
|
||||
axesNode.setLocalTranslation(Vector3f.ZERO);
|
||||
}
|
||||
|
||||
// Motion-Keyframe-Vorschau: Versatz auf Modell anwenden (TX/TY/TZ in Welt-Koordinaten)
|
||||
// Anim-Offset-Vorschau: Versatz auf Modell anwenden (TX/TY/TZ in Welt-Koordinaten)
|
||||
if (currentModel != null) {
|
||||
if (input.animPreviewKfActive) {
|
||||
if (input.animPreviewOffsetActive) {
|
||||
currentModel.setLocalTranslation(
|
||||
input.animPreviewKfTx, input.animPreviewKfTy, input.animPreviewKfTz);
|
||||
input.animPreviewOffsetTx, input.animPreviewOffsetTy, input.animPreviewOffsetTz);
|
||||
} else {
|
||||
currentModel.setLocalTranslation(Vector3f.ZERO);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user