feat(lib): expose Camera and Entity with Transform (data foundation)
Étape 1 du plan 3D+Phong : fondations de données sans toucher au rendu.
- resources: exporte Camera (fichier auparavant orphelin), migre les fonctions
glam dépréciées look_at_rh/perspective_rh_gl vers glam::camera::rh::* (induit
par la compilation de camera.rs, sinon 2 warnings).
- scene: nouveau type Entity { mesh_id, material_id, transform } (scene/entity.rs);
Scene::entities passe de HashMap<String,(String,String)> à HashMap<String,Entity>.
- API: add_entity conserve sa signature (transform identité par défaut), ajout de
add_entity_with_transform, entity_transform, set_entity_transform; iter_entities
rend désormais aussi le &Transform. renderer et examples inchangés a posteriori.
- Validation: cargo check --workspace et examples 0 warning, cargo doc 0 warning, fmt OK.
This commit is contained in:
+10
-6
@@ -25,17 +25,21 @@
|
||||
**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
|
||||
- [X] 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é). *(fait — 2026-09-16)*
|
||||
- [X] 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)
|
||||
*(fait — `lib/src/scene/entity.rs`)*
|
||||
- [X] 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).
|
||||
*(fait — 2026-09-16)*
|
||||
- [X] **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). *(fait — 0 warning. Au passage,
|
||||
`camera.rs` étant désormais compilée, les fonctions glam dépréciées `look_at_rh`/`perspective_rh_gl` ont été
|
||||
migrées vers `glam::camera::rh::view::look_at_mat4` / `glam::camera::rh::proj::opengl::perspective`.)*
|
||||
|
||||
## Étape 2 — Shader Phong `standard_shader.wgsl`
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ impl Renderer {
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
for (_label, mesh, material) in scene.iter_entities() {
|
||||
for (_label, mesh, material, _transform) in scene.iter_entities() {
|
||||
draw_entity(&mut render_pass, mesh, material);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
//! - `view_matrix()`: Computes the view matrix
|
||||
//! - `projection_matrix()`: Computes the projection matrix
|
||||
|
||||
use glam::{Vec3, Mat4};
|
||||
use glam::{Mat4, Vec3};
|
||||
|
||||
/// Represents a 3D camera for viewing the scene.
|
||||
///
|
||||
@@ -31,7 +31,11 @@ pub struct Camera {
|
||||
impl Camera {
|
||||
/// Creates a new camera with specified position, target, and up vector.
|
||||
pub fn new(position: Vec3, target: Vec3, up: Vec3) -> Self {
|
||||
Self { position, target, up }
|
||||
Self {
|
||||
position,
|
||||
target,
|
||||
up,
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the view matrix for this camera.
|
||||
@@ -39,7 +43,7 @@ impl Camera {
|
||||
/// # Returns
|
||||
/// A `Mat4` representing the view transformation matrix
|
||||
pub fn view_matrix(&self) -> Mat4 {
|
||||
Mat4::look_at_rh(self.position, self.target, self.up)
|
||||
glam::camera::rh::view::look_at_mat4(self.position, self.target, self.up)
|
||||
}
|
||||
|
||||
/// Computes the projection matrix for this camera.
|
||||
@@ -53,6 +57,6 @@ impl Camera {
|
||||
/// # Returns
|
||||
/// A `Mat4` representing the projection transformation matrix
|
||||
pub fn projection_matrix(&self, fov: f32, aspect: f32, near: f32, far: f32) -> Mat4 {
|
||||
Mat4::perspective_rh_gl(fov, aspect, near, far)
|
||||
glam::camera::rh::proj::opengl::perspective(fov, aspect, near, far)
|
||||
}
|
||||
}
|
||||
@@ -10,11 +10,13 @@
|
||||
//! - `mesh::new()` uploads Vertex arrays from CPU memory into GPU vertex buffers via DeviceExt::create_buffer_init().
|
||||
//! - `material::new()` requests RenderPipelines from PipelineCache during scene initialization.
|
||||
|
||||
pub mod camera;
|
||||
pub mod material;
|
||||
pub mod mesh;
|
||||
pub mod vertex;
|
||||
|
||||
// Re-exports
|
||||
pub use camera::Camera;
|
||||
pub use material::Material;
|
||||
pub use mesh::Mesh;
|
||||
pub use vertex::Vertex;
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
//! # Entity Module
|
||||
//!
|
||||
//! Defines `Entity`, the renderable association between a Mesh and a Material together with its
|
||||
//! own world-space `Transform`. Each entry of `Scene::entities` is an `Entity`: it references the
|
||||
//! resource by identifier while carrying the per-entity placement data.
|
||||
//!
|
||||
//! ## Interaction with Other Modules
|
||||
//! - `scene::Scene` stores entities in a `HashMap<String, Entity>` keyed by label.
|
||||
//! - `math::Transform` provides the placement (translation / rotation / scale) converted to a
|
||||
//! matrix during rendering.
|
||||
//! - `resources::{Mesh, Material}` are the referenced render resources, resolved by `Scene`.
|
||||
|
||||
use crate::math::Transform;
|
||||
|
||||
/// A renderable entity: a mesh + material pair with its own world-space transform.
|
||||
///
|
||||
/// Entities are created through [`crate::scene::Scene::add_entity`] (identity transform) or
|
||||
/// [`crate::scene::Scene::add_entity_with_transform`]. Fields are exposed via accessors.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Entity {
|
||||
/// Identifier of the referenced Mesh resource.
|
||||
mesh_id: String,
|
||||
/// Identifier of the referenced Material resource.
|
||||
material_id: String,
|
||||
/// World-space placement of this entity.
|
||||
transform: Transform,
|
||||
}
|
||||
|
||||
impl Entity {
|
||||
/// Creates a new entity associating a mesh and a material under the given transform.
|
||||
/// Called internally by `Scene::add_entity*` after resource existence is validated.
|
||||
pub fn new(
|
||||
mesh_id: impl Into<String>,
|
||||
material_id: impl Into<String>,
|
||||
transform: Transform,
|
||||
) -> Self {
|
||||
Self {
|
||||
mesh_id: mesh_id.into(),
|
||||
material_id: material_id.into(),
|
||||
transform,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the identifier of the referenced Mesh resource.
|
||||
pub fn mesh_id(&self) -> &str {
|
||||
&self.mesh_id
|
||||
}
|
||||
|
||||
/// Returns the identifier of the referenced Material resource.
|
||||
pub fn material_id(&self) -> &str {
|
||||
&self.material_id
|
||||
}
|
||||
|
||||
/// Returns a reference to this entity's world-space transform.
|
||||
pub fn transform(&self) -> &Transform {
|
||||
&self.transform
|
||||
}
|
||||
|
||||
/// Sets this entity's world-space transform.
|
||||
/// Called by `Scene::set_entity_transform` during dynamic updates.
|
||||
pub fn set_transform(&mut self, transform: Transform) {
|
||||
self.transform = transform;
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,9 @@
|
||||
//! all resources are declared before the render loop begins, while keeping the freedom to build the engine
|
||||
//! "brick by brick" through direct Context/PipelineCache/Renderer manipulation.
|
||||
|
||||
pub mod entity;
|
||||
pub mod scene;
|
||||
|
||||
// Re-export
|
||||
pub use entity::Entity;
|
||||
pub use scene::Scene;
|
||||
|
||||
+51
-12
@@ -10,20 +10,22 @@
|
||||
//! and avoiding borrow checker issues during dynamic updates.
|
||||
//! - **Ergonomie**: Users interact only with entity-level operations (add/remove/get) rather than wgpu buffers/pipelines directly.
|
||||
|
||||
use crate::math::Transform;
|
||||
use crate::resources::{Material, Mesh};
|
||||
use crate::scene::Entity;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Resource depot and entity graph. Stores Meshes and Materials keyed by identifier strings,
|
||||
/// and maps entity labels to their associated mesh+material pairs for rendering iteration.
|
||||
/// and maps entity labels to their associated `Entity` (mesh + material + transform) for rendering iteration.
|
||||
/// Created once during application setup; entities are added before the render loop starts.
|
||||
pub struct Scene {
|
||||
/// Map of mesh identifiers to owned `Arc<Mesh>` instances. Populated via `add_mesh()`.
|
||||
meshes: HashMap<String, Arc<Mesh>>,
|
||||
/// Map of material identifiers to owned `Arc<Material>` instances. Populated via `add_material()`.
|
||||
materials: HashMap<String, Arc<Material>>,
|
||||
/// Map of entity labels to (mesh_id, material_id) associations. Populated via `add_entity()`.
|
||||
entities: HashMap<String, (String, String)>,
|
||||
/// Map of entity labels to `Entity` associations. Populated via `add_entity()` / `add_entity_with_transform()`.
|
||||
entities: HashMap<String, Entity>,
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
@@ -59,17 +61,33 @@ impl Scene {
|
||||
Ok(id.to_string())
|
||||
}
|
||||
|
||||
/// Associates an entity label with a mesh and material pair for rendering iteration.
|
||||
/// Associates an entity label with a mesh and material pair for rendering iteration, using an identity transform.
|
||||
/// Inputs: label (entity identifier string), mesh_id (key into meshes map), material_id (key into materials map).
|
||||
/// Returns Ok(label) on success or Err(String) if either referenced resource does not exist.
|
||||
/// Called during scene initialization to build the renderable entity graph.
|
||||
/// Internal steps: 1) validate mesh_id exists → 2) validate material_id exists →
|
||||
/// 3) insert association into entities HashMap.
|
||||
/// 3) insert an `Entity` with identity transform into the entities HashMap.
|
||||
pub fn add_entity(
|
||||
&mut self,
|
||||
label: &str,
|
||||
mesh_id: &str,
|
||||
material_id: &str,
|
||||
) -> Result<String, String> {
|
||||
self.add_entity_with_transform(label, mesh_id, material_id, Transform::identity())
|
||||
}
|
||||
|
||||
/// Associates an entity label with a mesh and material pair together with an explicit world-space transform.
|
||||
/// Inputs: label (entity identifier string), mesh_id (key into meshes map), material_id (key into materials map),
|
||||
/// transform (world-space placement). Returns Ok(label) on success or Err(String) if either referenced resource does not exist.
|
||||
/// Called during scene initialization to build the renderable entity graph.
|
||||
/// Internal steps: 1) validate mesh_id exists → 2) validate material_id exists →
|
||||
/// 3) insert the `Entity` into the entities HashMap.
|
||||
pub fn add_entity_with_transform(
|
||||
&mut self,
|
||||
label: &str,
|
||||
mesh_id: &str,
|
||||
material_id: &str,
|
||||
transform: Transform,
|
||||
) -> Result<String, String> {
|
||||
if !self.meshes.contains_key(mesh_id) {
|
||||
return Err(format!("Mesh '{}' does not exist.", mesh_id));
|
||||
@@ -79,7 +97,7 @@ impl Scene {
|
||||
}
|
||||
self.entities.insert(
|
||||
label.to_string(),
|
||||
(mesh_id.to_string(), material_id.to_string()),
|
||||
Entity::new(mesh_id, material_id, transform),
|
||||
);
|
||||
Ok(label.to_string())
|
||||
}
|
||||
@@ -96,16 +114,37 @@ impl Scene {
|
||||
self.materials.get(id)
|
||||
}
|
||||
|
||||
/// Iterates all entity associations, yielding (label, mesh_ref, material_ref) triples.
|
||||
/// Iterates all entity associations, yielding (label, mesh_ref, material_ref, transform_ref) tuples.
|
||||
/// Called by the orchestrator during each render pass to draw every entity in order.
|
||||
pub fn iter_entities(&self) -> impl Iterator<Item = (&str, &Arc<Mesh>, &Arc<Material>)> + '_ {
|
||||
self.entities.iter().map(|(label, (mesh_id, mat_id))| {
|
||||
let mesh = self.meshes.get(mesh_id).unwrap(); // safe: add_entity validates existence
|
||||
let mat = self.materials.get(mat_id).unwrap(); // same invariant
|
||||
(label.as_str(), mesh, mat)
|
||||
pub fn iter_entities(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&str, &Arc<Mesh>, &Arc<Material>, &Transform)> + '_ {
|
||||
self.entities.iter().map(|(label, entity)| {
|
||||
let mesh = self.meshes.get(entity.mesh_id()).unwrap(); // safe: add_entity validates existence
|
||||
let mat = self.materials.get(entity.material_id()).unwrap(); // same invariant
|
||||
(label.as_str(), mesh, mat, entity.transform())
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a reference to the transform of the entity with the given label, if it exists.
|
||||
/// Called by the user to read an entity's current placement during updates.
|
||||
pub fn entity_transform(&self, label: &str) -> Option<&Transform> {
|
||||
self.entities.get(label).map(|e| e.transform())
|
||||
}
|
||||
|
||||
/// Overwrites the transform of the entity with the given label.
|
||||
/// Returns true if the entity existed and was updated, false otherwise.
|
||||
/// Called by the user to move/rotate/scale an entity during `AppHandler::update`.
|
||||
pub fn set_entity_transform(&mut self, label: &str, transform: Transform) -> bool {
|
||||
match self.entities.get_mut(label) {
|
||||
Some(entity) => {
|
||||
entity.set_transform(transform);
|
||||
true
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes an entity from the graph without freeing its underlying resources.
|
||||
/// The referenced Mesh and Material remain registered; only the association is dropped.
|
||||
/// Called during dynamic updates when an entity should be hidden or removed temporarily.
|
||||
|
||||
Reference in New Issue
Block a user