Commit Graph
253 Commits
Author SHA1 Message Date
Nicholas ButzkeandClaude Fable 5 a7fa7f207c feat: Akiba Crossing — parametric Akihabara rebuild of the urban map
Complete new approach after the hand-placed layout shipped misaligned:
every element now derives from a street grid and its building's footprint,
so alignment is structural, not luck.

District structure (modeled on Akihabara's Chuo-dori / Denki-gai / JR
viaduct):
- 16m avenue flanked by continuous mid-rise rows; buildings defined ONLY
  by z-interval + floor count (4m quantized floors), packed edge-to-edge
- _building() derives everything from the footprint: parapet lips on the
  roof rim, inset glass storefront, sidewalk canopy, stacked per-floor
  sign bands, a solid vertical corner sign column with glowing segments,
  evenly spaced roof AC units, rooftop billboard on 4+ floor buildings
- Brick rail viaduct crosses the whole block on piers with an under-track
  vending arcade; sidewalk-aligned slide ramps with handrails reach the
  deck on both flanks
- Denki-gai alley cut through both rows (wallrun walls + fire escape
  platforms climbing per floor); red SEGA-style landmark + blue
  electronics building anchor the north corners
- Matched streetlight pairs every 16m with arms over the road; zebra
  crossings at the alley mouth and south block; spawns on sidewalks/deck
  facing the map center

Movement: viaduct high lane + roof-hop onto the 2-floor row, canopy lane
above each sidewalk, alley wallruns + fire escapes, grapple anchors on
lamp arms / sign columns / viaduct edge / billboards, slide ramps.

Acoustics: facades concrete, storefronts glass, canopies wood, viaduct
brick, signs/AC/vending/lights/escapes metal.

Renamed in the level selector: "Akiba Crossing". Verified via aerial and
street captures (aligned rows, clean canyon, viaduct framing); movement
11/11, smoke 0 failures, acoustics ALL PASSED.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-18 14:34:31 -04:00
Nicholas ButzkeandClaude Fable 5 233f88aee3 feat: full cel treatment for Neon Alley — flat toon colors + ink outlines
- LevelMaterials.flat(): toon-banded FLAT color material (no greybox grid
  texture) for dressed maps; cached per tint, rim/spec off
- Neon Alley overrides _box_static: every structure renders flat cel color;
  prop-scale pieces (<10m) get an inverted-hull ink outline scaled to their
  size. Building-scale boxes skip the hull — the inflated plates smear into
  black voids at close range (found via fixed street-camera capture)
- Pylon wires render as flat ink lines; warmer wood + hotter vermilion for
  flat shading (the grid multiply used to brighten tints)
- visual_capture: deterministic street-level shot after the aerial for
  style comparisons

Street capture now reads properly BoomerangX: flat lavender asphalt, hard
light bands, torii framed down the underpass, neon strips + sign glow.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-18 14:23:37 -04:00
Nicholas ButzkeandClaude Fable 5 0813d0b40d feat: Neon Alley — urban-Japan movement map built on the acoustic materials
New map (scenes/maps/neon_alley/, auto-appears in the level selectors):

Layout for movement flow:
- Main N-S street = the long sightline, crossed by a pedestrian bridge
  (slide ramps up both ends, underpass cover below, railed high ground)
- West machiya row: wooden two-story houses with a jump route
  (awning -> balcony -> roof deck) and 25-degree slide roofs facing the street
- East concrete towers with glass storefronts; brick wallrun alley between
  them (AC units to vault); metal fire-escape platform route to the roofs;
  angled rooftop billboard as cover + grapple anchor
- Three power pylons with crossarms over the street chain a grapple route;
  the torii gate crossbeam at the north shrine plaza is a fourth anchor
- South market: wooden stalls with neon canopies + metal vending machines
  = scattered mid cover; shipping container corner piece
- Spawns ring the block and face the map center

Acoustics as level design: every surface is tagged (machiya/stalls/torii
wood, towers concrete, storefronts glass, alley+perimeter brick, escapes/
AC/rails/container metal) so occlusion tells you which structure a shot
came through and reflections differ per street.

Style: sunset sky + fixed dusk-lavender ambient (calibrated for the toon
shader's 1/PI light response), warm low sun, neon sign columns, shop signs,
lantern strings, billboard glow, underpass strip lights.

Verified: aerial + street-level + third-person captures; movement 11/11,
smoke 0 failures, acoustics ALL PASSED.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-18 14:18:06 -04:00
Nicholas ButzkeandClaude Fable 5 a51595e3b9 feat: material-aware sound occlusion + early reflections for localization
New Acoustics system (globals/acoustics.gd):
- Occlusion: rays from listener to source collect up to 3 occluding bodies;
  each surface's material adds transmission loss and lowers the lowpass
  cutoff. A gunshot through wood stays warm (-8dB, 1.4kHz), brick muffles
  hard (-15dB, 600Hz), concrete harder (-18dB, 450Hz), metal keeps more
  highs (-11dB, 1kHz), glass barely muffles (-5dB, 2.6kHz). Applied via
  each AudioStreamPlayer3D's attenuation filter.
- Early reflections: loud sounds (gunfire, explosions) probe 8 directions
  for nearby walls; each close surface plays a quiet, delayed (path/343ms),
  material-filtered copy from the mirror point — shots audibly slap back
  off structures so players can triangulate. Sub-30ms echoes are skipped
  (they perceptually merge with the direct sound).
- Surface classification: builder-set acoustic_material meta -> node-name
  keywords -> metallic-material inference -> concrete default; cached.

Integration:
- AudioManager.play_3d applies occlusion at fire time and re-filters all
  playing positional sounds every 0.12s (walking behind a wall mid-sound
  audibly muffles it); flight loops opt in via "acoustic_occluded" group
- Remote players' gunshots were entirely SILENT — rpc_play_fire_effects now
  plays the right weapon's shot at the shooter's position, occluded and
  reflected like everything else
- Remote players' footsteps were also silent — now positional + occluded,
  cadence scaled to their speed
- Test level tagged: brick arena walls, wooden platforms/obstacles, metal
  wall-run corridor/rails/dash platforms; dust2 sandstone reads as brick

Tests: debug/acoustics_test.gd (12 checks: per-material loss ordering,
multi-wall stacking, reflection delay/filter, name classification) — all
pass; movement 11/11; smoke 0 failures.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-17 23:48:38 -04:00
Nicholas ButzkeandClaude Fable 5 44460b52ae fix: exactly one bang per bullet — cut bursts and range echoes from gun audio
The library's master sheet revealed the primary AK-47 sample (C_27P) was a
full-auto LONG BURST, and two other slots used burst takes (C_34P, P_22P) —
one trigger pull audibly played several rounds. Beyond that, the single-shot
recordings carry outdoor-range slap-back echoes arriving as discrete pops at
~0.23s/0.33s/0.45s, which also read as extra shots.

- All fire sounds rebuilt from sheet-verified SINGLE-shot takes only
  (AK-47 gets C_28/C_31 + same-caliber SKS as third variation; MP7 gets
  single PPSh takes + Walther PPQ 9mm)
- Every sample now ends before the first echo can play: full level through
  the crack (~0.145s), faded out by 0.21s (times scale with pitch rate)
- Verified with silencedetect: every fire WAV is a single transient with
  no mid-file re-attacks

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-17 23:36:30 -04:00
Nicholas ButzkeandClaude Fable 5 0155bcdf04 fix: tighten gun audio timing and replace off-character sounds
- Gunshots: strip lead-in silence from every firearm recording (the audible
  delay between trigger and bang) and hard-trim to a single 0.7s crack with
  a fast fade — the outdoor-range echo read as 2-3 extra shots. Big single
  shots (AWP/shotgun/rocket/mortar) keep a 1s tail but still one boom.
- Hit confirm: short fleshy punch thock instead of the bell ("taco bell")
- Dash: clean synthesized air whoosh (bandpassed pink noise swell) instead
  of thruster fire crackle; grapple shoot inherits it
- Jump: push-off foot scuff + faint whoosh instead of the balloon boing
- Wind speed-loop: deep lowpass so it reads as rushing air, not fire
- Grenade: silent in flight — the rocket-engine loop made it sound like it
  was burning; bounces still audible

Verified: reimport + smoke test 0 failures, 11/11 movement tests.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-17 23:29:34 -04:00
Nicholas ButzkeandClaude Fable 5 a9b0775de3 feat: real gunshots, localized 3D audio, seamless music, better animations
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]>
2026-07-17 21:49:10 -04:00
Nicholas ButzkeandClaude Fable 5 6c8b0ddd28 feat: cel-shaded look & feel overhaul — real audio, comic UI, themed VFX
Audio (all CC0 — Kenney packs + OpenGameArt, see assets/sounds/SOURCES.md):
- Replace every procedural synth WAV with forged sounds (ffmpeg pitch/layer
  mixes): distinct fire sounds per weapon with 2-3 variations, explosions,
  footsteps x5, land/jump/dash/vault, knife swing, hit/kill confirms,
  bullet impacts, reload, flight loops, UI hover/click/confirm/error,
  match jingles, menu music
- AudioManager.stream_for(): AudioStreamRandomizer per sound id — every
  weapon now gets variation + pitch randomization on each shot
- Explosions and bullet impacts play positional audio; landing has its own
  sound instead of a pitched footstep; ambient wind bed on every map

Visuals:
- ExplosionVFX: shared cel-shaded burst (white-hot stepped core, ink
  shockwave ring, star spikes, flat smoke puffs) replaces the orange
  sphere in both local and remote-replay paths
- Comic star muzzle flashes on all weapons; unified cel tracer bolts with
  ink outlines across hitscan/shotgun/remote paths
- Impact decals: hard-stepped ink-splat gradients instead of soft airbrush
- Toon shading on first-person view models + arms, third-person weapons,
  and the procedural humanoid fallback (no hull outlines on FBX weapons —
  their hard normals tear the inverted hull)
- Fix: giant soft "blob" highlight on floors — toon rim/specular disabled
  on level-geometry materials (pre-existing artifact since the cel commit)
- Fix: own third-person weapon rendered into the first-person camera
  (shadows-only now covers first_person_mode)

UI:
- UITheme: comic theme on the root window — Bangers display font (OFL),
  paper panels with thick ink borders + hard drop shadows, papaya accent,
  themed buttons/inputs/popups; hover/click sounds on all menu buttons
- Main menu: tilted comic wordmark, sunset toon diorama, looping menu music
- Match HUD: themed scoreboard/killfeed/kill counter

Viewports:
- 4x MSAA project-wide + viewmodel/diorama viewports (crisp ink lines)
- Viewmodel camera near plane 0.01; third-person camera FOV syncs settings
- debug/visual_capture.gd: dev tool to screenshot menu + level for review

Verified: 11/11 movement tests, spawn smoke test 0 failures, before/after
screenshot comparison of menu and level.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-17 18:15:42 -04:00
Dotts 1358183f98 Merge pull request 'Feat/14 movement overhaul' (#20) from feat/14-movement-overhaul into main
Reviewed-on: #20
2026-07-17 10:44:23 -07:00
Nicholas ButzkeandClaude Fable 5 5bcbcfdc3f feat: emotive animations + anime speed lines — hit flinch, dance emote (B)
- SkinnedPlayerModel: generic play_oneshot() (generalizes the Land lock),
  Hit and Dance in the clip table (Dance loops), restartable clips
- Hit flinch: victims visibly react on every peer's screen via the existing
  damage broadcast — no new RPC
- Dance emote on B: toggles while grounded and idle, breaks on any movement;
  synced to remotes via synced_is_dancing (all three spawner configs)
- Anime radial speed-lines overlay (canvas shader): fades in past ~1.2x walk
  speed, spikes on dash, widescreen-corrected clear center
- New input action 'emote' (B) in project.godot

11/11 FSM tests, spawn smoke 0 failures.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-17 13:40:49 -04:00
Nicholas ButzkeandClaude Fable 5 37c15bed1a feat: cel-shaded look — toon shader, ink outlines, stylized environment
- toon.gdshader: banded diffuse with cool-tinted shadows (3 tones), stepped
  specular, rim light, and an optional world-triplanar albedo path so level
  geometry keeps the 0.5 m grid with no UVs
- toon_outline.gdshader: inverted-hull ink outline, applied via
  material_overlay so it works on skinned (deforming) meshes
- LevelMaterials: tinted() now returns toon grid materials; toonify()/
  apply_toon_recursive() convert imported character/prop materials in place
- SkinnedPlayerModel: characters get toon shading + outline on load
- LevelEnvironment: shared stylized environment for all maps — saturated
  anime sky, linear tonemap (filmic/ACES crush cel bands), bloom for
  emissives, saturation+contrast grade, light depth haze; test level and
  dust2 now use it and the procedural arena (which had NO environment)
  gets it plus runtime toonification of its baked geometry

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-07-17 13:37:27 -04:00
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