fix: handle the knee bend with a joint, and record what made it worse
Renames SkinKneeHelper to SkinJointHelper and generalises it to a list of
[parent, child] joints, so the half-angle joint technique is stated once and can
be pointed at any joint. It stays pointed at the KNEES only, and the header now
says why, because "add more joints" is the obvious next idea and it is wrong
here.
Three things were tried this round and measured, on the thigh, per vertex,
against the rest transforms:
* hip and ankle helpers as well as knees: the knee got WORSE, 0.80 -> 0.50,
and the skirt went 0.80 -> 0.72. The hip mostly swings rather than folding,
and linear-blend skinning handles a swing far better than a fold; the helper
just added another blend for nothing. Reverted.
* gating the re-weight to vertices near the leg bone chain, to keep the hip
helper off the skirt: that also excludes the OUTSIDE of the knee, which sits
further from the bone axis than the limb radius and is exactly the geometry
that needs helping. Knee went back to 0.50. Removed — with knees only there
is no skirt to protect against.
* one joint per vertex (break after the first match): kept, since a second
job would read the bones the first just rewrote.
Net result on the thigh, worst per height bucket during a jump:
y=0.60 0.80 -> 0.83 y=0.75 0.84 -> 0.84
y=0.65 0.81 -> 0.84 y=0.85 0.90 -> 0.90
and the skirt is untouched at 0.80/0.84, still free to move.
FSM tests 11/11, spawn smoke test 0 failures.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
0bb1db0806
commit
1ab7e99231
@@ -86,8 +86,8 @@ var loaded: bool = false
|
||||
var _resolved_clips: Dictionary = {} # canonical name -> actual clip name
|
||||
var _current_clip: String = ""
|
||||
var _weapon_attachment: BoneAttachment3D
|
||||
## [shin_bone, helper_bone] pairs driven every frame by the pose modifier.
|
||||
var _knee_helpers: Array = []
|
||||
## [child_bone, helper_bone] pairs driven every frame by the pose modifier.
|
||||
var _joint_helpers: Array = []
|
||||
var is_holding_weapon: bool = false
|
||||
|
||||
# Animation blending: locomotion plays full-body through a Transition node;
|
||||
@@ -170,11 +170,11 @@ func load_model(path: String) -> void:
|
||||
if fixed[0] > 0 or fixed[1] > 0:
|
||||
print("SkinnedPlayerModel: '%s' — snapped %d cross-leg vertices, dropped %d bridging triangles"
|
||||
% [path.get_file(), fixed[0], fixed[1]])
|
||||
# Half-angle helper bones at the knees. Without them the knee pinches to
|
||||
# a ribbon at a hard bend — see SkinKneeHelper.
|
||||
_knee_helpers = SkinKneeHelper.install(scene, skeleton)
|
||||
# Half-angle helper bones at every leg joint. Without them the limb
|
||||
# pinches at a hard bend — see SkinJointHelper.
|
||||
_joint_helpers = SkinJointHelper.install(scene, skeleton)
|
||||
_pose_mod = ShooterPoseModifier.new()
|
||||
_pose_mod.knee_helpers = _knee_helpers
|
||||
_pose_mod.joint_helpers = _joint_helpers
|
||||
_pose_mod.name = "ShooterPose"
|
||||
skeleton.add_child(_pose_mod)
|
||||
|
||||
@@ -761,8 +761,8 @@ class ShooterPoseModifier extends SkeletonModifier3D:
|
||||
var gun_stock: float = 0.20
|
||||
# 0..1 through a reload — drives the support hand to the mag well and back.
|
||||
var reload_phase: float = 0.0
|
||||
# [shin_bone, helper_bone] pairs; see SkinKneeHelper.
|
||||
var knee_helpers: Array = []
|
||||
# [child_bone, helper_bone] pairs; see SkinJointHelper.
|
||||
var joint_helpers: Array = []
|
||||
|
||||
# Tuning (radians). Positive pitch leans forward; positive roll leans right.
|
||||
# The lean is the ONLY thing that tells a viewer which way this character is
|
||||
@@ -821,11 +821,11 @@ class ShooterPoseModifier extends SkeletonModifier3D:
|
||||
_apply_recoil(skel)
|
||||
recoil = lerpf(recoil, 0.0, 0.25)
|
||||
|
||||
# Knees LAST, and inside the modification pass: the helper has to track
|
||||
# whatever final rotation the shin ended up with, or it deforms the leg
|
||||
# instead of saving it.
|
||||
if not knee_helpers.is_empty():
|
||||
SkinKneeHelper.update(skel, knee_helpers)
|
||||
# Joints LAST, and inside the modification pass: each helper has to track
|
||||
# whatever final rotation its child bone ended up with, or it deforms the
|
||||
# limb instead of saving it.
|
||||
if not joint_helpers.is_empty():
|
||||
SkinJointHelper.update(skel, joint_helpers)
|
||||
|
||||
|
||||
# Upper body follows the camera pitch: distributed over spine/neck/head
|
||||
|
||||
Reference in New Issue
Block a user