228 Commits

Author SHA1 Message Date
math-pixel 7696519452 Merge branch 'develop' into fix/repair-game 2026-06-02 09:26:40 +02:00
math-pixel a3e8e732f1 its functionning 2026-06-02 00:23:43 +02:00
math-pixel 489499f5d2 Merge pull request 'Feat/polish-mission1' (#12) from feat/polish-mission1 into develop
🔍 Lint / 🪄 Check lint (push) Has been cancelled
🔍 Lint / 🎨 Check format (push) Has been cancelled
🔍 Lint / 🔎 Typecheck (push) Has been cancelled
📊 Quality / 🔒 Security Audit (push) Has been cancelled
📊 Quality / 📋 Dependency Freshness (push) Has been cancelled
📊 Quality / 📦 Bundle Size (push) Has been cancelled
🔍 Lint / 🏗 Build (push) Has been cancelled
Reviewed-on: #12
2026-06-01 21:51:09 +00:00
Tom Boullay 39b996eb31 Update GameMapCollision.tsx
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-06-01 23:38:19 +02:00
Tom Boullay 134c0aecb7 fix(world): reallocate shadow map after Suspense + clear LaFabrik doorway
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
Shadows occasionally failed to render on initial load and the Fabrik
doorway sometimes blocked the player. Both issues are tracked down to
geometry that mounts after Lighting:

- Shadows: GLTFs and the merged static map mount imperatively after
  Lighting, so materials get compiled against a renderer state that
  pre-dates the final scene and bake a 'no shadow map' permutation,
  silently dropping shadows. A WebGL context-restore cycle fixes it,
  but is too invasive. New 'useShadowMapWarmup' hook replays it
  cheaply: once the scene mesh count has been stable for ~1s, it
  disposes the directional shadow map (three.js reallocates it on
  the next render) and marks every material 'needsUpdate' so shaders
  rebind to the freshly created shadow sampler.
- Doorway: the door slab + its Solidify-modifier frame (children of
  the 'Thicken' parent in the LaFabrik GLTF) sat inside the doorway
  AABB and prevented the player from walking through. Stripped from
  the collision octree alongside the existing 'porte' slab; visual
  rendering is unaffected.

Also: extract sun-relative-to-camera placement into a small helper,
remove the temporary diagnostic logs, and document the shadow warmup
in three-debugging.md.
2026-06-01 23:37:57 +02:00
Tom Boullay b144dc1c18 Update model.gltf
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-06-01 22:42:21 +02:00
Tom Boullay 69c720b86b fix(world): restore multi-frame shadow warmup and unblock fabrik doorway
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
- Lighting: replace single-frame needsUpdate with a 3-rAF warmup that forces
  scene.updateMatrixWorld + sun.shadow.needsUpdate + gl.shadowMap.needsUpdate.
  This restores the SceneShadowWarmup behaviour (deleted in 777e51e) inline,
  so shadows survive Physics Suspense remounts and webglcontextrestored.
- octreeCollisionConfig: remove (comment out) the thin LA_FABRIK interior box
  at x=-6.93 that was sealing the doorway despite the mesh hole; fabrik mesh
  octree already provides surrounding wall collision.
- DebugOctreeVisualization: add Fabrik-only filter to inspect interior
  collisions/non-collisions in isolation.
2026-06-01 22:41:45 +02:00
math-pixel d975aac018 o think is not that 2026-06-01 22:26:58 +02:00
Tom Boullay 1b57a25e5f fix(world): strip blender-suffixed porte variants from la fabrik collision
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-06-01 22:19:58 +02:00
Tom Boullay f6db7d74e2 chore(world): add temporary diagnostics for porte strip, octree, ctx loss 2026-06-01 22:19:27 +02:00
Tom Boullay a1798aecb3 refactor(ui): split talkie dialogue overlay
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-06-01 21:43:58 +02:00
Tom Boullay 3b07f40f2d fix(ui): restore talkie idle vs active animation
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
The previous talkie-overlay refactor lost the rest/active behaviour and
left the radio-shake CSS animation running constantly. Restore the
intended polish:

- Position lerp between TALKIE_REST_Y (idle) and TALKIE_ACTIVE_Y
  (raised when narrator is speaking) with a subtle floating bob.
- Subtle z-axis float at idle, faster shake when active.
- Gate the CSS radio-shake on the new --active modifier so the talkie
  is calm when no narrator dialogue is playing.
- Keep the face-camera rotation [0.18, PI, -0.08] from the original
  overlay version.

Visibility still kicks in at the reveal step (no regression on the
recent fix).
2026-06-01 17:01:08 +02:00
Tom Boullay 27416143e3 update: add dialogue 2026-06-01 16:57:22 +02:00
Tom Boullay a2a491bd5c chore(world): add temporary shadow pipeline diagnostics
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
Shadows still go missing intermittently despite the per-frame
needsUpdate fix. Add temporary console logs to narrow down the cause:

- [shadow:mount]: one-shot snapshot of renderer shadow flags and a
  scene.traverse count of meshes with castShadow / receiveShadow at
  Lighting mount time.
- [shadow:tick]: every 2s during useFrame, log shadow map enabled flag,
  autoUpdate, sun.castShadow, sun intensity, shadow map texture
  presence, sun and target world positions, and renderer draw calls.

To be removed once the root cause is identified.
2026-06-01 16:50:21 +02:00
Tom Boullay da7d66e1fd feat(debug): add filters to octree visualization
Default visualization was unreadable because every node from depth 0 to
maxDepth was rendered with rainbow-coloured edges. Add three filters
exposed in the Debug folder:

- Octree Leaves Only (default true): skip internal nodes
- Octree Min Depth (default 4): hide the largest enclosing boxes
- Octree Opacity (default 0.35): tone down line density

Also skip nodes without triangles, drop the per-depth HSL palette in
favour of a uniform cyan, and bump default Octree Max Depth to 8.
2026-06-01 16:50:08 +02:00
Tom Boullay 5faf4b4197 fix(ui): keep talkie overlay visible after reveal step
Regression introduced by the narrator-video revert (1ad0c4d): the talkie
overlay was hidden whenever no narrator subtitle was active. Restore the
prior behaviour where the talkie stays visible from the reveal step
onward and only the --raised modifier and signal lines depend on the
active narrator dialogue.
2026-06-01 16:49:58 +02:00
Tom Boullay bee0c7f223 fix(world): make octree collision proxies solid
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-06-01 15:15:55 +02:00
Tom Boullay 216d29ae59 docs(three): document sun shadow needsUpdate fix
Update three-debugging.md to reflect that the shadow intermittence
is resolved by explicit sun.shadow.needsUpdate = true at mount and
in useFrame after updateMatrixWorld.
2026-06-01 14:47:29 +02:00
Tom Boullay e13cf1e4c7 fix(world): force per-frame sun shadow refresh
Restore sun.shadow.needsUpdate = true at mount and in useFrame
after updateMatrixWorld. Lost during SHADOW_CONFIG centralization.
Matches develop's belt-and-suspenders pattern; autoUpdate alone
is insufficient because the sun follows the camera (matrix dirty
every frame) and three.js can skip shadow map re-render.
2026-06-01 14:46:57 +02:00
math-pixel cd0afcda8c feat mission-2 2026-06-01 14:40:17 +02:00
Tom Boullay d20bdc4934 fix(ui): render loading loader as raw image
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-06-01 14:39:55 +02:00
Tom Boullay 7c35090dbd fix(ui): animate scene loading logo
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-06-01 14:28:48 +02:00
Tom Boullay a766784ce8 docs: update scene runtime and debug toggles
- Drop SceneShadowWarmup section, document centralized shadow config.
- Document the localized Suspense boundaries in World.tsx.
- Document the new player model and octree debug visualizations.
- Open note about intermittent first-load shadow rendering.
2026-06-01 14:16:01 +02:00
Tom Boullay 63952912b5 fix(world): wrap stage and player in suspense to prevent scene remount
Late asset loads inside GameStageContent (e.g. EbikeSpeedometer's
useTexture) and the spawn-player block were bubbling Suspense up to the
root boundary in pages/page.tsx, which unmounted World mid-load and
triggered a redundant octree rebuild + shadow re-config. Localize the
suspension by wrapping each block in its own Suspense fallback.

Also mount DebugOctreeVisualization conditionally on the new debug
toggle.
2026-06-01 14:14:27 +02:00
Tom Boullay fd0b9e2749 feat(debug): add player model and octree visualization toggles
- Show Player Model: render the main character GLTF in camera-local
  space so it stays visible at any pitch.
- Show Octree: overlay the collision octree as colored line segments,
  one wireframe per spatial cell, colored by depth.
- Octree Max Depth: cap recursion to keep the scene readable.
2026-06-01 14:14:20 +02:00
Tom Boullay 777e51efeb fix(world): centralize shadow config and remove warmup
- Extract SHADOW_CONFIG into lightingConfig.ts (bias=0, normalBias=0,
  cameraSize=95) matching the historically working values from develop.
- Drop SceneShadowWarmup; rely on sun.shadow.autoUpdate=true for
  steady-state refresh.
- Enable cloud castShadow and traverse Ebike meshes for cast/receive.
2026-06-01 14:14:14 +02:00
Tom Boullay 1ad0c4de37 revert(ui): remove narrator video on talkie 2026-06-01 14:14:03 +02:00
Tom Boullay 7a378afad3 feat(world): add octree collision proxies 2026-06-01 14:11:23 +02:00
Tom Boullay d52ec7e5a9 fix(model): clean lafabrik props and materials 2026-06-01 13:50:16 +02:00
math-pixel 813c10f3f7 wip mission 2 refine 2026-06-01 11:49:48 +02:00
Tom Boullay 153833deec Update favicon.ico
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-06-01 11:32:31 +02:00
Tom Boullay b617885aa2 chore(ui): clean loading overlay logo styling
Show the loading logo as a raw, contained image without rounded corners
or drop shadow, slightly larger to balance the empty space.
2026-06-01 11:28:15 +02:00
Tom Boullay 5d2e7e2aab fix(world): allow walking through la fabrik door
Strip the 'porte' mesh from the cloned scene used to build the la fabrik
collision octree. The wall geometry already has a doorway cutout, so
removing the door slab leaves the opening passable. The visual model is
rendered separately by MergedStaticMapModel and is unaffected.

Drops the stop-gap LA_FABRIK_COLLISION_Y_OFFSET added during debugging.
2026-06-01 11:28:15 +02:00
Tom Boullay de77f76d48 fix(world): restore shadow auto-update
Reverts the manual shadow refresh throttle introduced in 6d58b90 which
prevented shadows from rendering. Renderer and sun shadow now use
autoUpdate=true and a per-frame needsUpdate=true pulse, matching the
behaviour that produced visible shadows before that commit.
2026-06-01 11:28:07 +02:00
Tom Boullay bdc704fe8e feat(ui): show narrator video on talkie
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-06-01 10:52:28 +02:00
Tom Boullay bce7d11b66 fix(ebike): snap parked model to terrain 2026-06-01 10:52:17 +02:00
Tom Boullay 8aa755da7a fix(model): replace electricienne animated asset 2026-06-01 10:52:08 +02:00
Tom Boullay 6d58b90856 fix(world): throttle shadows and tune high preset 2026-06-01 10:45:07 +02:00
Tom Boullay bafca5a936 fix(ui): apply mobile blocker globally 2026-06-01 09:45:45 +02:00
Tom Boullay dcf3a8564c feat(ui): add narrator talkie overlay 2026-06-01 01:32:46 +02:00
Tom Boullay bc862960a7 fix(settings): persist pause menu preferences 2026-06-01 01:32:36 +02:00
Tom Boullay 597ebcfbd4 fix(ebike): sync parked position from config 2026-06-01 01:32:29 +02:00
Tom Boullay aa2d411b0c fix(world): stabilize lafabrik spawn and vegetation 2026-06-01 01:32:21 +02:00
Tom Boullay 061e0dc677 feat: update ui and intro sequence 2026-06-01 00:54:59 +02:00
Tom Boullay 9ef94af488 Merge branch 'develop' into feat/polish-mission1
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-06-01 00:15:46 +02:00
Tom Boullay 27b4a2c392 upatde(fabrik): zone + herbe
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-06-01 00:14:39 +02:00
math-pixel d5feb07ff0 Merge pull request 'Feat/polish-perf' (#13) from feat/polish-perf into develop
📊 Quality / 🔒 Security Audit (push) Has been cancelled
📊 Quality / 📋 Dependency Freshness (push) Has been cancelled
📊 Quality / 📦 Bundle Size (push) Has been cancelled
🔍 Lint / 🪄 Check lint (push) Has been cancelled
🔍 Lint / 🎨 Check format (push) Has been cancelled
🔍 Lint / 🔎 Typecheck (push) Has been cancelled
🔍 Lint / 🏗 Build (push) Has been cancelled
Reviewed-on: #13
2026-05-31 22:11:58 +00:00
Tom Boullay 7dff4a1238 feat(characters): populate residential zones
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-06-01 00:09:39 +02:00
Tom Boullay a8cd66dcaa fix(model): sync animated character textures 2026-05-31 23:59:36 +02:00
Tom Boullay 116746f838 fix(model): sync animated gerant textures 2026-05-31 23:45:36 +02:00
Tom Boullay a388c02ab3 fix(model): repair gerant materials 2026-05-31 23:36:54 +02:00
Tom Boullay 4b4162b7d2 tune(characters): adjust npc placement 2026-05-31 23:35:39 +02:00
Tom Boullay 4415faa1f1 chore(models): remove lafabrik old assets 2026-05-31 23:35:17 +02:00
Tom Boullay 4c5f08d772 fix(model): restore lafabrik window transparency 2026-05-31 23:07:24 +02:00
Tom Boullay 51569af7b8 feat(ui): add transient loading indicator
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-31 22:43:48 +02:00
Tom Boullay d26c676edf polish(ui): compact pause menu backdrop 2026-05-31 22:22:29 +02:00
Tom Boullay d3b4a55e71 fix(model): load lafabrik glb 2026-05-31 22:14:13 +02:00
Tom Boullay e212e4bbd5 update(model): use a glb
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-31 22:08:08 +02:00
Tom Boullay 39ec9feb0e fix(model): externalize lafabrik geometry buffer 2026-05-31 21:58:13 +02:00
Tom Boullay 4a43083178 fix(ui): preserve site cookie on restart
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-31 21:45:55 +02:00
Tom Boullay efcbf9e972 polish(ui): refine pause settings menu 2026-05-31 21:42:59 +02:00
Tom Boullay f11ed67452 fix(model): reconnect lafabrik textures 2026-05-31 21:37:52 +02:00
Tom Boullay 3e7edcb1b7 docs(world): document map lod system
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-31 21:23:07 +02:00
Tom Boullay b9c5d0c563 feat(world): add lafabrik lod support 2026-05-31 21:22:48 +02:00
Tom Boullay ebdb72ce0d update(model): add lafabrik lod models 2026-05-31 21:22:24 +02:00
Tom Boullay 34c198ebfd feat(world): add map lod graphics presets 2026-05-31 19:03:55 +02:00
Tom Boullay 564a455520 update(model): add lod models
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-31 18:42:13 +02:00
Tom Boullay c33d973f12 fix(ui): update logo asset path 2026-05-31 11:51:33 +02:00
Tom Boullay 396e7e4ff0 feat(ebike): add speedometer
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-31 11:36:19 +02:00
Tom Boullay 2c2a90264d Merge branch 'develop' of https://git.fabrik.mathieu-chavanel.fr/math-pixel/La-Fabrik into develop
🔍 Lint / 🪄 Check lint (push) Has been cancelled
🔍 Lint / 🎨 Check format (push) Has been cancelled
🔍 Lint / 🔎 Typecheck (push) Has been cancelled
📊 Quality / 🔒 Security Audit (push) Has been cancelled
📊 Quality / 📋 Dependency Freshness (push) Has been cancelled
📊 Quality / 📦 Bundle Size (push) Has been cancelled
🔍 Lint / 🏗 Build (push) Has been cancelled
2026-05-31 11:02:34 +02:00
Tom Boullay e02d06b8a5 Update hand_landmarker.task 2026-05-31 11:02:31 +02:00
math-pixel 1901075e3a Merge pull request 'Feat/polish-intro' (#11) from feat/polisth-intro into develop
🔍 Lint / 🪄 Check lint (push) Has been cancelled
🔍 Lint / 🎨 Check format (push) Has been cancelled
🔍 Lint / 🔎 Typecheck (push) Has been cancelled
📊 Quality / 🔒 Security Audit (push) Has been cancelled
📊 Quality / 📋 Dependency Freshness (push) Has been cancelled
📊 Quality / 📦 Bundle Size (push) Has been cancelled
🔍 Lint / 🏗 Build (push) Has been cancelled
Reviewed-on: #11
2026-05-31 09:01:17 +00:00
Tom Boullay e073fc375b fix(world): warm up map shadows from environment
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-31 11:00:40 +02:00
Tom Boullay bff8a16290 feat(intro): add ebike onboarding sequence
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-31 10:42:46 +02:00
Tom Boullay a3f611e227 fix(webgl): auto-restore context after loss
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
The Canvas onCreated callback used to log Context Lost but never asked
the GPU to restore it, which left the page on a frozen black canvas
until the user reloaded. We now grab the WEBGL_lose_context extension
on mount and call restoreContext() 500ms after a loss, giving the GPU
time to free memory before we ask for a new context. The existing
webglcontextrestored handler reinstates the shadow map settings, so
recovery is transparent to the user.

This does not prevent context loss itself — frequent losses still
indicate VRAM pressure or HMR-driven context churn — but it removes
the need to reload manually when the GPU recycles us.
2026-05-30 20:58:58 +02:00
Tom Boullay b578e68c2e Update SiteTransitionOverlay.tsx 2026-05-30 20:55:51 +02:00
Tom Boullay 7c691a8044 fix: show dialogue subtitles on black screen
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-30 20:25:21 +02:00
Tom Boullay f24704091a chore(logging): downgrade 'lite map skipped' to debug
This log fires every time the lite map loader skips heavy nodes, which
is the expected fast-path. It does not need to show up in a normal
console session — moving it to logger.debug keeps it accessible under
?debug for diagnostics while removing the noise from default runs.
2026-05-30 20:20:15 +02:00
Tom Boullay e6bfcbe960 feat(intro): polish loading transition 2026-05-30 20:11:40 +02:00
Tom Boullay 0fa7a82175 fix(perf): prevent Canvas double-mount on /site redirect
HomePage used to mount the Canvas before its effect fired the redirect
to /site, then unmount it as soon as the route changed. That left the
WebGL context torn down mid-load with GLTF requests still in flight,
which on slow GPUs ended in a 'Context Lost' and a stuck 1 FPS render
once the user came back from /site. The fix is a synchronous cookie
check after all hooks: if the user has not visited /site today we
return null and let the redirect happen without ever creating a GL
context.

Also drops the GameMap 'lite map skipped' log from warn to info: it
is an expected lite-loading path, not a problem worth a yellow warning.
2026-05-30 19:51:57 +02:00
Tom Boullay 82dc47a296 fix(assets): correct texture URIs in gant_r and pylone GLTFs
- gant_r/model.gltf: align casing with the actual files on disk
  (gant_basecolor / gant_occlusionroughnessmetallic) so the GLTFLoader
  stops logging 'Couldn't load texture' on case-sensitive filesystems
- pylone/model.gltf: nine missing Image_N.png references replaced with
  the existing color.png / normal.png so the pylone renders without
  console errors. Material slots are mapped by role: normalTexture ->
  normal.png, baseColorTexture and metallicRoughnessTexture -> color.png
2026-05-30 19:51:51 +02:00
Tom Boullay 970adf4853 feat(a11y): WCAG AA polish on the site onboarding flow
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
- index.css: add visible :focus-visible rings for .site-card-button
  and .site-button so keyboard users can see where focus lives
- SiteCard: drop outline:none, add aria-pressed and aria-label so
  screen readers announce selection state
- SiteButton: add the .site-button class for the shared focus ring
- SiteDisclaimerScreen: keyboard skip via Enter / Space / Escape, a
  role="region" + aria-label wrapper and aria-live="polite" on the
  message; honour prefers-reduced-motion on the fade
- IntroVideoPlayer: role="region" with a skip hint in aria-label,
  preload="auto", and aria-hidden on the decorative caption span
2026-05-30 18:44:03 +02:00
Tom Boullay 07b09c22af fix(site): repair onboarding audio cleanup, redirect, and manifest fetches
- loadDialogueManifest: cache the resolved manifest at module level and
  dedupe concurrent fetches so each screen no longer re-downloads it
- useGameStore: completeIntroState now also advances intro.currentStep
  to "playing" so callers do not need a separate setIntroStep call
- SiteNamingScreen and SiteTransitionOverlay: replace ref-based guards
  with an isCancelled flag captured per effect. The previous guards
  persisted across StrictMode remounts, leaving mount 2 unable to
  re-run the effect after mount 1's chain was cancelled, which broke
  the fade animations, the second narrator dialogue and the redirect.
  Both screens now also call stopCurrentDialogue on unmount so audio
  cannot bleed across routes, and the transition gets a safety timeout
  in case the dialogue audio fails to fire its "ended" event
- SiteTransitionOverlay: keep the <Subtitles /> mount inside the
  overlay so it renders inside the z-index 1000 stacking context
  (above the black screen); the one in SiteLayout sits behind it
- IntroDialogueOverlay: route through playDialogueById instead of
  AudioManager.playSoundWithCallback so the narrator subtitles play
  in sync, and add the same isCancelled cleanup pattern
- IntroRevealOverlay: rely on completeIntro alone now that it advances
  intro.currentStep, and skip the fade when reduced motion is requested
- SiteMobileBlocker: correct logo path from public/... to /...
2026-05-30 18:43:53 +02:00
Tom Boullay 0f6860f1ae refactor(site): extract shared utilities and centralise dialogue IDs
- new src/hooks/ui/useIsMobile.ts (matchMedia + useSyncExternalStore)
  replacing the resize-handler hook inlined inside pages/site/page.tsx
- new src/hooks/ui/usePrefersReducedMotion.ts
- new src/data/site/dialogueIds.ts so site and intro components stop
  carrying hard-coded narrator IDs
- siteConfig: add SITE_BACKGROUND_STYLE shared by SiteLayout and
  SiteMobileBlocker, rename forcedName to presetPlayerName, fix the
  swapped id/label pairing on situation cards
- useSiteStore: rename selectedExperience/Situation to *Index so the
  stored value (an array index) is obvious in callers
- audioConfig: drop dead AUDIO_PATHS placeholders
- propagate the renames and SITE_BACKGROUND_STYLE through SiteLayout,
  SiteWelcomeScreen, SiteSituationScreen and pages/site/page.tsx
2026-05-30 18:43:35 +02:00
Tom Boullay 6ae21a2427 fix(site): unified card styles, import Nersans One font, native naming input
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-30 17:56:42 +02:00
Tom Boullay 29342d796c fix(site): reduce situation card font size 2026-05-30 17:21:44 +02:00
Tom Boullay 60e3c92511 fix(site): update situation cards 2026-05-30 17:06:29 +02:00
Tom Boullay 02c1fb33d0 feat(dialogues): support multi-cue subtitles
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-30 04:00:25 +02:00
Tom Boullay ce5dc8ada0 update: intro flow overlays 2026-05-30 04:00:20 +02:00
Tom Boullay a2cff0567e feat: add site onboarding route 2026-05-30 04:00:09 +02:00
Tom Boullay 8cfee1ac93 update: reorganize public assets 2026-05-30 03:59:45 +02:00
Tom Boullay 4c5e2ed945 feat(types): add SiteStep and refactor GameStep for new intro flow
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-30 02:14:10 +02:00
Tom Boullay 345d49f485 add: cinemactics and assets
🔍 Lint / 🪄 Check lint (push) Has been cancelled
🔍 Lint / 🎨 Check format (push) Has been cancelled
🔍 Lint / 🔎 Typecheck (push) Has been cancelled
📊 Quality / 🔒 Security Audit (push) Has been cancelled
📊 Quality / 📋 Dependency Freshness (push) Has been cancelled
📊 Quality / 📦 Bundle Size (push) Has been cancelled
🔍 Lint / 🏗 Build (push) Has been cancelled
2026-05-30 02:04:39 +02:00
math-pixel a6cc028848 Merge pull request 'Feat/gallery' (#9) from feat/galerie into develop
🔍 Lint / 🪄 Check lint (push) Has been cancelled
🔍 Lint / 🎨 Check format (push) Has been cancelled
🔍 Lint / 🔎 Typecheck (push) Has been cancelled
📊 Quality / 🔒 Security Audit (push) Has been cancelled
📊 Quality / 📋 Dependency Freshness (push) Has been cancelled
📊 Quality / 📦 Bundle Size (push) Has been cancelled
🔍 Lint / 🏗 Build (push) Has been cancelled
Reviewed-on: #9
2026-05-29 07:00:36 +00:00
Tom Boullay 52bb1b2915 chore: code quality audit and lint fixes
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
- Fix all 63 ESLint errors across codebase
- Consolidate MaterialWithTextureSlots type in src/types/three/three.ts
- Add CSS custom properties for design tokens
- Extract ebike constants to src/data/ebike/ebikeConfig.ts
- Add proper TypeScript types for window extensions
- Fix React hooks violations (refs during render, setState in effects)
- Remove unused exports and redundant CSS
- Add type guards for Three.js material handling
- Clean up AI slop comments and legacy CSS patterns
2026-05-29 09:00:04 +02:00
Tom Boullay ade301389e merge develop into feat/galerie - resolve model and code conflicts 2026-05-29 02:25:46 +02:00
Tom Boullay 47e50d9318 fix: issue in galley mode 2026-05-29 02:18:17 +02:00
math-pixel c7df58099a Merge pull request 'Feat/map-environment' (#6) from feat/map-environment into develop
🔍 Lint / 🪄 Check lint (push) Has been cancelled
🔍 Lint / 🎨 Check format (push) Has been cancelled
🔍 Lint / 🔎 Typecheck (push) Has been cancelled
📊 Quality / 🔒 Security Audit (push) Has been cancelled
📊 Quality / 📋 Dependency Freshness (push) Has been cancelled
📊 Quality / 📦 Bundle Size (push) Has been cancelled
🔍 Lint / 🏗 Build (push) Has been cancelled
Reviewed-on: #6
2026-05-29 00:00:50 +00:00
Tom Boullay f7b4a07e41 fix: bug on textute vegetation item
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-29 02:00:35 +02:00
Tom Boullay 89044a18ec merge develop into feat/map-environment
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-29 01:45:08 +02:00
Tom Boullay 95ca1bbfde hore(review): tighten pre-merge audit cleanup
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-29 01:34:10 +02:00
Tom Boullay 093ffd726d fix(review): address audit findings before merge
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-29 01:23:08 +02:00
Tom Boullay 4728690a11 Create outro.mp4
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-29 00:52:53 +02:00
Tom Boullay 343a122c06 fix(editor): restore stable map editing behavior 2026-05-29 00:52:44 +02:00
tom-boullay d5675fe82c feat: restaure l'éditeur map et ajoute les personnages
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-28 15:49:57 +02:00
tom-boullay fcdbf7270c perf: applique les échelles aux assets instanciés 2026-05-28 15:49:26 +02:00
tom-boullay 0b3d49e8d1 feat: ajoute les potagers à la map 2026-05-28 15:48:33 +02:00
tom-boullay 9bbed06ddc feat: ancre les réparations sur la map chargée 2026-05-28 15:47:53 +02:00
tom-boullay ba50224e6e refactor: nettoie l'architecture monde et les docs 2026-05-28 15:47:16 +02:00
tom-boullay 1a91b1d7ae refactor: clean map gameplay architecture 2026-05-28 11:15:45 +02:00
tom-boullay d9cf87d2d6 feat: launch ebike repair from map interaction
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-28 10:13:59 +02:00
Tom Boullay d654565f87 chore: address code quality audit findings
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-28 08:31:42 +02:00
math-pixel fb466a63cb Merge pull request 'Merge e_bike + gps into develop' (#7) from feat/gps into develop
🔍 Lint / 🪄 Check lint (push) Has been cancelled
🔍 Lint / 🎨 Check format (push) Has been cancelled
🔍 Lint / 🔎 Typecheck (push) Has been cancelled
📊 Quality / 🔒 Security Audit (push) Has been cancelled
📊 Quality / 📋 Dependency Freshness (push) Has been cancelled
📊 Quality / 📦 Bundle Size (push) Has been cancelled
🔍 Lint / 🏗 Build (push) Has been cancelled
Reviewed-on: #7
2026-05-28 05:55:18 +00:00
math-pixel a75c3fd896 uptd : change location of map_backougnround & fix : remove old netshader into world tsx
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-28 07:54:47 +02:00
math-pixel 603e521714 Merge branch 'develop' into feat/gps 2026-05-28 07:50:25 +02:00
math-pixel 49ef8f58b4 Merge pull request 'Add Net shader into develop' (#8) from feat/shader-net into develop
🔍 Lint / 🪄 Check lint (push) Has been cancelled
🔍 Lint / 🎨 Check format (push) Has been cancelled
🔍 Lint / 🔎 Typecheck (push) Has been cancelled
📊 Quality / 🔒 Security Audit (push) Has been cancelled
📊 Quality / 📋 Dependency Freshness (push) Has been cancelled
📊 Quality / 📦 Bundle Size (push) Has been cancelled
🔍 Lint / 🏗 Build (push) Has been cancelled
Reviewed-on: #8
2026-05-28 05:47:52 +00:00
Tom Boullay 947025cbf5 Update windConfig.ts 2026-05-28 01:35:43 +02:00
Tom Boullay f2cecfbbc9 fix(lighting): keep shadows enabled after scene loading
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-28 01:33:15 +02:00
Tom Boullay 1b9ac5c996 feat(environment): apply shared wind to vegetation 2026-05-28 01:28:16 +02:00
Tom Boullay e45fdbf97d fix(lighting): keep shadow map centered on player 2026-05-28 01:20:12 +02:00
Tom Boullay 08b01715c0 feat(map): stream secondary map assets by chunk 2026-05-28 01:16:29 +02:00
Tom Boullay a2fc417be6 fix(environment): preserve grass blade colors
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-28 01:12:21 +02:00
Tom Boullay 57498b9bb1 tune(environment): rebalance procedural grass zones 2026-05-28 01:08:33 +02:00
Tom Boullay b87a7e929c feat(environment): vary grass density by procedural zones 2026-05-28 01:05:40 +02:00
Tom Boullay ea23b4bb46 fix(environment): tune grass and terrain grounding 2026-05-28 00:59:36 +02:00
Tom Boullay 3881e38a6d feat(collision): include static map models in octree 2026-05-28 00:47:40 +02:00
Tom Boullay 7a72743e5c refactor(environment): use player-centered ghibli grass patch 2026-05-28 00:31:45 +02:00
Tom Boullay 65651405b6 feat(editor): add multi-selection transforms 2026-05-28 00:29:00 +02:00
Tom Boullay 81cd935bba fix(environment): reduce grass generation cost 2026-05-28 00:25:23 +02:00
Tom Boullay fe989c9550 refactor(environment): replace grass with terrain-mesh ghibli shader 2026-05-28 00:20:01 +02:00
Tom Boullay e15fb18d6b update: models
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-28 00:07:17 +02:00
Tom Boullay 0230795f55 feat: add chunked grass and organize environment systems 2026-05-28 00:07:02 +02:00
Tom Boullay b1fca3a25b Merge remote map editor updates 2026-05-27 22:24:59 +02:00
math-pixel 0a322acf88 Merge branch 'develop' into feat/shader-net
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-27 18:08:46 +02:00
tom-boullay 3eba38a80b fix: ebike
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-27 17:21:11 +02:00
tom-boullay 0992aacec6 fix(editor): update transforms while dragging
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-27 14:22:26 +02:00
tom-boullay bfe184dea4 feat(editor): focus camera on selected object
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-27 11:58:33 +02:00
tom-boullay 4ee13b0336 fix(editor): keep selection when clicking empty space
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-27 11:41:42 +02:00
tom-boullay fdd530a3e7 fix(map): prevent visual loading from blocking gameplay 2026-05-27 11:39:44 +02:00
tom-boullay 2b676d985d feat(editor): focus selected model editing 2026-05-27 11:06:14 +02:00
tom-boullay b89eedd5be fix(map): align terrain visual collision and snapping 2026-05-27 09:47:08 +02:00
tom-boullay d38ad242d6 fix(editor): preserve hierarchical map saves 2026-05-27 09:47:01 +02:00
Tom Boullay c2b16434fb feat(editor): edit hierarchical map nodes 2026-05-27 08:30:54 +02:00
Tom Boullay ab100c683f Update fogConfig.ts
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-27 01:01:32 +02:00
Tom Boullay b8cff43545 fix(environment): disable fog by default 2026-05-27 01:01:25 +02:00
Tom Boullay 25e0f7e062 feat(environment): add adaptive atmospheric fog
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-27 00:54:17 +02:00
Tom Boullay ab3943eef3 tune(environment): add cloud controls and visibility fixes
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-27 00:34:01 +02:00
Tom Boullay 4ebb5b8c25 feat(environment): add wind-driven cloud system 2026-05-27 00:33:53 +02:00
Tom Boullay a6787a7ecb chore: tune lighting and world plane material 2026-05-27 00:09:26 +02:00
Tom Boullay d816e4b07e feat(lighting): expose light colors in debug controls 2026-05-26 23:53:04 +02:00
Tom Boullay 665d9f9702 fix(map): add world plane collision and respawn 2026-05-26 23:52:12 +02:00
Tom Boullay 0696ca2ae3 perf: stream water shader by distance 2026-05-26 23:19:41 +02:00
Tom Boullay d6d3d5b685 fix: stabilize water depth and rounded mask 2026-05-26 22:56:50 +02:00
Tom Boullay 1c27d55e5a feat(map): add terrain boundary collision 2026-05-26 22:06:13 +02:00
Tom Boullay fd558db034 tune(environment): make water surface adjustable 2026-05-26 22:05:00 +02:00
Tom Boullay 054cb975da fix: hide gallery export planes
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-25 19:13:02 +02:00
Tom Boullay fbe8c0c854 feat(environment): add terrain water shader 2026-05-25 19:09:13 +02:00
Tom Boullay 88b6db6166 fix(map): disable generated path system 2026-05-25 18:42:38 +02:00
Tom Boullay cf71148935 fix: smooth gallery preview seams
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-25 18:02:36 +02:00
Tom Boullay 1b2241df49 feat: add gallery lighting controls 2026-05-25 17:57:51 +02:00
Tom Boullay 2b08665508 fix(map): align path preview with terrain projection 2026-05-25 17:54:57 +02:00
Tom Boullay d7351e5f37 fix: render gallery skybox unlit double-sided 2026-05-25 17:53:46 +02:00
Tom Boullay 4f8355e934 debug(map): add path surface sampling preview 2026-05-25 17:40:46 +02:00
Tom Boullay 417afdc1d5 fix(terrain): map surface colors with configurable projection 2026-05-25 17:40:01 +02:00
Tom Boullay 235a38f67b fix(map): disable generated paths while correcting mapping 2026-05-25 17:39:00 +02:00
Tom Boullay 6a412c7b00 fix: stabilize gallery skybox rendering 2026-05-25 17:31:27 +02:00
Tom Boullay e9fb36f9dc style: simplify gallery UI and rename route 2026-05-25 17:13:21 +02:00
Tom Boullay f54e71fc03 feat(map): generate path tiles from terrain colors 2026-05-25 17:08:07 +02:00
Tom Boullay 36180279b2 docs: document model gallery 2026-05-25 16:24:12 +02:00
Tom Boullay 626dc47bbe feat: add model gallery viewer 2026-05-25 16:23:36 +02:00
Tom Boullay 6d178dc59e feat: expose terrain surface data
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-25 16:03:22 +02:00
Tom Boullay b4a3545460 feat: add terrain surface sampler 2026-05-25 15:55:56 +02:00
Tom Boullay 1f6d9659ed chore: add terrain surface config 2026-05-25 15:51:02 +02:00
Tom Boullay a52d57ae6c fix: keep terrain collision during visual filtering 2026-05-25 01:28:25 +02:00
Tom Boullay d0497ec42c fix(debug): defer hand tracking startup 2026-05-25 01:19:14 +02:00
Tom Boullay 4c42e11268 fix(debug): stabilize map debug controls 2026-05-25 01:10:10 +02:00
Tom Boullay f175ad6240 tune(debug): refine fog and debug controls 2026-05-25 01:00:31 +02:00
Tom Boullay a4383a7cec update: file order
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-25 00:52:06 +02:00
Tom Boullay d07ffc4662 docs: update map performance notes 2026-05-25 00:51:09 +02:00
Tom Boullay d17738eaf1 perf(map): snap assets to terrain 2026-05-25 00:51:03 +02:00
Tom Boullay 50fa94b3ad docs: expose map performance notes 2026-05-25 00:08:29 +02:00
Tom Boullay 44f9d68ef1 tune: expand vegetation streaming range 2026-05-24 23:52:37 +02:00
Tom Boullay e4857135b1 feat: stream vegetation chunks near player 2026-05-24 23:51:40 +02:00
Tom Boullay 1f6335092a fix: harden merged map resource cleanup 2026-05-24 23:51:28 +02:00
Tom Boullay f035195b56 Merge branch 'feat/map-environment' of https://git.fabrik.mathieu-chavanel.fr/math-pixel/La-Fabrik into feat/map-environment
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-24 22:07:15 +02:00
Tom Boullay b8e5c4d1a9 upadte: gain some fps 2026-05-24 22:06:59 +02:00
tom-boullay 4e6582b543 add: panneaux solaire model
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-21 15:39:01 +02:00
tom-boullay e4ee2d768b feat(debug): add map performance visibility controls 2026-05-21 15:38:23 +02:00
tom-boullay 5e594c51f7 docs(perf): document map rendering bottlenecks 2026-05-21 15:34:56 +02:00
tom-boullay 26ddbebe14 refactor(map): add generated R3F model for ecole 2026-05-21 15:34:49 +02:00
tom-boullay 48c2b4f0cd perf(map): merge instanced map asset geometry 2026-05-21 15:34:39 +02:00
tom-boullay cf08062def refactor(world): restore sky fallback chain
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-21 12:10:31 +02:00
tom-boullay 4f25b33d3b feat(debug): persist and reset debug game state 2026-05-21 12:09:12 +02:00
tom-boullay 072dec03b4 refactor: tighten terrain and sky model resource ownership
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-21 11:55:49 +02:00
tom-boullay 529c60adae update: add new sounds + comrpessed models
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-21 10:52:14 +02:00
math-pixel 8b619bfc28 feat: add NetShader and UnicolorShader with a debug component for visual testing in the world scene 2026-05-19 22:54:29 +02:00
Tom Boullay 6957b9e4f0 update: merge instanced map geometry 2026-05-15 23:29:07 +02:00
Tom Boullay 9dff245aab update: instance map renderables 2026-05-15 21:48:13 +02:00
Tom Boullay 27951d13fd upatde: json + models
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-15 00:18:03 +02:00
Tom Boullay cdd919c010 update: clean map hierarchy and mesh model names
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-14 17:25:39 +02:00
Tom Boullay cea2856fd0 update: models 2026-05-14 17:17:54 +02:00
Tom Boullay d245d6b460 Update vegetationConfig.ts 2026-05-14 16:13:34 +02:00
Tom Boullay dba7aec6fa fix: dupplicate buisson and ecole 2026-05-14 16:13:32 +02:00
Tom Boullay d376d0ba6b update: add new models 2026-05-14 16:13:16 +02:00
Tom Boullay 242a3dcd37 refactor: remove vegetation instance limit (will be handled by chunks later) 2026-05-14 00:26:42 +02:00
Tom Boullay 225ac828df feat: enable all vegetation types and remove debug logs 2026-05-14 00:23:18 +02:00
Tom Boullay 28f7db172c upatde: add new models
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-14 00:20:20 +02:00
Tom Boullay 2063656f29 chore: update eslint config and CI workflow 2026-05-14 00:18:02 +02:00
Tom Boullay 592cfa405f feat: add graphics settings hook 2026-05-14 00:17:53 +02:00
Tom Boullay 0a32cd1d21 feat: add map node caching for vegetation system 2026-05-14 00:17:44 +02:00
Tom Boullay 4516cf4ec6 feat: configure shadow map settings for better shadow quality 2026-05-14 00:17:33 +02:00
Tom Boullay c6f60d1ca7 update: vegetation models 2026-05-14 00:17:08 +02:00
Tom Boullay aa35e97cbb update: skybox model files 2026-05-14 00:16:58 +02:00
Tom Boullay 57c142c8ef fix: correct texture paths case sensitivity in GLTF models 2026-05-14 00:16:47 +02:00
Tom Boullay 4843bf1d75 fix: correct skybox model path 2026-05-14 00:16:37 +02:00
Tom Boullay d02cf29a1d debug: add logging for scene loading flow 2026-05-14 00:16:28 +02:00
Tom Boullay 3b4c9c2529 feat: add WebGL context loss handler and GPU performance config 2026-05-14 00:16:18 +02:00
Tom Boullay fdf03349cf fix: enable terrain loading for collision octree build 2026-05-14 00:16:09 +02:00
Tom Boullay 439f9c1dad feat: add VegetationSystem with InstancedMesh rendering
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled
2026-05-14 00:16:00 +02:00
Tom Boullay 260bfea716 fix: add disposal on unmount in SkyModel and SimpleModel 2026-05-14 00:15:52 +02:00
Tom Boullay b3a3f3557c fix: add disposal on unmount in TerrainModel 2026-05-14 00:15:41 +02:00
Tom Boullay 621556b38c fix: add disposal on unmount in useClonedObject hook 2026-05-14 00:15:32 +02:00
Tom Boullay 5c55f2c7f4 feat: add disposeObject3D utility for GPU memory cleanup 2026-05-14 00:15:23 +02:00
Tom Boullay bb4bccb175 Update weekly-branch-promotions.yml 2026-05-13 15:26:36 +02:00
Tom Boullay ae835e5008 update: models 2026-05-13 15:00:53 +02:00
Tom Boullay 1d3aa1c9f2 add: world config (wind, graphics, terrain, fog) 2026-05-13 15:00:13 +02:00
Tom Boullay 23cab2da5e update: CI branch promotions schedule 2026-05-13 15:00:05 +02:00
Tom Boullay 44216f9395 update: models 2026-05-13 14:59:57 +02:00
895 changed files with 94923 additions and 40100 deletions
+16 -52
View File
@@ -15,12 +15,9 @@ export class SomeManager {
return SomeManager._instance;
}
private constructor() {
// init logic
}
private constructor() {}
destroy(): void {
// cleanup logic
SomeManager._instance = null;
}
}
@@ -28,43 +25,12 @@ export class SomeManager {
## Managers in this project
| Manager | File | Role |
| -------------------- | ------------------------------------ | ----------------------------------------------------------------------------- |
| `AudioManager` | `src/managers/AudioManager.ts` | Music and SFX playback. |
| `InteractionManager` | `src/managers/InteractionManager.ts` | Focus, nearby, trigger, grab, and hand-grab interaction state. |
| `GameManager` | target-state only | Future single source of truth for phase, zone, mission, input lock, dialogue. |
| `CinematicManager` | target-state only | Future GSAP timeline orchestrator. |
| `ZoneManager` | target-state only | Future zone entry/exit detection and LOD triggers. |
| Manager | File | Role |
| -------------------- | ------------------------------------ | -------------------------------------------------------------- |
| `AudioManager` | `src/managers/AudioManager.ts` | Music and SFX playback. |
| `InteractionManager` | `src/managers/InteractionManager.ts` | Focus, nearby, trigger, grab, and hand-grab interaction state. |
## Target-State GameManager
`GameManager` does not exist in the current implementation. The following pattern is target-state guidance only and should not be applied until the manager exists in code.
```ts
export class GameManager {
cinematic!: CinematicManager;
audio!: AudioManager;
zone!: ZoneManager;
private constructor() {
this.cinematic = CinematicManager.getInstance();
this.audio = AudioManager.getInstance();
this.zone = ZoneManager.getInstance();
}
}
```
When a `GameManager` exists, components and hooks should access other managers through it:
```ts
// Correct
GameManager.getInstance().cinematic.play("intro");
// Wrong — never import sub-managers directly in components
CinematicManager.getInstance().play("intro");
```
## Target-State Subscribe Pattern
## Subscribe Pattern
```ts
private listeners = new Set<() => void>()
@@ -79,28 +45,26 @@ private emit(): void {
}
```
In that target-state manager, every `set*()` method calls `this.emit()` to notify subscribers.
Managers that expose state to React call `this.emit()` from every `set*()` method that changes subscribed state.
## Target-State React Bridge Hook
## React Bridge Hook
```ts
// hooks/useGameState.ts
export function useGameState() {
const game = GameManager.getInstance();
const [state, setState] = useState(game.getState());
// hooks/interaction/useInteraction.ts
const manager = InteractionManager.getInstance();
useEffect(() => {
return game.subscribe(() => setState({ ...game.getState() }));
}, [game]);
return state;
export function useInteraction(): InteractionSnapshot {
return useSyncExternalStore(
manager.subscribe.bind(manager),
manager.getState.bind(manager),
);
}
```
## Rules
- Do not add a `GameManager` unless the feature requires a real shared gameplay state owner.
- Current managers may be imported directly until the target-state orchestrator exists.
- Current managers may be imported directly.
- Keep singleton managers limited to side-effect services or shared interaction state.
- Always call `destroy()` on cleanup when a manager owns external resources.
- Never create manager instances with `new` — always use `.getInstance()`.
+11
View File
@@ -0,0 +1,11 @@
{
"version": "0.0.1",
"configurations": [
{
"name": "dev",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "dev"],
"port": 5173
}
]
}
+3
View File
@@ -23,3 +23,6 @@
# Video (cinematics)
*.mp4 filter=lfs diff=lfs merge=lfs -text
*.webm filter=lfs diff=lfs merge=lfs -text
# ML models
*.task filter=lfs diff=lfs merge=lfs -text
+8 -3
View File
@@ -69,16 +69,21 @@ jobs:
- name: 📥 Install
run: npm ci
- name: 🧹 Lint
run: npm run lint
- name: 🎨 Format check
run: npm run format:check
- name: 📦 Build
run: npm run build
- name: 📏 Check bundle size
run: |
# Check generated app assets only; public/ model files are runtime assets copied to dist.
SIZE=$(du -k dist/assets | cut -f1)
# Check generated JS/CSS bundles only; public runtime assets are copied to dist/assets too.
SIZE=$(node -e "const fs=require('fs');const path=require('path');function walk(dir){return fs.readdirSync(dir,{withFileTypes:true}).flatMap((entry)=>{const file=path.join(dir,entry.name);return entry.isDirectory()?walk(file):file;});}const bytes=walk('dist/assets').filter((file)=>/\.(js|css)$/.test(file)).reduce((sum,file)=>sum+fs.statSync(file).size,0);console.log(Math.ceil(bytes/1024));")
echo "Bundle size: ${SIZE}KB"
# Threshold: 5000KB (configurable)
THRESHOLD=5000
if [ "$SIZE" -gt "$THRESHOLD" ]; then
+71 -29
View File
@@ -1,59 +1,58 @@
name: 🔁 Weekly Branch Promotions
name: 🔁 Branch Promotions
on:
schedule:
- cron: "0 6 * * 1"
- cron: "0 6 * * 1,4" # Lundi et Jeudi à 6h UTC (design → develop)
- cron: "0 6 * * 1" # Lundi à 6h UTC (develop → main)
workflow_dispatch:
inputs:
promotion:
description: "Which promotion to run"
required: true
type: choice
options:
- design-to-develop
- develop-to-main
- both
permissions:
contents: read
pull-requests: write
concurrency:
group: weekly-branch-promotions
group: branch-promotions
cancel-in-progress: false
jobs:
open-promotion-pr:
name: Open ${{ matrix.head }} → ${{ matrix.base }}
design-to-develop:
name: Open design → develop
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- head: develop
base: design
title: "chore: merge develop into design"
- head: design
base: main
title: "chore: merge design into main"
if: |
(github.event_name == 'schedule') ||
(github.event_name == 'workflow_dispatch' && (github.event.inputs.promotion == 'design-to-develop' || github.event.inputs.promotion == 'both'))
steps:
- name: ⬇️ Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 🔁 Open promotion PR
env:
GH_TOKEN: ${{ github.token }}
BASE_BRANCH: ${{ matrix.base }}
HEAD_BRANCH: ${{ matrix.head }}
PR_TITLE: ${{ matrix.title }}
run: |
set -euo pipefail
git fetch origin "$BASE_BRANCH" "$HEAD_BRANCH"
git fetch origin develop design
if git merge-base --is-ancestor "origin/$HEAD_BRANCH" "origin/$BASE_BRANCH"; then
echo "No promotion needed: $BASE_BRANCH already contains $HEAD_BRANCH."
if git merge-base --is-ancestor origin/design origin/develop; then
echo "No promotion needed: develop already contains design."
exit 0
fi
existing_pr="$(gh pr list \
--state open \
--base "$BASE_BRANCH" \
--head "$GITHUB_REPOSITORY_OWNER:$HEAD_BRANCH" \
--base develop \
--head "$GITHUB_REPOSITORY_OWNER:design" \
--json number \
--jq '.[0].number // empty')"
@@ -63,7 +62,50 @@ jobs:
fi
gh pr create \
--base "$BASE_BRANCH" \
--head "$HEAD_BRANCH" \
--title "$PR_TITLE" \
--body "Automated weekly promotion PR from \`$HEAD_BRANCH\` to \`$BASE_BRANCH\`."
--base develop \
--head design \
--title "chore: merge design into develop" \
--body "Automated promotion PR from \`design\` to \`develop\`."
develop-to-main:
name: Open develop → main
runs-on: ubuntu-latest
if: |
(github.event_name == 'schedule' && github.event.schedule == '0 6 * * 1') ||
(github.event_name == 'workflow_dispatch' && (github.event.inputs.promotion == 'develop-to-main' || github.event.inputs.promotion == 'both'))
steps:
- name: ⬇️ Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 🔁 Open promotion PR
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
git fetch origin main develop
if git merge-base --is-ancestor origin/develop origin/main; then
echo "No promotion needed: main already contains develop."
exit 0
fi
existing_pr="$(gh pr list \
--state open \
--base main \
--head "$GITHUB_REPOSITORY_OWNER:develop" \
--json number \
--jq '.[0].number // empty')"
if [ -n "$existing_pr" ]; then
echo "Promotion PR already open: #$existing_pr."
exit 0
fi
gh pr create \
--base main \
--head develop \
--title "chore: merge develop into main" \
--body "Automated weekly promotion PR from \`develop\` to \`main\`."
-187
View File
@@ -1,187 +0,0 @@
# Game Flow - La Fabrik
## Étapes du jeu
```
intro → start-intro → naming → bienvenue → star-move → mission2 → searching_problem → preparation → outOfFabrik
```
---
## Détail des étapes
### 1. `intro` (initial)
- État initial au chargement du jeu
- Aucune action, juste une étape de départ
- Transition automatique vers `start-intro`
### 2. `start-intro`
- **Déclenchement** : Auto-transition depuis `intro` quand la scène est chargée
- **Action** : Joue l'audio d'intro (`intro`)
- **Attente** : Attend que l'audio se termine
- **Transition** : Vers `naming` quand l'audio se termine
### 3. `naming`
- **Déclenchement** : Quand l'audio d'intro se termine
- **Action** : Affiche un input pour demander le prénom du joueur
- **Attente** : L'utilisateur entre son prénom et valide
- **Transition** : Vers `bienvenue` quand l'utilisateur valide
### 4. `bienvenue`
- **Déclenchement** : Quand l'utilisateur valide son prénom
- **Actions** :
- Affiche "Bienvenue {prénom} !" à l'écran
- Joue l'audio de bienvenue
- **Attente** : Attend que l'audio se termine
- **Transition** : Vers `star-move` quand l'audio se termine
### 5. `star-move`
- **Déclenchement** : Quand l'audio de bienvenue se termine
- **Action** : Active le mouvement du joueur (`setCanMove(true)`)
- **État** : Le joueur peut maintenant se déplacer librement
- **Zone** : La détection de zone devient active (ZoneDetection)
### 6. `mission2`
- **Déclenchement** : Quand le joueur entre dans la zone `fabrikExit` (position: `[-5, 25, -15]`)
- **Actions** :
- Stocke `activityCity: false` dans le store Zustand
- Joue l'audio `alertCentral`
- **État** : Les objets avec hook `useActivityCity()` détectent le changement et jouent leurs animations
- **Attente** : Le joueur atteint la zone de trigger pour `searching_problem`
### 7. `searching_problem`
- **Déclenchement** : Quand le joueur entre dans la zone `searchingProblemZone` (position: `[-5, 25, -30]`)
- **Actions** :
- Joue l'audio `searchingProblem`
- Affiche l'objet "central" (position: `[1, 15, -45]`)
- **Attente** : Le joueur interagit avec l'objet "central"
### 8. `preparation`
- **Déclenchement** : Quand le joueur interagit avec l'objet "central"
- **Actions** :
- Bloque le mouvement (`setCanMove(false)`)
- Cache l'objet "central"
### 9. `outOfFabrik`
- **Déclenchement** : (non implémenté pour le moment)
- **Action** : Transition vers l'étape finale
---
## Fichiers clés
| Fichier | Rôle |
| --------------------------------------- | --------------------------------------------------------- |
| `src/stores/gameStore.ts` | Store Zustand pour l'état global du jeu |
| `src/stateManager/GameStepManager.ts` | Synchronise avec le store Zustand |
| `src/components/game/GameFlow.tsx` | Gère les transitions automatiques et la lecture audio |
| `src/components/ui/IntroUI.tsx` | Affiche l'input pour le prénom et le message de bienvenue |
| `src/components/zone/ZoneDetection.tsx` | Détecte quand le joueur entre dans une zone |
| `src/components/3d/CentralObject.tsx` | Objet interactif "central" pour la mission 2 |
| `src/data/audioConfig.ts` | Chemins des fichiers audio |
| `src/data/zones.ts` | Configuration des zones de transition |
| `src/hooks/useActivityCity.ts` | Hook pour détecter le changement d'activité de la ville |
---
## Configuration audio
```typescript
// src/data/audioConfig.ts
export const AUDIO_PATHS = {
intro: "/sounds/fa.mp3",
bienvenue: "/sounds/fa.mp3",
alertCentral: "/sounds/fa.mp3",
searchingProblem: "/sounds/fa.mp3",
};
```
---
## Configuration des zones
```typescript
// src/data/zones.ts
export const ZONES: Zone[] = [
{
id: "fabrikExit",
position: [-5, 25, -15],
radius: 10,
height: 20,
targetStep: "mission2",
},
{
id: "searchingProblemZone",
position: [-5, 25, -30],
radius: 10,
height: 20,
targetStep: "searching_problem",
},
];
```
---
## Store Zustand
```typescript
// src/stores/gameStore.ts
interface GameState {
step: GameStep;
activityCity: boolean;
playerName: string;
canMove: boolean;
setStep: (step: GameStep) => void;
setActivityCity: (value: boolean) => void;
setPlayerName: (name: string) => void;
setCanMove: (canMove: boolean) => void;
}
```
---
## Hooks personnalisés
### useActivityCity
Permet aux objets 3D de réagir au changement d'activité de la ville :
```typescript
import { useActivityCity } from "@/hooks/useActivityCity";
function MyAnimatedObject() {
const activityCity = useActivityCity(); // true par défaut, false en mission2
// L'animation se déclenche quand activityCity change à false
// Utiliser useEffect pour réagir au changement
}
```
---
## Debug
En mode debug (`?debug` dans l'URL), on peut voir :
- **Game Step** : L'étape actuelle dans le panneau lil-gui
- **Player Position** : Position X, Y, Z du joueur en temps réel
- **Zone Visualization** : Anneaux visuels au sol pour les zones + cylindres transparents
---
## Notes techniques
- Le mouvement du joueur est bloqué tant que `canMove` est `false`
- Le store Zustand (`useGameStore`) est la source principale de vérité
- `GameStepManager` synchronise automatiquement avec le store Zustand lors des transitions
- Les transitions via les zones utilisent `GameStepManager.transitionTo()` qui met à jour le store
- L'audio utilise un callback `onEnded` pour déclencher les transitions automatiques
+21 -11
View File
@@ -10,7 +10,7 @@ The current prototype puts the player in a repair-oriented world where they prog
- Production map loaded from `public/map.json`
- Progressive map/model/collision/stage loading overlay
- Player controller with pointer lock, `ZQSD` movement, jump, octree collision, trigger input, and grab input
- Reusable repair-game flow for `bike`, `pylone`, and `ferme`
- Reusable repair-game flow for `ebike`, `pylon`, and `farm`
- Repair case animation, exploded model scan, broken-part markers, grabbable replacements, snap-to-placeholder placement, install validation, reassembly, and completion
- Shared interaction system for trigger and grab objects
- Rapier physics for gameplay objects while the player keeps a Three.js octree collision controller
@@ -18,19 +18,20 @@ The current prototype puts the player in a repair-oriented world where they prog
- Category-based audio manager for music, SFX, and dialogue
- Dialogue manifest, SRT subtitles, subtitle overlay, and dialogue queueing
- Cinematic manifest with GSAP camera keyframes and optional dialogue cues
- In-game settings menu for volumes, subtitles, subtitle language, and the currently staged repair-runtime toggle
- In-game settings menu for volumes, subtitles, and subtitle language
- Debug mode with `?debug`, lil-gui controls, game-state panel, hand-tracking panel, debug camera, physics playground, and R3F perf
- `/editor` route for map transforms, SRT editing, dialogue manifest editing, cinematic manifest editing, preview, validation, export, and dev-server saves
- `/docs` route that renders the repository documentation inside the app
## Routes
| Route | Purpose |
| --------- | --------------------------------------------------- |
| `/` | Playable 3D experience |
| `/?debug` | Playable scene with debug GUI and overlays |
| `/editor` | Local map, dialogue, subtitle, and cinematic editor |
| `/docs` | In-app documentation index |
| Route | Purpose |
| ---------- | --------------------------------------------------- |
| `/` | Playable 3D experience |
| `/?debug` | Playable scene with debug GUI and overlays |
| `/editor` | Local map, dialogue, subtitle, and cinematic editor |
| `/gallery` | 3D model gallery for browsing project assets |
| `/docs` | In-app documentation index |
## Tech Stack
@@ -98,6 +99,7 @@ Useful local URLs:
```txt
http://localhost:5173/?debug
http://localhost:5173/editor
http://localhost:5173/gallery
http://localhost:5173/docs
```
@@ -110,9 +112,15 @@ npm run format:check
npm run build
```
Regenerate runtime map data after editing `public/map_raw.json` that came from the hierachy node of the model Blocking.gltf:
```bash
npm run map:transform
```
## Optional Hand-Tracking Backend
The app can use browser-side MediaPipe, but the default debug source is the local backend.
The app can use the local Python backend, but the default debug source is browser-side MediaPipe.
```bash
python3.11 -m venv backend/.venv
@@ -143,18 +151,20 @@ WS ws://localhost:8000/ws
| `docs/technical/hand-tracking.md` | Webcam, backend/browser MediaPipe, glove, and gesture flow |
| `docs/technical/zustand.md` | Game, settings, and subtitle stores |
| `docs/technical/three-debugging.md` | DevTools workflow for stepping into Three.js internals |
| `docs/technical/map-performance.md` | Map draw-call bottlenecks and optimization notes |
| `docs/technical/map-lod.md` | Runtime map LOD presets, paths, and workflow |
| `docs/technical/editor.md` | Editor implementation details |
| `docs/technical/animation.md` | Animated, explodable, and reusable 3D model components |
| `docs/user/features.md` | Implemented feature inventory |
| `docs/user/main-feature.md` | User-facing repair-game walkthrough |
| `docs/user/editor.md` | Editor user guide |
| `docs/user/gallery.md` | Model gallery user guide |
| `docs/code-review-preparation.md` | French code-review preparation support |
## Current Caveats
- This is still a prototype, not a complete game runtime.
- The repair-runtime toggle is stored in settings and displayed in the UI, but the repair game currently still runs locally in React/Three.
- `useRepairMovementLocked()` currently returns `false`, so the movement-lock rule and indicator are present but disabled on `develop`.
- `useRepairMovementLocked()` locks player movement during focused repair steps and drives the repair movement indicator.
- Production editor persistence does not exist. Save endpoints in `vite.config.ts` are local Vite dev-server helpers.
- The player uses octree collision while gameplay objects use Rapier. Keep that boundary deliberate unless the whole player controller is migrated.
+1 -2
View File
@@ -1,6 +1,6 @@
from __future__ import annotations
from dataclasses import dataclass, field
from dataclasses import dataclass
from typing import Any
from uuid import uuid4
@@ -13,7 +13,6 @@ class ClientConnection:
websocket: WebSocket
is_processing: bool = False
last_frame_at: float = 0.0
metadata: dict[str, Any] = field(default_factory=dict)
class ConnectionManager:
Binary file not shown.
+19 -28
View File
@@ -19,7 +19,7 @@ La-Fabrik est une expérience web 3D en React, Vite, Three.js et React Three Fib
Le joueur est dans un monde 3D et avance dans une progression de réparation :
```txt
intro -> bike -> pylone -> ferme -> outro
intro -> ebike -> pylon -> farm -> outro
```
Les trois piliers à connaître pour la review :
@@ -62,7 +62,7 @@ HomePage
-> World
-> GameMap
-> GameStageContent
-> RepairGame bike/pylone/ferme
-> RepairGame ebike/pylon/farm
-> GameMusic
-> GameDialogues
-> Player
@@ -121,7 +121,7 @@ Phrase à retenir :
Piège à connaître :
`useRepairMovementLocked()` retourne actuellement `false`. Le lock de mouvement est prévu dans le code et l'UI, mais il est désactivé sur `develop`.
`useRepairMovementLocked()` lit maintenant l'étape de mission active et verrouille le déplacement pendant les phases de réparation qui doivent immobiliser le joueur.
### Interaction
@@ -324,7 +324,7 @@ Ouvrir dans cet ordre :
`RepairGame` reçoit une mission :
```tsx
<RepairGame mission="bike" position={[8, 0, -6]} />
<RepairGame mission="ebike" position={[42.2399, 4.5484, 34.6468]} />
```
Puis il vérifie :
@@ -347,7 +347,7 @@ Les variations mission sont dans `repairMissions.ts` :
### Pourquoi c'est bien
- Un seul flow réutilisable.
- Moins de duplication entre `bike`, `pylone`, `ferme`.
- Moins de duplication entre `ebike`, `pylon`, `farm`.
- Les règles générales restent dans les composants.
- Les variations restent dans la data.
- Le debug panel peut tester les mêmes steps que le vrai jeu.
@@ -471,9 +471,9 @@ Main states :
```txt
intro
bike
pylone
ferme
ebike
pylon
farm
outro
```
@@ -509,12 +509,7 @@ Gère :
- menu ouvert/fermé ;
- volumes ;
- sous-titres ;
- langue ;
- `repairRuntime`.
Piège :
`repairRuntime` est stocké et affiché, mais pas encore utilisé par `RepairGame`.
- langue.
### Subtitle store
@@ -531,16 +526,15 @@ Phrase simple :
Si on te pose une question précise, réponds vrai.
| Sujet | Réponse honnête |
| ------------------------- | ------------------------------------------------------------------------ |
| Lock mouvement réparation | Le hook existe mais retourne `false`, donc pas actif actuellement. |
| `repairRuntime` JS/Python | Le choix est stocké dans settings, mais pas consommé par le repair game. |
| Old debug flags | `noMusic`, `noMap`, `noDialogues`, etc. ne sont plus branchés. |
| Player physics | Le joueur n'est pas Rapier, il utilise capsule + octree. |
| Collision map | L'octree vient de nodes dédiés, actuellement `terrain`. |
| Editor save | Ce sont des endpoints Vite dev, pas une API de prod. |
| Cinematics | `GameCinematics` est monté seulement pendant `outro` dans `World`. |
| Hand tracking depth | Le `z` MediaPipe est relatif, pas une vraie profondeur monde stable. |
| Sujet | Réponse honnête |
| ------------------------- | -------------------------------------------------------------------- |
| Lock mouvement réparation | Les étapes repair actives bloquent le déplacement via le hook dédié. |
| Old debug flags | `noMusic`, `noMap`, `noDialogues`, etc. ne sont plus branchés. |
| Player physics | Le joueur n'est pas Rapier, il utilise capsule + octree. |
| Collision map | L'octree vient de nodes dédiés, actuellement `terrain`. |
| Editor save | Ce sont des endpoints Vite dev, pas une API de prod. |
| Cinematics | `GameCinematics` est monté seulement pendant `outro` dans `World`. |
| Hand tracking depth | Le `z` MediaPipe est relatif, pas une vraie profondeur monde stable. |
## Si l'évaluateur ouvre un fichier au hasard
@@ -833,8 +827,6 @@ Pour réutiliser le même flow sur plusieurs missions et garder les variations d
### Qu'est-ce qui est incomplet ?
- pas de vrai `GameManager` global ;
- lock mouvement réparation désactivé ;
- `repairRuntime` pas consommé ;
- editor save uniquement en dev ;
- hand tracking encore approximatif sur profondeur et smoothing.
@@ -869,8 +861,7 @@ Fichiers à avoir en tête :
Réponses pièges à réviser :
- lock mouvement repair désactivé actuellement ;
- `repairRuntime` pas consommé ;
- lock mouvement repair actif sur les étapes dédiées ;
- player pas Rapier ;
- save editor pas production ;
- old boot flags non branchés.
+1 -3
View File
@@ -46,13 +46,11 @@ It supports:
The debug physics scene currently uses it to preview:
```txt
public/models/electricienne_animated/model.gltf
public/models/electricienne-animated/model.gltf
```
with the `Dance` animation.
`src/hooks/animation/useCharacterAnimation.ts` is a hook-level alternative for components that need to own their group ref and animation controls directly.
## GLTF Reuse
Use `useClonedObject` when a GLTF scene is reused by a component instance. It memoizes `scene.clone(true)` and keeps clone creation out of render churn.
+2 -3
View File
@@ -107,7 +107,7 @@ It owns:
- `mainState`
- intro state
- `bike`, `pylone`, and `ferme` mission state
- `ebike`, `pylon`, and `farm` mission state
- outro state
- `isCinematicPlaying`
- progression actions
@@ -297,8 +297,7 @@ public/models/{name}/model.gltf
- The repository is still a prototype.
- There is no central production `GameManager`.
- The repair game is implemented, but broader mission orchestration is still light.
- `useRepairMovementLocked()` currently returns `false`, so repair movement lock is disabled even though the rule and UI component exist.
- The repair-runtime setting is stored in settings but not consumed by the repair-game implementation.
- `useRepairMovementLocked()` locks player movement during focused repair steps.
- Player collision and Rapier gameplay physics are separate systems.
- Editor persistence is local development tooling only.
- Debug systems are still part of active scene composition and should remain easy to identify.
+25 -29
View File
@@ -4,7 +4,7 @@ This document describes the map editor that exists in the current codebase.
## Purpose
The editor is a React route used to inspect and adjust the `public/map.json` scene data from inside the La-Fabrik app. It shares the same `MapNode` data format as the game scene and uses React Three Fiber for rendering.
The editor is a React route used to inspect and adjust the current hierarchical `public/map.json` scene data from inside the La-Fabrik app. It exposes editable object nodes as a flat list for UI selection, while preserving and saving the full map tree.
## Routing
@@ -52,7 +52,7 @@ src/
## Responsibilities
`src/pages/editor/page.tsx` is the route-level composition component. It owns route-specific state such as selected object, hovered object, transform mode, selection lock, player-mode toggle, cinematic preview requests, and editor scene loading state.
`src/pages/editor/page.tsx` is the route-level composition component. It owns route-specific state such as primary selected object, selected object indexes, hovered object, transform mode, selection lock, player-mode toggle, cinematic preview requests, and editor scene loading state.
`src/hooks/editor/useEditorSceneData.ts` loads the default map data and handles folder uploads.
@@ -60,7 +60,7 @@ src/
`src/components/editor/scene/EditorScene.tsx` composes the editor canvas scene, camera controls, lights, keyboard shortcuts, and `EditorMap`.
`src/components/editor/scene/EditorMap.tsx` renders map nodes, fallback cubes, selection highlighting, and transform controls.
`src/components/editor/scene/EditorMap.tsx` renders map nodes, fallback cubes, selection highlighting, and transform controls. For multi-selection, it attaches `TransformControls` to a temporary group centered on the selected nodes, then decomposes the group delta back into each selected node transform.
`src/components/editor/EditorControls.tsx` renders the HTML control panel outside the canvas. The panel is organized into top-level `details` groups: `Editor`, `Cinematics`, `Dialogues`, and `SRT`.
@@ -72,7 +72,7 @@ src/
`src/controls/editor/FlyController.tsx` provides editor movement controls for player-style navigation.
`src/utils/map/loadMapSceneData.ts` is shared by the game map and editor. It loads `/map.json` and resolves available `public/models/{name}/model.glb` files first, then falls back to `public/models/{name}/model.gltf`.
`src/utils/map/loadMapSceneData.ts` is shared by the game map and editor. It loads `/map.json`, validates the hierarchical payload, exposes editable nodes with their `sourcePath` back to the tree, and resolves available `public/models/{name}/model.glb` files first, then falls back to `public/models/{name}/model.gltf`.
`src/utils/editor/loadEditorScene.ts` contains editor-only upload handling for user-selected folders.
@@ -87,22 +87,13 @@ interface MapNode {
position: [number, number, number];
rotation: [number, number, number];
scale: [number, number, number];
sourcePath?: number[];
}
```
`public/map.json` is expected to be a `MapNode[]`.
`public/map.json` may be hierarchical. The editor keeps the hierarchy in `SceneData.mapTree` and stores editable entries in `SceneData.mapNodes` with a `sourcePath` back to the real tree node.
```json
[
{
"name": "pylone",
"type": "Mesh",
"position": [0, 5, 0],
"rotation": [0, 1.57, 0],
"scale": [1, 1, 1]
}
]
```
Group nodes use `role: "group"`; editable nodes keep `name`, `type`, `position`, `rotation`, and `scale`.
Each node `name` maps to a model folder:
@@ -122,20 +113,26 @@ If `model.glb` and `model.gltf` are both missing, the editor renders a fallback
2. `useEditorSceneData` calls `loadMapSceneData()`.
3. `loadMapSceneData()` loads `/map.json` and available model URLs.
4. If `/map.json` is missing, the page displays a folder-upload flow.
5. `EditorSceneLoadingTracker` uses drei `useProgress()` to update the fullscreen editor loading overlay while models load.
5. The route-level loading overlay reports map JSON loading, then hands off to the editor scene once the map payload is ready.
6. `EditorScene` renders the grid, lights, camera controls, and map nodes inside `Suspense`.
7. `EditorControls` exposes transform mode, history actions, export, save, JSON preview, selection lock, and the cinematic/dialogue/SRT editors.
7. `EditorControls` exposes transform mode, terrain snap, terrain-selection lock, add/delete node, precise scale inputs, history actions, camera focus/reset, export, save, JSON preview, selection lock, multi-selection status, and the cinematic/dialogue/SRT editors.
## Controls
- Click: select a node.
- `Shift` + right click: add or remove a node from the multi-selection.
- `Esc`: clear selection.
- Click empty space: clear selection.
- Selection lock button: prevent object clicks, empty-space clicks, and `Esc` from changing the current selection.
- Selection lock button: prevent object clicks and `Esc` from changing the current selection.
- Selection clear button: intentionally clear the current selection even when the lock is active.
- `T`: translate mode.
- `R`: rotate mode.
- `S`: scale mode.
- Snap terrain on move: enabled by default and applied while translating an object.
- Multi-selection transforms use a temporary centered group and write the resulting position, rotation, and scale back to every selected map node.
- Lock terrain: enabled by default so terrain remains visible but ignores selection clicks.
- Camera action: centers on the selected object or resets to the editor home view.
- Add node: creates a fallback cube under `blocking` using the requested model folder name.
- Delete selected node: removes the editable node from the preserved map tree.
- `Ctrl+Z` or `Cmd+Z`: undo.
- `Ctrl+Y` or `Cmd+Y`: redo.
- `WASD`, `ZQSD`, or arrow keys: move in player-controller mode.
@@ -146,21 +143,20 @@ If `model.glb` and `model.gltf` are both missing, the editor renders a fallback
The editor supports two output paths:
- Export JSON downloads the current `MapNode[]` as `map.json`.
- Save to Server posts the current `MapNode[]` to `/api/save-map`.
- Export JSON downloads the current hierarchical map tree as `map.json`.
- Save to Server posts the current hierarchical map tree to `/api/save-map`.
The dev-only `/api/save-map` endpoint is implemented by the Vite plugin in `vite.config.ts`. It writes to `public/map.json` and enforces a maximum payload size.
The dev-only `/api/save-map` endpoint is implemented by the Vite plugin in `vite.config.ts`. It validates the payload through the shared map parser, writes to `public/map.json`, and enforces a maximum payload size.
## Editor Loading Overlay
The editor uses `SceneLoadingOverlay` like the runtime scene. `EditorSceneLoadingTracker` lives in `src/pages/editor/page.tsx` and reads drei `useProgress()` inside the canvas.
The editor uses `SceneLoadingOverlay` like the runtime scene for the route-level map JSON loading phase.
The route tracks two loading phases:
The route tracks the map JSON loading phase:
- map JSON loading through `useEditorSceneData()`
- model loading through `useProgress()`
The overlay is rendered outside the canvas so it remains visible while the R3F scene mounts. The scene itself is wrapped in `Suspense` with a `null` fallback; the visual feedback is handled by the overlay instead of by the canvas fallback.
The overlay is rendered outside the canvas so it remains visible while the editor route mounts. Model loading is left to R3F `Suspense` boundaries to avoid progress updates during model render.
## Panel Groups
@@ -192,9 +188,9 @@ The state is passed to:
- `EditorControls`, to render the lock/unlock button
- `EditorScene`, to block `Esc` deselection when locked
- `EditorMap`, to block object selection and empty-space deselection when locked
- `EditorMap`, to block object selection when locked
The clear button calls `onClearSelection` directly from `EditorControls`. This is intentionally separate from scene click behavior so the user always has an explicit way to clear the selection.
The clear button calls `onClearSelection` directly from `EditorControls`. Clicking empty canvas space does not clear the current selection; use `Esc` or the explicit clear button instead.
## Dialogue SRT Editing
-187
View File
@@ -1,187 +0,0 @@
# Game Flow - La Fabrik
## Étapes du jeu
```
intro → start-intro → naming → bienvenue → star-move → mission2 → searching_problem → preparation → outOfFabrik
```
---
## Détail des étapes
### 1. `intro` (initial)
- État initial au chargement du jeu
- Aucune action, juste une étape de départ
- Transition automatique vers `start-intro`
### 2. `start-intro`
- **Déclenchement** : Auto-transition depuis `intro` quand la scène est chargée
- **Action** : Joue l'audio d'intro (`intro`)
- **Attente** : Attend que l'audio se termine
- **Transition** : Vers `naming` quand l'audio se termine
### 3. `naming`
- **Déclenchement** : Quand l'audio d'intro se termine
- **Action** : Affiche un input pour demander le prénom du joueur
- **Attente** : L'utilisateur entre son prénom et valide
- **Transition** : Vers `bienvenue` quand l'utilisateur valide
### 4. `bienvenue`
- **Déclenchement** : Quand l'utilisateur valide son prénom
- **Actions** :
- Affiche "Bienvenue {prénom} !" à l'écran
- Joue l'audio de bienvenue
- **Attente** : Attend que l'audio se termine
- **Transition** : Vers `star-move` quand l'audio se termine
### 5. `star-move`
- **Déclenchement** : Quand l'audio de bienvenue se termine
- **Action** : Active le mouvement du joueur (`setCanMove(true)`)
- **État** : Le joueur peut maintenant se déplacer librement
- **Zone** : La détection de zone devient active (ZoneDetection)
### 6. `mission2`
- **Déclenchement** : Quand le joueur entre dans la zone `fabrikExit` (position: `[-5, 25, -15]`)
- **Actions** :
- Stocke `activityCity: false` dans le store Zustand
- Joue l'audio `alertCentral`
- **État** : Les objets avec hook `useActivityCity()` détectent le changement et jouent leurs animations
- **Attente** : Le joueur atteint la zone de trigger pour `searching_problem`
### 7. `searching_problem`
- **Déclenchement** : Quand le joueur entre dans la zone `searchingProblemZone` (position: `[-5, 25, -30]`)
- **Actions** :
- Joue l'audio `searchingProblem`
- Affiche l'objet "central" (position: `[1, 15, -45]`)
- **Attente** : Le joueur interagit avec l'objet "central"
### 8. `preparation`
- **Déclenchement** : Quand le joueur interagit avec l'objet "central"
- **Actions** :
- Bloque le mouvement (`setCanMove(false)`)
- Cache l'objet "central"
### 9. `outOfFabrik`
- **Déclenchement** : (non implémenté pour le moment)
- **Action** : Transition vers l'étape finale
---
## Fichiers clés
| Fichier | Rôle |
| --------------------------------------- | --------------------------------------------------------- |
| `src/stores/gameStore.ts` | Store Zustand pour l'état global du jeu |
| `src/stateManager/GameStepManager.ts` | Synchronise avec le store Zustand |
| `src/components/game/GameFlow.tsx` | Gère les transitions automatiques et la lecture audio |
| `src/components/ui/IntroUI.tsx` | Affiche l'input pour le prénom et le message de bienvenue |
| `src/components/zone/ZoneDetection.tsx` | Détecte quand le joueur entre dans une zone |
| `src/components/3d/CentralObject.tsx` | Objet interactif "central" pour la mission 2 |
| `src/data/audioConfig.ts` | Chemins des fichiers audio |
| `src/data/zones.ts` | Configuration des zones de transition |
| `src/hooks/useActivityCity.ts` | Hook pour détecter le changement d'activité de la ville |
---
## Configuration audio
```typescript
// src/data/audioConfig.ts
export const AUDIO_PATHS = {
intro: "/sounds/fa.mp3",
bienvenue: "/sounds/fa.mp3",
alertCentral: "/sounds/fa.mp3",
searchingProblem: "/sounds/fa.mp3",
};
```
---
## Configuration des zones
```typescript
// src/data/zones.ts
export const ZONES: Zone[] = [
{
id: "fabrikExit",
position: [-5, 25, -15],
radius: 10,
height: 20,
targetStep: "mission2",
},
{
id: "searchingProblemZone",
position: [-5, 25, -30],
radius: 10,
height: 20,
targetStep: "searching_problem",
},
];
```
---
## Store Zustand
```typescript
// src/stores/gameStore.ts
interface GameState {
step: GameStep;
activityCity: boolean;
playerName: string;
canMove: boolean;
setStep: (step: GameStep) => void;
setActivityCity: (value: boolean) => void;
setPlayerName: (name: string) => void;
setCanMove: (canMove: boolean) => void;
}
```
---
## Hooks personnalisés
### useActivityCity
Permet aux objets 3D de réagir au changement d'activité de la ville :
```typescript
import { useActivityCity } from "@/hooks/useActivityCity";
function MyAnimatedObject() {
const activityCity = useActivityCity(); // true par défaut, false en mission2
// L'animation se déclenche quand activityCity change à false
// Utiliser useEffect pour réagir au changement
}
```
---
## Debug
En mode debug (`?debug` dans l'URL), on peut voir :
- **Game Step** : L'étape actuelle dans le panneau lil-gui
- **Player Position** : Position X, Y, Z du joueur en temps réel
- **Zone Visualization** : Anneaux visuels au sol pour les zones + cylindres transparents
---
## Notes techniques
- Le mouvement du joueur est bloqué tant que `canMove` est `false`
- Le store Zustand (`useGameStore`) est la source principale de vérité
- `GameStepManager` synchronise automatiquement avec le store Zustand lors des transitions
- Les transitions via les zones utilisent `GameStepManager.transitionTo()` qui met à jour le store
- L'audio utilise un callback `onEnded` pour déclencher les transitions automatiques
+1 -1
View File
@@ -32,7 +32,7 @@ This keeps hand tracking active while the player is inside an interaction zone,
The production repair activation conditions are:
- active `mainState` is `bike`, `pylone`, or `ferme`
- active `mainState` is `ebike`, `pylon`, or `farm`
- the active mission step is `inspected`, `repairing`, `reassembling`, or `done`
This keeps the webcam off during `waiting`, `fragmented`, and `scanning`, then enables hand input only when the repair flow is expected to use hands.
+1 -1
View File
@@ -184,7 +184,7 @@ Input is ignored while:
- the settings menu is open
- a cinematic is playing
Movement lock is read separately from `useRepairMovementLocked`, but that hook currently returns `false` on this branch.
Movement lock is read separately from `useRepairMovementLocked`, which locks the player during focused repair steps.
## UI Prompt
+119
View File
@@ -0,0 +1,119 @@
# Map LOD System
This document describes the runtime LOD system used by the production map.
## Goal
The map now supports two visual versions for selected models:
- the regular model in `public/models/<name>/`
- the lighter model in `public/models/<name>-LOD/`
The runtime chooses between those paths from the active graphics preset. This keeps nearby objects visually richer while reducing the cost of distant objects.
## Graphics Presets
Presets are configured in:
```txt
src/data/world/graphicsConfig.ts
```
Current behavior:
| Preset | Chunk load distance | Fog | LOD behavior |
| -------- | ------------------: | --- | ------------------------------------- |
| `low` | 10m | On | Always use `*-LOD` models |
| `medium` | 20m | On | Always use `*-LOD` models |
| `high` | 35m | Off | Regular model up to 10m, then `*-LOD` |
| `ultra` | 50m | Off | Regular model up to 20m, then `*-LOD` |
The unload distance stays slightly larger than the load distance to avoid rapid mount/unmount flickering when the player stands near a boundary.
## Runtime Selection
LOD path mapping lives in:
```txt
src/data/world/mapLodConfig.ts
```
The main selector is `selectMapModelPathByDistance()`. It receives:
- the current camera distance
- the map model name
- the regular model path
- the active graphics preset
It returns either the regular path or the `*-LOD` path.
## Chunked Instanced Models
Repeated static assets are rendered through:
```txt
src/world/map-instancing/MapInstancingSystem.tsx
```
For each visible chunk, the system checks the nearest instance in that chunk. If the nearest instance is inside the high-detail threshold, the whole chunk uses the regular model. Otherwise, it uses the `*-LOD` model.
This is intentionally chunk-level LOD instead of per-instance LOD. It matches the existing chunk streaming architecture and avoids splitting every object into many tiny batches.
## Single And Generated Models
Single map nodes use:
```txt
src/hooks/world/useMapLodModelPath.ts
src/world/GameMap.tsx
```
Some named map objects are rendered through dedicated generated components instead of the generic `GameMap` path. Those components must call `useMapLodModelPath()` directly.
Current dedicated generated components with LOD support:
```txt
src/components/three/world/EcoleModel.tsx
src/components/three/world/LaFabrikMapModel.tsx
```
This matters for `lafabrik`: adding `public/models/lafabrik-LOD/` is not enough by itself. The component must also be connected to `useMapLodModelPath()`.
## Adding A New LOD Model
To add LOD support for a model:
1. Add the light model in `public/models/<name>-LOD/model.gltf`.
2. Keep the regular model in `public/models/<name>/model.glb` or `public/models/<name>/model.gltf`.
3. Add the mapping in `src/data/world/mapLodConfig.ts`.
4. If the model uses a dedicated component, call `useMapLodModelPath()` in that component.
5. Preload both paths when the component is dedicated and uses `useGLTF.preload()`.
6. Verify the GLTF/GLB references: buffers, textures, opacity maps, and relative paths.
## Current LOD Models
The current explicit LOD mappings are:
```txt
ebike
eolienne
pylone
boiteimmeuble
ecole
immeuble1
lafabrik
maison1
panneauaffichage
talkie
```
## Regression Risks
The most common failure modes are:
- the `*-LOD` folder exists but is missing from `mapLodConfig.ts`
- a dedicated generated component keeps a hardcoded model path
- GLTF references point to textures that were renamed during export
- a model is added to LOD config but does not spawn through `GameMap` or `MapInstancingSystem`
Before committing model changes, validate both the regular and LOD folders for missing GLTF refs.
+268
View File
@@ -0,0 +1,268 @@
# Map Performance Notes
This document tracks the current map-rendering performance pass.
## Current Runtime Path
- `public/map.json` is the source of map transforms.
- `src/world/GameMap.tsx` renders regular visual map nodes.
- `src/world/vegetation/VegetationSystem.tsx` already instances dense vegetation.
- `src/world/map-instancing/MapInstancingSystem.tsx` instances selected repeated static map assets.
- `src/world/GameMapCollision.tsx` keeps terrain collision separate for the player octree.
## Draw-Call Bottlenecks Found
The first performance bottleneck was draw calls. Some assets were exported as many small GLTF primitives even when they used only a few materials.
| Model | Instances | Meshes / primitives | Notes |
| ---------------- | --------: | ------------------: | ------------------------------------------------------------------------------------ |
| `generateur` | 3 | 3152 | Worst draw-call offender. Needs asset-side mesh merging. |
| `lafabrik` | 4 | 474 | High primitive count; current HD GLB has embedded geometry and no external textures. |
| `ecole` | 1 | 107 | One material but many primitives; should be merged. |
| `fermeverticale` | 3 | 1 | Geometry is fine; textures are large for the visible complexity. |
`generateur` was especially expensive because three visible instances could multiply thousands of primitives into thousands of draw calls. Instancing reduces repeated instance cost, but the source asset still needs a cleaner export.
## Runtime Merge Pass
`InstancedMapAsset` now groups source meshes by material and compatible geometry attributes before creating `THREE.InstancedMesh` objects. This reduces the runtime draw groups even when the source GLTF is exported as many small meshes.
Estimated source primitive count versus runtime merged groups:
| Model | Source primitives | Runtime merged groups |
| ------------ | ----------------: | --------------------: |
| `generateur` | 3152 | 8 |
| `ecole` | 107 | 2 |
| `eolienne` | 118 | 8 |
| `lafabrik` | 474 | ~77 |
This is a code-side safety net, not a replacement for clean asset exports. Clean GLB exports with merged meshes and fewer textures remain the preferred long-term path.
## Current Triangle Bottleneck
After the runtime merge pass, draw calls can drop dramatically, but FPS can still stay low because the scene now remains triangle-bound. A debug capture after the merge showed roughly:
```txt
138 draw calls
~69.6M triangles
~10 FPS
```
That means the renderer is no longer mostly blocked by draw-call submission. It is mostly drawing too many visible triangles.
Estimated triangle contribution from `map.json` instance counts:
| Model | Instances | Triangles each | Estimated total triangles |
| ------------------- | --------: | -------------: | ------------------------: |
| `buisson` | 646 | 37 500 | ~24.2M |
| `champdesoja` | 1181 | 16 268 | ~19.2M |
| `arbre` | 291 | 38 906 | ~11.3M |
| `champdeble` | 1307 | 6 260 | ~8.2M |
| `champsdetournesol` | 1163 | 3 264 | ~3.8M |
| `sapin` | 93 | 23 972 | ~2.2M |
These vegetation and crop assets account for almost all of the current `~69M` triangle count. By comparison, the previously suspicious static buildings are much smaller in triangle cost:
| Model | Estimated total triangles |
| ---------------- | ------------------------: |
| `generateur` | ~123k |
| `lafabrik` | ~124k |
| `ecole` | ~5k |
| `fermeverticale` | ~1k |
`InstancedMesh` reduces draw calls, but it does not reduce triangle count. If 646 bushes each contain 37 500 triangles, the GPU still has to draw about 24 million bush triangles when those instances are visible.
## Debug Performance Controls
The debug-only performance folder can isolate model families when `?debug` is enabled.
Proposed controls:
```txt
Performance / Map
- vegetation
- crops
- trees
- buildings
- landmarks
- props
- terrain
- sky
```
Useful per-model toggles:
```txt
buisson
arbre
sapin
champdeble
champdesoja
champsdetournesol
fermeverticale
lafabrik
immeuble1
eolienne
pylone
```
The purpose is diagnostic, not final gameplay behavior. The expected workflow is:
1. Open `/?debug` with R3F perf enabled.
2. Disable one family or model type.
3. Watch `triangles`, `calls`, and FPS.
4. Identify which model groups need LOD, density reduction, or asset re-export.
Recommended implementation files:
```txt
src/managers/stores/useMapPerformanceStore.ts
src/hooks/debug/useMapPerformanceDebug.ts
src/world/vegetation/VegetationSystem.tsx
src/world/map-instancing/MapInstancingSystem.tsx
src/world/GameMap.tsx
```
The store should stay runtime/debug-only. It should not change persisted production map data.
## Triangle-Reduction Follow-Up
Once the expensive model families are isolated, the real triangle fixes are:
1. Lower-poly vegetation and crop exports.
2. LOD variants for trees, bushes, and crop fields.
3. Distance-based culling for vegetation/crop instances.
4. Chunked instancing so Three.js can frustum-cull groups instead of one huge global `InstancedMesh`.
5. Billboard/impostor versions for far vegetation.
Chunked instancing is especially important. A single `InstancedMesh` containing every bush has one global bounding sphere. If that bounding sphere is visible, Three.js may keep the whole batch visible. Splitting instances into grid chunks allows entire offscreen chunks to be skipped.
## Player-Only Vegetation Streaming
The first distance-streaming pass is intentionally limited to vegetation and crop instances:
- `arbre`
- `sapin`
- `buisson`
- `champdeble`
- `champdesoja`
- `champsdetournesol`
The behavior is configured in:
```txt
src/data/world/fogConfig.ts
```
Current runtime values:
```txt
chunkSize: 35
low load/unload radius: 10m / 18m
medium load/unload radius: 20m / 30m
high load/unload radius: 35m / 45m
ultra load/unload radius: 50m / 65m
updateInterval: 250ms
fog near: 30
fog far: 45
```
The streaming and fog are scoped to the production game scene with the player camera only:
```txt
sceneMode === "game" && cameraMode === "player"
```
This matters for debugging. In debug camera mode there is no fog and no distance streaming, so the developer can inspect the full map freely. In player mode, chunks mount and unmount around the camera to reduce visible triangle count while fog hides vegetation pop-in.
Chunk cleanup is handled through React unmounting. `VegetationSystem` removes chunks from the tree, and `InstancedVegetation` removes its `THREE.InstancedMesh` objects from the group while disposing the locally created merged geometries/material clones in its own cleanup path.
## Runtime Texture Filtering
Loaded GLTF textures are normalized in code through:
```txt
src/utils/three/optimizeGLTFScene.ts
```
The runtime pass applies conservative texture filtering:
1. Cap anisotropy to a small value.
2. Enable mipmap generation for regular PNG/JPG/WebP textures.
3. Use trilinear mipmap filtering for minification.
4. Keep existing opacity/alpha material mapping intact.
This mirrors the intent of the designer upload pipeline without rewriting model files at runtime. The sibling `upload-GLTF` project already has the stronger asset-side path: Blender GLB export with Draco, texture resizing, KTX2 generation with mipmaps, WebP fallback, and GLTF JSON URI/extension rewriting for `KHR_texture_basisu`.
Runtime texture filtering improves distant texture stability and GPU sampling behavior, but it does not reduce mesh triangle count. Triangle reduction still comes from streaming, distance unloading, or optimized source assets.
## Terrain-Snapped Map Placement
Map object heights are corrected at runtime through:
```txt
src/hooks/three/useTerrainHeight.ts
```
The terrain raycast is not done every frame. The terrain mesh list is built from the cached terrain GLTF, then each model or instance computes its snapped `y` when it is mounted or when its instance data changes.
Applied paths:
1. Regular `GameMap` model instances.
2. Generated static map models.
3. Instanced static map assets.
4. Vegetation and crop chunks.
Only the `y` coordinate is replaced. `x`, `z`, and rotation stay from `map.json`. Runtime scale is also normalized when a static map node has a non-uniform scale, which prevents exported values like `[1, 2, 1]` from stretching or shrinking a map model unexpectedly.
## Current Code-Side Optimization
Repeated static assets are configured in:
```txt
src/world/map-instancing/mapInstancingConfig.ts
```
Those names are excluded from the regular `GameMap` clone path, then rendered by `MapInstancingSystem` with merged `THREE.InstancedMesh` batches.
This keeps the existing map authoring format while reducing repeated draw calls for selected assets.
## Generated R3F Model Path
Unique static map assets can use explicit R3F components instead of the generic cloned GLTF path. This follows the same intent as `gltfjsx`: expose the model as a React component, then keep control over mesh/material setup in code.
Current generated map-model entry point:
```txt
src/world/map-generated/GeneratedMapNodeInstance.tsx
```
Current generated model component:
```txt
src/components/three/world/EcoleModel.tsx
src/components/three/world/LafabrikModel.tsx
src/components/three/world/FermeVerticaleModel.tsx
src/components/three/world/GenerateurModel.tsx
```
`ecole`, `lafabrik`, `fermeverticale`, and `generateur` use this path. Their components share the same merged static model renderer, which groups compatible geometry by material before mounting meshes.
This path should be used selectively. It improves control and can remove clone overhead, but it does not reduce source triangle count by itself.
## Asset-Side Follow-Up
Design/export should prioritize:
1. Produce lower-poly `buisson`, `arbre`, `sapin`, and crop assets.
2. Add LOD or billboard variants for far vegetation.
3. Merge `generateur` meshes from 3152 primitives to a small number of material groups.
4. Keep `lafabrik` exports texture-light, and merge repeated material primitives where possible.
5. Merge `ecole` primitives because it uses a single material.
6. Prefer runtime `.glb` or compressed runtime textures when the pipeline supports it.
## Safety Rules
- Do not instance `terrain` for player collision without validating `Octree.fromGraphNode` support.
- Do not replace repair-game models with optimized map models unless repair node names are preserved.
- Dispose only GPU resources created locally. Do not dispose textures or geometries owned by `useGLTF`'s cache.
+4 -5
View File
@@ -14,7 +14,6 @@ The store owns the `missionFlow` slice:
```ts
missionFlow: {
step: GameStep;
activityCity: boolean;
playerName: string;
canMove: boolean;
@@ -31,14 +30,14 @@ Managers stay responsible for local runtime services:
- `AudioManager` owns audio elements, audio pools, music playback, category volume, and stereo pan.
- `InteractionManager` owns transient focused/nearby/held interaction handles.
Mission progression is not owned by a manager. Components update the store through explicit actions such as `setFlowStep`, `setCanMove`, `showDialog`, and `hideDialog`.
Mission progression is not owned by a manager. Components update the store through explicit actions such as `setIntroStep`, `setCanMove`, `showDialog`, and `hideDialog`.
## Runtime Components
- `src/components/game/GameFlow.tsx` reacts to `missionFlow.step` and triggers one-off side effects such as intro audio and movement unlocks.
- `src/components/game/GameFlow.tsx` reacts to intro state and triggers one-off side effects such as intro audio and movement unlocks.
- `src/components/zone/ZoneDetection.tsx` reads the camera position and moves the flow to a target step when the player enters a configured zone.
- `src/components/three/interaction/CentralObject.tsx` and `VillageoisHelperObject.tsx` expose temporary interactive mission objects.
- `src/pages/page.tsx` mounts mission HTML overlays: `IntroUI`, `BienvenueDisplay`, and `DialogMessage`.
- `src/world/GameStageContent.tsx` mounts repair games and their mission-start triggers.
- `src/pages/page.tsx` mounts mission HTML overlays: `IntroUI`, `DialogMessage`, and subtitles.
- `src/world/player/PlayerController.tsx` reads `missionFlow.canMove` as an additional movement lock.
## Step Sequence
+10 -10
View File
@@ -8,11 +8,11 @@ The repair game is the current core gameplay loop. It gives three missions the s
Implemented missions:
| Mission | Object | Role |
| -------- | ------------- | --------------------------------------------- |
| `bike` | E-bike | Repair a damaged cooling core |
| `pylone` | Power pylon | Restore relay/panel-like broken parts |
| `ferme` | Vertical farm | Stabilize irrigation/sensor-like broken parts |
| Mission | Object | Role |
| ------- | ------------- | --------------------------------------------- |
| `ebike` | E-bike | Repair a damaged cooling core |
| `pylon` | Power pylon | Restore relay/panel-like broken parts |
| `farm` | Vertical farm | Stabilize irrigation/sensor-like broken parts |
## Main Files
@@ -79,7 +79,7 @@ src/managers/stores/useGameStore.ts
- `setMissionStep(mission, nextStep)`
- `completeMission(mission)`
The important architectural choice is that reusable repair components do not call `setBikeState`, `setPyloneState`, or `setFermeState` directly. They use generic mission actions so the same component can run for all three missions.
The important architectural choice is that reusable repair components do not call `setEbikeState`, `setPylonState`, or `setFarmState` directly. They use generic mission actions so the same component can run for all three missions.
## Data-Driven Mission Config
@@ -159,7 +159,7 @@ The repair case appears near the mission object. The player can:
Both paths move to `fragmented`.
Important current detail: `useRepairMovementLocked()` currently returns `false`, so the movement-lock rule and indicator are present but disabled in the current branch.
`useRepairMovementLocked()` locks player movement during focused repair steps and drives the repair movement indicator.
### Fragmented
@@ -324,9 +324,9 @@ src/world/GameStageContent.tsx
Current positions:
```tsx
<RepairGame mission="bike" position={[8, 0, -6]} />
<RepairGame mission="pylone" position={[64, 0, -66]} />
<RepairGame mission="ferme" position={[-24, 0, 42]} />
<RepairGame mission="ebike" position={[42.2399, 4.5484, 34.6468]} />
<RepairGame mission="pylon" position={[64, 0, -66]} />
<RepairGame mission="farm" position={[-24, 0, 42]} />
```
Only the repair game whose `mission` matches `useGameStore().mainState` renders active content.
+22 -1
View File
@@ -32,6 +32,8 @@ The loading progress in `HomePage` is monotonic:
This prevents the overlay from jumping backward when nested loaders finish in a slightly different order.
After the initial map boot is complete, late loading signals no longer reopen the full-screen loading overlay. Instead, `HomePage` shows the compact `AppLoadingIndicator` while the game remains visible. This is reserved for explicit runtime reload signals such as graphics preset changes, repair-state transitions, or late world loading events; chunk streaming intentionally does not drive this indicator.
## World Composition
`src/world/World.tsx` is the main scene composer.
@@ -74,12 +76,31 @@ It tracks:
- `showGameStage`: true when the map is ready enough to mount gameplay content
- `gameplayReady`: true when map, stage, and octree are all ready
The final game-scene readiness condition is:
The game-scene readiness condition is:
```ts
showGameStage && gameStageLoaded && octree !== null;
```
Shadows are configured once when `Lighting` mounts (renderer `shadowMap.enabled`, sun
`shadow.autoUpdate = true`, bias and frustum from `SHADOW_CONFIG` in
`src/data/world/lightingConfig.ts`). The shadow map then refreshes every frame and
follows the player camera through the sun's `target`. The earlier `SceneShadowWarmup`
step has been removed — the visible loading overlay no longer waits for a forced
shadow refresh because `autoUpdate` covers steady-state rendering.
### Avoiding global scene remounts
Heavy stage components (`GameStageContent`, `Player`, dialogues) load assets via
`useGLTF`/`useTexture` without preload (e.g. `EbikeSpeedometer` calls `useTexture`
when the bike mounts). To prevent any late suspension from bubbling up to the
root `<Suspense>` boundary in `src/pages/page.tsx` and unmounting the entire
world (which would trigger a redundant octree rebuild and shadow re-config), the
game stage block and the spawn-player block are wrapped in their own
`<Suspense fallback={null}>` boundaries inside `src/world/World.tsx`. Any new
sibling that suspends late should be added inside one of these boundaries or get
its own.
The debug physics scene is ready when:
```ts
+60
View File
@@ -20,3 +20,63 @@ If DevTools still opens a bundled file, stop the dev server, clear Vite's cached
rm -rf node_modules/.vite
npm run dev:three-debug
```
## Visual debug toggles
The `Debug` folder of the runtime debug GUI exposes inspection toggles backed by
`src/managers/stores/useDebugVisualsStore.ts`:
- **Show Player Model** — renders the main character GLTF in front of the
current camera (`src/components/debug/DebugPlayerModel.tsx`). The model is
positioned in camera-local space so it stays visible regardless of pitch.
- **Show Octree** — overlays the collision octree as colored line segments,
one wireframe per spatial cell (`src/components/debug/DebugOctreeVisualization.tsx`).
Cells are colored by depth. Use it to inspect collision precision around
doorways or passages.
- **Octree Max Depth** — caps how deep the octree visualization recurses
(default 6). Increase to see leaf-level subdivisions; decrease to keep the
scene readable when the tree is large.
The octree visualization reads the live `Octree` instance from `World`. The
mesh uses `depthTest: false` and a high `renderOrder`, so cells stay visible
through opaque geometry.
## Shadow rendering intermittence
Shadows occasionally failed to render on initial load and could disappear
mid-session even though the `Lighting` configuration ran to completion. The
fix has two layers:
### Per-frame refresh (steady state)
The sun follows the camera, so its world matrix is dirty every frame. With
`shadow.autoUpdate` alone, three.js can skip the shadow map re-render on a
frame where the matrix update has happened but the renderer's internal dirty
tracking does not pick it up. To prevent that, `Lighting.useFrame` sets
`sun.shadow.needsUpdate = true` after the per-frame matrix updates. Shadow
config is centralized in `src/data/world/lightingConfig.ts` (`bias=0`,
`normalBias=0`, `cameraSize=95`).
### Mount-time shadow map reallocation (`useShadowMapWarmup`)
The merged static map and other GLTFs mount imperatively after `Lighting`,
so the shadow render target ends up linked to a renderer state that pre-dates
the final scene. Materials compiled at that point bake a "no shadow map"
permutation into their shader program and silently fail to render shadows
until a WebGL context-restore cycle (the kind triggered by Chrome DevTools
in `?debug` runs) reallocates everything.
`src/hooks/three/useShadowMapWarmup.ts` replays that cycle programmatically
without the cost of a full context loss. It runs a `useFrame` watchdog that
samples the scene mesh count every 6 frames; once the count has been stable
for ~1 s (or after a 5 s safety cap), it:
1. Disposes the directional light shadow map and nulls it. three.js
reallocates the render target on the next render at the configured
`mapSize`.
2. Marks every material's `needsUpdate = true`, forcing a shader recompile
that rebinds every program to the freshly created shadow sampler.
3. Forces a single shadow pass and invalidates the renderer.
The watchdog runs once per mount and adds a single traversal every 6 frames
during the warmup window, after which it self-terminates.
+23 -30
View File
@@ -28,11 +28,11 @@ They are under `src/managers/stores/` because they are shared runtime state, not
## Store Responsibilities
| Store | Responsibility |
| ------------------ | ----------------------------------------------------------------- |
| `useGameStore` | Durable game progression, mission steps, cinematic input lock |
| `useSettingsStore` | Menu visibility, volumes, subtitle options, repair-runtime toggle |
| `useSubtitleStore` | Currently displayed subtitle cue |
| Store | Responsibility |
| ------------------ | ------------------------------------------------------------- |
| `useGameStore` | Durable game progression, mission steps, cinematic input lock |
| `useSettingsStore` | Menu visibility, volumes, and subtitle options |
| `useSubtitleStore` | Currently displayed subtitle cue |
## Managers vs Stores
@@ -65,18 +65,18 @@ Main states:
| Main state | Role |
| ---------- | ------------------------------- |
| `intro` | Onboarding and opening sequence |
| `bike` | E-bike repair sequence |
| `pylone` | Power pylon repair sequence |
| `ferme` | Vertical farm repair sequence |
| `ebike` | E-bike repair sequence |
| `pylon` | Power pylon repair sequence |
| `farm` | Vertical farm repair sequence |
| `outro` | Ending sequence |
Other important state:
- `isCinematicPlaying`
- `intro`
- `bike`
- `pylone`
- `ferme`
- `ebike`
- `pylon`
- `farm`
- `outro`
Mission steps:
@@ -125,28 +125,28 @@ For development and debug tooling, direct setters also exist:
```ts
const setMainState = useGameStore((state) => state.setMainState);
setMainState("bike");
setMainState("ebike");
```
Direct setters are useful for debug panels, but production gameplay should prefer business actions such as:
- `advanceGameState`
- `completeBike`
- `completePylone`
- `completeFerme`
- `completeEbike`
- `completePylon`
- `completeFarm`
- `completeMission`
Mission gameplay that can target `bike`, `pylone`, or `ferme` should prefer generic mission actions:
Mission gameplay that can target `ebike`, `pylon`, or `farm` should prefer generic mission actions:
```ts
const setMissionStep = useGameStore((state) => state.setMissionStep);
const completeMission = useGameStore((state) => state.completeMission);
setMissionStep("bike", "inspected");
completeMission("bike");
setMissionStep("ebike", "inspected");
completeMission("ebike");
```
This keeps reusable gameplay components such as `RepairGame` from duplicating mission-specific branches like `setBikeState`, `setPyloneState`, and `setFermeState`.
This keeps reusable gameplay components such as `RepairGame` from duplicating mission-specific branches like `setEbikeState`, `setPylonState`, and `setFarmState`.
## Settings Store
@@ -160,7 +160,6 @@ State:
- `dialogueVolume`
- `subtitlesEnabled`
- `subtitleLanguage`
- `repairRuntime`
Audio setters clamp values between `0` and `1`, then call:
@@ -170,8 +169,6 @@ AudioManager.getInstance().setCategoryVolume(category, nextVolume);
This keeps UI state and browser audio state synchronized.
Current caveat: `repairRuntime` is stored and displayed in the settings menu, but the repair game does not consume it yet. Treat it as a staged architecture hook rather than an active runtime switch.
## Subtitle Store
`useSubtitleStore` is intentionally tiny.
@@ -191,9 +188,9 @@ State/actions:
Current production repair placement:
```tsx
<RepairGame mission="bike" position={[8, 0, -6]} />
<RepairGame mission="pylone" position={[64, 0, -66]} />
<RepairGame mission="ferme" position={[-24, 0, 42]} />
<RepairGame mission="ebike" position={[42.2399, 4.5484, 34.6468]} />
<RepairGame mission="pylon" position={[64, 0, -66]} />
<RepairGame mission="farm" position={[-24, 0, 42]} />
```
`RepairGame` reads the active mission step from the store and writes transitions through generic actions such as `setMissionStep` and `completeMission`.
@@ -222,13 +219,11 @@ Current overlays:
- `GameStateDebugPanel`: compact debug UI for viewing and switching main/sub states
- `Crosshair`: player aiming helper
- `InteractPrompt`: interaction prompt
- `RepairMovementLockIndicator`: indicator intended for repair movement lock
- `RepairMovementLockIndicator`: indicator shown while repair steps lock movement
- `HandTrackingVisualizer`: hand tracking SVG fallback/debug visualization
- `Subtitles`: active dialogue subtitle overlay
- `GameSettingsMenu`: options menu and settings controls
Current caveat: `useRepairMovementLocked()` returns `false` immediately on the current branch, so the movement-lock rule and indicator exist but are disabled at runtime.
## Regression Rules
- Do not store per-frame values in Zustand.
@@ -241,6 +236,4 @@ Current caveat: `useRepairMovementLocked()` returns `false` immediately on the c
## Next Steps
- Decide whether `repairRuntime` should be removed, implemented, or clearly labeled as experimental.
- Re-enable or remove the repair movement-lock rule depending on desired gameplay.
- Move broader mission orchestration into a clearer layer if intro, mission, dialogue, and cinematic branching grows.
+43 -14
View File
@@ -6,7 +6,7 @@ The map editor is available at `/editor`. It is a browser-based tool for editing
Use the editor when you need to:
- move, rotate, or scale objects from `public/map.json`
- move, rotate, scale, add, or delete objects from `public/map.json`
- inspect the raw JSON generated by the editor
- preview and edit cinematics from `public/cinematics.json`
- create, preview, and validate dialogue entries from `public/sounds/dialogue/dialogues.json`
@@ -14,13 +14,13 @@ Use the editor when you need to:
The map editor reads the same map data as the runtime scene:
- `public/map.json` contains the object list.
- `public/map.json` contains the current hierarchical runtime map.
- `public/models/{name}/model.glb` contains the matching 3D model for each object name. `model.gltf` is still supported as a fallback during migration.
- Missing models are displayed as gray fallback cubes, so incomplete maps remain editable.
## Map Node Format
Each entry in `public/map.json` represents one object:
`public/map.json` is hierarchical. Group nodes such as `Scene`, `blocking`, `vegetation`, or `agriculture` organize the map. Editable object nodes still use the same transform fields:
| Field | Description |
| ---------- | ------------------------------------------------- |
@@ -45,18 +45,34 @@ Only the `Editor` group is open by default. Open the other groups when you need
1. Open `/editor` in the local app.
2. Click an object in the scene to select it.
3. Choose a transform mode: translate, rotate, or scale.
4. Drag the transform gizmo in the 3D view.
5. Check the JSON inspector if you need exact values.
6. Use undo or redo if the transform is not correct.
7. Export the JSON or save it to the dev server.
3. Use `Shift + right click` on other objects to add or remove them from the current multi-selection.
4. Choose a transform mode: translate, rotate, or scale.
5. Drag the transform gizmo in the 3D view. With multiple objects selected, the gizmo transforms the selected group and writes each object transform back to `map.json`.
6. Keep `Snap terrain on move` enabled when placing objects on the terrain.
7. Use `Center on object` or `Reset camera` from the `View` section when navigating large maps.
8. Adjust scale numerically from the `Selection` section if the gizmo is not precise enough.
9. Check the JSON inspector if you need exact values.
10. Use undo or redo if the transform is not correct.
11. Export the JSON or save it to the dev server.
## Adding And Deleting Nodes
Use `Add Node` to create a new editable object under the `blocking` group.
- The new object starts as a fallback cube at `[0, 0, 0]`.
- Name it with the model folder name you want, for example `maison1`.
- If `public/models/{name}/model.glb` or `model.gltf` exists, saving and reloading will display the matching model.
- If no matching model exists, the node stays editable as a gray cube.
Use the trash button in `Selection` to delete the selected node from the map tree.
## Controls
| Action | Input |
| -------------------- | -------------------------- |
| Select object | Click object |
| Deselect | `Esc` or click empty space |
| Toggle multi-select | `Shift` + right click |
| Deselect | `Esc` |
| Lock selection | `Lock` button in Selection |
| Clear selection | `X` button in Selection |
| Translate mode | `T` |
@@ -73,14 +89,25 @@ Only the `Editor` group is open by default. Open the other groups when you need
The `Selection` section shows the selected object name and its index in `public/map.json`.
- Click an object to select it.
- Click empty space or press `Esc` to clear the selection.
- Use `Shift + right click` on objects to add or remove them from a multi-selection.
- When several objects are selected, the gizmo appears on the selection group and applies translate, rotate, or scale to each selected node.
- Press `Esc` to clear the selection.
- Use the `X` button to clear the selection explicitly.
- Use the `Lock` button to protect the current selection while editing.
- Use the scale fields to edit X/Y/Z scale precisely.
- Use the trash button to remove the selected object.
## Terrain Snapping
`Snap terrain on move` is enabled by default. When you move an object, the editor samples the terrain height at the object's X/Z position and updates its Y position.
This is intended for map objects that should sit on the ground. Disable it when you intentionally need a floating object.
`Lock terrain` is also enabled by default. The terrain stays visible, but terrain clicks are ignored so normal objects remain easier to select. Disable it only when you need to select or transform the terrain node itself.
When selection is locked:
- clicking another object does not change the selection
- clicking empty space does not clear the selection
- pressing `Esc` does not clear the selection
- the `X` button still clears the selection intentionally
@@ -88,9 +115,11 @@ When selection is locked:
The `Lock view` action switches the editor into a movement mode closer to the runtime player camera. Use it to navigate larger scenes while keeping the transform tools available.
The camera action switches between `Center on object` and `Reset camera`. Selecting an object also focuses the camera on that object automatically.
## JSON Inspector
The `JSON` section shows the raw map data that will be exported or saved:
The `JSON` section shows the editable node data:
- When no object is selected, it shows the full map node list.
- When an object is selected, it highlights the JSON lines for that object.
@@ -101,11 +130,11 @@ Use it to verify exact numeric transform values before saving or exporting. The
### Export JSON
`Export JSON` downloads the current map node list as `map.json`. Use this when you want to manually replace `public/map.json`.
`Export JSON` downloads the current hierarchical map tree as `map.json`. Use this when you want to manually replace `public/map.json`.
### Save To Server
`Save to server` is available only during local development. It writes the edited map back to `public/map.json` through the Vite dev-server endpoint.
`Save to server` is available only during local development. It writes the edited hierarchical map back to `public/map.json` through the Vite dev-server endpoint.
The button is hidden in production builds because production persistence is not implemented.
+11 -12
View File
@@ -37,7 +37,7 @@ This document lists the user-visible and developer-facing features implemented i
- Input lock while the settings menu is open
- Input lock while a cinematic is playing
- Octree collision against dedicated map collision nodes, currently scoped to the `terrain` node
- Repair movement-lock hook and indicator exist, but the hook currently returns `false`, so movement is not locked during repair on the current branch
- Repair movement lock during focused repair steps, with a matching UI indicator
## Physics And Collision
@@ -63,12 +63,12 @@ This document lists the user-visible and developer-facing features implemented i
## Repair Gameplay
- Reusable `RepairGame` mounted for `bike`, `pylone`, and `ferme`
- Reusable `RepairGame` mounted for `ebike`, `pylon`, and `farm`
- Mission progression driven by Zustand and shared `MissionStep` types
- Production repair positions:
- `bike` at `[8, 0, -6]`
- `pylone` at `[64, 0, -66]`
- `ferme` at `[-24, 0, 42]`
- `ebike` at `[42.2399, 4.5484, 34.6468]`
- `pylon` at `[64, 0, -66]`
- `farm` at `[-24, 0, 42]`
- Debug physics repair playground zones for all three missions
- Data-driven mission config in `src/data/gameplay/repairMissions.ts`
- Mission flow: `locked -> waiting -> inspected -> fragmented -> scanning -> repairing -> reassembling -> done`
@@ -80,9 +80,9 @@ This document lists the user-visible and developer-facing features implemented i
- Fragmentation through repair-case trigger or two-fists hand gesture
- Exploded model visualization through `ExplodableModel`
- Scan visual that steps through exploded parts
- Broken-part detection by configured `nodeName`, with fallback to first scanned parts
- Broken-part detection by configured `nodeName`, with diagnostics when configured parts are missing
- Persistent broken-part highlight and broken-part prompt after discovery
- Grabbable replacement part choices, including decoys
- Grabbable replacement part choices, including distractor parts
- Grabbable broken parts that must be deposited back into the case
- Snap-to-placeholder placement
- Correct-part, wrong-part, and stored-part visual feedback
@@ -95,7 +95,7 @@ This document lists the user-visible and developer-facing features implemented i
## Game Progression Store
- Zustand `useGameStore` for durable gameplay progression
- Main states: `intro`, `bike`, `pylone`, `ferme`, `outro`
- Main states: `intro`, `ebike`, `pylon`, `farm`, `outro`
- Per-mission repair step state
- Per-mission completion flags
- Generic mission helpers: `setMissionStep`, `completeMission`, `advanceGameState`, `rewindGameState`, `resetGame`
@@ -108,12 +108,11 @@ This document lists the user-visible and developer-facing features implemented i
- Music, SFX, and dialogue volume sliders
- Subtitle visibility toggle
- Subtitle language choice between French and English
- Repair-runtime choice between JavaScript and Python modes stored in settings
- Quit action that clears browser-accessible cookies and returns to `/`
- Crosshair overlay
- Interaction prompt
- Subtitle overlay
- Repair movement-lock indicator component, currently inactive because the lock hook returns `false`
- Repair movement-lock indicator
- Debug overlay layout
- Scene loading overlay
@@ -192,7 +191,7 @@ This document lists the user-visible and developer-facing features implemented i
- Debug game-state panel
- Debug hand-tracking panel
- Physics test scene with floor, grabbable object, trigger object, repair zones, and animated model preview
- Animated `electricienne_animated` model preview restored in the debug physics scene
- Animated `electricienne-animated` model preview restored in the debug physics scene
## Map And Content Editor
@@ -230,7 +229,7 @@ This document lists the user-visible and developer-facing features implemented i
- Technical docs for architecture, scene runtime, repair game, interaction, editor, audio, hand tracking, Zustand, Three debugging, animation, and target architecture
- User docs for implemented features, main feature, editor usage, and code-review preparation
## Not Implemented Or Incomplete
## Known Gaps
- Complete production mission manager/orchestrator
- Full mission HUD or minimap
+46
View File
@@ -0,0 +1,46 @@
# Galerie des modèles
La galerie est disponible sur `/gallery`. Elle permet de parcourir les modèles 3D présents dans `public/models/` sans lancer la boucle de gameplay principale.
## Objectif
Cette page sert à remercier et valoriser le travail des designers du projet La Fabrik. Chaque modèle est affiché dans un canvas dédié, avec la même skybox et le même lighting que l'expérience principale.
## Utilisation
1. Ouvrir `/gallery`.
2. Utiliser les flèches en bas de l'écran pour passer au modèle précédent ou suivant.
3. Tourner autour du modèle avec la souris ou le doigt.
4. Utiliser le bouton de réglages à droite pour ouvrir ou fermer le panneau lumière.
5. Lire le diagnostic texture discret pour savoir si le modèle chargé semble correct côté textures.
## Fonctionnement
- La liste des modèles est déclarée dans `src/data/galleryModels.ts`.
- Le viewer utilise `@react-three/fiber` et `@react-three/drei`.
- `OrbitControls` permet de manipuler la caméra autour du modèle.
- `Bounds` et `Center` recadrent automatiquement le modèle actif.
- `SkyModel` réutilise la skybox du jeu, avec un matériau non éclairé uniquement dans la galerie pour éviter que certaines faces deviennent noires avec une caméra orbitale libre.
- Les lumières reprennent les valeurs par défaut du jeu, puis peuvent être ajustées dans le panneau latéral.
- `OrbitControls` autorise une orbite verticale complète pour inspecter le dessous des modèles.
- Le viewer désactive les normal maps dans la preview pour limiter les coutures visibles sur certains exports découpés en plusieurs meshes.
- Les animations GLTF présentes dans un modèle sont lancées automatiquement.
- Un diagnostic simple inspecte les matériaux chargés pour signaler les textures absentes ou non exploitables.
## Ajouter un modèle
1. Ajouter le dossier du modèle dans `public/models/{nom}`.
2. Vérifier que le modèle possède un fichier chargeable, par exemple `model.gltf`, `model.glb` ou un nom explicite comme `potager.gltf`.
3. Ajouter une entrée dans `src/data/galleryModels.ts` avec un `id`, un `name` et un `path`.
Exemple :
```ts
{ id: "nouveau-modele", name: "Nouveau modèle", path: "/models/nouveau-modele/model.gltf" }
```
## Limites connues
- Le navigateur ne liste pas automatiquement les dossiers de `public/models/`, donc la liste reste déclarative.
- Les modèles très lourds peuvent prendre du temps à charger.
- La galerie est un viewer simple : elle ne remplace pas les outils d'inspection avancée comme Blender ou le viewer d'upload.
+10 -10
View File
@@ -8,7 +8,7 @@ The main feature is a reusable repair flow mounted in the production game scene.
The current user flow is:
1. Enter a mission state such as `bike`, `pylone`, or `ferme`.
1. Enter a mission state such as `ebike`, `pylon`, or `farm`.
2. Move close to the active repair object in the game scene.
3. Aim at the object and press the interaction key when prompted.
4. The mission step moves from `waiting` to `inspected`.
@@ -21,7 +21,7 @@ The current user flow is:
11. Move each scanned broken part into a compatible placeholder so the damaged parts are stored in the case.
12. Press `E` on the green install target to move to `reassembling`. Wrong parts turn the target red and cannot finish the repair.
13. The exploded object animates back into its assembled form with completion particles, then moves to `done`.
14. Press `E` on the completion target. The repair case closes, returns to the ground, disappears, then `completeMission` moves to the next mission or to `outro` after `ferme`.
14. Press `E` on the completion target. The repair case closes, returns to the ground, disappears, then `completeMission` moves to the next mission or to `outro` after `farm`.
## Why It Matters
@@ -33,17 +33,17 @@ For implementation details, see `docs/technical/repair-game.md`.
In `waiting`, the active mission renders its repair object and the `interagir.webm` prompt in the game scene. The interaction uses the shared focus/raycast interaction system, so the player still gets the normal `E` prompt.
When the player inspects the object, `RepairGame` writes `inspected` through the generic mission store action. The repair case then appears from the mission config with a small pop animation. When the player is close enough, the case model floats upward and rotates gently to signal interactivity. The codebase also contains a shared repair movement-lock hook and HTML indicator, but `useRepairMovementLocked()` currently returns `false`, so movement remains available during the repair flow on the current branch.
When the player inspects the object, `RepairGame` writes `inspected` through the generic mission store action. The repair case then appears from the mission config with a small pop animation. When the player is close enough, the case model floats upward and rotates gently to signal interactivity. The shared repair movement-lock hook and HTML indicator keep movement disabled during active repair steps.
In `inspected`, `RepairGame` can also move to `fragmented`. Keyboard input goes through the shared focus/raycast interaction system on the repair case, so the player must be close enough and aim at the case before pressing `E`. The hand-tracking path still uses a two-fists hold gesture and is state-based, so it does not depend on being inside a local object interaction radius.
In `fragmented`, the repair object is rendered with `ExplodableModel`, then automatically advances to `scanning`. In `scanning`, the exploded model remains visible, a blue scan visual moves from part to part, and a red halo/wire marker plus the configured broken UI video stay attached to configured broken parts after the scanner reaches them. The scan can match a specific `nodeName` when mission data provides one, otherwise it falls back to the first scanned parts as placeholder broken parts. In `repairing`, the case opens in a larger focused transform, `RepairCaseModel` traverses the case GLTF for empty nodes named `placeholder_*`, several grabbable replacement parts appear on those placeholder positions, and releasing a part near a placeholder snaps it into place with a short GSAP animation. Scanned broken parts are also rendered as grabbable objects and must be deposited into a compatible placeholder before the final install target validates. If `brokenParts[].placeholderName` is configured, that broken part snaps only to the matching placeholder; otherwise it can use any available placeholder. If the current case asset has no placeholder nodes, the flow keeps using fallback focus positions. Replacement parts show green or red placement feedback after snapping, broken parts show stored feedback after deposit, and the install target gives a short blocked feedback if the player tries to validate too early. The install target only validates when the configured correct replacement part is placed and all scanned broken parts have been deposited. In `reassembling`, the exploded model animates back into its assembled position with green completion particles before the flow moves to `done`. In `done`, the repaired object remains visible with a completion target; validating closes the repair case first, then plays the case exit animation before advancing the global mission progression.
In `fragmented`, the repair object is rendered with `ExplodableModel`, then automatically advances to `scanning`. In `scanning`, the exploded model remains visible, a blue scan visual moves from part to part, and a red halo/wire marker plus the configured broken UI video stay attached to configured broken parts after the scanner reaches them. The scan matches configured broken parts by `nodeName` and reports diagnostics when a configured node is missing. In `repairing`, the case opens in a larger focused transform, `RepairCaseModel` traverses the case GLTF for empty nodes named `placeholder_*`, several grabbable replacement parts appear on those slot positions, and releasing a part near a slot snaps it into place with a short GSAP animation. Scanned broken parts are also rendered as grabbable objects and must be deposited into a compatible slot before the final install target validates. If `brokenParts[].caseSlotName` is configured, that broken part snaps only to the matching slot; otherwise it can use any available slot. If the current case asset has no slot nodes, the flow keeps using fallback focus positions and logs the fallback. Replacement parts show green or red placement feedback after snapping, broken parts show stored feedback after deposit, and the install target gives a short blocked feedback if the player tries to validate too early. The install target only validates when the configured correct replacement part is placed and all scanned broken parts have been deposited. In `reassembling`, the exploded model animates back into its assembled position with green completion particles before the flow moves to `done`. In `done`, the repaired object remains visible with a completion target; validating closes the repair case first, then plays the case exit animation before advancing the global mission progression.
The mission config now carries the mission-specific variations. `bike` repairs one cooling core, `pylone` scans and stores both the lamp relay and a damaged panel with slower scan/reassembly timing, and `ferme` scans and stores an irrigation pump plus humidity sensor with faster scan/reassembly timing.
The mission config now carries the mission-specific variations. `ebike` repairs one cooling core, `pylon` scans and stores both the lamp relay and a damaged panel with slower scan/reassembly timing, and `farm` scans and stores an irrigation pump plus humidity sensor with faster scan/reassembly timing.
## Key Files
- `src/world/GameStageContent.tsx` mounts production `RepairGame` instances for `bike`, `pylone`, and `ferme`.
- `src/world/GameStageContent.tsx` mounts production `RepairGame` instances for `ebike`, `pylon`, and `farm`.
- `src/components/three/gameplay/RepairCompletionStep.tsx` renders the final repaired object, completion target, case exit animation, and mission UI prompt.
- `src/components/three/gameplay/RepairGame.tsx` composes the reusable production repair flow.
- `src/components/three/gameplay/RepairBrokenPartHighlight.tsx` renders the red halo and wire marker around detected broken parts during scanning.
@@ -56,16 +56,16 @@ The mission config now carries the mission-specific variations. `bike` repairs o
- `src/components/three/gameplay/RepairPromptVideo.tsx` renders `.webm` prompts inside the 3D scene.
- `src/components/three/gameplay/RepairScanSequence.tsx` keeps the exploded model visible and advances the scan from part to part.
- `src/components/three/gameplay/RepairScanVisual.tsx` renders the scan halo and scan line around the active part.
- `src/components/ui/RepairMovementLockIndicator.tsx` renders the HTML indicator intended for repair movement lock.
- `src/components/ui/RepairMovementLockIndicator.tsx` renders the HTML repair movement-lock indicator.
- `src/hooks/gameplay/useRepairFragmentationInput.ts` handles the `inspected -> fragmented` two-fists input and can optionally bind keyboard input for non-trigger flows.
- `src/hooks/gameplay/useRepairMissionStep.ts` reads the active mission step from the game store.
- `src/hooks/gameplay/useRepairMovementLocked.ts` exposes the shared repair movement-lock rule used by the player controller and UI indicator, but currently returns `false`.
- `src/hooks/gameplay/useRepairMovementLocked.ts` exposes the shared repair movement-lock rule used by the player controller and UI indicator.
- `src/hooks/handTracking/useBothFistsHold.ts` detects the reusable two-fists hold gesture.
- `src/components/three/gameplay/RepairCaseModel.tsx` renders and animates the case model, and exposes `placeholder_*` transforms when the GLTF provides them.
- `src/components/three/models/ExplodableModel.tsx` renders selectable models with split/exploded visualization.
- `src/data/gameplay/repairCaseConfig.ts` stores repair case model, sound, and animation constants.
- `src/data/gameplay/repairGameConfig.ts` stores repair flow timing constants.
- `src/data/gameplay/repairMissions.ts` stores reusable repair mission config for `bike`, `pylone`, and `ferme`.
- `src/data/gameplay/repairMissions.ts` stores reusable repair mission config for `ebike`, `pylon`, and `farm`.
- `src/managers/stores/useGameStore.ts` stores mission progression state and generic mission step helpers.
- `src/types/gameplay/repairMission.ts` contains shared repair mission ids, mission steps, and guards used by the store, data config, debug UI, and gameplay components.
@@ -73,7 +73,7 @@ The mission config now carries the mission-specific variations. `bike` repairs o
The production repair flow currently requires:
- the active `mainState` to be one of `bike`, `pylone`, or `ferme`
- the active `mainState` to be one of `ebike`, `pylon`, or `farm`
- `GameStageContent` mounted inside the game scene Rapier `Physics` boundary
- model assets available under `public/models/`
- sound assets available under `public/sounds/`
+1
View File
@@ -22,6 +22,7 @@
"react-markdown": "^10.1.0",
"remark-gfm": "^4.0.1",
"three": "0.182.0",
"three-stdlib": "^2.36.1",
"zustand": "^5.0.12"
},
"devDependencies": {
+2
View File
@@ -14,6 +14,7 @@
"lint:fix": "eslint . --fix",
"format": "prettier --write .",
"format:check": "prettier --check .",
"map:transform": "node scripts/transformMap.cjs",
"preview": "vite preview",
"typecheck": "tsc -b"
},
@@ -32,6 +33,7 @@
"react-markdown": "^10.1.0",
"remark-gfm": "^4.0.1",
"three": "0.182.0",
"three-stdlib": "^2.36.1",
"zustand": "^5.0.12"
},
"devDependencies": {
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.
Binary file not shown.
+40324 -35030
View File
File diff suppressed because it is too large Load Diff
+35051
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More