3 Commits

Author SHA1 Message Date
math-pixel 1ebc3069dd update: upload-gltf add a new model -> blocking
📦 Model
   model.gltf
2026-05-17 12:19:52 +00:00
Tom Boullay 214c2bdaa6 Merge branch 'develop' into design 2026-05-12 16:36:32 +02:00
Tom Boullay 8c52d2e84f update: new and actual models 2026-05-11 17:13:08 +02:00
512 changed files with 4943 additions and 81067 deletions
@@ -1,111 +0,0 @@
name: 🔁 Branch Promotions
on:
schedule:
- 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: branch-promotions
cancel-in-progress: false
jobs:
design-to-develop:
name: Open design → develop
runs-on: ubuntu-latest
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@v4
with:
fetch-depth: 0
- name: 🔁 Open promotion PR
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
git fetch origin develop design
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 develop \
--head "$GITHUB_REPOSITORY_OWNER:design" \
--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 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
-1
View File
@@ -143,7 +143,6 @@ 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/editor.md` | Editor implementation details |
| `docs/technical/animation.md` | Animated, explodable, and reusable 3D model components |
| `docs/user/features.md` | Implemented feature inventory |
-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
-51
View File
@@ -1,51 +0,0 @@
# 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.
## Main Bottlenecks Found
The most important signal is draw calls, not only triangle count.
| Model | Instances | Meshes / primitives | Notes |
| ---------------- | --------: | ------------------: | ---------------------------------------------------------------- |
| `generateur` | 3 | 3152 | Worst draw-call offender. Needs asset-side mesh merging. |
| `lafabrik` | 4 | 56 | Moderate draw calls, heavy 2048 texture set. |
| `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` is especially expensive because three visible instances can multiply thousands of primitives into thousands of draw calls. Instancing reduces repeated instance cost, but the source asset still needs a cleaner export.
## 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 `THREE.InstancedMesh`.
This keeps the existing map authoring format while reducing repeated draw calls for selected assets.
## Asset-Side Follow-Up
Design/export should prioritize:
1. Merge `generateur` meshes from 3152 primitives to a small number of material groups.
2. Reduce `lafabrik` texture count and downscale flat/low-detail maps.
3. Merge `ecole` primitives because it uses a single material.
4. 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.
-79
View File
@@ -1,79 +0,0 @@
# Mission Flow
This document describes the mission intro and mission 2 prototype flow after it was merged into the current architecture.
## Source Of Truth
Mission flow state lives in the global game store:
```txt
src/managers/stores/useGameStore.ts
```
The store owns the `missionFlow` slice:
```ts
missionFlow: {
step: GameStep;
activityCity: boolean;
playerName: string;
canMove: boolean;
dialogMessage: string | null;
}
```
This keeps global gameplay state in Zustand instead of splitting it across a separate mission store or a gameplay manager.
## Managers Boundary
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`.
## 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/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/player/PlayerController.tsx` reads `missionFlow.canMove` as an additional movement lock.
## Step Sequence
The prototype currently uses these steps:
```ts
"intro" |
"start-intro" |
"naming" |
"bienvenue" |
"star-move" |
"mission2" |
"searching" |
"helped" |
"manipulation" |
"outOfFabrik";
```
These steps are mission-flow prototype states. They do not replace `mainState` or the repair mission step machine used by `RepairGame`.
## Zone Configuration
Zone triggers live in:
```txt
src/data/zones.ts
```
Each zone has an id, position, radius, height, and `targetStep`. `ZoneDetection` marks a zone as triggered after the first activation so the same zone does not replay its transition every frame.
## Rules
- Keep mission flow state in `useGameStore.missionFlow`.
- Do not reintroduce `GameStepManager` for global state transitions.
- Do not create a second Zustand store for mission flow unless the state becomes independent from game progression.
- Keep side effects such as audio playback in components or service managers, but keep the state transition itself in the store.
- Keep per-frame values such as camera position and zone distance checks out of Zustand.
+1 -1
View File
@@ -7,7 +7,7 @@ import tseslint from "typescript-eslint";
import { defineConfig, globalIgnores } from "eslint/config";
export default defineConfig([
globalIgnores(["dist", "POC-grass"]),
globalIgnores(["dist"]),
{
files: ["**/*.{ts,tsx}"],
extends: [
Binary file not shown.
+3593 -39806
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.
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.
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.
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.
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.
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