Compare commits
3 Commits
93a3c266e1
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| f189e6d81f | |||
| 238776cfde | |||
| 60741d3116 |
Binary file not shown.
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.6 KiB |
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
#Mon Oct 12 14:17:44 CEST 2026
|
||||
#Mon Oct 19 14:39:04 CEST 2026
|
||||
attachedEmitters.count=0
|
||||
attachedLights.count=0
|
||||
attachedSounds.count=0
|
||||
|
||||
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
#Thu Oct 15 15:05:26 CEST 2026
|
||||
#Mon Oct 19 14:39:11 CEST 2026
|
||||
attachedEmitters.count=0
|
||||
attachedLights.count=0
|
||||
attachedSounds.count=0
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
"damage": 50,
|
||||
"grip": {
|
||||
"posX": 0.10248406,
|
||||
"posY": 0.104730606,
|
||||
"posY": 0.10473058,
|
||||
"posZ": 0.4630257,
|
||||
"rotX": 0.0,
|
||||
"rotY": 0.0,
|
||||
"rotZ": 0.0,
|
||||
"grip2PosX": 0.0024840608,
|
||||
"grip2PosY": 0.104730606,
|
||||
"grip2PosY": 0.10473058,
|
||||
"grip2PosZ": 0.6630257,
|
||||
"handOffsetX": 0.0,
|
||||
"handOffsetY": 0.0,
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
"damage": 0,
|
||||
"grip": {
|
||||
"posX": 0.1114975,
|
||||
"posY": 0.07676052,
|
||||
"posZ": -0.27503356,
|
||||
"posY": 0.07795838,
|
||||
"posZ": 0.36503196,
|
||||
"rotX": 0.0,
|
||||
"rotY": 0.0,
|
||||
"rotZ": 0.0,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"SWORD": {
|
||||
"handOffX": 0.105,
|
||||
"handOffY": 0.1,
|
||||
"handOffZ": 0.5,
|
||||
"handOffX": 0.085,
|
||||
"handOffY": 0.115,
|
||||
"handOffZ": -0.095,
|
||||
"hand2OffX": 0.0,
|
||||
"hand2OffY": 0.0,
|
||||
"hand2OffZ": 0.0
|
||||
|
||||
@@ -9,11 +9,14 @@ import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Lädt und speichert per-Kategorie Schild-Attachment-Konfiguration (shield_attachment.json).
|
||||
* Siehe WeaponAttachmentIO: liest vom Dateisystem (Dev-Umgebung) und prüft bei jedem Zugriff
|
||||
* die mtime der Datei, damit Editor-Änderungen ohne Neustart ankommen.
|
||||
*/
|
||||
public final class ShieldAttachmentIO {
|
||||
|
||||
@@ -21,8 +24,17 @@ public final class ShieldAttachmentIO {
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
||||
private static final String CLASSPATH_FILE = "shield_attachment.json";
|
||||
|
||||
private static final String[] ASSET_BASES = {
|
||||
"blight-assets/src/main/resources",
|
||||
"../blight-assets/src/main/resources",
|
||||
"assets",
|
||||
".",
|
||||
};
|
||||
|
||||
private static final Map<String, ShieldCategoryAttachment> CONFIGS = new HashMap<>();
|
||||
private static boolean loaded = false;
|
||||
private static Path cachedFile = null;
|
||||
private static long cachedMtime = -1;
|
||||
|
||||
private ShieldAttachmentIO() {}
|
||||
|
||||
@@ -77,9 +89,33 @@ public final class ShieldAttachmentIO {
|
||||
}
|
||||
|
||||
private static synchronized void ensureLoaded() {
|
||||
Path file = resolveFile();
|
||||
if (file != null) {
|
||||
try {
|
||||
long mtime = Files.getLastModifiedTime(file).toMillis();
|
||||
if (!file.equals(cachedFile) || mtime != cachedMtime) {
|
||||
parse(Files.readString(file, StandardCharsets.UTF_8));
|
||||
cachedFile = file;
|
||||
cachedMtime = mtime;
|
||||
}
|
||||
loaded = true;
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
log.warn("[ShieldAttachmentIO] Ladefehler: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
if (!loaded) loadFromClasspath();
|
||||
}
|
||||
|
||||
/** Sucht {@code shield_attachment.json} im Dateisystem der Dev-Umgebung (Editor/Gradle-Run). */
|
||||
private static Path resolveFile() {
|
||||
for (String base : ASSET_BASES) {
|
||||
Path p = Paths.get(base, CLASSPATH_FILE);
|
||||
if (Files.isRegularFile(p)) return p;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void parse(String json) {
|
||||
CONFIGS.clear();
|
||||
JsonObject obj = JsonParser.parseString(json).getAsJsonObject();
|
||||
|
||||
@@ -9,12 +9,16 @@ import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Lädt und speichert per-Kategorie Waffen-Attachment-Konfiguration (weapon_attachment.json).
|
||||
* Singleton-Map: wird beim ersten Zugriff automatisch vom Classpath geladen.
|
||||
* Singleton-Map: wird vom Dateisystem geladen (Dev-Umgebung, siehe ASSET_BASES) und bei jedem
|
||||
* Zugriff gegen die mtime der Datei geprüft, damit Änderungen aus dem Editor ohne Neustart
|
||||
* ankommen. Nur wenn keine Quelldatei auffindbar ist (z.B. gepackter Build), wird einmalig
|
||||
* vom Classpath geladen.
|
||||
*/
|
||||
public final class WeaponAttachmentIO {
|
||||
|
||||
@@ -22,8 +26,19 @@ public final class WeaponAttachmentIO {
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
||||
private static final String CLASSPATH_FILE = "weapon_attachment.json";
|
||||
|
||||
/** Siehe AnimationLibrary.ASSET_BASES (blight-game) – hier dupliziert, da blight-common
|
||||
* keine Abhängigkeit auf blight-game haben darf. */
|
||||
private static final String[] ASSET_BASES = {
|
||||
"blight-assets/src/main/resources",
|
||||
"../blight-assets/src/main/resources",
|
||||
"assets",
|
||||
".",
|
||||
};
|
||||
|
||||
private static final Map<String, WeaponCategoryAttachment> CONFIGS = new HashMap<>();
|
||||
private static boolean loaded = false;
|
||||
private static Path cachedFile = null;
|
||||
private static long cachedMtime = -1;
|
||||
|
||||
private WeaponAttachmentIO() {}
|
||||
|
||||
@@ -84,9 +99,33 @@ public final class WeaponAttachmentIO {
|
||||
}
|
||||
|
||||
private static synchronized void ensureLoaded() {
|
||||
Path file = resolveFile();
|
||||
if (file != null) {
|
||||
try {
|
||||
long mtime = Files.getLastModifiedTime(file).toMillis();
|
||||
if (!file.equals(cachedFile) || mtime != cachedMtime) {
|
||||
parse(Files.readString(file, StandardCharsets.UTF_8));
|
||||
cachedFile = file;
|
||||
cachedMtime = mtime;
|
||||
}
|
||||
loaded = true;
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
log.warn("[WeaponAttachmentIO] Ladefehler: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
if (!loaded) loadFromClasspath();
|
||||
}
|
||||
|
||||
/** Sucht {@code weapon_attachment.json} im Dateisystem der Dev-Umgebung (Editor/Gradle-Run). */
|
||||
private static Path resolveFile() {
|
||||
for (String base : ASSET_BASES) {
|
||||
Path p = Paths.get(base, CLASSPATH_FILE);
|
||||
if (Files.isRegularFile(p)) return p;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void parse(String json) {
|
||||
CONFIGS.clear();
|
||||
JsonObject obj = JsonParser.parseString(json).getAsJsonObject();
|
||||
|
||||
@@ -174,7 +174,7 @@ public class EditorApp extends Application {
|
||||
animPreviewController = new de.blight.editor.toolset.anim.AnimPreviewController(toolCtx);
|
||||
animSetController = new de.blight.editor.toolset.anim.AnimSetController(toolCtx);
|
||||
objectController = new de.blight.editor.toolset.object.ObjectController(toolCtx);
|
||||
modelEditorController = new de.blight.editor.toolset.model.ModelEditorController(toolCtx, () -> assetController.openOverlay());
|
||||
modelEditorController = new de.blight.editor.toolset.model.ModelEditorController(toolCtx, () -> { assetController.openOverlay(); assetController.expandModelsNode(); });
|
||||
modelEditorController.setObjectController(objectController);
|
||||
atmosphereController = new de.blight.editor.toolset.atmosphere.AtmosphereController(toolCtx);
|
||||
waterController = new de.blight.editor.toolset.water.WaterController(toolCtx);
|
||||
|
||||
@@ -296,6 +296,13 @@ public class ModelEditorState extends BaseAppState {
|
||||
updateCharPreviewWeapon();
|
||||
}
|
||||
|
||||
// Orbit-Kamera im Charakter-Preview immer um den aktuellen Hand-Ankerpunkt drehen
|
||||
// (statt um die Bounding-Box der isolierten Waffe, die am Boden/"Fuß" verankert ist)
|
||||
if (charPreviewNode != null) {
|
||||
Vector3f anchor = computeCharAnchorWorld();
|
||||
if (anchor != null) orbitCenter = anchor;
|
||||
}
|
||||
|
||||
if (input.modelEditorEmbedLodsRequest) {
|
||||
input.modelEditorEmbedLodsRequest = false;
|
||||
embedLodsAndSave(input.modelEditorEmbedLodsPath);
|
||||
@@ -1551,7 +1558,7 @@ public class ModelEditorState extends BaseAppState {
|
||||
|
||||
// Schritt 3: Richtung A→B auf +Z ausrichten
|
||||
Vector3f bladeDir = B.subtract(A).normalizeLocal();
|
||||
if (bladeDir.z < 0) bladeDir.negateLocal(); // +Z-Richtung bevorzugen
|
||||
if (bladeDir.z > 0) bladeDir.negateLocal(); // -Z-Richtung bevorzugen
|
||||
Quaternion rot1 = awRotateToZ(bladeDir);
|
||||
|
||||
// Mitte der Hauptachse → Ursprung
|
||||
@@ -1609,11 +1616,11 @@ public class ModelEditorState extends BaseAppState {
|
||||
float guardNorm = (float) guardBucket / (BKTS - 1);
|
||||
log.info("[ModelEditor] AlignWeapon: Parierstange bei normZ={} (Bucket {}/{})",
|
||||
String.format("%.2f", guardNorm), guardBucket, BKTS);
|
||||
if (guardNorm > 0.5f) {
|
||||
// Parierstange in +Z-Hälfte → Pommel bei +Z, Klinge bei -Z → 180° um X flippen
|
||||
if (guardNorm < 0.5f) {
|
||||
// Parierstange in -Z-Hälfte → Pommel bei -Z, Klinge bei +Z → 180° um X flippen
|
||||
Quaternion flip = new Quaternion().fromAngles(FastMath.PI, 0f, 0f);
|
||||
combined = flip.mult(combined);
|
||||
log.info("[ModelEditor] AlignWeapon: Pommel bei +Z detektiert → Schwert geflippt");
|
||||
log.info("[ModelEditor] AlignWeapon: Pommel bei -Z detektiert → Schwert geflippt");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2133,6 +2140,38 @@ public class ModelEditorState extends BaseAppState {
|
||||
if (modelWrapper != null) modelWrapper.setCullHint(Spatial.CullHint.Inherit);
|
||||
if (gripGizmoNode != null) gripGizmoNode.setCullHint(Spatial.CullHint.Always);
|
||||
input.gripEditorCharPreviewReady = false;
|
||||
updateBounds(); // Orbit-Zentrum zurück auf die Bounding-Box der isolierten Waffe
|
||||
}
|
||||
|
||||
/**
|
||||
* Liefert die aktuelle Position des Hand-Ankerpunkts (Waffen-Griff bzw. Schild-Anker)
|
||||
* in Weltkoordinaten, oder null wenn kein Charakter-Preview aktiv ist.
|
||||
*/
|
||||
private Vector3f computeCharAnchorWorld() {
|
||||
if (charPreviewNode == null) return null;
|
||||
Quaternion charWorldRot = charPreviewNode.getWorldRotation();
|
||||
Quaternion charWorldRotInv = charWorldRot.inverse();
|
||||
Vector3f charWorldPos = charPreviewNode.getWorldTranslation();
|
||||
|
||||
if (input.shieldEditorActive) {
|
||||
if (charLeftHandNode == null) return null;
|
||||
Vector3f leftBoneInChar = charWorldRotInv.mult(
|
||||
charLeftHandNode.getWorldTranslation().subtract(charWorldPos));
|
||||
Quaternion leftBoneRotInChar = charWorldRotInv.mult(charLeftHandNode.getWorldRotation());
|
||||
Vector3f anchorInChar = leftBoneInChar.add(
|
||||
leftBoneRotInChar.mult(new Vector3f(0f, -input.shieldEditorElbowOffset, 0f)));
|
||||
return charWorldPos.add(charWorldRot.mult(anchorInChar));
|
||||
}
|
||||
|
||||
if (charHandNode == null) return null;
|
||||
Vector3f boneInChar = charWorldRotInv.mult(
|
||||
charHandNode.getWorldTranslation().subtract(charWorldPos));
|
||||
Quaternion boneRotInChar = charWorldRotInv.mult(charHandNode.getWorldRotation());
|
||||
Vector3f palmOff = charPalmOffset;
|
||||
Vector3f attachInChar = (palmOff != null)
|
||||
? boneInChar.add(boneRotInChar.mult(palmOff.mult(0.5f)))
|
||||
: boneInChar;
|
||||
return charWorldPos.add(charWorldRot.mult(attachInChar));
|
||||
}
|
||||
|
||||
private void updateCharPreviewWeapon() {
|
||||
@@ -2156,8 +2195,9 @@ public class ModelEditorState extends BaseAppState {
|
||||
float g2x = input.gripEditorGrip2PosX;
|
||||
float g2y = input.gripEditorGrip2PosY;
|
||||
float g2z = input.gripEditorGrip2PosZ;
|
||||
boolean bladeAtMinusZ = g2z > g1z;
|
||||
boolean isTwoHand = bladeAtMinusZ && (g2x != 0f || g2y != 0f || g2z != 0f);
|
||||
boolean hasGrip2 = g2x != 0f || g2y != 0f || g2z != 0f;
|
||||
boolean bladeAtMinusZ = hasGrip2 ? (g2z > g1z) : (g1z > 0f);
|
||||
boolean isTwoHand = hasGrip2 && bladeAtMinusZ;
|
||||
|
||||
Quaternion charWorldRot = charPreviewNode.getWorldRotation();
|
||||
Quaternion charWorldRotInv = charWorldRot.inverse();
|
||||
|
||||
@@ -277,12 +277,9 @@ public class ModelEditorController implements ToolController {
|
||||
public void openModelEditor(String relPath, Path absolutePath) {
|
||||
this.pendingRelPath = relPath;
|
||||
this.pendingAbsPath = absolutePath;
|
||||
if (ctx.registry().getActive() == this) {
|
||||
activate();
|
||||
} else {
|
||||
ctx.switchToWorld().run();
|
||||
ctx.registry().activate(this);
|
||||
}
|
||||
}
|
||||
|
||||
/** Öffnet den Datei-Dialog für den Modell-Import. */
|
||||
public void openModelImport(Window owner) {
|
||||
|
||||
@@ -217,7 +217,10 @@ public class NpcWeaponAttachmentControl extends AbstractControl {
|
||||
Quaternion animDelta = (handBindRotInChar != null)
|
||||
? boneRotInChar.mult(handBindRotInChar.inverse())
|
||||
: boneRotInChar;
|
||||
boolean bladeAtMinusZ = grip != null && grip.getGrip2PosZ() != 0f && grip.getGrip2PosZ() > grip.getPosZ();
|
||||
boolean bladeAtMinusZ = grip != null && (
|
||||
(grip.getGrip2PosX() != 0f || grip.getGrip2PosY() != 0f || grip.getGrip2PosZ() != 0f)
|
||||
? grip.getGrip2PosZ() > grip.getPosZ()
|
||||
: grip.getPosZ() > 0f);
|
||||
Quaternion bladeFlip = bladeAtMinusZ
|
||||
? new Quaternion().fromAngles(com.jme3.math.FastMath.PI, 0f, 0f)
|
||||
: new Quaternion();
|
||||
@@ -227,6 +230,9 @@ public class NpcWeaponAttachmentControl extends AbstractControl {
|
||||
: new Vector3f(cfg.getHandOffX(), cfg.getHandOffY(), cfg.getHandOffZ());
|
||||
Vector3f weaponPos = bonePosInChar.subtract(weaponRot.mult(gripPos));
|
||||
weaponPos = weaponPos.add(weaponRot.mult(handOff));
|
||||
if (grip != null && (grip.getHandOffsetX() != 0f || grip.getHandOffsetY() != 0f || grip.getHandOffsetZ() != 0f)) {
|
||||
weaponPos = weaponPos.add(grip.getHandOffsetX(), grip.getHandOffsetY(), grip.getHandOffsetZ());
|
||||
}
|
||||
s.setLocalRotation(weaponRot);
|
||||
s.setLocalTranslation(weaponPos);
|
||||
}
|
||||
@@ -288,17 +294,20 @@ public class NpcWeaponAttachmentControl extends AbstractControl {
|
||||
DEG_TO_RAD * grip.getRotY(),
|
||||
DEG_TO_RAD * grip.getRotZ())
|
||||
: new Quaternion();
|
||||
boolean bladeAtMinusZ2 = grip != null && grip.getGrip2PosZ() != 0f && grip.getGrip2PosZ() > grip.getPosZ();
|
||||
boolean bladeAtMinusZ2 = grip != null && (
|
||||
(grip.getGrip2PosX() != 0f || grip.getGrip2PosY() != 0f || grip.getGrip2PosZ() != 0f)
|
||||
? grip.getGrip2PosZ() > grip.getPosZ()
|
||||
: grip.getPosZ() > 0f);
|
||||
Quaternion bladeFlip2 = bladeAtMinusZ2
|
||||
? new Quaternion().fromAngles(com.jme3.math.FastMath.PI, 0f, 0f)
|
||||
: new Quaternion();
|
||||
Quaternion weaponRot = boneRotInChar.mult(BACK_ROT_NORM).mult(bladeFlip2).mult(gripRot.inverse());
|
||||
Vector3f target = boneInChar.add(boneRotInChar.mult(
|
||||
twoHanded ? TWO_HAND_BACK_GRIP_OFFSET : BACK_GRIP_OFFSET));
|
||||
// X=0: Waffenmitte (nicht handspezifischer Griffpunkt) auf Wirbelsäule zentrieren
|
||||
Vector3f gripPos = grip != null
|
||||
? new Vector3f(grip.getPosX(), grip.getPosY(), grip.getPosZ()) : Vector3f.ZERO;
|
||||
? new Vector3f(0f, grip.getPosY(), grip.getPosZ()) : Vector3f.ZERO;
|
||||
Vector3f backPos = target.subtract(weaponRot.mult(gripPos));
|
||||
if (bladeAtMinusZ2) backPos = backPos.add(0.1f, 0f, 0f);
|
||||
s.setLocalRotation(weaponRot);
|
||||
s.setLocalTranslation(backPos);
|
||||
}
|
||||
|
||||
@@ -407,6 +407,8 @@ public class PlayerInputControl implements CombatStyleBase.AnimReceiver {
|
||||
this.visual = visual;
|
||||
}
|
||||
|
||||
public Spatial getVisual() { return visual; }
|
||||
|
||||
/**
|
||||
* Setzt die Navigationsquellen (PathFinder + Terrain). Muss vor oder beim
|
||||
* ersten setAnimationContext gesetzt sein, damit der CharacterNavigator
|
||||
@@ -1427,6 +1429,12 @@ public class PlayerInputControl implements CombatStyleBase.AnimReceiver {
|
||||
private void handleCombatAlt(boolean isPressed) {
|
||||
if (!weaponDrawn) return;
|
||||
if (isMeleeType(drawnWeaponType)) {
|
||||
// Schwert blockt per Schild – ohne Schild ist Blocken nicht möglich.
|
||||
// Zweihänder/Axt blocken mit der Waffe selbst, brauchen keinen Schild.
|
||||
if (drawnWeaponType == ItemSubCategory.SWORD && !hasShieldEquipped()) {
|
||||
rightMouseHeld = false;
|
||||
return;
|
||||
}
|
||||
if (isPressed) {
|
||||
rightMouseHeld = true;
|
||||
// Sofort blocken nur wenn keine andere Animation läuft
|
||||
|
||||
@@ -72,6 +72,7 @@ public class WeaponAttachmentControl extends AbstractControl {
|
||||
// Wird im 2. Frame aus den Runtime-Node-Positionen berechnet (skalierungs-unabhängig).
|
||||
private Vector3f palmOffsetLocal;
|
||||
|
||||
|
||||
private Item lastEquippedWeapon;
|
||||
private boolean lastDrawnState = false;
|
||||
private Spatial weaponSpatial;
|
||||
@@ -224,7 +225,10 @@ public class WeaponAttachmentControl extends AbstractControl {
|
||||
Quaternion animDelta = (handBindRotInChar != null)
|
||||
? boneRotInChar.mult(handBindRotInChar.inverse())
|
||||
: boneRotInChar;
|
||||
boolean bladeAtMinusZ2 = grip != null && grip.getGrip2PosZ() != 0f && grip.getGrip2PosZ() > grip.getPosZ();
|
||||
boolean bladeAtMinusZ2 = grip != null && (
|
||||
(grip.getGrip2PosX() != 0f || grip.getGrip2PosY() != 0f || grip.getGrip2PosZ() != 0f)
|
||||
? grip.getGrip2PosZ() > grip.getPosZ()
|
||||
: grip.getPosZ() > 0f);
|
||||
Quaternion bladeFlip2 = bladeAtMinusZ2
|
||||
? new Quaternion().fromAngles(com.jme3.math.FastMath.PI, 0f, 0f)
|
||||
: new Quaternion();
|
||||
@@ -368,20 +372,22 @@ public class WeaponAttachmentControl extends AbstractControl {
|
||||
DEG_TO_RAD * grip.getRotY(),
|
||||
DEG_TO_RAD * grip.getRotZ())
|
||||
: new Quaternion();
|
||||
// Klinge bei -Z erkannt (grip2PosZ > posZ) → 180° um X vorschalten, damit BACK_ROT_NORM
|
||||
// dieselbe Wirkung hat wie bei korrekt ausgerichtetem Modell (Klinge bei +Z).
|
||||
boolean bladeAtMinusZ = grip != null && grip.getGrip2PosZ() != 0f && grip.getGrip2PosZ() > grip.getPosZ();
|
||||
boolean bladeAtMinusZ = grip != null && (
|
||||
(grip.getGrip2PosX() != 0f || grip.getGrip2PosY() != 0f || grip.getGrip2PosZ() != 0f)
|
||||
? grip.getGrip2PosZ() > grip.getPosZ()
|
||||
: grip.getPosZ() > 0f);
|
||||
Quaternion bladeFlip = bladeAtMinusZ
|
||||
? new Quaternion().fromAngles(com.jme3.math.FastMath.PI, 0f, 0f)
|
||||
: new Quaternion();
|
||||
|
||||
Quaternion weaponRot = boneRotInChar.mult(BACK_ROT_NORM).mult(bladeFlip).mult(gripRot.inverse());
|
||||
Vector3f gripTarget = bonePosInChar.add(boneRotInChar.mult(
|
||||
twoHanded ? TWO_HAND_BACK_GRIP_OFFSET : BACK_GRIP_OFFSET));
|
||||
Vector3f offset = twoHanded ? TWO_HAND_BACK_GRIP_OFFSET : BACK_GRIP_OFFSET;
|
||||
Vector3f gripTarget = bonePosInChar.add(boneRotInChar.mult(offset));
|
||||
// X=0: Waffenmitte (nicht handspezifischer Griffpunkt) auf Wirbelsäule zentrieren
|
||||
Vector3f gripPos = (grip != null)
|
||||
? new Vector3f(grip.getPosX(), grip.getPosY(), grip.getPosZ())
|
||||
? new Vector3f(0f, grip.getPosY(), grip.getPosZ())
|
||||
: Vector3f.ZERO;
|
||||
Vector3f backPos = gripTarget.subtract(weaponRot.mult(gripPos));
|
||||
if (bladeAtMinusZ) backPos = backPos.add(0.1f, 0f, 0f);
|
||||
weaponSpatial.setLocalRotation(weaponRot);
|
||||
weaponSpatial.setLocalTranslation(backPos);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user