Skip to content

[FEATURE] First-person presentation: viewmodel layer, decals, textured particles, impact feedback #1681

Description

@Noisemaker111

Problem: the first-person viewmodel shares the world camera and near plane, its origin is module-level state, and there are no decals, textured particles, or hitmarkers. Verified on main: packages/shell/src/camera/GameFirstPersonCamera.tsx:16-21 (VIEWMODEL_ORIGIN, MUZZLE_TIP_LOCAL, module muzzleWorld/muzzleTracked); no DecalGeometry in packages/; packages/shell/src/vfx/ParticleField.tsx draws untextured gl_Points. Related: #1661 (ADS FOV).

Tasks, one PR each, in order. Every task needs before/after shots from bun run shoot the-robots --mode play (it has a viewmodel and a gun).

  1. Viewmodel config and layer. Add viewmodel?: { fov?: number; near?: number; far?: number; origin?: [x, y, z]; muzzle?: [x, y, z]; sway?: number; bob?: number } to FirstPersonCameraConfig in packages/core/src/game/cameraConfig.ts. In GameFirstPersonCamera.tsx: put the viewmodel on its own THREE.Layers bit, render it in a second pass with its own PerspectiveCamera (fov/near/far from config, cleared depth) via an onAfterRender/useFrame at post priority that plays with PostProcessing.tsx (render the viewmodel after the post chain's render pass but before SMAA: add a small extraPasses hook in PostProcessing.tsx rather than restructuring it). Replace the module-level origin, muzzle, and tracked flag with a per-rig object exposed through ctx.camera.viewmodel() ({ muzzleWorld(): [x,y,z] }). Shots: the gun against a wall, no clipping.

  2. Decals (core + shell). New packages/core/src/vfx/decals.ts: DecalSpec { position; normal; size; url; ttl; atlasFrame?; rotation? }, createDecalPool({ capacity }) with add(spec, nowMs), active(nowMs), snapshot/restore/retune; expose ctx.decals. New packages/shell/src/vfx/DecalProjector.tsx: one InstancedMesh of quads oriented to the normal with a small offset and polygonOffset, textured from the URL (atlas frame UVs), fading over ttl. Tests for the pool. Shots: bullet holes on a shack wall.

  3. Textured particles. Extend EmitterConfig in packages/core/src/vfx/particles.ts with sprite?: { url; columns; rows; fps }, stretch?: number, softDepth?: number, rotation?: { min; max; speed }. In ParticleField.tsx switch to an instanced quad ShaderMaterial when sprite is set (billboard, velocity stretch, sheet animation by particle age, soft depth using the depth texture the post chain already has). Shots: a textured muzzle flash and smoke.

  4. Impact feedback. In packages/core/src/combat/ add impactPresets entries for muzzleFlash and bulletImpact composed from tasks 2 and 3 and fired from the existing weaponFire/hit events; Hitmarker block in packages/react/src/hitmarker.tsx keyed on hit and kill events with a HudTheme-styled cross; aim-punch and a recoil pattern (recoil?: { pattern: [x, y][]; recoverMs }) on the camera shake channel in cameraConfig.ts. Clip via bun run drive the-robots with a scripted fire sequence (declare a capture.views entry for it). Closes #1681.

Rules: wait for bun run agent:bootstrap --check; branch claude/<slug> off origin/main; claim comment first; one task per PR with Refs #1681; bun run gen after export changes (JSDoc on every export; create* factories need snapshot/restore, no module-level mutable state); CHANGELOG bullet; check-types and test on core, shell, react as touched plus check-stateful-ratchet, check-doc-symbols, check-orphan-ratchet; merge origin/main before pushing; shots via bun run games:clone, bun run shoot daemon start, bun run shoot the-robots --mode play --size half --out shots/x.png, before shot from stashed origin/main shell code, bun run pr-shots --dir <branch> shots/*.png, clips via bun run pr-video; squash auto-merge; fix CI on the same branch.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions