Third-person player characters now animate every action with readable
intent (all networked, cel style preserved):
- Look-around: upper body follows the owner's camera pitch — distributed
over spine/neck/head so aiming up/down reads on the whole silhouette
(driven by the already-synced HeadPivot rotation on remotes)
- Reload: the baked PistolReload clip plays as a one-shot whenever the
equipped weapon starts reloading (local edge-detect poll -> new synced
action counter replays it on every peer)
- Grenade throw: new Throw clip baked from the library's overhead swing
(Sword_Attack retarget); triggers on throw, synced the same way
- Shot recoil: visible kick on the shooter's model (shoulders snap back,
forearms rise, fast decay) driven by the existing fire-effects RPC —
remote players' shots now look like shots
- Slide: trailing arm braces against the ground for balance on top of the
feet-first pose
- Armed idle confirmed in capture: weapon held at ready, not arms-down
Plumbing: synced_action/synced_action_seq added to all three player
spawners' replication configs; ACTIONS table on the model maps names to
clips + lock times.
Verified: 18 clips resolve (incl Throw), smoke 0 failures, movement
11/11, acoustics PASSED; third-person run + armed idle captures clean.
Co-Authored-By: Claude Fable 5 <[email protected]>
Audio:
- All ballistic weapons now fire real firearm recordings (The Free Firearm
Sound Library, CC0): AK-47 uses actual AK-47 shots, M4=AR-15, MP7=PPSh,
AWP=Mosin Nagant, DMR=Savage 10, shotgun=Mossberg/Nova, nailgun=Ruger .22,
rocket/mortar layered with real booms. Plasma keeps its energy sound.
- Reload is real lever-action foley instead of interface clicks
- Projectile flight loops localized: unit_size 50->6, max_db 6->0,
max_distance 45 — no more mortar/rocket drone in every player's ear;
loops also got proper loop_end (was a zero-length loop)
- Flight loops lowpassed into soft whooshes instead of sci-fi engine drones
- Menu music converted to OGG with native full-track looping (47s seamless,
replaces the broken manual WAV loop points)
Animations (movement-shooter pass):
- Rebaked miku.glb with 4 new clips from the CC0 library: Grapple (swim
reach — reads as a swing), PistolIdle (armed idle with weapon up),
PistolShoot, PistolReload
- Armed idle: standing with a weapon now uses PistolIdle instead of
arms-at-sides
- Slide pose overhaul: legs kick out front (lead/trail thighs, knees
straighten) — feet-first slide instead of "sitting in a crouch"
- Wall run: forward drive pitch + inner arm reaches out to the wall
- Per-transition blend times: dash/hit/jump cut fast (0.05-0.08s),
locomotion cross-fades smoothly (0.2-0.25s); CrouchWalk speed-scales
Fixes:
- Third-person camera no longer renders the first-person viewmodel layer
(the gun/arms floated in front of the third-person view)
Verified: 11/11 movement tests, smoke test 0 failures (17 clips resolved),
third-person run screenshot shows grounded animated model, no floating gun.
Co-Authored-By: Claude Fable 5 <[email protected]>
The model animated but deformed wrong: arms crossed the torso and foot heels
stretched to the model's center-bottom. Two root causes, both fixed:
1. Pose mismatch. Sketchfab models are modelled in an A-pose (arms angled
down) but the Quaternius library rests in a T-pose. Binding the A-pose mesh
to T-pose arm bones misweighted the arms, and the library clips (which
rotate arms ~70° down from the T rest) then shoved the already-drooped arms
across the body.
Fix: autorig.fit_arms_to_mesh() reorients each arm bone chain along the
model's real arm direction; merge_animations retargets every clip from the
library's T-pose rest onto the fitted rest via world-space constraint bake
(Copy Rotation/Location + nla.bake), so world motion is reproduced
regardless of the rest difference.
2. Feet-to-center stretch. The library's deform-flagged `root` bone runs up
the body centre-line; nearest-bone weighting assigned inner-foot/heel verts
to it, stretching them to center when animated.
Fix: exclude root/control/mechanism bones from weighting (_NON_DEFORM_HINTS).
Also upgraded nearest-bone weighting from rigid (1 bone/vert) to a smooth
inverse-distance blend across the nearest 4 bones, so joints deform cleanly.
Bone-heat still fails on Miku's joined hair/clothing mesh, so this is the
primary path.
Verified by rendering Idle/Walk/Run poses (Blender): natural stance, arms at
sides swinging correctly, feet planted, proper running stride. Smoke test
still 30/30.
Co-Authored-By: Claude Fable 5 <[email protected]>
Verified the full chain on a real Sketchfab model (Miku UID -> rigged ->
13 clips -> loads and animates in-engine, 24/24 smoke checks pass).
Fixes found while running it for real:
- sketchfab_import: read .sketchfab_token as UTF-16/BOM too (PowerShell's
`echo >` writes UTF-16LE, which crashed the UTF-8 reader)
- autorig: rig to the animation library's OWN skeleton when _library.glb
is present (zero retargeting); robust binding — fall back to rigid
nearest-bone weights when Blender bone-heat fails on complex meshes
(Miku's joined hair/clothing gave 0 weighted verts); export as
parent_type=ARMATURE with no modifier (Blender 5.x only emits a glTF
skin in that exact form — a lingering Armature modifier gave skins:0);
export_apply=False (applying modifiers baked away the skin); flatten
the Sketchfab empty hierarchy before binding
- merge_animations: support a single multi-clip library GLB via
LIBRARY_CLIP_MAP; version-safe fcurve access for Blender 4.4+/5.x
slotted actions (Action.fcurves was removed)
Assets/tooling:
- bundle assets/characters/animations/_library.glb — CC0 Quaternius
Universal Animation Library (13 game clips), + .gdignore so Godot
skips the pipeline-input folder
- requirements.txt (stdlib-only; documents Blender-python separation)
- pipeline docs updated for the bundled library + swap instructions
Co-Authored-By: Claude Fable 5 <[email protected]>
Character pipeline (tools/):
- sketchfab_import.py: search/download via Sketchfab Download API with
license/attribution tracking (SKETCHFAB_API_TOKEN)
- autorig.py: headless Blender auto-rig — fits a Mixamo-named skeleton
to unrigged humanoids, binds automatic weights
- merge_animations.py: merges the shared Mixamo-skeleton animation
library onto any rigged character, strips root motion, canonical names
- pipeline.py: one command chains download -> rig -> animate -> register
In-game skin system:
- SkinManager autoload reads skins.json (auto-written by the pipeline)
- SkinnedPlayerModel rewritten: canonical clips with fallback chains,
blend times, speed-scaled locomotion, weapon bone attachment
- First-person: full animated body for the owner, head hidden via
SkeletonModifier3D; third-person: full model for other players
- Skin selector in main menu; skin id synced in multiplayer
- Fixed GLBLoader crash (GLTFDocument.get_animation_count doesn't exist)
Multiplayer sync overhaul:
- Movement is now client-authoritative: the owning peer simulates
locally (no input round-trip), server keeps health/kills/death
- Remote players interpolate synced_position/velocity with
extrapolation and snap-on-teleport
- Knockback/impulses routed to the simulating peer
Audio:
- AudioManager autoload: SFX/Weapons/Footsteps/UI/Music buses, pooled
3D players, variation + pitch randomization, auto-registration from
assets/sounds
Docs: 3D_ASSET_PIPELINE.md rewritten end-to-end, new ASSET_SOURCES.md
(non-procedural animation/map/sound sources) and SOUND_DESIGN.md.
Verified with debug/spawn_smoke_test.gd (headless: 24/24 checks pass).
Co-Authored-By: Claude Fable 5 <[email protected]>