Zwischenstand
This commit is contained in:
BIN
assets/imported/animations/alive_again.glb
Normal file
BIN
assets/imported/animations/alive_again.glb
Normal file
Binary file not shown.
BIN
assets/imported/animations/sit_down_bench.glb
Normal file
BIN
assets/imported/animations/sit_down_bench.glb
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,11 +3,28 @@
|
||||
"level": 0,
|
||||
"xp": 0,
|
||||
"currentHp": 0,
|
||||
"maxHp": 0,
|
||||
"currentStamina": 0,
|
||||
"maxStamina": 0,
|
||||
"currentMana": 0,
|
||||
"myMana": 0,
|
||||
"maxHp": 0,
|
||||
"maxStamina": 0,
|
||||
"maxMana": 0,
|
||||
"openHpRegeneration": 0,
|
||||
"openManaRegeneration": 0,
|
||||
"openStaminaRegeneration": 0,
|
||||
"restorationValue": 10,
|
||||
"abilities": {
|
||||
"lvlMagic": 1,
|
||||
"lvlStaffCombat": 1,
|
||||
"lvlSwordsmanship": 1,
|
||||
"lvlArchery": 1,
|
||||
"lvlHeavyWeapons": 1,
|
||||
"lvlCrossbow": 1,
|
||||
"lvlThievery": 0,
|
||||
"lvlAlchemy": 0,
|
||||
"lvlEngineering": 0,
|
||||
"lvlSmithery": 0,
|
||||
"lvlEnchanting": 0
|
||||
},
|
||||
"listeners": [],
|
||||
"characterId": "hero",
|
||||
"name": {
|
||||
|
||||
@@ -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 ──────────────────────────────────────────────────
|
||||
|
||||
@@ -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 ──────────────────────────────────────────────────
|
||||
/**
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -24,7 +24,8 @@ public enum AnimationAction {
|
||||
SITTING,
|
||||
SIT_DOWN_FLOOR,
|
||||
SITTING_FLOOR,
|
||||
GET_UP_FLOOR;
|
||||
GET_UP_FLOOR,
|
||||
REVIVE;
|
||||
|
||||
/** Lesbare Bezeichnung für UI-Anzeige, via TextRegistry aufgelöst. */
|
||||
public String displayName() {
|
||||
@@ -48,6 +49,7 @@ public enum AnimationAction {
|
||||
case SIT_DOWN_FLOOR -> TextRegistry.resolve(null, key, "Hinsetzen (Boden)");
|
||||
case SITTING_FLOOR -> TextRegistry.resolve(null, key, "Sitzen (Boden)");
|
||||
case GET_UP_FLOOR -> TextRegistry.resolve(null, key, "Aufstehen (Boden)");
|
||||
case REVIVE -> TextRegistry.resolve(null, key, "Wiederbeleben");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,7 +270,7 @@ public class AnimationLibrary extends BaseAppState {
|
||||
* wird Hips gesnappt. So passt der Snap zu beiden Rig-Strukturen.
|
||||
* Erstellt einen neuen in-memory-Clip; J3O-Dateien bleiben unverändert.
|
||||
*/
|
||||
private static AnimClip snapRootBoneXZ(AnimClip clip, Armature armature) {
|
||||
public static AnimClip snapRootBoneXZ(AnimClip clip, Armature armature) {
|
||||
if (clip == null || armature == null) return clip;
|
||||
|
||||
// Tiefe des flachsten Joints mit Translation-Track bestimmen
|
||||
|
||||
Reference in New Issue
Block a user