Skip to content

Fix gun viewmodel orientation: point the barrel forward, not at the player - #42

Merged
yassinsolim merged 1 commit into
mainfrom
fix/weapon-viewmodel-orientation
Jul 3, 2026
Merged

Fix gun viewmodel orientation: point the barrel forward, not at the player#42
yassinsolim merged 1 commit into
mainfrom
fix/weapon-viewmodel-orientation

Conversation

@yassinsolim

Copy link
Copy Markdown
Owner

Problem

After #41 the guns rendered on the knife's gripping arms, but the orientation was wrong in-game: the Deagle read upside-down and pointed back toward the player, and the AWP (though upright) also pointed at the player. The hands also sat too far away.

Root cause: the Gun mesh is authored barrel-up (+Y) in the knife rig's wrist frame, so an identity transform aims it at the ceiling/back, not forward.

Fix

Instead of guessing Euler angles, seatGunForward() computes the correction analytically: it reorients the Gun mesh — relative to its animated wrist parent — so the muzzle (local +Y) points along the viewmodel forward (camera −Z) with a small downward pitch, and the top (local +Z) points up. Key property: the math is frame-invariant (the camera's world rotation and the per-frame sway cancel out), so the barrel keeps pointing where the player aims as they look around and the idle animation plays. The grip stays in the hand because the correction rotates about the gun's grip origin.

  • Per-gun barrelPitch / barrelYaw: the pistol aims essentially straight; the long rifle gets a slight yaw so it reads as a canted profile with a visible scope instead of a foreshortened pole.
  • Pulled the models closer / reframed so the hands sit like the knife reference ("hands too far away").

Verification

Built a faithful in-engine harness that drives the real ViewmodelRenderer exactly like GameApp, with a programmatic barrel/up-direction probe (no more guessing from dark screenshots). Both guns now report FORWARD ✓ + UPRIGHT ✓ and screenshot correctly in POV and side views. Harness removed before commit.

npm run ci green: 138 tests, typecheck, build.

Note

This is a focused 1-file change (WeaponViewmodels.ts, +66/−6). Fire/reload still use the procedural recoil kick (the shared knife clip has no gun-specific segments) — unchanged from #41.

…layer

Both guns were mounted barrel-up in the knife rig's wrist frame, so with an
identity transform the Deagle read as upside-down and both pointed back toward
the player. Rather than guess Euler angles, compute the Gun mesh's correction
analytically: reorient it (relative to its animated wrist parent) so the muzzle
points along the viewmodel forward with a small downward pitch, and the top
points up. The math is frame-invariant (camera + sway rotation cancel), so it
holds as the player looks around and the idle animation plays.

- New seatGunForward() seats each gun's barrel forward/up from a per-gun pitch
  and yaw (the AWP gets a slight yaw so a long rifle reads as a canted profile
  instead of a foreshortened pole).
- Pulled the models closer and reframed so the hands sit like the knife
  reference, addressing 'hands too far away'.

Verified in-engine (real ViewmodelRenderer harness) with a programmatic
barrel/up direction probe: both guns read FORWARD + UPRIGHT. CI green (138
tests, typecheck, build).
@vercel

vercel Bot commented Jul 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
webstrafe Ready Ready Preview, Comment Jul 3, 2026 9:19am

@yassinsolim
yassinsolim merged commit 6bc9aec into main Jul 3, 2026
3 checks passed
yassinsolim added a commit that referenced this pull request Jul 3, 2026
…at per frame (#43)

Follow-up to #42. Two bugs remained that made the Deagle and AWP point at the
ground:

1. Wrong barrel axis. The previous seat assumed the gun's muzzle was its local
   +Y axis. The real downloaded GLBs are authored with the standard glTF
   viewmodel convention — barrel along local -Z, up along local +Y (confirmed by
   measuring each Gun mesh's geometry: the longest extent is -Z). Forcing +Y
   forward therefore aimed the true barrel downward. seatGunForward now aligns
   the actual -Z/+Y axes to the target world forward/up.

2. One-time seat drifted. The seat was computed once at load, but the shared
   idle animation rotates the wrist every frame, so the barrel dipped as the
   hand moved. We now cache the Gun mesh and re-seat it every frame in update(),
   after the mixer moves the wrist — so the muzzle stays locked forward through
   the whole idle loop. gun.world is forced to the desired orientation each
   frame, which tracks the camera + sway (so the gun follows the view and sways)
   but ignores the idle wrist rotation (no drift).

Also reframed both guns closer/tuned so the hands sit like the knife reference
('arms too far away'), and gave the AWP a sideways cant so a long rifle reads as
a scoped profile instead of a foreshortened pole.

Verified in the ACTUAL game (not just a harness): drove the real build with
Playwright, enabled combat, switched to slot 2/3, and confirmed both guns point
forward, upright, gripped — and stay forward while looking around (the
frame-invariant seat holds). CI green (138 tests, typecheck, build).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant