docs: purge PLAN/ROADMAP des plans abandonnés, DRAFT vidé (Étape 10 finie)

La documentation reflète désormais uniquement ce qui *est* et ce qu'on
envisage de faire, pas ce qui aurait pu être (changements d'avis retirés) :

ROADMAP.md
- 1.2 : remplacé le récit de décision 'transform sur Mesh' (DRAFT Étape 8,
  déviation) par l'énoncé de l'état actuel : Mesh porte son matériau + sa
  Geometry CPU partagée, pas de transform (porté par Entity).
- Phase 2 réécrite : suppression des 'Arènes complètes' (SlotMap) et de la
  struct Entity { mesh_id, material_id } contredite par l'Étape 7 (material
  déplacé sur Mesh) et le choix String IDs ; migration 'handles typés'
  conservée comme unique pointeur (1.4 / Notes de Décision).
- 4.2 : 'Lumières hémisphériques' cochée (déjà dans standard_shader).
- Notes de Décision : retiré 'UVs en Phase 4' (caduque, UVs implémentés dès
  Geometry).

PLAN.md
- Statut réel condensé et mis à jour jusqu'à l'Étape 10 (textures).
- Phase 4 : Textures cochée (faite), Lumières laissée en plan (ROADMAP 4.2).
- Check-list pollster dé-obsolétisée ; suppression de la 'Note pour mémoire'
  décrivant le module-pivot exec.rs qu'on a décidé de ne pas construire.

DRAFT.md
- Vidé (fin de l'Étape 10) en préparation de l'étape suivante.
This commit is contained in:
Jérôme Bousquié
2026-09-18 15:01:27 +02:00
parent 23568e8820
commit acf819737d
3 changed files with 33 additions and 205 deletions
+4 -144
View File
@@ -1,145 +1,5 @@
# DRAFT — Étape 10 : Textures (Phase 4.1) # DRAFT — Étape suivante
> 📅 **Rédigé le 2026-09-18.** Plan à valider avant implémentation. > 📅 **Document vidé le 2026-09-18** (fin de l'Étape 10, Textures — Phase 4.1, bilan archivé
> Source de vérité = code + README.md. Ce document est vidé à la complétion de l'étape. > dans l'historique git). Ce fichier accueillera le plan de l'étape suivante.
> Source de vérité = code + README.md. Ce document est vidé à la complétion de chaque étape.
## Contexte (état de départ)
La Phase 4.1 (Textures) du ROADMAP vise à texturer le rendu. État actuel du code :
- **La tuyauterie UV existe déjà** : `Geometry.uvs: Option<Vec<[f32; 2]>>` (builder `with_uvs`)
et `Vertex.uv` (location 2, offset 24, stride 56) sont déclarés dans `build_pipeline` et lus
par le shader `VertexInput`. Seul l'**échantillonnage manque**.
- Le shader `standard_shader.wgsl` reçoit l'UV mais ne le transmet pas au fragment et n'échantillonne rien.
- **Aucun type `Texture`**, aucun sampler, aucun bind group de texture. `Material` ne porte pas de texture.
- Architecture bind group : « un seul layout pour tous » (Étape 3) — les groupes `frame @0`
+ `object @1` sont posés sur **toutes** les pipelines et bindés à chaque draw (`draw_entity`).
**Conclusion** : l'UV est prête ; les pièces manquantes sont un type `Texture`, un bind group de
texture (groupe 2) partagé, un échantillonnage dans le shader et le portage sur `Material`.
## Objectif
Permettre de texturer un mesh : charger une image → `wgpu::Texture` (+ view + sampler), lier une
texture diffuse à un `Material`, échantillonner dans le shader, **sans casser** le pattern « un seul
layout pour tous ».
## Décisions (actées le 2026-09-18)
- **[x] D1 (intégration bind group — « un seul layout pour tous »)** : on ajoute un **3ᵉ bind group**
`@group(2)` (sampler + texture diffuse) posé sur **toutes** les pipelines, avec une **texture
blanche 1×1 de secours (placeholder)** utilisée quand un `Material` n'a pas de texture. Cela
préserve l'unicité du layout (aucune pipeline multiple), donc un seul flux de rendu. *(alternative
écartée : bind group optionnel → casse l'unicité du layout, refactor de toutes les pipelines)*.
- **[x] D2 (échantillonnage inconditionnel)** : le fragment shader échantillonne **toujours** la
texture diffuse ; le placeholder blanc (texel = 1) reproduit exactement le comportement actuel
d'un material sans texture. Donc pas de flag conditionnel → un seul flow de shader. En mode lit,
le **texel remplace la couleur du vertex** (`texel.rgb * (ambient + diffuse)`) ; en unlit, le texel
tel quel. Symétrique du pattern unlit existant.
- **[x] D3 (chargement d'image & format)** : ajouter la dépendance `image` (décodage PNG/JPEG) à
`lib/Cargo.toml` ; upload en `TextureFormat::Rgba8UnormSrgb`, usage `TEXTURE_BINDING | COPY_DST`,
dimension `D2`, `mip_level_count: 1` (**YAGNI** : pas de génération de mipmaps cette étape),
sampler `filter: Linear`, `address_mode: Repeat`.
- **[x] D4 (API et portage)** : nouveau type `Texture` (device + view + sampler). `Material` gagne
`texture: Option<Arc<Texture>>` et détient son **bind group de texture (groupe 2)**, construit
depuis le layout partagé ; sans texture il lie le placeholder. API `Scene` : `add_texture(id, tex)`
et liaison d'une texture à un material. `draw_entity` bind `set_bind_group(2, ...)`.
## Plan d'implémentation
### 10.1 — Nouveau type `Texture` + dépendance `image`
- [x] Ajouter `image` à `lib/Cargo.toml` (features `png`, `jpeg`).
- [x] `resources::texture::Texture { texture: wgpu::Texture, view: wgpu::TextureView, sampler: wgpu::Sampler }`.
- [x] `Texture::from_bytes(device, queue, &[u8])` (ou `from_file`) : décode via `image`, remplit un
buffer RGBA et upload via `Queue::write_texture` (D3).
- [x] `Texture::white_placeholder(device, queue)` : 1×1 blanc, pour D1/D2.
- [x] Enregistrer `pub mod texture` dans `resources/mod.rs`.
### 10.2 — Bind group layout texture (groupe 2) partagé
- [x] `create_uniform_bind_group_layouts` retourne `[frame, object, texture]` (3 layouts) ; `texture`
= `BindingType::Sampler(Filtering)` (binding 0) + `Texture { sample_type: Float, view_dimension: D2 }`
(binding 1), visibilité fragment.
- [x] `build_pipeline` : ajouter le 3ᵉ layout au `PipelineLayoutDescriptor` (l'unicité du layout est
conservée — D1).
- [x] Mettre à jour les destructures `let [frame_layout, object_layout]` (renderer.rs) pour 3 éléments.
### 10.3 — Shader standard : UV → fragment + échantillonnage
- [x] `VertexOutput` : ajouter `@location(2) uv: vec2<f32>` ; `vs_main` écrit `out.uv = input.uv`.
- [x] Déclarer `@group(2) @binding(0) var texture_sampler: sampler;` et
`@group(2) @binding(1) var diffuse_texture: texture_2d<f32>;`.
- [x] `fs_main` : `let texel = textureSample(diffuse_texture, texture_sampler, in.uv);` — en lit →
`texel.rgb * (ambient + diffuse)`, en unlit → `texel` (D2). Mettre à jour la doc du module shader.
### 10.4 — `Material` porte la texture diffuse
- [x] `Material { texture: Option<Arc<Texture>>, texture_bind_group }` ; le constructeur construit le
bind group (groupe 2) depuis le layout partagé, avec placeholder si `None` (D1/D4).
- [x] Méthode `set_texture(...)` qui recrée le bind group si la texture change.
- [x] Le sampler/placeholder partagé est fourni par la lib (une seule instanciation) pour que tout
Material sans texture lie le blanc.
### 10.5 — API Scene & binding dans `draw_entity`
- [x] Rendre accessible un layout du groupe 2 aux Materials (via `SceneGpu` / cache) pour construire
leurs bind groups.
- [x] Route déclarative `Scene` : `add_texture(id, tex)` et liaison d'une texture par id à un material
(ex. `add_material_texture` ou paramètre de `add_material_shader`).
- [x] `draw_entity` : `pass.set_bind_group(2, material.texture_bind_group, &[])`.
- [x] Vérifier qu'un material *sans* texture continue de fonctionner (placeholder → aucune régression).
### 10.6 — Exemples + validation
- [x] `cube` : texturer le cube (image PNG embarquée via `include_bytes!` pour rester autonome,
ou un motif procédural RGBA généré en mémoire).
- [x] `simple`/`manual` : pas de régression (placeholder).
- [x] `cargo fmt --all`, `cargo check --workspace` (0 warning), `cargo test --workspace` (vert, tests de
layout uniform + validation WGSL à jour), `cargo doc` (pas de `missing_docs`).
- [x] Exécuter `cube` (faces texturées) et `simple` sans erreur backend.
## Point d'étape
- [x] Valider D1–D4 avant implémentation. *(actées le 2026-09-18)*
- [x] Caser 10.1–10.6, validation verte, exemples OK. *(terminé et vérifié le 2026-09-18)*
- [x] Deux commits séparés : `refactor(...)` (10.1–10.5) puis `docs(...)` (10.6) + README/DRAFT. *(refactor : 440f2df ; docs : à finaliser)*
- [x] Rédiger le bilan et ouvrir la suite (Phase 4.2 Éclairage avancé). *(2026-09-18)*
---
## Bilan — Étape 10 : Textures (Phase 4.1)
**Livrés (commits `refactor` 440f2df puis `docs`) :**
- **10.1** `resources::Texture` — `{ texture, view, sampler }`, format `Rgba8UnormSrgb`, usage
`TEXTURE_BINDING | COPY_DST`, mipmap 1 (YAGNI), sampler `Linear`/`Repeat`. Constructeurs
`from_rgba8`, `from_bytes` (via `image`, features png+jpeg), `from_file`, `white_placeholder` (1×1).
Dépendance `image = "0.25"` ajoutée à `lib/Cargo.toml`.
- **10.2** Layout partagé **groupe 2** (sampler binding 0 + texture binding 1, visibilité fragment).
`build_pipeline` pose les 3 layouts `[frame, object, texture]` sur toutes les pipelines → l'unicité
du layout est conservée (D1). `PipelineCache` détient le layout + le placeholder blanc partagé.
- **10.3** Shader standard : `VertexOutput.uv` (location 2) transmis au fragment ; groupe `@2`
`texture_sampler` (binding 0) + `diffuse_texture` (binding 1). Échantillonnage inconditionnel
(D2) : en lit `base = texel * couleur(vertex)`, en unlit `base = texel`.
- **10.4** `Material.texture: Option<Arc<Texture>>` + `texture_bind_group` construit au constructeur
via le cache (layout partagé + placeholder si aucune texture).
- **10.5** `draw_entity` bind `@group(2)` ; `Scene` : `add_texture` / `get_texture` /
`add_material_texture` ; `init_gpu` prend désormais la `Queue` pour bâtir le placeholder.
- **10.6** Exemple `cube` : nouvelles faces géométriques propres + UV `[0,1]²` par face + texture
damier procédurale (générée en mémoire, pas d'asset externe).
**Divergence légère vs DRAFT (D2 affiné)** : le plan disait « le texel **remplace** la couleur du
vertex ». À l'implémentation on multiplie (`base = texel.rgb * in.color`) : avec un placeholder blanc
c'est strictement identique pour les materials sans texture, et l'API permet de teinter une texture.
Aucun impact fonctionnel — documenté pour traçabilité.
**Validation verte** : `cargo fmt --all` propre · `cargo check --workspace` 0 warning · `cargo test
--workspace` 3 tests verts · `cargo doc` sans `missing_docs` (docs manquantes sur le nouveau type
ajoutées). Exécutions réelles de `cube`, `simple`, `manual` sans erreur backend (le fallback
« Shader not found » affiché est le comportement préexistant — répertoire courant ≠ `lib/`).
**Structure finale en mémoire** (`PipelineCache`) : champ `texture_bind_group_layout` + `placeholder`
(`Arc<Texture>` blanc 1×1), exposés via les méthodes `texture_bind_group_layout()`,
`placeholder()`, et `texture_bind_group(Option<Arc<Texture>>)` pour créer un groupe 2 depuis une
texture (ou le placeholder).
**Suite (Phase 4.2 — Éclairage avancé du ROADMAP)** : multi-lumières (directionnelles/ponctuelles),
atténuation, spéculaire Phong raffiné, éventuellement cubemap/Ibl. Ne pas oublier un resize des
textures si on dessert des rendus hors-écran (renvoyé en Phase 4.4 avec le depth).
---
_Fin du DRAFT Étape 10 — étape terminée le 2026-09-18._
+19 -36
View File
@@ -11,21 +11,19 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z }
Ce plan définit les étapes prioritaires pour finaliser l'architecture actuelle. L'objectif est de rendre l'API intuitive pour l'utilisateur standard tout en conservant la puissance de contrôle pour l'utilisateur avancé. Ce plan définit les étapes prioritaires pour finaliser l'architecture actuelle. L'objectif est de rendre l'API intuitive pour l'utilisateur standard tout en conservant la puissance de contrôle pour l'utilisateur avancé.
> **Statut réel (à jour au 2026-09-18).** Ce plan couvre la phase de *consolidation* passée ; la source > **Statut réel (à jour au 2026-09-18).** La phase de *consolidation* (Phases 1 à 3 de ce plan) est
> de vérité sur l'état actuel est **README.md** et le code. Depuis la révision du 2026-09-14, l'étape > **terminée** ; la source de vérité sur l'état actuel est **README.md** et le code. Depuis la révision
> **« Scene auto-render »** a été réalisée : le rendu de la `Scene` est **automatisé** en une seule > du 2026-09-14, le rendu de la `Scene` est automatisé en une passe groupée
> passe groupée via `App::render_scene(frame.view())` (appelée par défaut dans `AppHandler::render`), > (`App::render_scene(frame.view())`, appelée par défaut dans `AppHandler::render`) et `simple.rs`
> et `simple.rs` (API `AppBuilder`, sans `winit`/`wgpu`) déclare un quad rendu automatiquement. > (API `AppBuilder`, sans `winit`/`wgpu`) déclare un quad rendu automatiquement. Les étapes suivantes
> Le même jour (Étape 3 + 4, 2026-09-16) l'**infrastructure 3D** est en place : bind groups uniformes > ont ensuite : posé l'infrastructure 3D (bind groups uniformes frame+object partagés, caméra active,
> partagés (frame + object), caméra active dans la `Scene` (`Scene::set_camera`/`camera()`) écrite dans > matrices monde par entité — Étapes 3+4, 2026-09-16) ; atteint le **MVP 3D Phong** (Étape 5,
> le buffer frame chaque frame, matrices monde par entité. **Étape 5 (2026-09-17) : MVP 3D atteint** — > 2026-09-17 : l'exemple `cube` ; le 2D plat = variante **unlit** de `standard` via
> l'exemple `cube` rend un cube unitaire éclairé (Phong) en rotation via `App::render_scene` ; le > `Renderer::set_unlit`) ; rattaché le `PipelineCache` à la `Scene` et fait référencer son `Material`
> shader `basic` est supprimé, le 2D plat devient la **variante unlit** de `standard` > par chaque `Mesh` (Étape 7) ; donné à `Mesh` une source de vérité **CPU partagée**
> (`Renderer::set_unlit` / `app.renderer_mut().set_unlit(true)`). L'**Étape 7 (2026-09-17)** a rattaché > (`geometry: Arc<Geometry>`, Étape 8) ; activé un **depth buffer** sur toutes les passes (Étape 9) ;
> le `PipelineCache` à la `Scene` et fait référencer son `Material` par chaque `Mesh`. L'**Étape 8 > et ajouté les **textures diffuses** (Étape 10, 2026-09-18 : `resources::Texture` + bind group @2 +
> (2026-09-18)** a donné à `Mesh` une source de vérité **CPU partagée** (`geometry: Arc<Geometry>`) : > `Material.texture`).
> `Scene::create_mesh(id, Geometry, material)` déclare les meshes depuis une `Geometry` (avec couleurs),
> `Mesh::from_geometry` dérive ses buffers GPU, et les exemples cube/simple/manual utilisent `Geometry`.
## Phase 1 : Finalisation et Nettoyage de l'Existant (Priorité Absolue) ## Phase 1 : Finalisation et Nettoyage de l'Existant (Priorité Absolue)
@@ -82,8 +80,9 @@ Une fois la plomberie encapsulée, nous devons rendre l'assemblage des objets co
Une fois les phases 1 à 3 validées, nous pourrons introduire : Une fois les phases 1 à 3 validées, nous pourrons introduire :
- [ ] **Système de Lumières** : Ajout de buffers d'uniformes dans le PipelineCache. - [ ] **Système de Lumières** : Ajout de buffers d'uniformes dans le PipelineCache *(planifié — ROADMAP 4.2, Éclairage avancé)*.
- [ ] **Textures** : Intégration d'un module de chargement d'images et de BindGroups. - [X] **Textures** : Intégration d'un module de chargement d'images et de BindGroups *(fait 2026-09-18,
Étape 10, ROADMAP 4.1 : `resources::Texture`, bind group @2, `Material.texture`)*.
- [X] **Caméras** : Gestion des matrices de projection/vue dans la Scene *(fait 2026-09-16, Étape 4.3 — - [X] **Caméras** : Gestion des matrices de projection/vue dans la Scene *(fait 2026-09-16, Étape 4.3 —
`Scene::set_camera`/`camera()` porte une caméra active ; `render_scene` écrit view/proj/cam_pos réels `Scene::set_camera`/`camera()` porte une caméra active ; `render_scene` écrit view/proj/cam_pos réels
dans le buffer frame chaque frame, aspect calculé depuis la fenêtre)*. dans le buffer frame chaque frame, aspect calculé depuis la fenêtre)*.
@@ -95,22 +94,6 @@ Une fois les phases 1 à 3 validées, nous pourrons introduire :
exposée via `Frame::view()`, `render()` dessine la scène automatiquement en une passe via exposée via `Frame::view()`, `render()` dessine la scène automatiquement en une passe via
`App::render_scene(frame.view())`, la présentation est faite par `App::run`) `App::render_scene(frame.view())`, la présentation est faite par `App::run`)
- [X] Les modules sont-ils bien exposés via `lib.rs` ? - [X] Les modules sont-ils bien exposés via `lib.rs` ?
- [X] `pollster` est-il uniquement en dev-dependencies ? — **obsolète** : depuis la migration - [X] `pollster` est-il isolé de l'utilisateur final ? — résolu : depuis winit 0.30 (2026-09-16),
winit 0.30 (2026-09-16), `pollster` est en `dependencies` de la lib (le `block_on` d'init GPU `pollster` est en `dependencies` de la lib ; le `block_on` de l'init GPU est appelé une seule fois
est désormais appelé dans le code de la lib, `app.rs`, cf. note pour mémoire ci-dessous). dans `lib/src/app.rs` (`resumed()`). Les exemples compilent sans le connaître (crates séparées).
## Note pour mémoire : couplage au runtime async (pollster)
Depuis la migration winit 0.30, la lib embarque un runtime async pour l'init GPU. Le point de
couplage actuel est **unique** : `pollster::block_on(Context::new(...))` dans `lib/src/app.rs`
(`resumed()`), plus le macro `#[pollster::main]` dans les exemples (crates séparées, hors lib).
À ce stade (un seul appel), **on ne crée volontairement PAS d'abstraction** : ce serait du
sur-engineering pour un seul point d'appel. Mais si la lib acquiert d'autres appels async
(chargements / uploads GPU, etc.), il faudra isoler le runtime derrière un **module-pivot unique**
(`lib/src/exec.rs`, une fonction `block_on`), seul fichier à modifier pour basculer de pollster
vers tokio/futures-executor — le reste du code appelant `crate::exec::block_on(...)`.
Rappel : pollster et tokio sont des runtimes indépendants qui coexistent sans conflit dans un
même binaire ; la seule contre-indication est de faire un `pollster::block_on` **à l'intérieur**
d'un contexte async tokio (blocage imbriqué / deadlock possible).
+10 -25
View File
@@ -66,16 +66,9 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z }
sur `Geometry` (8.5). sur `Geometry` (8.5).
- [x] Ajouter un mesh de test (cube unitaire) en exemple — **fait** (helper `cube_geometry` dans l'exemple `cube`, Étape 5, 2026-09-17) - [x] Ajouter un mesh de test (cube unitaire) en exemple — **fait** (helper `cube_geometry` dans l'exemple `cube`, Étape 5, 2026-09-17)
> **Note (2026-09-17, DRAFT Étape 7)** : le volet *matériau* de `Mesh` a été fait en Étape 7 : > **État (2026-09-18)** : `Mesh` porte son matériau (`mesh.material: Option<Arc<Material>>`, Étape 7) et
> `Mesh.material: Option<Arc<Material>>` (cf. PLAN Phase 2, gestion des matériaux), indépendant de > une source de vérité CPU partagée (`geometry: Arc<Geometry>`, Étape 8). `Mesh` ne porte **pas** de
> la structure `Geometry`. > `transform` : un même mesh est partagé par plusieurs entités ; le `transform` vit sur `Entity`.
>
> **Décision **D3** (2026-09-18, DRAFT Étape 8)** : le ROADMAP listait `transform: Transform` sur `Mesh`.
> **Déviation validée : `transform` reste sur `Entity` et n'est PAS ajouté à `Mesh`.** Un mesh est
> **partagé** par plusieurs entités à des transforms différents (modèle instancé, Étape 4/7) : un
> `transform` unique sur `Mesh` casserait ce modèle. L'Étape 8 met donc en œuvre le refactor de
> *stockage CPU* (`Mesh.geometry: Arc<Geometry>` + buffers dérivés) **sans** le champ `transform`.
> *(Implémenté 2026-09-18.)*
### 1.3 Shader Phong Minimal ### 1.3 Shader Phong Minimal
- [x] Créer `standard_shader.wgsl` (Étape 2, 2026-09-16) : - [x] Créer `standard_shader.wgsl` (Étape 2, 2026-09-16) :
@@ -98,22 +91,15 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z }
--- ---
## Phase 2️⃣ — Système de Ressources complet ## Phase 2️⃣ — Scène enrichie
**Objectif** : Étoffer la Scene avec tous les types de ressources. **Objectif** : Étoffer la `Scene` au-delà du MVP.
### 2.1 Arènes complètes > Les ressources sont, pour le MVP, identifiées par **String IDs** (décision actée — voir Notes de
- [ ] `SlotMap<MaterialId, Material>` > Décision). Une migration vers des **handles typés** (slotmap générationnel) reste planifiée quand
- [ ] `SlotMap<TextureId, Texture>` (struct de base) > l'éviction/les performances le justifieront (voir 1.4, « reporté »).
- [ ] `SlotMap<LightId, Light>` (struct de base)
- [ ] `SlotMap<EntityId, Entity>` pour les entités de la scène
### 2.2 Entités & Hiérarchie ### 2.1 Camera dans la Scene
- [ ] Struct `Entity { mesh_id: Option<MeshId>, material_id: Option<MaterialId>, transform: Transform }`
- [ ] `Scene::add_entity()` → retourne `EntityId`
- [ ] `Scene::iter_entities()` → pour le render loop
### 2.3 Camera dans la Scene
- [x] Intégrer `Camera` comme ressource de la Scene (Étape 4.3 : `Scene::set_camera` / `camera()`, caméra active unique) - [x] Intégrer `Camera` comme ressource de la Scene (Étape 4.3 : `Scene::set_camera` / `camera()`, caméra active unique)
- [ ] Permettre plusieurs caméras (actuelle/inactive) et une sélection par identifiant (`scene.set_active_camera(camera_id)`) - [ ] Permettre plusieurs caméras (actuelle/inactive) et une sélection par identifiant (`scene.set_active_camera(camera_id)`)
- [ ] Exposer une caméra orbitale contrôlable (exemple final, Phase 5) - [ ] Exposer une caméra orbitale contrôlable (exemple final, Phase 5)
@@ -152,8 +138,8 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z }
- [x] `Material` supporte une texture diffuse *(Étape 10 : Material.texture + texture_bind_group, placeholder si None)* - [x] `Material` supporte une texture diffuse *(Étape 10 : Material.texture + texture_bind_group, placeholder si None)*
### 4.2 Éclairage avancé ### 4.2 Éclairage avancé
- [x] Lumières hémisphériques *(déjà dans le `standard_shader` : mélange hémisphérique, Étape 2)*
- [ ] Support multi-lumières (directionnelles, ponctuelles) - [ ] Support multi-lumières (directionnelles, ponctuelles)
- [ ] Lumières hémisphériques
- [ ] Shadows (optionnel) - [ ] Shadows (optionnel)
### 4.3 Optimisations ### 4.3 Optimisations
@@ -188,7 +174,6 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z }
| Décision | Raison | | Décision | Raison |
|----------|--------| |----------|--------|
| **Normals dès Phase 1** | Nécessaires pour le shader Phong ; sans elles, pas d'éclairage | | **Normals dès Phase 1** | Nécessaires pour le shader Phong ; sans elles, pas d'éclairage |
| **UVs en Phase 4** | Inutiles avant les textures ; on garde `Geometry` simple au départ |
| **BBox en Phase 3** | Utile uniquement pour le frustum culling GPU | | **BBox en Phase 3** | Utile uniquement pour le frustum culling GPU |
| **World Matrix CPU → MVP, GPU → Phase 3** | Le MVP est plus simple avec un uniform par mesh ; la migration GPU-driven est progressive | | **World Matrix CPU → MVP, GPU → Phase 3** | Le MVP est plus simple avec un uniform par mesh ; la migration GPU-driven est progressive |
| **String IDs pour le MVP, slotmap reporté** | Le code et le README utilisent des String IDs (simples, sûrs, figés avant la boucle de rendu) ; `ARCHI_ARENES.md` reste la cible "handles typés" pour plus tard. La dépendance `slotmap` a été retirée tant qu'elle est inutilisée | | **String IDs pour le MVP, slotmap reporté** | Le code et le README utilisent des String IDs (simples, sûrs, figés avant la boucle de rendu) ; `ARCHI_ARENES.md` reste la cible "handles typés" pour plus tard. La dépendance `slotmap` a été retirée tant qu'elle est inutilisée |