Commit Graph
292 Commits
Author SHA1 Message Date
Nicholas ButzkeandClaude Fable 5 2c7b5a1aca feat: lighter air control (steer + stronger strafe) and 2-charge dash
- Air: direct steering bends existing velocity toward input without changing
  its magnitude (air_steer_rate), on top of Quake accelerate with raised
  gains (air_strafe_accel 55->90, per-tick cap 1.2->2.5). No-input air drag
  nearly removed so held momentum carries. Steering skips opposing input to
  avoid flipping through zero.
- Dash: charge system on the machine — dash_charges (2) spend instantly,
  each recharges in dash_cooldown (2 s). get_dash_cooldown_remaining() now
  reports time to the NEXT charge (0 while one is banked) so existing HUDs
  keep working; get_dash_charges() added for pips.
- Tests: dash tests updated to the charge API + new coverage for
  spend-2-then-recharge; 11/11 pass, spawn smoke clean.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-17 13:32:01 -04:00
Nicholas ButzkeandClaude Fable 5 198345177a feat: world-grid level materials + real movement test suite
- Generated CC0-style prototype grid textures (0.5 m cells) and a shared
  LevelMaterials factory: world-space triplanar mapping over every code-built
  level (test level, dust2, procedural arena) with the existing color coding
  kept as tints. Readable surfaces at speed instead of flat color boxes;
  materials are cached per tint so identical surfaces batch.
- .gdignore + .gitignore the raw downloaded asset packs in addons/ whose
  overlong paths broke Godot import scans and spammed git warnings.
- Replaced the rotted FSM test file (invalid call(t) on Callables, stateless
  stub nodes, hung forever without quitting) with a real suite: 10 tests
  covering state registration, jump bookkeeping, wall-run momentum
  preservation and upward-carry cap, per-player dash cooldown (regression for
  the old shared static), dash speed stacking, chain soft cap, landing
  events, and slide entry momentum. New headless entrypoint:
  godot --headless --path . -s res://movement/tests/run_fsm_tests.gd

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-17 12:14:30 -04:00
Nicholas ButzkeandClaude Fable 5 7da7f9f3f2 feat: rebuild character skin with full 13-clip CC0 animation set
The shipped skin GLB only had Idle/Walk/Run/Jump, so Slide, WallRun, Crouch,
Dash, Fall and Land all fell back to the wrong clips. Rebuilt via the Blender
pipeline from the library-skeleton rigged model (the old miku_proper_anim was
on a different skeleton, so retargeting silently baked flat clips — and the
shooter pose layer's DEF-* bone names never matched it either).

miku.glb now ships Idle, Walk, Run, Sprint, Jump, Fall, Land, CrouchIdle,
CrouchWalk, Dash (roll), Death, Hit, Dance from the Quaternius Universal
Animation Library (CC0). Verified per-bone keyframe motion in the export and
rendered pose stills for deformation QA (no crossed arms / stretched feet).

Also removed five stale experimental skin GLBs + extracted textures and the
one-off editor import scripts; skins.json now lists only the working skin.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-17 12:04:52 -04:00
Nicholas ButzkeandClaude Fable 5 0ba14a9469 feat: overhaul movement for flow — momentum-preserving states, smooth crouch, landing feel
- Quake-style air-strafe accelerate (real speed gain from strafing; external
  speed from dash/rockets never clamped)
- Wall run: keeps entry momentum (incl. upward carry), accelerates along the
  wall instead of hard-setting velocity, gravity fades in over the run
- Slide: slope physics (gravity projected along floor accelerates downhill),
  flat-ground decel instead of multiplicative friction, entry boost, direct
  slide->wallrun and slide->dash transitions
- Dash: cooldown 10s -> 2s, per-player (was a static shared across instances),
  momentum fully kept on exit, FOV punch event
- Grapple: taut pendulum with active rope control (W reels in, S pays out),
  release keeps swing energy
- Wall climb: carries upward momentum in, jump-off kick, shared vault helper
- Crouch capsule: single owner in the machine, smoothly lerped, ceiling check;
  camera eye height follows via crouch factor (no more head snapping)
- Landing: machine emits 'land' events scaled by impact; camera dip + pitched
  down thud; footsteps get pitch variation
- Camera rig: event-driven (land dip, dash FOV kick, vault pitch impulse),
  slide roll tilt
- Model: Jump vs Fall split by vertical velocity, Land one-shot on heavy
  landings, wall-run body lean (synced to remotes via synced_wall_side)

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-17 11:58:14 -04:00
Nicholas ButzkeandClaude Opus 4.8 c4a538a469 chore: add miku_test skin textures and script .uid files
Godot import artifacts that pair with already-committed assets:
- miku_test_Image_0..3.png: textures extracted from miku_test.glb on import
- .uid files for spawn_smoke_test.gd and audio_manager.gd

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-07 01:31:52 -04:00
Nicholas ButzkeandClaude Fable 5 29fdca3565 feat: shooter animation feel — directional lean, slide, composed airborne, weapon hold
Layer a procedural SkeletonModifier3D (ShooterPoseModifier) on top of the base
clip so the character reads like a movement-shooter avatar:

- Directional lean: banks into the movement direction (right/left/back) and
  blends smoothly for diagonals, driven by velocity relative to facing.
- Slide: leans the torso back and pitches the head up to look forward, instead
  of the base clip's forward-torso/legs-out "spine break".
- Airborne: plays a composed Jump pose (weapon ready) rather than a flailing
  fall.
- Weapon hold: the base clip already keeps the arms down (weapon at the hip);
  on ADS both arms lift and swing in toward centre-front to aim. ADS is read
  from the active weapon and synced (synced_is_ads) so remote players raise
  their weapons too.

The controller feeds movement direction + ADS to SkinnedPlayerModel.set_locomotion()
each frame (works for local and remote via synced velocity/rotation). All
rotations are authored in skeleton space (fwd=+Z, up=+Y, right=-X) and converted
per-bone; tuning constants are at the top of ShooterPoseModifier.

Verified by rendering the poses (idle/strafe/back/slide/ads) in a real Godot
viewport. Smoke test 30/30.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-06 23:23:51 -04:00
Nicholas ButzkeandClaude Fable 5 9939e7e524 fix: correct rig deformation — arms no longer cross body, feet no longer stretch to center
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]>
2026-07-06 19:38:59 -04:00
Nicholas ButzkeandClaude Fable 5 22cb0a58b7 fix: T-posing skinned models — autorig produced an orphan skin with no weights
The model animated its skeleton but rendered a permanent T-pose: the mesh
wasn't skin-bound. Root cause was in autorig's binding, introduced while
chasing an earlier "skins:0" export:

- The earlier "skins:0" was actually caused by bone-heat weighting failing
  (0 weighted verts → empty skin gets dropped), NOT by the Armature modifier.
- The "fix" then stripped the Armature modifier and used parent_type=ARMATURE.
  That makes the glTF exporter emit a skin OBJECT but with no node.skin
  reference and no per-vertex JOINTS/WEIGHTS — an orphan skin. The mesh then
  renders its bind pose (T-pose) forever while the skeleton animates unseen.

Fix: keep the standard ARMATURE_AUTO result (Armature modifier + vertex
groups). With the nearest-bone fallback ensuring real weights, the exporter
now writes a COMPLETE skin (verified: meshnode.skin=0, JOINTS/WEIGHTS present).

Also:
- skinned_player_model: _ensure_meshes_bound() re-binds any skinned mesh whose
  skeleton NodePath doesn't resolve at load — graceful degradation instead of
  a silent T-pose for imperfect GLBs.
- smoke test now asserts the spawned player's mesh is bound to its skeleton,
  so this class of bug fails the test instead of shipping. 30/30 pass.
- regenerated miku_test.glb with the corrected pipeline.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-06 18:11:26 -04:00
Nicholas ButzkeandClaude Fable 5 e2fbc424a7 feat: third-person camera toggle (V) + fix first-person showing inside the model
The first-person head-shrink (SkeletonModifier3D) left the camera looking at
the inside of the neck/torso/hair. Replaced it: the owner's model now renders
shadows-only in first person (clean FPS view, still fully animated and visible
to others and in shadows).

Added a third-person toggle (V / toggle_camera_view) so you can actually see
your own animated model — the easiest way to verify a new skin's animations:
- over-the-shoulder SpringArm3D camera (wall-collision aware) on the local
  player; press V to swap, press again to return
- firing still uses the first-person camera, so aim is unchanged
- reveals the owner's model via set_owner_visible() on both SkinnedPlayerModel
  and HumanoidModel; hides the first-person weapon viewmodel while in TP
- death forces first person so the toggle can't fight the death cam

Smoke test extended to cover the toggle (camera boom created, becomes current,
round-trips) — 28/28 checks pass for both color and GLB skins.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-06 17:18:16 -04:00
Nicholas ButzkeandClaude Fable 5 64bbbf93c6 fix: make the character pipeline work end-to-end in Blender 5.1 + bundle CC0 animation library
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]>
2026-07-06 12:42:31 -04:00
Nicholas ButzkeandClaude Fable 5 d05477c135 feat: Sketchfab-to-game character pipeline, FP/TP animated views, client-auth netcode, audio system
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]>
2026-07-06 02:28:58 -04:00
Hermes 552338112e remove duplicate scenes/test_level.tscn 2026-06-25 23:30:08 -04:00
Hermes 2537ad19c0 fix: queue_free() returns void - split into remove_child + queue_free 2026-06-25 23:25:31 -04:00
Hermes 11d079b38c restore test level and dust2 from main branch 2026-06-25 17:30:30 -04:00
Hermes a351cf2dc4 restore test level and dust2, fix dust2 builder to not use TestLevelBuilder 2026-06-25 17:27:47 -04:00
Hermes 6b82258cec remove stale scenes/test_level.tscn 2026-06-25 17:22:02 -04:00
Hermes e0844a3d3a remove stale dust2 files referencing deleted TestLevelBuilder 2026-06-25 17:16:06 -04:00
Hermes b23a307201 clean up stale test_level files (project uses LevelRuntime now) 2026-06-25 17:12:58 -04:00
Hermes 1121a2f0ee restore test_level_builder.gd (accidentally deleted) 2026-06-25 17:07:00 -04:00
Hermes d5a8267cd2 rewrite skinned_player_model.gd from scratch with clean indentation 2026-06-25 17:02:24 -04:00
Hermes b2e2ce2856 fix: re-export GLB with dense per-frame keyframes to prevent animation stripping 2026-06-25 16:50:51 -04:00
Hermes 8415ceb926 fix: only override animated bones, not all bones 2026-06-25 16:39:09 -04:00
Hermes d6df49844e fix: explicit type for peak variable 2026-06-25 16:24:28 -04:00
Hermes 9e498370fb replace AP animation with code-driven bone override system 2026-06-25 16:22:58 -04:00
Hermes 03e2c29fa5 fix: call update_skeleton() to force bone transform refresh for rendering 2026-06-25 15:53:48 -04:00
Hermes 252137d10b debug: add error logging to animation creation 2026-06-25 15:51:38 -04:00
Hermes 9da0bc3297 fix: remove _update_skin() call 2026-06-25 15:46:29 -04:00
Hermes f776fe59e7 fix: use TYPE_ROTATION_3D=2 with Quaternion values and clean up dead code 2026-06-25 15:03:43 -04:00
Hermes 7e40085b80 fix: use TYPE_VALUE (0) for all animation tracks, not TYPE_ROTATION (1)
Animation.TrackType in Godot 4.2.1 doesn't have a named enum member for
value 1, so add_track(1) fails. Use add_track(0) for all tracks - the
Animation API accepts both Vector3 and Quaternion values regardless of
track type.
2026-06-25 12:26:21 -04:00
Hermes 6cfa8aa631 fix: use raw integer 1 for TYPE_ROTATION and remove unused param
- Animation.TYPE_ROTATION doesn't exist as enum in Godot 4.2.1
- Use raw integer: add_track(1) for ROTATION
- Remove unused 'length' parameter from _add_rot_track
- Rename 'name' to 'anim_name' to avoid shadowing Node.name
- Remove all extra parameters from _add_rot_track calls
2026-06-25 12:21:56 -04:00
Hermes aa9a957eac fix: use Vector3 euler for rotation tracks, NOT Quaternion
track_insert_key() returns -1 (error) when using Quaternion values
on TYPE_ROTATION (type=1) tracks in Godot 4.2.1.

The correct format is: add_track(1) + track_insert_key(track, time, Vector3(euler))

This applies to both Walk/Run/Jump/Idle animations - all must use
Vector3 euler angles for rotation keyframes.
2026-06-25 12:10:00 -04:00
Hermes 92ebd6c8d2 refactor: use AnimationPlayer with TYPE_ROTATION tracks instead of manual bone poses
After extensive testing discovered:
- set_bone_global_pose_override() does NOT affect the GPU skinning pipeline
- set_bone_pose_rotation() only works when AP is stopped but changes don't
  appear during render because _update_skeleton() runs during render pass
- The AnimationPlayer DOES work in the actual game (position logs prove it)
- GLB animations are stripped (near-zero motion) causing T-pose
- Solution: Create proper Animation resources with type=1 (ROTATION) tracks
  using Quaternion keyframes and play through the AnimationPlayer
- Removed all manual bone pose code that was fighting the render pipeline
2026-06-25 12:05:01 -04:00
Hermes 6e73fd7703 fix: use local pose (rest*offset) instead of global override for skinning
CRITICAL DISCOVERY: set_bone_global_pose_override() does NOT affect
the MeshInstance3D skinning pipeline. It only changes get_bone_global_pose()
return value. The actual skinning uses rest * pose * parent transforms.

The correct approach:
1. Stop AnimationPlayer (ap.stop() + ap.active = false)
2. Set local pose = rest_local_quat * offset_quat
3. This makes the final bone transform = rest * (rest * offset) = rest * offset
4. The skinning pipeline correctly uses this for vertex deformation

Also discovered: set_bone_pose_rotation() only works when AP is stopped.
When AP is active, it overwrites the local pose every frame.
2026-06-25 11:45:23 -04:00
Hermes f085fdf7b7 debug: add logging for parent/SM null checks 2026-06-25 11:38:48 -04:00
Hermes b525db7c53 debug: add logging to _process and _apply_procedural_animation 2026-06-25 11:38:19 -04:00
Hermes 000caa7791 fix: clear global pose overrides when switching to procedural anim 2026-06-25 11:34:30 -04:00
Hermes e4ea177eaf fix: use world-X rotation for locomotion swing via global pose override
After extensive testing, discovered that:
1. set_bone_pose_rotation() sets ABSOLUTE local rotation, not offset
2. Bones have complex non-identity rest poses (e.g. LeftUpperArm euler=(-0.161,-2.760,-2.760))
3. Setting local pose to identity collapses the model
4. Correct approach: use set_bone_global_pose_override() with rotation
   applied around world X axis (forward/back swing) pre-multiplied on
   the rest pose basis

Also cache rest pose local transforms (rotation + position) and use them
when resetting bones or applying position offsets.
2026-06-25 11:33:49 -04:00
Hermes 35decd65b3 fix: use world-space rotation via global pose override
The bone rest poses have complex non-identity rotations (e.g. LeftUpperArm
euler=(-0.161,-2.760,-2.760)). Applying local rotations on top of these
produces incorrect results. Fix by using set_bone_global_pose_override()
with world-space rotation pre-multiplied on the rest pose basis.
2026-06-25 11:24:54 -04:00
Hermes 35cb2ef10c chore: remove debug logging from procedural animation 2026-06-25 11:14:15 -04:00
Hermes f457c01f38 fix: always replace all locomotion animations (Jump was not being detected as broken) 2026-06-25 11:13:39 -04:00
Hermes ea23f1c843 fix: stop AnimationPlayer from overwriting procedural bone poses
AnimationPlayer was still processing GLB animations and overwriting
our manually-set bone rotations. Fix by:
1. Setting animation_player.active = false for procedural anims
2. Resetting bones to rest pose when switching to procedural anim
3. Re-enabling AP only for non-procedural anims (Death, Crouch)
2026-06-25 11:13:07 -04:00
Hermes 30e2a49730 debug: add logging to verify procedural animation execution 2026-06-25 11:11:41 -04:00
Hermes 5d360d256e fix: replace AnimationPlayer tracks with code-driven bone rotation system
Godot 4.2.1 AnimationPlayer cannot rotate runtime-loaded GLB bones via
':rotation' track paths. The AnimationPlayer only updates bone positions,
not rotations, on runtime GLB skeletons.

This fix replaces the entire animation approach:
- Remove AnimationPlayer track-based rotation attempts
- Add code-driven system that stores animation keyframe data in
  dictionaries and applies bone rotations directly via
  skeleton.set_bone_pose_rotation() each frame
- _is_animation_broken() detects Blender GLTF track stripping
- _ensure_locomotion_animations() replaces broken GLB anims with
  procedural data
- _apply_procedural_animation() interpolates euler angles, converts
  to Quaternion via Quaternion.from_euler(), and applies per bone
2026-06-25 10:53:54 -04:00
Hermes 86a13bc782 fix: replace stripped GLB animations with procedural rotation-based animations
Blender's GLTF exporter aggressively strips animation tracks that are
near rest pose. The Miku GLB has 54 tracks per animation but all
keyframe values are essentially rest pose (max delta 0.008 units),
causing the model to appear frozen in T-pose despite the AnimationPlayer
playing animations correctly.

This fix:
- Adds _is_animation_broken() to detect near-zero motion in animations
- Changes _ensure_locomotion_animations() to replace broken animations
  (not just create when missing)
- Uses TYPE_VALUE tracks with ':rotation' suffix for proper bone rotation
- Fixes hips bounce to use position track instead of rotation
- Adds _add_rotation_track, _add_position_track, _add_position_track_catmull
  helper functions
2026-06-25 10:38:19 -04:00
Hermes d6da221583 fix(characters): preserve all animation tracks in GLTF runtime loading
remove_immutable_tracks=true (default) strips bone tracks where keyframes
match rest pose, removing 80%+ of tracks and causing T-pose.
Editor import uses false; runtime must match.

Closes T-pose bug where animations reported playing but model stayed rigid.
2026-06-25 10:04:56 -04:00
HermesandClaude 4.7 d297da4c35 fix: resolve GLB character T-pose by fixing skeleton binding order and skin update timing
Two root causes fixed:
1. Skeleton binding fix was skipped because `skeleton` variable was null at the
   time of the check (assigned AFTER the mesh fix block). Moved skeleton
   discovery BEFORE the mesh binding fix so the MeshInstance.skeleton path
   is correctly set to "..".

2. Skin deformation wasn't triggering because `_update_skin()` (which calls
   `force_update_all_bone_transforms()`) was throttled to 30fps AND ran BEFORE
   the AnimationPlayer's `_process()` due to tree order. AnimationPlayer.advance()
   sets bone poses but does NOT emit `bone_pose_changed`, so the MeshInstance
   never knew to re-compute its vertex buffers. Fixed by using `call_deferred`
   to ensure `_update_skin()` runs AFTER all `_process()` calls (including AP),
   and removing the throttling so it runs every frame.

Co-Authored-By: Claude 4.7 <[email protected]>
2026-06-25 01:29:14 -04:00
Hermes df4c70017c fix: use force_update_all_bone_transforms() instead of undeclared constant
NOTIFICATION_UPDATE_SKELETON is not accessible in GDScript scope.
Use Skeleton3D.force_update_all_bone_transforms() which recomputes the
full bone hierarchy and emits bone_pose_changed signals.

Co-Authored-By: Hermes Agent <[email protected]>
2026-06-25 01:00:34 -04:00
Hermes 43b870465b fix: correct skin deformation and scale for GLB character models
The Miku model T-posed because the previous _update_skin() used
set_bone_pose(i, get_bone_pose(i)) which is a no-op when the pose
is unchanged. Replace with skeleton.notification(NOTIFICATION_UPDATE_SKELETON)
to force the Skeleton3D to recompute bone transforms and notify the
MeshInstance to update vertex buffers.

Also fix scale: the GLB is ~1.2m tall (not 1.6m as documented), so
set scale_factor=1.25 to fit the 1.8m player capsule, and add
position_y_offset=-0.2 to align the model's feet with the player origin.

Co-Authored-By: Hermes Agent <[email protected]>
2026-06-25 00:56:25 -04:00
Hermes c4dc4ca77c fix: force skeleton skin update at runtime for GLTF models
The AnimationPlayer correctly updates bone poses (proven in tests)
but the MeshInstance visual doesn't reflect the changes. Added
throttled _update_skin() that re-applies bone poses every ~33ms
to trigger NOTIFICATION_UPDATE_SKELETON on the Skeleton3D,
which causes the MeshInstance to update its vertex buffers.

Also set AnimationPlayer process_mode to ALWAYS to ensure it
processes regardless of parent node state.
2026-06-25 00:30:28 -04:00
Hermes 73831cb418 fix: force bone pose update for GLTF runtime skin deformation
GLTF runtime loaded models may not trigger automatic MeshInstance
vertex deformation when the AnimationPlayer updates bone poses.
Added throttled _update_skin() that re-applies current bone poses
to trigger NOTIFICATION_UPDATE_SKELETON, ensuring the renderer
sees the updated vertex positions.
2026-06-25 00:26:34 -04:00