feat: real two-hand rifle hold — armed animations finally read as a shooter

The clips were playing all along, but every locomotion state used unarmed
jog arms while the gun floated at the hip, so nothing looked animated.

- ShooterPoseModifier now REPLACES the arm chain with a direction-based FK
  rifle hold (low-ready at hip, shouldered on ADS) instead of nudging the
  unarmed clip additively; the wrist is solved so the muzzle points exactly
  along the aim line, gun kept upright (no-roll constraint).
- Per-arm release logic: reload/throw/hit one-shots, dance, death, the
  slide ground-brace, wall-run reach and grapple free the authored clips.
- Third-person weapon: fixed re-hide on weapon swap while in third person,
  scaled to character proportions, gripped along the hand.
- Armed idle uses Idle (arms owned by the hold) instead of arms-crossed
  PistolIdle base.
- FP viewmodel arms: character-styled sleeve + teal cuff + skin hand
  instead of the blue slabs.
- debug/anim_capture.gd: front+side screenshots of every movement state,
  one-shot action and ADS for visual regression of the model pipeline.

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
Nicholas Butzke
2026-07-19 17:55:05 -04:00
co-authored by Claude Fable 5
parent 39af109893
commit 6c227b5538
3 changed files with 409 additions and 72 deletions
+47 -40
View File
@@ -273,47 +273,54 @@ func _set_layer_recursive(node: Node, layer_mask: int) -> void:
func _add_procedural_arms(weapon: Node3D) -> void:
# Attach to weapon instead of model_root to avoid arms spinning on reload
var mat = StandardMaterial3D.new()
mat.albedo_color = Color(0.2, 0.4, 0.8) # Player color
mat.roughness = 0.8
# Right Arm (Main Grip)
var r_arm = MeshInstance3D.new()
var r_mesh = BoxMesh.new()
r_mesh.size = Vector3(0.08, 0.7, 0.08)
r_mesh.material = mat
r_arm.mesh = r_mesh
var r_pivot = Node3D.new()
var r_shoulder = Vector3(0.25, -0.3, 0.5)
var r_hand = Vector3(0.04, -0.05, 0.05)
r_pivot.position = r_shoulder
r_pivot.look_at_from_position(r_shoulder, r_hand, Vector3.UP)
r_arm.rotation_degrees.x = 90 # Mesh points UP by default, we need it to point forward along -Z
r_arm.position.z = -0.35
r_pivot.add_child(r_arm)
weapon.add_child(r_pivot)
# Left Arm (Foregrip)
_build_arm(weapon, Vector3(0.25, -0.3, 0.5), Vector3(0.04, -0.05, 0.05))
if "weapon_name" in weapon and weapon.weapon_name != "Knife":
var l_arm = MeshInstance3D.new()
var l_mesh = BoxMesh.new()
l_mesh.size = Vector3(0.08, 0.7, 0.08)
l_mesh.material = mat
l_arm.mesh = l_mesh
var l_pivot = Node3D.new()
var l_shoulder = Vector3(-0.25, -0.3, 0.4)
var l_hand = Vector3(-0.02, -0.02, -0.3)
l_pivot.position = l_shoulder
l_pivot.look_at_from_position(l_shoulder, l_hand, Vector3.UP)
l_arm.rotation_degrees.x = 90
l_arm.position.z = -0.35
l_pivot.add_child(l_arm)
weapon.add_child(l_pivot)
_build_arm(weapon, Vector3(-0.25, -0.3, 0.4), Vector3(-0.02, -0.02, -0.3))
## A first-person arm styled after the character skin: dark detached sleeve,
## glowing cuff, bare hand — instead of the old featureless blue slab.
func _build_arm(weapon: Node3D, shoulder: Vector3, hand: Vector3) -> void:
var sleeve_mat = StandardMaterial3D.new()
sleeve_mat.albedo_color = Color(0.10, 0.11, 0.14) # near-black sleeve
sleeve_mat.roughness = 0.8
var cuff_mat = StandardMaterial3D.new()
cuff_mat.albedo_color = Color(0.25, 0.95, 0.90) # signature teal cuff
cuff_mat.emission_enabled = true
cuff_mat.emission = Color(0.10, 0.55, 0.52)
var skin_mat = StandardMaterial3D.new()
skin_mat.albedo_color = Color(0.98, 0.88, 0.82) # skin
skin_mat.roughness = 0.9
var pivot = Node3D.new()
pivot.position = shoulder
pivot.look_at_from_position(shoulder, hand, Vector3.UP)
var sleeve = MeshInstance3D.new()
var sleeve_mesh = BoxMesh.new()
sleeve_mesh.size = Vector3(0.075, 0.075, 0.46)
sleeve_mesh.material = sleeve_mat
sleeve.mesh = sleeve_mesh
sleeve.position.z = -0.27
pivot.add_child(sleeve)
var cuff = MeshInstance3D.new()
var cuff_mesh = BoxMesh.new()
cuff_mesh.size = Vector3(0.085, 0.085, 0.05)
cuff_mesh.material = cuff_mat
cuff.mesh = cuff_mesh
cuff.position.z = -0.52
pivot.add_child(cuff)
var hand_box = MeshInstance3D.new()
var hand_mesh = BoxMesh.new()
hand_mesh.size = Vector3(0.06, 0.055, 0.12)
hand_mesh.material = skin_mat
hand_box.mesh = hand_mesh
hand_box.position.z = -0.60
pivot.add_child(hand_box)
weapon.add_child(pivot)
func _equip_slot(slot: int) -> void:
if weapons.has(active_slot):