Feat/outline thickness and tp weapon hold #22
@@ -134,9 +134,13 @@ faces the other way, the two compose to a character running backwards — and
|
||||
nothing anywhere measures the finished result.
|
||||
|
||||
**Rule:** facing is measurable from the skeleton — the toes are forward of the
|
||||
ankles, and the nose/head is forward of the spine. Assert it on the OUTPUT, after
|
||||
every transform has been applied. A blanket constant like `facing_flip` is a
|
||||
guess about the source that must be replaced by a measurement.
|
||||
ankles. `flatten_and_scale()` now snaps that to Blender -Y, the convention the
|
||||
runtime flip is built around, so every character leaves the pipeline pointing the
|
||||
same way whatever the source did. Kiyoko was 180 degrees off and is now correct.
|
||||
|
||||
Snap to the nearest QUARTER TURN, not to the measured angle: a rest pose with the
|
||||
feet slightly splayed is not a character who is 7 degrees turned, and correcting
|
||||
it as one puts a permanent yaw on the whole skeleton.
|
||||
|
||||
---
|
||||
|
||||
@@ -174,9 +178,21 @@ against proportions like Taila's. A rig with different arm lengths, a different
|
||||
rest pose (A-pose vs T-pose) or a different bone roll will put the hands
|
||||
somewhere plausible for the maths and wrong for the eye.
|
||||
|
||||
**Rule:** the hold has measurable success criteria — the distance from each hand
|
||||
bone to the grip point it was solved onto. Assert those, per character, rather
|
||||
than judging by eye.
|
||||
**The real cause turned out to be #2's sibling, and it affected every model.**
|
||||
`set_weapon` mounted the gun with a fixed `rotation_degrees = (0, 90, -90)`. A
|
||||
bone attachment is expressed in the BONE's axes, and no two rigs agree on those,
|
||||
so a constant there mounts the weapon differently on every character.
|
||||
|
||||
It does not need to be right at all. The pose layer aims the gun by rotating the
|
||||
WRIST until the weapon's forward axis lies on the aim line, so handing it the
|
||||
IDENTITY means "the gun's forward is the hand bone's -Z" — true by construction
|
||||
on any rig — and the wrist absorbs whatever roll that bone happens to have. The
|
||||
grip is placed at the bone's origin, so the gun sits *in* the hand instead of at
|
||||
a fixed offset from a differently-oriented bone.
|
||||
|
||||
**Rule:** never express a mount as a constant in a bone's local frame. Either
|
||||
derive the frame from the skeleton, or hand the downstream solver an identity and
|
||||
let it do the work — it already knows where the gun must point.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -9,20 +9,20 @@
|
||||
"Neck",
|
||||
"Head"
|
||||
],
|
||||
"shoulder.L": "Left shoulder",
|
||||
"shoulder.R": "Right shoulder",
|
||||
"foot.R": "Right ankle",
|
||||
"shoulder.L": "Left shoulder",
|
||||
"foot.L": "Left ankle",
|
||||
"hand.L": "Left wrist",
|
||||
"foot.R": "Right ankle",
|
||||
"hand.R": "Right wrist",
|
||||
"forearm.L": "Left elbow",
|
||||
"hand.L": "Left wrist",
|
||||
"forearm.R": "Right elbow",
|
||||
"shin.R": "Right knee",
|
||||
"forearm.L": "Left elbow",
|
||||
"shin.L": "Left knee",
|
||||
"shin.R": "Right knee",
|
||||
"toe.R": "Right toe",
|
||||
"toe.L": "Left toe",
|
||||
"upper_arm.L": "Left arm",
|
||||
"upper_arm.R": "Right arm",
|
||||
"upper_arm.L": "Left arm",
|
||||
"thigh.L": "Left leg",
|
||||
"thigh.R": "Right leg"
|
||||
},
|
||||
|
||||
Binary file not shown.
@@ -15,16 +15,16 @@
|
||||
"shin.L": "J_Bip_L_LowerLeg",
|
||||
"thigh.R": "J_Bip_R_UpperLeg",
|
||||
"thigh.L": "J_Bip_L_UpperLeg",
|
||||
"shoulder.R": "J_Bip_R_Shoulder",
|
||||
"shoulder.L": "J_Bip_L_Shoulder",
|
||||
"shoulder.R": "J_Bip_R_Shoulder",
|
||||
"forearm.R": "J_Bip_R_LowerArm",
|
||||
"forearm.L": "J_Bip_L_LowerArm",
|
||||
"upper_arm.R": "J_Bip_R_UpperArm",
|
||||
"upper_arm.L": "J_Bip_L_UpperArm",
|
||||
"upper_arm.R": "J_Bip_R_UpperArm",
|
||||
"toe.L": "J_Bip_L_ToeBase",
|
||||
"toe.R": "J_Bip_R_ToeBase",
|
||||
"foot.R": "J_Bip_R_Foot",
|
||||
"foot.L": "J_Bip_L_Foot",
|
||||
"foot.R": "J_Bip_R_Foot",
|
||||
"hand.L": "J_Bip_L_Hand",
|
||||
"hand.R": "J_Bip_R_Hand"
|
||||
},
|
||||
@@ -550,7 +550,7 @@
|
||||
],
|
||||
[
|
||||
-0.02312,
|
||||
0.02503,
|
||||
0.02504,
|
||||
0.00687
|
||||
]
|
||||
],
|
||||
@@ -1899,7 +1899,7 @@
|
||||
0.0013
|
||||
],
|
||||
[
|
||||
-0.01334,
|
||||
-0.01333,
|
||||
0.01982,
|
||||
-0.00015
|
||||
],
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -11,18 +11,18 @@
|
||||
"Unused_Noname_004",
|
||||
"Unused_Noname_010"
|
||||
],
|
||||
"shoulder.L": "Left shoulder",
|
||||
"shoulder.R": "Right shoulder",
|
||||
"foot.R": "Right ankle",
|
||||
"shoulder.L": "Left shoulder",
|
||||
"foot.L": "Left ankle",
|
||||
"foot.R": "Right ankle",
|
||||
"hand.L": "Left wrist",
|
||||
"hand.R": "Right wrist",
|
||||
"forearm.L": "Left elbow",
|
||||
"forearm.R": "Right elbow",
|
||||
"shin.R": "Right knee",
|
||||
"shin.L": "Left knee",
|
||||
"toe.R": "Toes2_R",
|
||||
"toe.L": "Toes3_L",
|
||||
"shin.R": "Right knee",
|
||||
"toe.R": "Toes0_R",
|
||||
"toe.L": "Toes4_L",
|
||||
"upper_arm.L": "Left arm",
|
||||
"upper_arm.R": "Right arm",
|
||||
"thigh.L": "Left leg",
|
||||
@@ -1804,8 +1804,8 @@
|
||||
"Root_006",
|
||||
"Root_007",
|
||||
"Spine",
|
||||
"Toes2_R",
|
||||
"Toes3_L",
|
||||
"Toes0_R",
|
||||
"Toes4_L",
|
||||
"Unused_Noname_004",
|
||||
"Unused_Noname_010"
|
||||
]
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -908,17 +908,27 @@ func set_weapon(script_path: String) -> void:
|
||||
# (weapon swap while toggled), else the new weapon comes up invisible.
|
||||
if (shadows_only or first_person_mode) and not _owner_visible:
|
||||
_set_shadows_recursive(w)
|
||||
# Undo the first-person viewmodel placement from the weapon's _ready:
|
||||
# lie along the hand's grip, scaled down to character proportions.
|
||||
w.position = Vector3(-0.02, 0.07, 0.0)
|
||||
w.rotation_degrees = Vector3(0, 90, -90)
|
||||
w.scale = Vector3(1.0, 1.0, 1.0)
|
||||
# Tell the pose layer the gun's axes in hand-bone space so it can
|
||||
# aim the wrist to point the muzzle exactly where the player looks.
|
||||
# Seat the weapon in the hand with NO hand-relative rotation.
|
||||
#
|
||||
# There used to be a fixed `rotation_degrees = (0, 90, -90)` here, which
|
||||
# is the offset that happens to be right for the library's own DEF-hand.R
|
||||
# and is wrong for every rig whose hand bone has a different roll. A bone
|
||||
# attachment is expressed in the BONE's axes, and no two rigs agree on
|
||||
# those, so a constant here mounts the gun differently on every character
|
||||
# — which is what "the hand mount points are totally wrong on all models"
|
||||
# was.
|
||||
#
|
||||
# It does not need to be right, because the pose layer aims the gun by
|
||||
# rotating the WRIST until the weapon's forward axis lies on the aim line
|
||||
# (see _apply_rifle_hold). Handing it the identity means "the gun's
|
||||
# forward is the hand bone's -Z", which is true by construction on any
|
||||
# rig, and the wrist then absorbs whatever that bone's roll happens to be.
|
||||
# The grip is placed at the bone's origin below, so the gun sits IN the
|
||||
# hand rather than at a fixed offset from a differently-oriented bone.
|
||||
w.transform = Transform3D.IDENTITY
|
||||
if _pose_mod:
|
||||
var b: Basis = w.transform.basis.orthonormalized()
|
||||
_pose_mod.gun_fwd_hand = b * Vector3(0, 0, -1)
|
||||
_pose_mod.gun_up_hand = b * Vector3(0, 1, 0)
|
||||
_pose_mod.gun_fwd_hand = Vector3(0, 0, -1)
|
||||
_pose_mod.gun_up_hand = Vector3(0, 1, 0)
|
||||
_measure_weapon(w)
|
||||
)
|
||||
|
||||
|
||||
@@ -413,6 +413,45 @@ def flatten_and_scale(arm, meshes, target_height, roles=None):
|
||||
print(f"Stood the character up: feet->hips ran along "
|
||||
f"{'XYZ'[k]}{'+' if d[k] > 0 else '-'}, rotated to Z+")
|
||||
|
||||
# TURN THE CHARACTER TO FACE THE SAME WAY AS EVERY OTHER CHARACTER.
|
||||
#
|
||||
# Which way a model faces is a property of the file, not a constant, and two
|
||||
# separate things were guessing at it: facing_correction() aligns the rest
|
||||
# pose to the library's, and the runtime then applies a blanket 180 degrees
|
||||
# because "glTF forward is +Z". When a source disagrees with either, the two
|
||||
# compose into a character who runs backwards, and nothing measured the
|
||||
# result. Kiyoko shipped like that.
|
||||
#
|
||||
# The skeleton knows: toes are in front of ankles. Snap that to Blender -Y,
|
||||
# which is the convention the rest of the pipeline and the runtime flip are
|
||||
# built around, so every character leaves here pointing the same way whatever
|
||||
# the source did.
|
||||
if roles is not None:
|
||||
fwd = Vector((0.0, 0.0, 0.0))
|
||||
n_f = 0
|
||||
for side in ("L", "R"):
|
||||
a = arm.data.bones.get(roles.limb.get(("foot", side), ""))
|
||||
t = arm.data.bones.get(roles.limb.get(("toe", side), ""))
|
||||
if a and t:
|
||||
fwd += (t.head_local - a.head_local)
|
||||
n_f += 1
|
||||
if n_f:
|
||||
fwd.z = 0.0
|
||||
if fwd.length > 1e-4:
|
||||
fwd.normalize()
|
||||
want = Vector((0.0, -1.0, 0.0))
|
||||
ang = math.atan2(fwd.x, -fwd.y) # signed yaw from -Y to fwd
|
||||
# Snap to the nearest quarter turn: a rest pose with the feet
|
||||
# slightly splayed must not be counted as a turn.
|
||||
q = round(ang / (math.pi / 2)) * (math.pi / 2)
|
||||
if abs(q) > 1e-6:
|
||||
R = Matrix.Rotation(-q, 4, Vector((0.0, 0.0, 1.0)))
|
||||
for obj in [arm] + meshes:
|
||||
obj.matrix_world = R @ obj.matrix_world
|
||||
apply_all()
|
||||
print("Turned the character to face -Y: was %.0f degrees off"
|
||||
% math.degrees(q))
|
||||
|
||||
lo = Vector((1e9, 1e9, 1e9))
|
||||
hi = -lo.copy()
|
||||
for m in meshes:
|
||||
|
||||
Reference in New Issue
Block a user