refactor(resources): Scene owns pipeline cache, Mesh->Material link

Étape 7 (DRAFT): the PipelineCache moves into the Scene (SceneGpu holds
device + format + cache, wired via Scene::init_gpu in AppRunner::resumed),
so App no longer owns a cache field. Mesh now holds Option<Arc<Material>>
(with material()/set_material()/with_material()); Entity drops material_id
({mesh_id, transform}); iter_entities yields (label, &Mesh, &Transform);
Renderer::render_scene resolves the material from the mesh or the Scene's
lazy default_material(). New declarative Scene helpers: register_shader,
add_material_shader, create_mesh. cube/simple examples migrated; manual
remains low-level and unchanged.
This commit is contained in:
Jérôme Bousquié
2026-09-17 13:15:30 +02:00
parent df546a4ac9
commit 62b5cac11b
7 changed files with 261 additions and 113 deletions
+18 -19
View File
@@ -2,14 +2,14 @@
//!
//! Démonstration de l'objectif MVP du ROADMAP 1.3 + 1.5 : un mesh 3D avec éclairage Phong à l'écran.
//! On suit le workflow déclaratif (comme `simple`) : `AppBuilder` + scène automatique, **sans importer
//! wgpu**. La seule nouveauté déclarative est l'enregistrement du shader `standard` (Phong) au lieu de
//! `basic`. La caméra active par défaut (`Scene::default`, position (0,0,3), fov 45°) cadre le cube, et
//! wgpu**. Depuis l'Étape 7 la scène possède son `PipelineCache` : on passe par `register_shader` +
//! `add_material_shader` + `create_mesh` + `add_entity` (le matériau est lié au mesh, plus de material_id).
//! La caméra active par défaut (`Scene::default`, position (0,0,3), fov 45°) cadre le cube, et
//! `AppHandler::update` fait tourner l'entité via `set_entity_transform` chaque frame.
use glam::Quat;
use std::sync::Arc;
use wsg_lib::AppHandler;
use wsg_lib::app::AppBuilder;
use wsg_lib::resources::{Material, Mesh, Vertex};
use wsg_lib::resources::Vertex;
use wsg_lib::utils::WsgError;
/// Handler de démonstration : fait tourner le cube dans `update`.
@@ -80,25 +80,24 @@ fn cube_indices() -> Vec<u16> {
impl AppHandler for Cube {
fn setup(&mut self, app: &mut wsg_lib::App) {
let format = app.renderer().format();
// Shader Phong `standard` (porteur des bind groups frame + object) au lieu de `basic`.
app.cache()
// Shader Phong `standard` (porteur des bind groups frame + object). Depuis l'Étape 7 le
// PipelineCache vit dans la scène : `register_shader` / `add_material_shader` / `create_mesh`
// en sont la façade déclarative (le matériau est lié au mesh au moment du create_mesh).
app.scene
.register_shader("standard", wsg_lib::utils::STANDARD_SHADER_PATH)
.unwrap();
let mesh = Arc::new(Mesh::new(
app.renderer().device(),
&cube_vertices(),
Some(&cube_indices()),
));
let material = Arc::new(Material::new(format, "standard", app.cache()));
app.scene.add_mesh("cube_mesh", mesh).unwrap();
app.scene.add_material("cube_material", material).unwrap();
app.scene
.add_entity("cube", "cube_mesh", "cube_material")
.add_material_shader("cube_material", "standard")
.unwrap();
app.scene
.create_mesh(
"cube_mesh",
&cube_vertices(),
Some(&cube_indices()),
Some("cube_material"),
)
.unwrap();
app.scene.add_entity("cube", "cube_mesh").unwrap();
}
fn update(&mut self, app: &mut wsg_lib::App) {
+14 -17
View File
@@ -2,24 +2,23 @@
//! `AppBuilder` crée l'event loop puis `App::run` ouvre la fenêtre, construit le `Context`/`Renderer`
//! et fait tourner la boucle update → render → present. Depuis la migration winit 0.30, le GPU n'existe
//! qu'après `resumed` : c'est pourquoi l'enregistrement shader + la création mesh/matériau/entité vivent
//! dans le hook `AppHandler::setup`, appelé une fois le contexte prêt. La scène se rend automatiquement :
//! la méthode `render()` par défaut appelle `app.render_scene(frame.view())`.
use std::sync::Arc;
//! dans le hook `AppHandler::setup`, appelé une fois le contexte prêt. Depuis l'Étape 7 le PipelineCache
//! vit dans la scène (`Scene::init_gpu`, appelé dans `resumed`) : on passe par `register_shader` +
//! `add_material_shader` + `create_mesh` + `add_entity`, le matériau étant lié au mesh. La scène se rend
//! automatiquement : la méthode `render()` par défaut appelle `app.render_scene(frame.view())`.
use wsg_lib::AppHandler;
use wsg_lib::app::AppBuilder;
use wsg_lib::resources::{Material, Mesh, Vertex};
use wsg_lib::resources::Vertex;
use wsg_lib::utils::WsgError;
struct MonQuad;
impl AppHandler for MonQuad {
fn setup(&mut self, app: &mut wsg_lib::App) {
let format = app.renderer().format();
// Exemple 2D plat : le shader `standard` en mode **unlit** (options.x = 1) renvoie la couleur
// du vertex telle quelle. Ainsi le 2D est un cas particulier du 3D — un seul pipeline pour tous.
app.renderer_mut().set_unlit(true);
app.cache()
app.scene
.register_shader("standard", wsg_lib::utils::STANDARD_SHADER_PATH)
.unwrap();
let vertices = [
@@ -50,20 +49,18 @@ impl AppHandler for MonQuad {
];
let indices: [u16; 6] = [0, 1, 2, 0, 2, 3];
let mesh = Arc::new(Mesh::new(
app.renderer().device(),
&vertices,
Some(&indices),
));
let material = Arc::new(Material::new(format, "standard", app.cache()));
app.scene.add_mesh("quad_mesh", mesh).unwrap();
app.scene
.add_material("standard_material", material)
.add_material_shader("standard_material", "standard")
.unwrap();
app.scene
.add_entity("quad", "quad_mesh", "standard_material")
.create_mesh(
"quad_mesh",
&vertices,
Some(&indices),
Some("standard_material"),
)
.unwrap();
app.scene.add_entity("quad", "quad_mesh").unwrap();
}
}