Zwischenstand

This commit is contained in:
2026-06-21 23:04:40 +02:00
parent 52c1fb1fe8
commit e669e29096
24 changed files with 97 additions and 10 deletions

View File

@@ -498,6 +498,12 @@ public class EditorApp extends Application {
if (charEditorStatusLabel != null) charEditorStatusLabel.setText(embedStatus);
}
String stripStatus = input.animStripStatus;
if (stripStatus != null) {
input.animStripStatus = null;
if (charEditorStatusLabel != null) charEditorStatusLabel.setText(stripStatus);
}
String rts = input.randomTreeStatus;
if (randomTreeStatusLabel != null && rts != null) {
randomTreeStatusLabel.setText(rts);
@@ -9452,6 +9458,19 @@ public class EditorApp extends Application {
input.animEmbedRequest.set(new SharedInput.AnimEmbedRequest(modelPath, setName));
});
Button stripClipsBtn = new Button("Eingebettete Clips löschen");
stripClipsBtn.setMaxWidth(Double.MAX_VALUE);
stripClipsBtn.setDisable(true);
charModelCombo.valueProperty().addListener((obs, ov, nv) ->
stripClipsBtn.setDisable(nv == null || nv.isBlank()));
stripClipsBtn.setOnAction(e -> {
String modelPath = charModelCombo.getValue();
if (modelPath == null || modelPath.isBlank()) return;
if (charEditorStatusLabel != null)
charEditorStatusLabel.setText("Entferne eingebettete Clips…");
input.animStripClipsRequest.set(modelPath);
});
charEditContainer.getChildren().addAll(
new Label("ID:"), charIdField,
new Label("Name:"), charNameField,
@@ -9562,7 +9581,8 @@ public class EditorApp extends Application {
charEditContainer.getChildren().addAll(
new Label("Modell:"), charModelCombo,
new Label("Anim-Set:"), charAnimSetCombo,
embedAnimBtn
embedAnimBtn,
stripClipsBtn
);
// ── Aktions-Zuweisung ──────────────────────────────────────────────────

View File

@@ -594,10 +594,16 @@ public class SharedInput {
public final java.util.concurrent.atomic.AtomicReference<AnimEmbedRequest>
animEmbedRequest = new java.util.concurrent.atomic.AtomicReference<>();
/** JavaFX → JME3: Modell-Pfad, aus dem alle eingebetteten AnimClips entfernt werden sollen. */
public final java.util.concurrent.atomic.AtomicReference<String>
animStripClipsRequest = new java.util.concurrent.atomic.AtomicReference<>();
/** JME3 → JavaFX: Status-Meldung für Clip- und Set-Operationen. */
public volatile String animOpStatus = null;
public volatile String animOpStatus = null;
/** JME3 → JavaFX: Status-Meldung für Einbett-Operationen (Character Editor). */
public volatile String animEmbedStatus = null;
public volatile String animEmbedStatus = null;
/** JME3 → JavaFX: Status-Meldung für Strip-Operationen (Clips aus Modell entfernen). */
public volatile String animStripStatus = null;
// ── Modell-Konvertierung ──────────────────────────────────────────────────
/**

View File

@@ -178,6 +178,9 @@ public class AnimPreviewState extends BaseAppState {
SharedInput.AnimEmbedRequest embedReq = input.animEmbedRequest.getAndSet(null);
if (embedReq != null) executeAnimEmbed(embedReq);
String stripPath = input.animStripClipsRequest.getAndSet(null);
if (stripPath != null) executeStripClips(stripPath);
// Geschwindigkeit live anpassen
if (currentAction != null) {
try { currentAction.setSpeed(input.animPreviewSpeed); } catch (Exception ignored) {}
@@ -258,6 +261,19 @@ public class AnimPreviewState extends BaseAppState {
currentModelPath = assetPath;
previewHolder.attachChild(model);
// Alle Clips in-place snappen (verhindert Drift im Preview)
AnimComposer previewAC = findControl(model, AnimComposer.class);
SkinningControl previewSC = findControl(model, SkinningControl.class);
if (previewAC != null && previewSC != null) {
for (AnimClip c : new java.util.ArrayList<>(previewAC.getAnimClips())) {
AnimClip snapped = de.blight.game.animation.AnimationLibrary.snapRootBoneXZ(c, previewSC.getArmature());
if (snapped != c) {
previewAC.removeAnimClip(c);
previewAC.addAnimClip(snapped);
}
}
}
// Kamera auf Bounding Box ausrichten
model.updateGeometricState();
if (model.getWorldBound() instanceof BoundingBox bb) {
@@ -498,9 +514,12 @@ public class AnimPreviewState extends BaseAppState {
LOG.info("[AnimPreview] Clip '{}' als '{}' gespeichert (Dateiname-Alias)", name, saveName);
}
// XZ-Drift einfrieren bevor gespeichert wird Clip-Dateien bleiben immer sauber
com.jme3.anim.Armature snapArm = dstArm != null ? dstArm : srcArm;
toSave = de.blight.game.animation.AnimationLibrary.snapRootBoneXZ(toSave, snapArm);
// Direkt in die Clip-Bibliothek speichern das Modell wird nicht modifiziert
saveClipToFile(toSave, dstArm != null ? dstArm : srcArm,
clipsDir.resolve(saveName + ".j3o"));
saveClipToFile(toSave, snapArm, clipsDir.resolve(saveName + ".j3o"));
// Für den aktuellen Preview-Session auch auf das Modell anwenden (wenn geladen)
if (targetAC != null) targetAC.addAnimClip(toSave);
saved++;
@@ -925,6 +944,29 @@ public class AnimPreviewState extends BaseAppState {
}
}
private void executeStripClips(String modelPath) {
try {
Spatial model = loadFresh(modelPath);
AnimComposer ac = findControl(model, AnimComposer.class);
if (ac == null) {
input.animStripStatus = "Fehler: kein AnimComposer in " + modelPath;
return;
}
int count = ac.getAnimClips().size();
for (AnimClip c : new java.util.ArrayList<>(ac.getAnimClips())) {
ac.removeAnimClip(c);
}
java.nio.file.Path file = ASSET_ROOT.resolve(modelPath.replace('/', java.io.File.separatorChar));
BinaryExporter.getInstance().save(model, file.toFile());
assets.deleteFromCache(new com.jme3.asset.ModelKey(modelPath));
input.animStripStatus = count + " eingebettete Clip(s) aus '" + modelPath + "' entfernt";
LOG.info("[AnimStrip] {} Clips aus '{}' entfernt und gespeichert", count, modelPath);
} catch (Exception e) {
input.animStripStatus = "Strip-Fehler: " + e.getMessage();
LOG.error("[AnimStrip] Fehler beim Strip von {}", modelPath, e);
}
}
private void saveClipToFile(AnimClip clip, com.jme3.anim.Armature armature,
java.nio.file.Path outFile) throws Exception {
Node holder = new Node("clip_" + clip.getName());