diff --git a/assets/imported/animations/sitting.glb b/assets/imported/animations/sitting_bench.glb similarity index 96% rename from assets/imported/animations/sitting.glb rename to assets/imported/animations/sitting_bench.glb index 527905a..f47b3c1 100644 Binary files a/assets/imported/animations/sitting.glb and b/assets/imported/animations/sitting_bench.glb differ diff --git a/assets/imported/animations/get_up_sitting.glb b/assets/imported/animations/stand_up_bench.glb similarity index 99% rename from assets/imported/animations/get_up_sitting.glb rename to assets/imported/animations/stand_up_bench.glb index 938f52f..99c6391 100644 Binary files a/assets/imported/animations/get_up_sitting.glb and b/assets/imported/animations/stand_up_bench.glb differ diff --git a/assets/imported/animations/standup.glb b/assets/imported/animations/standup.glb deleted file mode 100644 index a8f4b47..0000000 Binary files a/assets/imported/animations/standup.glb and /dev/null differ diff --git a/blight-assets/src/main/resources/animations/clips/alive_again.j3o b/blight-assets/src/main/resources/animations/clips/alive_again.j3o index c02ba23..33dcfcc 100644 Binary files a/blight-assets/src/main/resources/animations/clips/alive_again.j3o and b/blight-assets/src/main/resources/animations/clips/alive_again.j3o differ diff --git a/blight-assets/src/main/resources/animations/clips/idle_jump.j3o b/blight-assets/src/main/resources/animations/clips/idle_jump.j3o index 374e699..9f99790 100644 Binary files a/blight-assets/src/main/resources/animations/clips/idle_jump.j3o and b/blight-assets/src/main/resources/animations/clips/idle_jump.j3o differ diff --git a/blight-assets/src/main/resources/animations/clips/sitting.j3o b/blight-assets/src/main/resources/animations/clips/sitting_bench.j3o similarity index 93% rename from blight-assets/src/main/resources/animations/clips/sitting.j3o rename to blight-assets/src/main/resources/animations/clips/sitting_bench.j3o index 80ad1b2..13a7ac5 100644 Binary files a/blight-assets/src/main/resources/animations/clips/sitting.j3o and b/blight-assets/src/main/resources/animations/clips/sitting_bench.j3o differ diff --git a/blight-assets/src/main/resources/animations/clips/get_up_sitting.j3o b/blight-assets/src/main/resources/animations/clips/stand_up_bench.j3o similarity index 99% rename from blight-assets/src/main/resources/animations/clips/get_up_sitting.j3o rename to blight-assets/src/main/resources/animations/clips/stand_up_bench.j3o index 1c6b149..45c4e30 100644 Binary files a/blight-assets/src/main/resources/animations/clips/get_up_sitting.j3o and b/blight-assets/src/main/resources/animations/clips/stand_up_bench.j3o differ diff --git a/blight-assets/src/main/resources/animations/clips/standup.j3o b/blight-assets/src/main/resources/animations/clips/standup.j3o deleted file mode 100644 index f3b210d..0000000 Binary files a/blight-assets/src/main/resources/animations/clips/standup.j3o and /dev/null differ diff --git a/blight-assets/src/main/resources/animations/clips/tpose.j3o b/blight-assets/src/main/resources/animations/clips/tpose.j3o index 465c246..8da9796 100644 Binary files a/blight-assets/src/main/resources/animations/clips/tpose.j3o and b/blight-assets/src/main/resources/animations/clips/tpose.j3o differ diff --git a/blight-assets/src/main/resources/animations/sets/human.animset.json b/blight-assets/src/main/resources/animations/sets/human.animset.json index 083ea5c..c3aba07 100644 --- a/blight-assets/src/main/resources/animations/sets/human.animset.json +++ b/blight-assets/src/main/resources/animations/sets/human.animset.json @@ -1,18 +1,19 @@ { "clips": [ - "get_up_sitting", + "alive_again", "idle", "idle_jump", "pickup", "running", "running_jump", - "sitting", + "sit_down_bench", + "sitting_bench", "sitting_floor", "sprinting", "stand_up", + "stand_up_bench", "tpose", - "walking", - "sit_down_bench" + "walking" ], "actionMap": { "DEFAULT": "tpose", @@ -20,12 +21,13 @@ "WALK": "walking", "RUN": "running", "SPRINT": "sprinting", - "RUNNING_JUMP": "running_jump", "JUMP": "idle_jump", + "RUNNING_JUMP": "running_jump", "PICK_UP": "pickup", - "SITTING": "sitting", "SIT_DOWN": "sit_down_bench", - "SIT_UP": "get_up_sitting" + "SIT_UP": "stand_up_bench", + "SITTING": "sitting_bench", + "REVIVE": "alive_again" }, "previewModelPath": "Models/Chars/mainchar.j3o", "motionKeyframes": { diff --git a/blight-editor/src/main/resources/logback.xml b/blight-editor/src/main/resources/logback.xml index 0ab35b2..eb72a9b 100644 --- a/blight-editor/src/main/resources/logback.xml +++ b/blight-editor/src/main/resources/logback.xml @@ -25,6 +25,7 @@ + diff --git a/blight-game/src/main/java/de/blight/game/animation/AnimOffset.java b/blight-game/src/main/java/de/blight/game/animation/AnimOffset.java new file mode 100644 index 0000000..9a76704 --- /dev/null +++ b/blight-game/src/main/java/de/blight/game/animation/AnimOffset.java @@ -0,0 +1,22 @@ +package de.blight.game.animation; + +/** + * Positions-/Rotations-Versatz für eine blockierende Animation. + * + * TX/TZ: Versatz in Charakter-lokalem Raum (TX=seitlich, TZ=vorwärts relativ zur Blickrichtung). + * TY: Versatz in Welt-Y (hoch/runter). + * RX/RY/RZ: Additiver Rotations-Versatz in Grad (Euler XYZ, relativ zur Startrotation). + */ +public class AnimOffset { + + public float tx, ty, tz; // Positions-Versatz (Meter) + public float rx, ry, rz; // Rotations-Versatz (Grad) + + public AnimOffset() {} + + public AnimOffset(float tx, float ty, float tz, + float rx, float ry, float rz) { + this.tx = tx; this.ty = ty; this.tz = tz; + this.rx = rx; this.ry = ry; this.rz = rz; + } +} diff --git a/blight-game/src/main/java/de/blight/game/animation/FootIKControl.java b/blight-game/src/main/java/de/blight/game/animation/FootIKControl.java new file mode 100644 index 0000000..f4d9496 --- /dev/null +++ b/blight-game/src/main/java/de/blight/game/animation/FootIKControl.java @@ -0,0 +1,226 @@ +package de.blight.game.animation; + +import com.jme3.anim.Armature; +import com.jme3.anim.Joint; +import com.jme3.math.FastMath; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.ViewPort; +import com.jme3.scene.Spatial; +import com.jme3.scene.control.AbstractControl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 2-Bone-Foot-IK – hält die Füße an ihrer World-Space-Position fest. + * + * Muss NACH AnimComposer und SkinningControl zur Spatial hinzugefügt werden, + * damit controlUpdate() nach der Animations-Anwendung läuft. + */ +public final class FootIKControl extends AbstractControl { + + private static final Logger log = LoggerFactory.getLogger(FootIKControl.class); + + private final Armature armature; + + private final Joint leftThigh, leftCalf, leftFoot; + private final Joint rightThigh, rightCalf, rightFoot; + + // IK-Ziele in World-Space; null = IK inaktiv + private Vector3f leftWorldTarget = null; + private Vector3f rightWorldTarget = null; + + private static final float MIN_CORRECTION_DIST = 0.005f; + + private int debugFramesLeft = 0; + + public FootIKControl(Armature armature) { + this.armature = armature; + leftThigh = findJoint(armature, "L_Thigh", "LeftUpLeg", "mixamorig:LeftUpLeg"); + leftCalf = findJoint(armature, "L_Calf", "LeftLeg", "mixamorig:LeftLeg"); + leftFoot = findJoint(armature, "L_Foot", "LeftFoot", "mixamorig:LeftFoot"); + rightThigh = findJoint(armature, "R_Thigh", "RightUpLeg", "mixamorig:RightUpLeg"); + rightCalf = findJoint(armature, "R_Calf", "RightLeg", "mixamorig:RightLeg"); + rightFoot = findJoint(armature, "R_Foot", "RightFoot", "mixamorig:RightFoot"); + log.info("[FootIK] Init: L={} R={}", + leftFoot != null ? leftFoot.getName() : "n/a", + rightFoot != null ? rightFoot.getName() : "n/a"); + } + + /** + * Fixiert die Füße an ihrer aktuellen World-Space-Position. + * Muss aufgerufen werden NACHDEM die neue Animation gesetzt wurde, + * aber BEVOR der kfOffset die Armature verschoben hat. + */ + public void lockFeetAtCurrentWorldPos() { + Spatial sp = getSpatial(); + if (sp == null || leftFoot == null || rightFoot == null) { + return; + } + leftWorldTarget = sp.localToWorld( + leftFoot.getModelTransform().getTranslation().clone(), new Vector3f()); + rightWorldTarget = sp.localToWorld( + rightFoot.getModelTransform().getTranslation().clone(), new Vector3f()); + debugFramesLeft = 10; + log.info("[FootIK] LockWorld: L={} R={}", fv(leftWorldTarget), fv(rightWorldTarget)); + } + + public void releaseFeet() { + leftWorldTarget = null; + rightWorldTarget = null; + debugFramesLeft = 0; + log.info("[FootIK] Freigegeben"); + } + + public boolean isActive() { + return leftWorldTarget != null || rightWorldTarget != null; + } + + // ── Control-Update ──────────────────────────────────────────────────────── + + @Override + protected void controlUpdate(float tpf) { + if (leftWorldTarget == null && rightWorldTarget == null) { + return; + } + + Spatial sp = getSpatial(); + if (sp == null) { + return; + } + + boolean dbg = debugFramesLeft > 0; + if (dbg) { + debugFramesLeft--; + } + + if (leftWorldTarget != null && leftThigh != null && leftCalf != null && leftFoot != null) { + Vector3f modelTarget = sp.worldToLocal(leftWorldTarget, new Vector3f()); + solveLeg(leftThigh, leftCalf, leftFoot, modelTarget, dbg, "L"); + } + if (rightWorldTarget != null && rightThigh != null && rightCalf != null && rightFoot != null) { + Vector3f modelTarget = sp.worldToLocal(rightWorldTarget, new Vector3f()); + solveLeg(rightThigh, rightCalf, rightFoot, modelTarget, dbg, "R"); + } + + armature.update(); + } + + @Override + protected void controlRender(RenderManager rm, ViewPort vp) {} + + // ── 2-Bone-IK-Solver ───────────────────────────────────────────────────── + + private void solveLeg(Joint thigh, Joint calf, Joint foot, + Vector3f target, boolean dbg, String side) { + Vector3f A = thigh.getModelTransform().getTranslation().clone(); + Vector3f B = calf.getModelTransform().getTranslation().clone(); + Vector3f C = foot.getModelTransform().getTranslation().clone(); + + float L1 = A.distance(B); + float L2 = B.distance(C); + if (L1 < 0.0001f || L2 < 0.0001f) { + return; + } + + float footErr = C.distance(target); + if (dbg) { + log.info("[FootIK][{}] A={} B={} C={} T={} err={} L1={} L2={}", + side, fv(A), fv(B), fv(C), fv(target), + String.format("%.4f", footErr), + String.format("%.3f", L1), String.format("%.3f", L2)); + } + if (footErr < MIN_CORRECTION_DIST) { + return; + } + + float d = A.distance(target); + d = FastMath.clamp(d, Math.abs(L1 - L2) + 0.0001f, L1 + L2 - 0.0001f); + + float cosA = (L1 * L1 + d * d - L2 * L2) / (2f * L1 * d); + float angleA = FastMath.acos(FastMath.clamp(cosA, -1f, 1f)); + + Vector3f dirAT = target.subtract(A).normalizeLocal(); + Vector3f kneeDir = B.subtract(A).normalizeLocal(); + float proj = dirAT.dot(kneeDir); + Vector3f perp = kneeDir.subtract(dirAT.mult(proj)); + if (perp.lengthSquared() < 0.0001f) { + perp = findPerp(dirAT); + } + perp.normalizeLocal(); + + Vector3f B_new = A.add( + dirAT.mult(L1 * FastMath.cos(angleA)).addLocal( + perp.mult(L1 * FastMath.sin(angleA))) + ); + + // Oberschenkel rotieren + Vector3f curThighDir = B.subtract(A).normalizeLocal(); + Vector3f dstThighDir = B_new.subtract(A).normalizeLocal(); + Quaternion thighArc = rotArc(curThighDir, dstThighDir); + Quaternion thighModelRot = thigh.getModelTransform().getRotation().clone(); + Quaternion newThighModelRot = thighArc.mult(thighModelRot); + Joint thighParent = thigh.getParent(); + Quaternion parentRot = thighParent != null + ? thighParent.getModelTransform().getRotation() : new Quaternion(); + thigh.setLocalRotation(parentRot.inverse().mult(newThighModelRot)); + + // Unterschenkel rotieren — Calf-Richtung NACH der Thigh-Rotation verwenden + Vector3f curCalfDirOrig = C.subtract(B).normalizeLocal(); + Vector3f curCalfDirRotated = thighArc.mult(curCalfDirOrig); + Vector3f dstCalfDir = target.subtract(B_new).normalizeLocal(); + Quaternion calfArc = rotArc(curCalfDirRotated, dstCalfDir); + Quaternion oldCalfLocalRot = calf.getLocalRotation().clone(); + Quaternion actualCalfModel = newThighModelRot.mult(oldCalfLocalRot); + Quaternion newCalfModelRot = calfArc.mult(actualCalfModel); + calf.setLocalRotation(newThighModelRot.inverse().mult(newCalfModelRot)); + + if (dbg) { + float thighAngle = thighArc.toAngleAxis(new Vector3f()) * FastMath.RAD_TO_DEG; + float calfAngle = calfArc.toAngleAxis(new Vector3f()) * FastMath.RAD_TO_DEG; + log.info("[FootIK][{}] B_new={} thigh={}° calf={}°", + side, fv(B_new), + String.format("%.1f", thighAngle), + String.format("%.1f", calfAngle)); + } + } + + // ── Hilfsmethoden ──────────────────────────────────────────────────────── + + private static Quaternion rotArc(Vector3f from, Vector3f to) { + float dot = FastMath.clamp(from.dot(to), -1f, 1f); + if (dot > 0.9999f) { + return new Quaternion(); + } + if (dot < -0.9999f) { + return new Quaternion().fromAngleAxis(FastMath.PI, findPerp(from)); + } + Vector3f axis = from.cross(to).normalizeLocal(); + Quaternion q = new Quaternion(); + q.fromAngleAxis(FastMath.acos(dot), axis); + return q; + } + + private static Vector3f findPerp(Vector3f v) { + Vector3f p = v.cross(Vector3f.UNIT_X); + if (p.lengthSquared() < 0.0001f) { + p = v.cross(Vector3f.UNIT_Z); + } + return p.normalizeLocal(); + } + + private static Joint findJoint(Armature arm, String... names) { + for (String n : names) { + Joint j = arm.getJoint(n); + if (j != null) { + return j; + } + } + return null; + } + + private static String fv(Vector3f v) { + return String.format("(%.3f,%.3f,%.3f)", v.x, v.y, v.z); + } +} diff --git a/blight-game/src/main/java/de/blight/game/control/PlayerInputControl.java b/blight-game/src/main/java/de/blight/game/control/PlayerInputControl.java index f30a063..3c514ae 100644 --- a/blight-game/src/main/java/de/blight/game/control/PlayerInputControl.java +++ b/blight-game/src/main/java/de/blight/game/control/PlayerInputControl.java @@ -557,7 +557,7 @@ public class PlayerInputControl { public void clearKfOffset() { log.info("[KF] clearKfOffset() → Lerp zu 0, current=({},{},{})", kfOffsetCurrent.x, kfOffsetCurrent.y, kfOffsetCurrent.z); kfOffsetTarget.set(0, 0, 0); - kfOffsetSpeed = 5.0f; // 0.25m in ~3 Frames bei 60fps + kfOffsetSpeed = 5.0f; } private void clearMotionKfOffset() { diff --git a/blight-game/src/main/resources/logback.xml b/blight-game/src/main/resources/logback.xml index 0a9fd60..338cdce 100644 --- a/blight-game/src/main/resources/logback.xml +++ b/blight-game/src/main/resources/logback.xml @@ -2,7 +2,7 @@ - ERROR + INFO %d{HH:mm:ss.SSS} %-5level [%logger{30}] %msg%n%ex