feat(rig lab): drag the anchors themselves, not just the gun under them
The lab could move the GUN and not the anchor points the hands are solved onto. `grip_offset` slides the weapon around inside the fist; `gun_fore` and `gun_stock` are distances ALONG the barrel, so the trigger and support hands could travel up and down the weapon's own axis and nowhere else. Nothing could take a hand off that axis, which is what a handguard below the bore, an angled foregrip, or a pistol whose grip is nowhere near its barrel line all need. Two things fix that. `grip_shift` and `fore_shift` give the two hand anchors real three-dimensional freedom, expressed in the GUN's own across/up/along frame so a sideways nudge stays sideways as the weapon pitches between low ready and ADS. Zero is exactly the old behaviour. Their z overlaps the along-axis distances, which is redundant and deliberate: keeping those separate is what lets the reach solver slide the support hand back down the handguard without undoing a considered sideways offset. And the markers are now draggable. They already showed the anchors; now they are handles. The one under the mouse swells and draws through the body — depth testing is right for judging whether a hand reached its target and wrong for a handle, because at any useful framing the hands occlude all three. Verified three ways, and each one had to be rebuilt once: anchor_shift_check first compared absolute positions and reported a 3.5 mm error that was the character BREATHING — there is a sin() on the muzzle pitch, so no anchor is ever in the same place twice. Measuring each anchor relative to the one it hangs off, rotated into the current gun basis, cancels the breathing, the ADS blend and the recoil exactly. 48 checks, six characters, both poses. anchor_drag_check asserts the drag writes the knob the MOUSE asked for, derived independently from the camera: 0.00-0.01 mm on all three. It does not assert the marker lands under the cursor, because it does not — the anchors hang off the shoulder and the arm chasing them moves the shoulder, so a drag settles at 0.77x-1.13x. Small enough to ignore interactively. That feedback first read as 1.5x-1.8x, because the cases were compounding on each other, and waiting LONGER for the pose to settle made it worse rather than better — which is the opposite of how a settling error behaves and is what gave it away. The buttstock case also failed for a while on a bug entirely in the test: it read an absent knob as zero when `pocket_hip` defaults to (30, -70, 60) mm. The lab has a note about that trap in `_reset`. It is just as easy to walk into from a test, and now has one there too. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
700d0925d7
commit
b1c8bab714
@@ -1307,6 +1307,11 @@ class ShooterPoseModifier extends SkeletonModifier3D:
|
||||
var dbg_grip: Vector3 = Vector3.ZERO
|
||||
var dbg_fore: Vector3 = Vector3.ZERO
|
||||
var dbg_stock: Vector3 = Vector3.ZERO
|
||||
## The gun's frame this frame — across, up, along the barrel. The lab needs
|
||||
## it to turn a mouse drag on an anchor marker back into the axes its knob is
|
||||
## expressed in; without it, dragging left would mean something different at
|
||||
## every pitch of the weapon.
|
||||
var dbg_gun_basis: Basis = Basis.IDENTITY
|
||||
|
||||
func _t(key: String, fallback: float) -> float:
|
||||
return float(tune.get(key, fallback))
|
||||
@@ -1654,7 +1659,14 @@ class ShooterPoseModifier extends SkeletonModifier3D:
|
||||
var shoulder := skel.get_bone_global_pose(ua_r).origin
|
||||
var pocket: Vector3 = t_pocket_hip.lerp(t_pocket_ads, ads)
|
||||
var stock_pos := shoulder + pocket
|
||||
var grip_pos := stock_pos + aim_dir * gun_stock
|
||||
# The gun's own frame: across, up, along the barrel. The hand anchors are
|
||||
# nudged in THIS rather than in skeleton space so a sideways offset stays
|
||||
# sideways relative to the weapon as it pitches from low ready to ADS,
|
||||
# instead of sliding around the gun as it tips.
|
||||
var gun_basis := Basis(side, gun_up, aim_dir)
|
||||
dbg_gun_basis = gun_basis
|
||||
var grip_pos := stock_pos + aim_dir * gun_stock \
|
||||
+ gun_basis * _tv("grip_shift", Vector3.ZERO)
|
||||
# The support hand rides as far out the handguard as it can actually
|
||||
# REACH. Without this a long rifle puts the foregrip past the left
|
||||
# arm's limit and the IK yanks the whole arm out straight.
|
||||
@@ -1677,7 +1689,8 @@ class ShooterPoseModifier extends SkeletonModifier3D:
|
||||
fore_dist = maxf(fore_dist * 0.8, floor_fore)
|
||||
if fore_dist <= floor_fore:
|
||||
break
|
||||
var fore_pos := grip_pos + aim_dir * fore_dist
|
||||
var fore_pos := grip_pos + aim_dir * fore_dist \
|
||||
+ gun_basis * _tv("fore_shift", Vector3.ZERO)
|
||||
dbg_grip = grip_pos
|
||||
dbg_fore = fore_pos
|
||||
dbg_stock = stock_pos
|
||||
|
||||
Reference in New Issue
Block a user