docs: detailed plan for 3D+Phong step in DRAFT.md
This commit is contained in:
+112
-3
@@ -1,7 +1,116 @@
|
||||
# DRAFT — Brouillon d'implémentation
|
||||
# DRAFT — Plan d'implémentation : « 3D + éclairage Phong »
|
||||
|
||||
> **Usage.** Ce fichier (dans `docs/`) sert de brouillon pour noter les idées et le plan détaillé de l'étape
|
||||
> en cours. **Son contenu est effacé au début de chaque nouvelle étape.** La source de vérité de l'état est
|
||||
> **Usage.** Ce fichier (dans `docs/`) sert de brouillon pour le plan détaillé de l'étape en cours.
|
||||
> **Son contenu est effacé au début de chaque nouvelle étape.** La source de vérité de l'état est
|
||||
> le code + README.md ; les autres docs `docs/*` restent stables.
|
||||
>
|
||||
> **Étape.** 3D + éclairage Phong (ROADMAP 1.3 + 1.5). Objectif MVP : **un mesh 3D éclairé à l'écran**,
|
||||
> rendu automatiquement par la boucle `App` (Scene auto-render déjà en place).
|
||||
>
|
||||
> **État de départ vérifié.**
|
||||
> - Rendu automatique fonctionnel mais **plat** : `basic_shader.wgsl` pose les positions telles quelles
|
||||
> (`vec4(position, 1.0)`), aucune matrice, aucune uniform, aucun éclairage.
|
||||
> - `Renderer::render_scene` parcourt `iter_entities()` en une passe (`&self`, `&Scene`), sans transform.
|
||||
> - `Scene::entities` : `HashMap<String, (mesh_id, material_id)>` — pas de `Transform` par entité.
|
||||
> - `Camera` (`resources/camera.rs`) : **fichier orphelin, non exporté** (absent de `resources/mod.rs`) ;
|
||||
> `Transform`/`Geometry` exportés via `math`.
|
||||
> - `PipelineCache::build_pipeline` : `bind_group_layouts: &[]`, `immediate_size: 0` — aucun binding.
|
||||
> - Défaut latente : `basic_shader.wgsl` déclare `@location(1) uv`, `(2) color` alors que le
|
||||
> `VertexBufferLayout` réel expose `(1) normal`, `(2) uv`, `(3) color`.
|
||||
|
||||
---
|
||||
|
||||
## Étape 1 — Fondations data : Transform + Camera exposées
|
||||
|
||||
**But** : donner à chaque entité un `Transform` et rendre `Camera` utilisable via l'API publique, **sans**
|
||||
toucher au rendu (pure façade de données, validable par compilation).
|
||||
|
||||
- [ ] 1.1 **Exporter `Camera`** : dans `lib/src/resources/mod.rs`, ajouter
|
||||
`pub mod camera;` et `pub use camera::Camera;` (aujourd'hui fichier orphelin non compilé).
|
||||
- [ ] 1.2 **Type `Entity` + transform** : nouvelle struct
|
||||
`Entity { mesh_id: String, material_id: String, transform: Transform }` (module `scene` ou `resources`).
|
||||
Remplacer `Scene::entities: HashMap<String, (String, String)>` par
|
||||
`HashMap<String, Entity>`. Sérialiser `iter_entities()` pour rendre le `&Transform`.
|
||||
- [ ] 1.3 **Compat API** : garder `add_entity(label, mesh_id, material_id)` (transform identité par défaut)
|
||||
+ ajouter `add_entity_with_transform(label, mesh_id, material_id, transform)`.
|
||||
Ajouter `entity_transform(label) -> Option<&Transform>` et `set_entity_transform(label, transform)`.
|
||||
- [ ] **Validation** : `cargo check --workspace` 0 warning ; `cargo doc --no-deps` 0 warning ; les exemples
|
||||
`simple`/`manual` compilent inchangés (défaut : identité ⇒ même rendu).
|
||||
|
||||
## Étape 2 — Shader Phong `standard_shader.wgsl`
|
||||
|
||||
**But** : produire un rendu 3D éclairé via un nouveau shader, sans encore le brancher.
|
||||
|
||||
- [ ] 2.1 **Créer `lib/src/shaders/standard_shader.wgsl`** avec le **contrat vertex correct** :
|
||||
`@location(0) position : vec3`, `(1) normal : vec3`, `(2) uv : vec2`, `(3) color : vec4`.
|
||||
- `@group(0) @binding(0)` : `FrameUniforms { view: mat4, proj: mat4, cam_pos: vec4, light_dir: vec4, light_color: vec4 }`
|
||||
- `@group(1) @binding(0)` : `ObjectUniform { model: mat4 }`
|
||||
- `vs_main` : `clip_position = proj * view * model * vec4(position,1)` ; passe `normal`/`color` en espace monde.
|
||||
- `fs_main` : éclairage hémisphérique (ambient) + diffuse directionnel (max(dot(N,L),0)), sortie `vec4(color*light, 1)`.
|
||||
- [ ] 2.2 **Constantes** : ajouter `STANDARD_SHADER_PATH = "assets/shaders/standard_shader.wgsl"` et
|
||||
`STANDARD_SHADER: &str = include_str!("../shaders/standard_shader.wgsl")` dans `lib/src/utils/conf.rs`.
|
||||
- [ ] 2.3 **Corriger le contrat du `basic_shader.wgsl`** (défaut latente) : aligner ses `@location` sur le
|
||||
`VertexBufferLayout` (position/normal/uv/color) pour que le rendu plat soit cohérent.
|
||||
- [ ] **Validation** : nouveau `shaders/mod.rs` si include_str le requiert ; `cargo check` OK (le shader n'est
|
||||
pas encore compilé par un pipeline tant que l'Étape 3 ne le charge pas).
|
||||
|
||||
## Étape 3 — Infrastructure uniforms dans le `PipelineCache`
|
||||
|
||||
**But** : permettre aux pipelines de recevoir des uniforms (bind groups) au lieu de `bind_group_layouts: &[]`.
|
||||
|
||||
- [ ] 3.1 **Types bytemuck `Pod`** (nouveau `lib/src/resources/uniform.rs`, ou `math/uniform.rs`) :
|
||||
- `#[repr(C)] #[derive(Pod, Zeroable, Copy, Clone)] FrameUniforms` (voir 2.1)
|
||||
- `#[repr(C)] #[derive(...)] ObjectUniform { model: Mat4 }`
|
||||
- (alignement 16 octets : utiliser `Vec4`/tableaux pour éviter le padding). Exporter via le `mod.rs` concerné.
|
||||
- [ ] 3.2 **Bind group layouts** : dans `build_pipeline`, créer 2 `BindGroupLayout`
|
||||
(frame @0 + object @1, chacun avec un buffer uniform `Vertex`/`Fragment`/`Vertex|Fragment` selon usage) et les
|
||||
passer dans `PipelineLayoutDescriptor.bind_group_layouts`. `immediate_size` reste 0 (pas de `var<immediate>`).
|
||||
- [ ] 3.3 **Compat** : le chemin `basic` (sans uniforms) continue de fonctionner soit via le même layout (bind
|
||||
groups optionnels), soit en gardant le pipeline sans layout pour les mathériaux non-3D. **Décision à acter.**
|
||||
- [ ] **Validation** : `cargo check` 0 warning ; `cargo doc` 0 warning (types documentés, `missing_docs` actif).
|
||||
|
||||
## Étape 4 — Rendu 3D dans le `Renderer`
|
||||
|
||||
**But** : `render_scene` applique matrices + éclairage par entité.
|
||||
|
||||
- [ ] 4.1 **Buffers frame partagés** : créer le `wgpu::Buffer` `FrameUniforms` + `BindGroup(0)` dans
|
||||
`Renderer::new` (ou à la 1re frame). Écrire chaque frame : view/proj (caméra active) + lumière.
|
||||
- [ ] 4.2 **Buffers object par entité** : `Renderer` maintient un cache
|
||||
`RefCell<HashMap<String, (wgpu::Buffer, wgpu::BindGroup)>>` clefé par label d'entité (créé à la 1re rencontre),
|
||||
car `render_scene(&self, &Scene)` est immuable. Chaque frame : écrire `ObjectUniform.world = entity.transform.to_matrix()` + `set_bind_group(1, ...)`.
|
||||
- [ ] 4.3 **Caméra active** : ajouter `scene.set_active_camera(Camera)` / `scene.active_camera() -> Option<&Camera>`.
|
||||
Calcul du `proj` avec l'aspect de la fenêtre (`window.inner_size()` accessible via `App.window`).
|
||||
- [ ] 4.4 **`draw_entity` étendu** : `set_bind_group(0, frame_bg)` + `set_bind_group(1, object_bg)` avant le draw.
|
||||
Le chemin bas-niveau `Renderer::render` peut prendre un `Transform`/uniform optionnel (ou rester non-éclairé).
|
||||
- [ ] **Validation** : `cargo check` 0 warning ; exécution `simple` (sans panique, boucle active) ;
|
||||
`manual` non-régressif (chemin bas-niveau).
|
||||
|
||||
## Étape 5 — Exemple 3D (cube éclairé)
|
||||
|
||||
**But** : démontrer l'objectif MVP à l'écran sans régression du modèle déclaratif plat.
|
||||
|
||||
- [ ] 5.1 **Nouvel exemple `lib/examples/cube.rs`** : cube unitaire (positions + normales), matériau
|
||||
`standard`, `Transform` non-identique, camera + lumière directionnelle, rotation dans `AppHandler::update`.
|
||||
Toujours via `AppBuilder`/scène automatique, **sans importer wgpu** (comme `simple`).
|
||||
- [ ] 5.2 Garder `simple.rs` (quad plat) et `manual.rs` (bas niveau) inchangés comme références.
|
||||
- [ ] **Validation** : compile + tourne sans panique ; rotation/éclairage visibles (à confirmer sur GPU/fenêtre).
|
||||
|
||||
## Étape 6 — Validation globale & docs
|
||||
|
||||
- [ ] 6.1 `cargo check --workspace` 0 warning ; `cargo doc --no-deps` 0 warning ; `cargo fmt --all`.
|
||||
- [ ] 6.2 Cas limites (comme à l'étape précédente) : scène vide, mesh non indexé, mesh 0-vertex.
|
||||
- [ ] 6.3 Mettre à jour `README.md` (statut 3D) + `docs/PLAN.md`/`docs/ROADMAP.md` (cases 1.3/1.5 actées).
|
||||
- [ ] 6.4 Commits conventionnels (`feat:`, `docs:`), diffs ciblés.
|
||||
|
||||
---
|
||||
|
||||
## Décisions à acter (à trancher avant l'implémentation)
|
||||
|
||||
| Décision | Option proposée | Justification |
|
||||
|----------|-----------------|---------------|
|
||||
| Schéma uniforms | 2 bind groups : frame partagé (@0) + object par entité (@1) | Simple, extensible ; évite `var<immediate>` (limites de taille, hazard) |
|
||||
| Cache object buffer | `RefCell<HashMap<label, (Buffer, BindGroup)>>` dans `Renderer` | `render_scene(&self)` immuable ; MVP petit nombre d'entités |
|
||||
| Transform dans l'entité | `Entity { mesh_id, material_id, transform }` + `add_entity_with_transform` | `add_entity` garde sa signature (transform identité) |
|
||||
| Layout pipeline non-3D | (à trancher — cf. 3.3) : layout commun avec bind groups optionnels OU pipeline `basic` distinct | À arbitrer selon la charge de travail |
|
||||
| Exemple démo | Nouvel exemple `cube.rs` (ne pas réécrire `simple.rs`) | `simple` reste le modèle déclaratif minimal |
|
||||
| Correction `basic_shader.wgsl` | Aligner les `@location` sur le vrai layout | Supprime une incohérence latente avant tout travail Phong |
|
||||
|
||||
Reference in New Issue
Block a user