Bank-Sitz-Fix: clearKfOffset-Timing, Approach-Distanz, Animationsübergänge
- clearKfOffset() erst nach get_up-Animation (Callback) statt sofort beim Start → kein Slide des Visuals während der Aufsteh-Animation - Approach-Distanz zur Bank um 17.5cm verkürzt (läuft näher ran, sitzt tiefer) - blockingAnimRemaining um 1 Frame (1/60s) gekürzt → verhindert Extra-Keyframe-Hold am Animationsende (noch zu beobachten) - Diverses aus vorheriger Session: AnimSet-Editor, Navigation, Assets Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -603,6 +603,17 @@ public class EditorApp extends Application {
|
||||
updateSpawnFields(input.pickedSpawnInfo);
|
||||
}
|
||||
|
||||
// Modell-Editor: gebakte Scale aus j3o erkannt → Spinner aktualisieren
|
||||
if (input.modelEditorBakedScaleDetected) {
|
||||
input.modelEditorBakedScaleDetected = false;
|
||||
double bsX = input.modelEditorScaleX;
|
||||
double bsY = input.modelEditorScaleY;
|
||||
double bsZ = input.modelEditorScaleZ;
|
||||
if (modelEditorSpinX != null) modelEditorSpinX.getValueFactory().setValue(bsX);
|
||||
if (modelEditorSpinY != null) modelEditorSpinY.getValueFactory().setValue(bsY);
|
||||
if (modelEditorSpinZ != null) modelEditorSpinZ.getValueFactory().setValue(bsZ);
|
||||
}
|
||||
|
||||
// Modell-Editor: Bounds-Aktualisierung
|
||||
if (input.modelEditorBoundsReady) {
|
||||
input.modelEditorBoundsReady = false;
|
||||
@@ -5999,6 +6010,8 @@ public class EditorApp extends Application {
|
||||
input.modelInteractableOffsetY = meta.interactableOffsetY();
|
||||
input.modelInteractableOffsetZ = meta.interactableOffsetZ();
|
||||
input.modelInteractableRotY = meta.interactableRotY();
|
||||
input.modelInteractableActive = meta.interactableType() == de.blight.common.model.InteractableType.BED
|
||||
|| meta.interactableType() == de.blight.common.model.InteractableType.BENCH;
|
||||
input.modelInteractableOffsetChanged = true;
|
||||
|
||||
Label restPosHint = new Label("Klicke auf das Modell um den Ruhepunkt zu setzen:");
|
||||
@@ -6116,8 +6129,9 @@ public class EditorApp extends Application {
|
||||
|| nv == de.blight.common.model.InteractableType.BENCH;
|
||||
restPointBox.setVisible(show);
|
||||
restPointBox.setManaged(show);
|
||||
// Pfeil ein-/ausblenden — über SharedInput-Flag signalisieren
|
||||
input.modelInteractableOffsetChanged = show;
|
||||
// Pfeil ein-/ausblenden — immer rebuilden, damit Sichtbarkeit aktualisiert wird
|
||||
input.modelInteractableActive = show;
|
||||
input.modelInteractableOffsetChanged = true;
|
||||
});
|
||||
|
||||
// ── Buttons ───────────────────────────────────────────────────────────
|
||||
@@ -6654,8 +6668,9 @@ public class EditorApp extends Application {
|
||||
de.blight.common.model.InteractableType interactableType,
|
||||
float interactableOffsetX, float interactableOffsetY,
|
||||
float interactableOffsetZ, float interactableRotY) {
|
||||
// Scale wird in j3o eingebrannt → Meta bekommt immer 1.0 (kein doppelter Scale beim Laden)
|
||||
de.blight.common.ModelMeta meta = new de.blight.common.ModelMeta(
|
||||
name, category, tags, sx, sy, sz, uniform,
|
||||
name, category, tags, 1f, 1f, 1f, uniform,
|
||||
pivotY, placeY, solid, cast, receive, rndMin, rndMax,
|
||||
lod1Path, lod2Path, 30f, 80f, 120f,
|
||||
lights, emitters,
|
||||
@@ -6712,12 +6727,22 @@ public class EditorApp extends Application {
|
||||
// Asset-Tree aktualisieren
|
||||
input.refreshAssets = true;
|
||||
|
||||
// Thumbnail generieren (JME3-Thread liest das Flag und rendert)
|
||||
java.nio.file.Path finalJ3o = category.isEmpty() ? absolutePath
|
||||
: ASSET_ROOT.resolve("Models")
|
||||
.resolve(java.nio.file.Path.of(category.replace('/', java.io.File.separatorChar)))
|
||||
.resolve(name.isEmpty() ? absolutePath.getFileName().toString()
|
||||
: name.replaceAll("[\\\\/:*?\"<>|]", "_") + ".j3o");
|
||||
|
||||
// Scale in j3o einbrennen (JME3-Thread) – muss vor Thumbnail passieren
|
||||
if (sx != 1f || sy != 1f || sz != 1f) {
|
||||
input.modelEditorScaleX = sx;
|
||||
input.modelEditorScaleY = sy;
|
||||
input.modelEditorScaleZ = sz;
|
||||
input.modelEditorBakeScalePath = finalJ3o;
|
||||
input.modelEditorBakeScaleRequest = true;
|
||||
}
|
||||
|
||||
// Thumbnail generieren (JME3-Thread liest das Flag und rendert)
|
||||
input.modelEditorThumbnailRequest = finalJ3o;
|
||||
}
|
||||
|
||||
|
||||
@@ -674,6 +674,13 @@ public class SharedInput {
|
||||
*/
|
||||
public volatile java.nio.file.Path modelEditorThumbnailRequest = null;
|
||||
|
||||
/** JFX → JME: Scale in j3o einbrennen (für animierte Modelle als Spatial-Transform, sonst Vertex-Bake). */
|
||||
public volatile boolean modelEditorBakeScaleRequest = false;
|
||||
public volatile java.nio.file.Path modelEditorBakeScalePath = null;
|
||||
|
||||
/** JME → JFX: j3o hatte eine gebakte Scale, die von der Meta abwich – Spinner aktualisieren. */
|
||||
public volatile boolean modelEditorBakedScaleDetected = false;
|
||||
|
||||
/** JME → JFX: true wenn das geladene Modell eingebettete LOD-Kinder hat (kein separater Pfad nötig). */
|
||||
public volatile boolean modelEditorHasEmbeddedLods = false;
|
||||
|
||||
@@ -882,6 +889,8 @@ public class SharedInput {
|
||||
public volatile float modelInteractableOffsetZ = 0f;
|
||||
public volatile float modelInteractableRotY = 0f;
|
||||
public volatile boolean modelInteractableOffsetChanged = false;
|
||||
/** true wenn Interactable-Typ aktiv (BED/BENCH) – steuert Pfeil-Sichtbarkeit. */
|
||||
public volatile boolean modelInteractableActive = false;
|
||||
/** Gesetzt vom JME-Thread nach Raycast-Klick, damit JFX-Spinner aktualisiert werden. */
|
||||
public volatile boolean modelInteractablePosSetFromJme = false;
|
||||
|
||||
|
||||
@@ -225,10 +225,9 @@ public class AnimPreviewState extends BaseAppState {
|
||||
previewTarget.z + FastMath.cos(rotY) * FastMath.cos(rotX) * dist));
|
||||
c.lookAt(previewTarget, Vector3f.UNIT_Y);
|
||||
|
||||
// Achsen: Größe proportional zur Kameradistanz, immer am Weltpunkt (0,0,0)
|
||||
// Achsen: feste 1m Weltlänge (Schaft = 0.5 lokal → Scale 2.0)
|
||||
if (axesNode != null) {
|
||||
float s = previewCamDist * input.animPreviewZoom * 0.18f;
|
||||
axesNode.setLocalScale(s);
|
||||
axesNode.setLocalScale(2.0f);
|
||||
axesNode.setLocalTranslation(Vector3f.ZERO);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.jme3.scene.shape.Box;
|
||||
import com.jme3.scene.shape.Cylinder;
|
||||
import com.jme3.scene.shape.Dome;
|
||||
import com.jme3.scene.shape.Sphere;
|
||||
import com.jme3.anim.SkinningControl;
|
||||
import com.jme3.util.BufferUtils;
|
||||
import de.blight.editor.SharedInput;
|
||||
import org.slf4j.Logger;
|
||||
@@ -219,6 +220,18 @@ public class ModelEditorState extends BaseAppState {
|
||||
input.modelEditorAttachedEmitters);
|
||||
}
|
||||
|
||||
// Scale in j3o einbrennen (vor Thumbnail, damit Thumbnail die gebackene Datei erhält)
|
||||
if (input.modelEditorBakeScaleRequest) {
|
||||
input.modelEditorBakeScaleRequest = false;
|
||||
Path bakePath = input.modelEditorBakeScalePath;
|
||||
if (bakePath != null) {
|
||||
bakeScaleIntoModel(bakePath,
|
||||
input.modelEditorScaleX,
|
||||
input.modelEditorScaleY,
|
||||
input.modelEditorScaleZ);
|
||||
}
|
||||
}
|
||||
|
||||
// Thumbnail auf Anforderung generieren
|
||||
Path thumbReq = input.modelEditorThumbnailRequest;
|
||||
if (thumbReq != null && modelWrapper != null) {
|
||||
@@ -350,6 +363,18 @@ public class ModelEditorState extends BaseAppState {
|
||||
modelWrapper.attachChild(box);
|
||||
}
|
||||
|
||||
// Gebakte Spatial-Scale erkennen: j3o hat explizit gesetzte Scale, Meta sagt 1.0
|
||||
// → Scale aus j3o übernehmen und JavaFX-Spinner aktualisieren lassen
|
||||
com.jme3.math.Vector3f jScale = modelWrapper.getChildren().isEmpty()
|
||||
? com.jme3.math.Vector3f.UNIT_XYZ
|
||||
: modelWrapper.getChild(0).getLocalScale();
|
||||
if (Math.abs(jScale.y - 1f) > 0.001f && Math.abs(input.modelEditorScaleY - 1f) < 0.001f) {
|
||||
input.modelEditorScaleX = jScale.x;
|
||||
input.modelEditorScaleY = jScale.y;
|
||||
input.modelEditorScaleZ = jScale.z;
|
||||
input.modelEditorBakedScaleDetected = true;
|
||||
}
|
||||
|
||||
// Skalierung aus SharedInput anwenden
|
||||
applyScale(input.modelEditorScaleX, input.modelEditorScaleY, input.modelEditorScaleZ);
|
||||
applyPivot(input.modelEditorPivotY);
|
||||
@@ -767,6 +792,48 @@ public class ModelEditorState extends BaseAppState {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Scale-Bake ────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Brennt den Scale (sx,sy,sz) in die j3o-Datei ein.
|
||||
* Animierte Modelle (SkinningControl vorhanden): Scale als Spatial-Transform gespeichert.
|
||||
* Statische Modelle: Scale in Vertex-Positionen gebacken (wie ModelImportState beim Import).
|
||||
*/
|
||||
private void bakeScaleIntoModel(Path j3oPath, float sx, float sy, float sz) {
|
||||
try {
|
||||
BinaryImporter importer = BinaryImporter.getInstance();
|
||||
importer.setAssetManager(app.getAssetManager());
|
||||
Savable savable = importer.load(j3oPath.toFile());
|
||||
if (!(savable instanceof Spatial root)) {
|
||||
log.warn("[ModelEditor] Bake: kein Spatial in {}", j3oPath.getFileName());
|
||||
return;
|
||||
}
|
||||
if (hasSkinningControl(root)) {
|
||||
root.setLocalScale(sx, sy, sz);
|
||||
log.info("[ModelEditor] Animiert: Scale ({},{},{}) als Spatial-Transform gespeichert", sx, sy, sz);
|
||||
} else {
|
||||
root.setLocalScale(sx, sy, sz);
|
||||
ModelImportState.stripControls(root);
|
||||
ModelImportState.bakeTransform(root, new Matrix4f());
|
||||
log.info("[ModelEditor] Statisch: Scale ({},{},{}) in Vertices gebacken", sx, sy, sz);
|
||||
}
|
||||
BinaryExporter.getInstance().save(root, j3oPath.toFile());
|
||||
log.info("[ModelEditor] j3o nach Bake gespeichert: {}", j3oPath.getFileName());
|
||||
} catch (Exception e) {
|
||||
log.error("[ModelEditor] Scale-Bake fehlgeschlagen: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasSkinningControl(Spatial s) {
|
||||
if (s.getControl(SkinningControl.class) != null) return true;
|
||||
if (s instanceof Node n) {
|
||||
for (Spatial c : n.getChildren()) {
|
||||
if (hasSkinningControl(c)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ── Thumbnail ─────────────────────────────────────────────────────────────
|
||||
|
||||
private void generateThumbnail(Path j3oPath) {
|
||||
@@ -842,7 +909,8 @@ public class ModelEditorState extends BaseAppState {
|
||||
group.attachChild(shaft);
|
||||
group.attachChild(head);
|
||||
interactableArrowNode.attachChild(group);
|
||||
interactableArrowNode.setCullHint(Spatial.CullHint.Inherit);
|
||||
interactableArrowNode.setCullHint(
|
||||
input.modelInteractableActive ? Spatial.CullHint.Inherit : Spatial.CullHint.Always);
|
||||
}
|
||||
|
||||
/** Setzt den Pfeil sichtbar/unsichtbar. */
|
||||
|
||||
@@ -619,7 +619,7 @@ public class ModelImportState extends BaseAppState {
|
||||
bakeTransform(s, new Matrix4f());
|
||||
}
|
||||
|
||||
private static void bakeTransform(Spatial s, Matrix4f accum) {
|
||||
static void bakeTransform(Spatial s, Matrix4f accum) {
|
||||
Matrix4f localMat = new Matrix4f();
|
||||
s.getLocalTransform().toTransformMatrix(localMat);
|
||||
Matrix4f combined = accum.mult(localMat);
|
||||
@@ -637,7 +637,7 @@ public class ModelImportState extends BaseAppState {
|
||||
}
|
||||
}
|
||||
|
||||
private static void applyMatrixToMesh(Geometry g, Matrix4f mat) {
|
||||
static void applyMatrixToMesh(Geometry g, Matrix4f mat) {
|
||||
Mesh newMesh = g.getMesh().deepClone();
|
||||
|
||||
FloatBuffer pos = newMesh.getFloatBuffer(VertexBuffer.Type.Position);
|
||||
@@ -673,7 +673,7 @@ public class ModelImportState extends BaseAppState {
|
||||
g.setMesh(newMesh);
|
||||
}
|
||||
|
||||
private static Matrix3f buildNormalMatrix(Matrix4f mat) {
|
||||
static Matrix3f buildNormalMatrix(Matrix4f mat) {
|
||||
Matrix3f m3 = new Matrix3f(
|
||||
mat.m00, mat.m01, mat.m02,
|
||||
mat.m10, mat.m11, mat.m12,
|
||||
@@ -683,7 +683,7 @@ public class ModelImportState extends BaseAppState {
|
||||
return m3;
|
||||
}
|
||||
|
||||
private static void stripControls(Spatial s) {
|
||||
static void stripControls(Spatial s) {
|
||||
while (s.getNumControls() > 0) s.removeControl(s.getControl(0));
|
||||
if (s instanceof Node n) n.getChildren().forEach(ModelImportState::stripControls);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user