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,
|
"level": 0,
|
||||||
"xp": 0,
|
"xp": 0,
|
||||||
"currentHp": 0,
|
"currentHp": 0,
|
||||||
"maxHp": 0,
|
|
||||||
"currentStamina": 0,
|
"currentStamina": 0,
|
||||||
"maxStamina": 0,
|
|
||||||
"currentMana": 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": [],
|
"listeners": [],
|
||||||
"characterId": "hero",
|
"characterId": "hero",
|
||||||
"name": {
|
"name": {
|
||||||
|
|||||||
@@ -498,6 +498,12 @@ public class EditorApp extends Application {
|
|||||||
if (charEditorStatusLabel != null) charEditorStatusLabel.setText(embedStatus);
|
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;
|
String rts = input.randomTreeStatus;
|
||||||
if (randomTreeStatusLabel != null && rts != null) {
|
if (randomTreeStatusLabel != null && rts != null) {
|
||||||
randomTreeStatusLabel.setText(rts);
|
randomTreeStatusLabel.setText(rts);
|
||||||
@@ -9452,6 +9458,19 @@ public class EditorApp extends Application {
|
|||||||
input.animEmbedRequest.set(new SharedInput.AnimEmbedRequest(modelPath, setName));
|
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(
|
charEditContainer.getChildren().addAll(
|
||||||
new Label("ID:"), charIdField,
|
new Label("ID:"), charIdField,
|
||||||
new Label("Name:"), charNameField,
|
new Label("Name:"), charNameField,
|
||||||
@@ -9562,7 +9581,8 @@ public class EditorApp extends Application {
|
|||||||
charEditContainer.getChildren().addAll(
|
charEditContainer.getChildren().addAll(
|
||||||
new Label("Modell:"), charModelCombo,
|
new Label("Modell:"), charModelCombo,
|
||||||
new Label("Anim-Set:"), charAnimSetCombo,
|
new Label("Anim-Set:"), charAnimSetCombo,
|
||||||
embedAnimBtn
|
embedAnimBtn,
|
||||||
|
stripClipsBtn
|
||||||
);
|
);
|
||||||
|
|
||||||
// ── Aktions-Zuweisung ──────────────────────────────────────────────────
|
// ── Aktions-Zuweisung ──────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -594,10 +594,16 @@ public class SharedInput {
|
|||||||
public final java.util.concurrent.atomic.AtomicReference<AnimEmbedRequest>
|
public final java.util.concurrent.atomic.AtomicReference<AnimEmbedRequest>
|
||||||
animEmbedRequest = new java.util.concurrent.atomic.AtomicReference<>();
|
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. */
|
/** 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). */
|
/** 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 ──────────────────────────────────────────────────
|
// ── Modell-Konvertierung ──────────────────────────────────────────────────
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -178,6 +178,9 @@ public class AnimPreviewState extends BaseAppState {
|
|||||||
SharedInput.AnimEmbedRequest embedReq = input.animEmbedRequest.getAndSet(null);
|
SharedInput.AnimEmbedRequest embedReq = input.animEmbedRequest.getAndSet(null);
|
||||||
if (embedReq != null) executeAnimEmbed(embedReq);
|
if (embedReq != null) executeAnimEmbed(embedReq);
|
||||||
|
|
||||||
|
String stripPath = input.animStripClipsRequest.getAndSet(null);
|
||||||
|
if (stripPath != null) executeStripClips(stripPath);
|
||||||
|
|
||||||
// Geschwindigkeit live anpassen
|
// Geschwindigkeit live anpassen
|
||||||
if (currentAction != null) {
|
if (currentAction != null) {
|
||||||
try { currentAction.setSpeed(input.animPreviewSpeed); } catch (Exception ignored) {}
|
try { currentAction.setSpeed(input.animPreviewSpeed); } catch (Exception ignored) {}
|
||||||
@@ -258,6 +261,19 @@ public class AnimPreviewState extends BaseAppState {
|
|||||||
currentModelPath = assetPath;
|
currentModelPath = assetPath;
|
||||||
previewHolder.attachChild(model);
|
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
|
// Kamera auf Bounding Box ausrichten
|
||||||
model.updateGeometricState();
|
model.updateGeometricState();
|
||||||
if (model.getWorldBound() instanceof BoundingBox bb) {
|
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);
|
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
|
// Direkt in die Clip-Bibliothek speichern – das Modell wird nicht modifiziert
|
||||||
saveClipToFile(toSave, dstArm != null ? dstArm : srcArm,
|
saveClipToFile(toSave, snapArm, clipsDir.resolve(saveName + ".j3o"));
|
||||||
clipsDir.resolve(saveName + ".j3o"));
|
|
||||||
// Für den aktuellen Preview-Session auch auf das Modell anwenden (wenn geladen)
|
// Für den aktuellen Preview-Session auch auf das Modell anwenden (wenn geladen)
|
||||||
if (targetAC != null) targetAC.addAnimClip(toSave);
|
if (targetAC != null) targetAC.addAnimClip(toSave);
|
||||||
saved++;
|
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,
|
private void saveClipToFile(AnimClip clip, com.jme3.anim.Armature armature,
|
||||||
java.nio.file.Path outFile) throws Exception {
|
java.nio.file.Path outFile) throws Exception {
|
||||||
Node holder = new Node("clip_" + clip.getName());
|
Node holder = new Node("clip_" + clip.getName());
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ public enum AnimationAction {
|
|||||||
SITTING,
|
SITTING,
|
||||||
SIT_DOWN_FLOOR,
|
SIT_DOWN_FLOOR,
|
||||||
SITTING_FLOOR,
|
SITTING_FLOOR,
|
||||||
GET_UP_FLOOR;
|
GET_UP_FLOOR,
|
||||||
|
REVIVE;
|
||||||
|
|
||||||
/** Lesbare Bezeichnung für UI-Anzeige, via TextRegistry aufgelöst. */
|
/** Lesbare Bezeichnung für UI-Anzeige, via TextRegistry aufgelöst. */
|
||||||
public String displayName() {
|
public String displayName() {
|
||||||
@@ -48,6 +49,7 @@ public enum AnimationAction {
|
|||||||
case SIT_DOWN_FLOOR -> TextRegistry.resolve(null, key, "Hinsetzen (Boden)");
|
case SIT_DOWN_FLOOR -> TextRegistry.resolve(null, key, "Hinsetzen (Boden)");
|
||||||
case SITTING_FLOOR -> TextRegistry.resolve(null, key, "Sitzen (Boden)");
|
case SITTING_FLOOR -> TextRegistry.resolve(null, key, "Sitzen (Boden)");
|
||||||
case GET_UP_FLOOR -> TextRegistry.resolve(null, key, "Aufstehen (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.
|
* wird Hips gesnappt. So passt der Snap zu beiden Rig-Strukturen.
|
||||||
* Erstellt einen neuen in-memory-Clip; J3O-Dateien bleiben unverändert.
|
* 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;
|
if (clip == null || armature == null) return clip;
|
||||||
|
|
||||||
// Tiefe des flachsten Joints mit Translation-Track bestimmen
|
// Tiefe des flachsten Joints mit Translation-Track bestimmen
|
||||||
|
|||||||
Reference in New Issue
Block a user