From 4a2362edf261ffd8a05bcf9c367ab1c490190536 Mon Sep 17 00:00:00 2001 From: yhh <359807859@qq.com> Date: Wed, 3 Dec 2025 16:20:13 +0800 Subject: [PATCH] =?UTF-8?q?feat(engine):=20=E6=B7=BB=E5=8A=A0=E6=9D=90?= =?UTF-8?q?=E8=B4=A8=E7=B3=BB=E7=BB=9F=E5=92=8C=E7=9D=80=E8=89=B2=E5=99=A8?= =?UTF-8?q?=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/engine/src/core/engine.rs | 174 ++++++++++++++ packages/engine/src/lib.rs | 150 +++++++++++- packages/engine/src/renderer/batch/mod.rs | 2 +- .../engine/src/renderer/batch/sprite_batch.rs | 71 ++++-- .../engine/src/renderer/material/manager.rs | 207 +++++++++++++++++ .../engine/src/renderer/material/material.rs | 177 ++++++++++++++ packages/engine/src/renderer/material/mod.rs | 10 + .../engine/src/renderer/material/uniform.rs | 185 +++++++++++++++ packages/engine/src/renderer/mod.rs | 3 + packages/engine/src/renderer/renderer2d.rs | 218 ++++++++++++++++-- .../engine/src/renderer/shader/manager.rs | 172 ++++++++++++++ packages/engine/src/renderer/shader/mod.rs | 2 + 12 files changed, 1321 insertions(+), 50 deletions(-) create mode 100644 packages/engine/src/renderer/material/manager.rs create mode 100644 packages/engine/src/renderer/material/material.rs create mode 100644 packages/engine/src/renderer/material/mod.rs create mode 100644 packages/engine/src/renderer/material/uniform.rs create mode 100644 packages/engine/src/renderer/shader/manager.rs diff --git a/packages/engine/src/core/engine.rs b/packages/engine/src/core/engine.rs index 1d5667f3..c0153157 100644 --- a/packages/engine/src/core/engine.rs +++ b/packages/engine/src/core/engine.rs @@ -185,6 +185,7 @@ impl Engine { texture_ids: &[u32], uvs: &[f32], colors: &[u32], + material_ids: &[u32], ) -> Result<()> { // Debug: log once use std::sync::atomic::{AtomicBool, Ordering}; @@ -199,6 +200,7 @@ impl Engine { texture_ids, uvs, colors, + material_ids, &self.texture_manager, ) } @@ -533,4 +535,176 @@ impl Engine { pub fn viewport_ids(&self) -> Vec { self.viewport_manager.viewport_ids().into_iter().cloned().collect() } + + // ===== Shader Management ===== + // ===== 着色器管理 ===== + + /// Compile and register a custom shader. + /// 编译并注册自定义着色器。 + pub fn compile_shader( + &mut self, + vertex_source: &str, + fragment_source: &str, + ) -> Result { + self.renderer.compile_shader(self.context.gl(), vertex_source, fragment_source) + } + + /// Compile a shader with a specific ID. + /// 使用特定ID编译着色器。 + pub fn compile_shader_with_id( + &mut self, + shader_id: u32, + vertex_source: &str, + fragment_source: &str, + ) -> Result<()> { + self.renderer.compile_shader_with_id(self.context.gl(), shader_id, vertex_source, fragment_source) + } + + /// Check if a shader exists. + /// 检查着色器是否存在。 + pub fn has_shader(&self, shader_id: u32) -> bool { + self.renderer.has_shader(shader_id) + } + + /// Remove a shader. + /// 移除着色器。 + pub fn remove_shader(&mut self, shader_id: u32) -> bool { + self.renderer.remove_shader(shader_id) + } + + // ===== Material Management ===== + // ===== 材质管理 ===== + + /// Create and register a new material. + /// 创建并注册新材质。 + pub fn create_material( + &mut self, + name: &str, + shader_id: u32, + blend_mode: u8, + ) -> u32 { + use crate::renderer::material::{Material, BlendMode}; + + let blend = match blend_mode { + 0 => BlendMode::None, + 1 => BlendMode::Alpha, + 2 => BlendMode::Additive, + 3 => BlendMode::Multiply, + 4 => BlendMode::Screen, + 5 => BlendMode::PremultipliedAlpha, + _ => BlendMode::Alpha, + }; + + let mut material = Material::with_shader(name, shader_id); + material.blend_mode = blend; + + self.renderer.register_material(material) + } + + /// Create a material with a specific ID. + /// 使用特定ID创建材质。 + pub fn create_material_with_id( + &mut self, + material_id: u32, + name: &str, + shader_id: u32, + blend_mode: u8, + ) { + use crate::renderer::material::{Material, BlendMode}; + + let blend = match blend_mode { + 0 => BlendMode::None, + 1 => BlendMode::Alpha, + 2 => BlendMode::Additive, + 3 => BlendMode::Multiply, + 4 => BlendMode::Screen, + 5 => BlendMode::PremultipliedAlpha, + _ => BlendMode::Alpha, + }; + + let mut material = Material::with_shader(name, shader_id); + material.blend_mode = blend; + + self.renderer.register_material_with_id(material_id, material); + } + + /// Check if a material exists. + /// 检查材质是否存在。 + pub fn has_material(&self, material_id: u32) -> bool { + self.renderer.has_material(material_id) + } + + /// Remove a material. + /// 移除材质。 + pub fn remove_material(&mut self, material_id: u32) -> bool { + self.renderer.remove_material(material_id) + } + + /// Set a material's float uniform. + /// 设置材质的浮点uniform。 + pub fn set_material_float(&mut self, material_id: u32, name: &str, value: f32) -> bool { + self.renderer.set_material_float(material_id, name, value) + } + + /// Set a material's vec2 uniform. + /// 设置材质的vec2 uniform。 + pub fn set_material_vec2(&mut self, material_id: u32, name: &str, x: f32, y: f32) -> bool { + if let Some(material) = self.renderer.get_material_mut(material_id) { + material.uniforms.set_vec2(name, x, y); + true + } else { + false + } + } + + /// Set a material's vec3 uniform. + /// 设置材质的vec3 uniform。 + pub fn set_material_vec3(&mut self, material_id: u32, name: &str, x: f32, y: f32, z: f32) -> bool { + if let Some(material) = self.renderer.get_material_mut(material_id) { + material.uniforms.set_vec3(name, x, y, z); + true + } else { + false + } + } + + /// Set a material's vec4 uniform. + /// 设置材质的vec4 uniform。 + pub fn set_material_vec4(&mut self, material_id: u32, name: &str, x: f32, y: f32, z: f32, w: f32) -> bool { + self.renderer.set_material_vec4(material_id, name, x, y, z, w) + } + + /// Set a material's color uniform. + /// 设置材质的颜色uniform。 + pub fn set_material_color(&mut self, material_id: u32, name: &str, r: f32, g: f32, b: f32, a: f32) -> bool { + if let Some(material) = self.renderer.get_material_mut(material_id) { + material.uniforms.set_color(name, r, g, b, a); + true + } else { + false + } + } + + /// Set a material's blend mode. + /// 设置材质的混合模式。 + pub fn set_material_blend_mode(&mut self, material_id: u32, blend_mode: u8) -> bool { + use crate::renderer::material::BlendMode; + + let blend = match blend_mode { + 0 => BlendMode::None, + 1 => BlendMode::Alpha, + 2 => BlendMode::Additive, + 3 => BlendMode::Multiply, + 4 => BlendMode::Screen, + 5 => BlendMode::PremultipliedAlpha, + _ => return false, + }; + + if let Some(material) = self.renderer.get_material_mut(material_id) { + material.blend_mode = blend; + true + } else { + false + } + } } diff --git a/packages/engine/src/lib.rs b/packages/engine/src/lib.rs index e4d61705..362a3fa3 100644 --- a/packages/engine/src/lib.rs +++ b/packages/engine/src/lib.rs @@ -142,6 +142,7 @@ impl GameEngine { /// * `texture_ids` - Uint32Array of texture IDs | 纹理ID数组 /// * `uvs` - Float32Array [u0, v0, u1, v1] per sprite | 每个精灵的UV坐标 /// * `colors` - Uint32Array of packed RGBA colors | 打包的RGBA颜色数组 + /// * `material_ids` - Uint32Array of material IDs (0 = default) | 材质ID数组(0 = 默认) #[wasm_bindgen(js_name = submitSpriteBatch)] pub fn submit_sprite_batch( &mut self, @@ -149,9 +150,10 @@ impl GameEngine { texture_ids: &[u32], uvs: &[f32], colors: &[u32], + material_ids: &[u32], ) -> std::result::Result<(), JsValue> { self.engine - .submit_sprite_batch(transforms, texture_ids, uvs, colors) + .submit_sprite_batch(transforms, texture_ids, uvs, colors, material_ids) .map_err(|e| JsValue::from_str(&e.to_string())) } @@ -463,4 +465,150 @@ impl GameEngine { pub fn get_viewport_ids(&self) -> Vec { self.engine.viewport_ids() } + + // ===== Shader API ===== + // ===== 着色器 API ===== + + /// Compile and register a custom shader. + /// 编译并注册自定义着色器。 + /// + /// # Arguments | 参数 + /// * `vertex_source` - Vertex shader GLSL source | 顶点着色器GLSL源代码 + /// * `fragment_source` - Fragment shader GLSL source | 片段着色器GLSL源代码 + /// + /// # Returns | 返回 + /// The shader ID for referencing this shader | 用于引用此着色器的ID + #[wasm_bindgen(js_name = compileShader)] + pub fn compile_shader( + &mut self, + vertex_source: &str, + fragment_source: &str, + ) -> std::result::Result { + self.engine + .compile_shader(vertex_source, fragment_source) + .map_err(|e| JsValue::from_str(&e.to_string())) + } + + /// Compile a shader with a specific ID. + /// 使用特定ID编译着色器。 + #[wasm_bindgen(js_name = compileShaderWithId)] + pub fn compile_shader_with_id( + &mut self, + shader_id: u32, + vertex_source: &str, + fragment_source: &str, + ) -> std::result::Result<(), JsValue> { + self.engine + .compile_shader_with_id(shader_id, vertex_source, fragment_source) + .map_err(|e| JsValue::from_str(&e.to_string())) + } + + /// Check if a shader exists. + /// 检查着色器是否存在。 + #[wasm_bindgen(js_name = hasShader)] + pub fn has_shader(&self, shader_id: u32) -> bool { + self.engine.has_shader(shader_id) + } + + /// Remove a shader. + /// 移除着色器。 + #[wasm_bindgen(js_name = removeShader)] + pub fn remove_shader(&mut self, shader_id: u32) -> bool { + self.engine.remove_shader(shader_id) + } + + // ===== Material API ===== + // ===== 材质 API ===== + + /// Create and register a new material. + /// 创建并注册新材质。 + /// + /// # Arguments | 参数 + /// * `name` - Material name for debugging | 材质名称(用于调试) + /// * `shader_id` - Shader ID to use | 使用的着色器ID + /// * `blend_mode` - Blend mode: 0=None, 1=Alpha, 2=Additive, 3=Multiply, 4=Screen, 5=PremultipliedAlpha + /// + /// # Returns | 返回 + /// The material ID for referencing this material | 用于引用此材质的ID + #[wasm_bindgen(js_name = createMaterial)] + pub fn create_material( + &mut self, + name: &str, + shader_id: u32, + blend_mode: u8, + ) -> u32 { + self.engine.create_material(name, shader_id, blend_mode) + } + + /// Create a material with a specific ID. + /// 使用特定ID创建材质。 + #[wasm_bindgen(js_name = createMaterialWithId)] + pub fn create_material_with_id( + &mut self, + material_id: u32, + name: &str, + shader_id: u32, + blend_mode: u8, + ) { + self.engine.create_material_with_id(material_id, name, shader_id, blend_mode); + } + + /// Check if a material exists. + /// 检查材质是否存在。 + #[wasm_bindgen(js_name = hasMaterial)] + pub fn has_material(&self, material_id: u32) -> bool { + self.engine.has_material(material_id) + } + + /// Remove a material. + /// 移除材质。 + #[wasm_bindgen(js_name = removeMaterial)] + pub fn remove_material(&mut self, material_id: u32) -> bool { + self.engine.remove_material(material_id) + } + + /// Set a material's float uniform. + /// 设置材质的浮点uniform。 + #[wasm_bindgen(js_name = setMaterialFloat)] + pub fn set_material_float(&mut self, material_id: u32, name: &str, value: f32) -> bool { + self.engine.set_material_float(material_id, name, value) + } + + /// Set a material's vec2 uniform. + /// 设置材质的vec2 uniform。 + #[wasm_bindgen(js_name = setMaterialVec2)] + pub fn set_material_vec2(&mut self, material_id: u32, name: &str, x: f32, y: f32) -> bool { + self.engine.set_material_vec2(material_id, name, x, y) + } + + /// Set a material's vec3 uniform. + /// 设置材质的vec3 uniform。 + #[wasm_bindgen(js_name = setMaterialVec3)] + pub fn set_material_vec3(&mut self, material_id: u32, name: &str, x: f32, y: f32, z: f32) -> bool { + self.engine.set_material_vec3(material_id, name, x, y, z) + } + + /// Set a material's vec4 uniform (also used for colors). + /// 设置材质的vec4 uniform(也用于颜色)。 + #[wasm_bindgen(js_name = setMaterialVec4)] + pub fn set_material_vec4(&mut self, material_id: u32, name: &str, x: f32, y: f32, z: f32, w: f32) -> bool { + self.engine.set_material_vec4(material_id, name, x, y, z, w) + } + + /// Set a material's color uniform (RGBA, 0.0-1.0). + /// 设置材质的颜色uniform(RGBA,0.0-1.0)。 + #[wasm_bindgen(js_name = setMaterialColor)] + pub fn set_material_color(&mut self, material_id: u32, name: &str, r: f32, g: f32, b: f32, a: f32) -> bool { + self.engine.set_material_color(material_id, name, r, g, b, a) + } + + /// Set a material's blend mode. + /// 设置材质的混合模式。 + /// + /// # Arguments | 参数 + /// * `blend_mode` - 0=None, 1=Alpha, 2=Additive, 3=Multiply, 4=Screen, 5=PremultipliedAlpha + #[wasm_bindgen(js_name = setMaterialBlendMode)] + pub fn set_material_blend_mode(&mut self, material_id: u32, blend_mode: u8) -> bool { + self.engine.set_material_blend_mode(material_id, blend_mode) + } } diff --git a/packages/engine/src/renderer/batch/mod.rs b/packages/engine/src/renderer/batch/mod.rs index 752b34a0..ae48ef8e 100644 --- a/packages/engine/src/renderer/batch/mod.rs +++ b/packages/engine/src/renderer/batch/mod.rs @@ -4,5 +4,5 @@ mod sprite_batch; mod vertex; -pub use sprite_batch::SpriteBatch; +pub use sprite_batch::{BatchKey, SpriteBatch}; pub use vertex::{SpriteVertex, VERTEX_SIZE}; diff --git a/packages/engine/src/renderer/batch/sprite_batch.rs b/packages/engine/src/renderer/batch/sprite_batch.rs index 45de8266..1526288f 100644 --- a/packages/engine/src/renderer/batch/sprite_batch.rs +++ b/packages/engine/src/renderer/batch/sprite_batch.rs @@ -27,6 +27,18 @@ const TRANSFORM_STRIDE: usize = 7; /// UV数据步长。 const UV_STRIDE: usize = 4; +/// Batch key combining material and texture IDs. +/// 组合材质ID和纹理ID的批次键。 +#[derive(Hash, Eq, PartialEq, Clone, Copy, Debug)] +pub struct BatchKey { + /// Material ID (0 = default material). + /// 材质ID(0 = 默认材质)。 + pub material_id: u32, + /// Texture ID. + /// 纹理ID。 + pub texture_id: u32, +} + /// Sprite batch renderer. /// 精灵批处理渲染器。 /// @@ -35,7 +47,7 @@ const UV_STRIDE: usize = 4; /// /// # Performance | 性能 /// - Uses dynamic vertex buffer for efficient updates | 使用动态顶点缓冲区以高效更新 -/// - Groups sprites by texture to minimize state changes | 按纹理分组精灵以最小化状态更改 +/// - Groups sprites by material and texture to minimize state changes | 按材质和纹理分组精灵以最小化状态更改 /// - Supports up to 10000+ sprites per batch | 每批次支持10000+精灵 pub struct SpriteBatch { /// Vertex array object. @@ -54,9 +66,9 @@ pub struct SpriteBatch { /// 最大精灵数。 max_sprites: usize, - /// Per-texture vertex data buffers. - /// 按纹理分组的顶点数据缓冲区。 - texture_batches: HashMap>, + /// Per-material-texture vertex data buffers. + /// 按材质和纹理分组的顶点数据缓冲区。 + batches: HashMap>, /// Total sprite count across all batches. /// 所有批次的总精灵数。 @@ -124,7 +136,7 @@ impl SpriteBatch { vbo, ibo, max_sprites, - texture_batches: HashMap::new(), + batches: HashMap::new(), sprite_count: 0, }) } @@ -192,7 +204,7 @@ impl SpriteBatch { /// Clear the batch for a new frame. /// 为新帧清空批处理。 pub fn clear(&mut self) { - for batch in self.texture_batches.values_mut() { + for batch in self.batches.values_mut() { batch.clear(); } self.sprite_count = 0; @@ -206,6 +218,7 @@ impl SpriteBatch { /// * `texture_ids` - Texture ID for each sprite | 每个精灵的纹理ID /// * `uvs` - [u0, v0, u1, v1] per sprite | 每个精灵的UV坐标 /// * `colors` - Packed RGBA color per sprite | 每个精灵的打包RGBA颜色 + /// * `material_ids` - Material ID for each sprite (0 = default) | 每个精灵的材质ID(0 = 默认) /// * `_texture_manager` - Texture manager for getting texture sizes | 纹理管理器 pub fn add_sprites( &mut self, @@ -213,6 +226,7 @@ impl SpriteBatch { texture_ids: &[u32], uvs: &[f32], colors: &[u32], + material_ids: &[u32], _texture_manager: &TextureManager, ) -> Result<()> { let sprite_count = texture_ids.len(); @@ -242,6 +256,14 @@ impl SpriteBatch { ))); } + if material_ids.len() != sprite_count { + return Err(EngineError::InvalidBatchData(format!( + "Material ID data length mismatch: expected {}, got {}", + sprite_count, + material_ids.len() + ))); + } + // Check capacity | 检查容量 if self.sprite_count + sprite_count > self.max_sprites { return Err(EngineError::InvalidBatchData(format!( @@ -250,7 +272,7 @@ impl SpriteBatch { ))); } - // Add each sprite grouped by texture | 按纹理分组添加每个精灵 + // Add each sprite grouped by material and texture | 按材质和纹理分组添加每个精灵 for i in 0..sprite_count { let t_offset = i * TRANSFORM_STRIDE; let uv_offset = i * UV_STRIDE; @@ -276,11 +298,14 @@ impl SpriteBatch { let width = scale_x; let height = scale_y; - let texture_id = texture_ids[i]; + let batch_key = BatchKey { + material_id: material_ids[i], + texture_id: texture_ids[i], + }; - // Get or create batch for this texture | 获取或创建此纹理的批次 - let batch = self.texture_batches - .entry(texture_id) + // Get or create batch for this material+texture combination | 获取或创建此材质+纹理组合的批次 + let batch = self.batches + .entry(batch_key) .or_insert_with(Vec::new); // Calculate transformed vertices and add to batch | 计算变换后的顶点并添加到批次 @@ -367,9 +392,9 @@ impl SpriteBatch { } } - /// Flush a specific texture batch to GPU and render. - /// 将特定纹理批次刷新到GPU并渲染。 - fn flush_texture_batch(&self, gl: &WebGl2RenderingContext, vertices: &[f32]) { + /// Flush a batch to GPU and render. + /// 将批次刷新到GPU并渲染。 + fn flush_batch(&self, gl: &WebGl2RenderingContext, vertices: &[f32]) { if vertices.is_empty() { return; } @@ -403,17 +428,17 @@ impl SpriteBatch { gl.bind_vertex_array(None); } - /// Get texture batches for rendering. - /// 获取用于渲染的纹理批次。 - pub fn texture_batches(&self) -> &HashMap> { - &self.texture_batches + /// Get all batches for rendering. + /// 获取所有批次用于渲染。 + pub fn batches(&self) -> &HashMap> { + &self.batches } - /// Flush a specific texture batch. - /// 刷新特定纹理批次。 - pub fn flush_for_texture(&self, gl: &WebGl2RenderingContext, texture_id: u32) { - if let Some(vertices) = self.texture_batches.get(&texture_id) { - self.flush_texture_batch(gl, vertices); + /// Flush a specific batch by key. + /// 按键刷新特定批次。 + pub fn flush_for_batch(&self, gl: &WebGl2RenderingContext, key: &BatchKey) { + if let Some(vertices) = self.batches.get(key) { + self.flush_batch(gl, vertices); } } diff --git a/packages/engine/src/renderer/material/manager.rs b/packages/engine/src/renderer/material/manager.rs new file mode 100644 index 00000000..a55ee5c9 --- /dev/null +++ b/packages/engine/src/renderer/material/manager.rs @@ -0,0 +1,207 @@ +//! Material manager for storing and retrieving materials. +//! 材质管理器,用于存储和检索材质。 + +use std::collections::HashMap; +use web_sys::WebGl2RenderingContext; + +use super::material::{Material, BlendMode}; + +/// Reserved material IDs for built-in materials. +/// 内置材质的保留ID。 +pub const MATERIAL_ID_DEFAULT: u32 = 0; +pub const MATERIAL_ID_ADDITIVE: u32 = 1; +pub const MATERIAL_ID_MULTIPLY: u32 = 2; +pub const MATERIAL_ID_UNLIT: u32 = 3; + +/// Material manager for creating and caching materials. +/// 材质管理器,用于创建和缓存材质。 +pub struct MaterialManager { + /// Stored materials indexed by ID. + /// 按ID索引的存储材质。 + materials: HashMap, + + /// Next available material ID for custom materials. + /// 下一个可用的自定义材质ID。 + next_material_id: u32, +} + +impl MaterialManager { + /// Create a new material manager with built-in materials. + /// 创建带有内置材质的新材质管理器。 + pub fn new() -> Self { + let mut manager = Self { + materials: HashMap::new(), + next_material_id: 100, // Reserve 0-99 for built-in materials + }; + + // Register built-in materials | 注册内置材质 + manager.materials.insert(MATERIAL_ID_DEFAULT, Material::sprite()); + manager.materials.insert(MATERIAL_ID_ADDITIVE, Material::additive()); + manager.materials.insert(MATERIAL_ID_MULTIPLY, Material::multiply()); + manager.materials.insert(MATERIAL_ID_UNLIT, Material::unlit()); + + log::info!("MaterialManager initialized with {} built-in materials | 材质管理器初始化完成,内置材质数量: {}", + manager.materials.len(), manager.materials.len()); + + manager + } + + /// Register a custom material. + /// 注册自定义材质。 + /// + /// # Returns | 返回 + /// The material ID for referencing this material | 用于引用此材质的ID + pub fn register_material(&mut self, material: Material) -> u32 { + let material_id = self.next_material_id; + self.next_material_id += 1; + + log::debug!("Registered material '{}' with ID: {} | 注册材质 '{}' ID: {}", + material.name, material_id, material.name, material_id); + + self.materials.insert(material_id, material); + material_id + } + + /// Register a material with a specific ID. + /// 使用特定ID注册材质。 + pub fn register_material_with_id(&mut self, material_id: u32, material: Material) { + log::debug!("Registered material '{}' with ID: {} | 注册材质 '{}' ID: {}", + material.name, material_id, material.name, material_id); + + self.materials.insert(material_id, material); + + // Update next_material_id if necessary + if material_id >= self.next_material_id { + self.next_material_id = material_id + 1; + } + } + + /// Get a material by ID. + /// 按ID获取材质。 + #[inline] + pub fn get_material(&self, material_id: u32) -> Option<&Material> { + self.materials.get(&material_id) + } + + /// Get a mutable material by ID. + /// 按ID获取可变材质。 + #[inline] + pub fn get_material_mut(&mut self, material_id: u32) -> Option<&mut Material> { + self.materials.get_mut(&material_id) + } + + /// Get the default material. + /// 获取默认材质。 + #[inline] + pub fn get_default_material(&self) -> &Material { + self.materials.get(&MATERIAL_ID_DEFAULT) + .expect("Default material should always exist | 默认材质应该始终存在") + } + + /// Check if a material exists. + /// 检查材质是否存在。 + #[inline] + pub fn has_material(&self, material_id: u32) -> bool { + self.materials.contains_key(&material_id) + } + + /// Remove a material. + /// 移除材质。 + /// + /// Note: Cannot remove built-in materials (ID < 100). + /// 注意:无法移除内置材质(ID < 100)。 + pub fn remove_material(&mut self, material_id: u32) -> bool { + if material_id < 100 { + log::warn!("Cannot remove built-in material: {} | 无法移除内置材质: {}", material_id, material_id); + return false; + } + + self.materials.remove(&material_id).is_some() + } + + /// Update a material's uniform value. + /// 更新材质的uniform值。 + pub fn set_material_float(&mut self, material_id: u32, name: &str, value: f32) -> bool { + if let Some(material) = self.materials.get_mut(&material_id) { + material.uniforms.set_float(name, value); + true + } else { + false + } + } + + /// Update a material's vec4 uniform. + /// 更新材质的vec4 uniform。 + pub fn set_material_vec4(&mut self, material_id: u32, name: &str, x: f32, y: f32, z: f32, w: f32) -> bool { + if let Some(material) = self.materials.get_mut(&material_id) { + material.uniforms.set_vec4(name, x, y, z, w); + true + } else { + false + } + } + + /// Apply blend mode to WebGL context. + /// 将混合模式应用到WebGL上下文。 + pub fn apply_blend_mode(gl: &WebGl2RenderingContext, blend_mode: BlendMode) { + match blend_mode { + BlendMode::None => { + gl.disable(WebGl2RenderingContext::BLEND); + } + BlendMode::Alpha => { + gl.enable(WebGl2RenderingContext::BLEND); + gl.blend_func( + WebGl2RenderingContext::SRC_ALPHA, + WebGl2RenderingContext::ONE_MINUS_SRC_ALPHA, + ); + } + BlendMode::Additive => { + gl.enable(WebGl2RenderingContext::BLEND); + gl.blend_func( + WebGl2RenderingContext::SRC_ALPHA, + WebGl2RenderingContext::ONE, + ); + } + BlendMode::Multiply => { + gl.enable(WebGl2RenderingContext::BLEND); + gl.blend_func( + WebGl2RenderingContext::DST_COLOR, + WebGl2RenderingContext::ZERO, + ); + } + BlendMode::Screen => { + gl.enable(WebGl2RenderingContext::BLEND); + gl.blend_func( + WebGl2RenderingContext::ONE, + WebGl2RenderingContext::ONE_MINUS_SRC_COLOR, + ); + } + BlendMode::PremultipliedAlpha => { + gl.enable(WebGl2RenderingContext::BLEND); + gl.blend_func( + WebGl2RenderingContext::ONE, + WebGl2RenderingContext::ONE_MINUS_SRC_ALPHA, + ); + } + } + } + + /// Get all material IDs. + /// 获取所有材质ID。 + pub fn material_ids(&self) -> Vec { + self.materials.keys().copied().collect() + } + + /// Get material count. + /// 获取材质数量。 + #[inline] + pub fn material_count(&self) -> usize { + self.materials.len() + } +} + +impl Default for MaterialManager { + fn default() -> Self { + Self::new() + } +} diff --git a/packages/engine/src/renderer/material/material.rs b/packages/engine/src/renderer/material/material.rs new file mode 100644 index 00000000..5958d6c8 --- /dev/null +++ b/packages/engine/src/renderer/material/material.rs @@ -0,0 +1,177 @@ +//! Material definition and properties. +//! 材质定义和属性。 + +use super::uniform::MaterialUniforms; + +/// Blend modes for material rendering. +/// 材质渲染的混合模式。 +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +pub enum BlendMode { + /// No blending, fully opaque | 无混合,完全不透明 + None, + /// Standard alpha blending | 标准透明度混合 + #[default] + Alpha, + /// Additive blending (good for glow effects) | 加法混合(适用于发光效果) + Additive, + /// Multiplicative blending (good for shadows) | 乘法混合(适用于阴影) + Multiply, + /// Screen blending (opposite of multiply) | 滤色混合(与乘法相反) + Screen, + /// Premultiplied alpha | 预乘透明度 + PremultipliedAlpha, +} + +/// Cull modes for material rendering. +/// 材质渲染的剔除模式。 +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +pub enum CullMode { + /// No face culling | 不剔除 + #[default] + None, + /// Cull front faces | 剔除正面 + Front, + /// Cull back faces | 剔除背面 + Back, +} + +/// Material definition for 2D rendering. +/// 2D渲染的材质定义。 +/// +/// A material combines a shader program with uniform parameters and render states. +/// 材质将着色器程序与uniform参数和渲染状态组合在一起。 +#[derive(Clone, Debug)] +pub struct Material { + /// Shader program ID | 着色器程序ID + pub shader_id: u32, + + /// Material uniform parameters | 材质uniform参数 + pub uniforms: MaterialUniforms, + + /// Blend mode | 混合模式 + pub blend_mode: BlendMode, + + /// Cull mode | 剔除模式 + pub cull_mode: CullMode, + + /// Depth test enabled | 是否启用深度测试 + pub depth_test: bool, + + /// Depth write enabled | 是否启用深度写入 + pub depth_write: bool, + + /// Material name (for debugging) | 材质名称(用于调试) + pub name: String, +} + +impl Default for Material { + fn default() -> Self { + Self { + shader_id: 0, // Default sprite shader + uniforms: MaterialUniforms::new(), + blend_mode: BlendMode::Alpha, + cull_mode: CullMode::None, + depth_test: false, + depth_write: false, + name: "Default".to_string(), + } + } +} + +impl Material { + /// Create a new material with default settings. + /// 使用默认设置创建新材质。 + pub fn new(name: &str) -> Self { + Self { + name: name.to_string(), + ..Default::default() + } + } + + /// Create a material with a specific shader. + /// 使用特定着色器创建材质。 + pub fn with_shader(name: &str, shader_id: u32) -> Self { + Self { + name: name.to_string(), + shader_id, + ..Default::default() + } + } + + /// Set the blend mode. + /// 设置混合模式。 + pub fn set_blend_mode(&mut self, mode: BlendMode) -> &mut Self { + self.blend_mode = mode; + self + } + + /// Set a float uniform. + /// 设置浮点uniform。 + pub fn set_float(&mut self, name: &str, value: f32) -> &mut Self { + self.uniforms.set_float(name, value); + self + } + + /// Set a vec2 uniform. + /// 设置vec2 uniform。 + pub fn set_vec2(&mut self, name: &str, x: f32, y: f32) -> &mut Self { + self.uniforms.set_vec2(name, x, y); + self + } + + /// Set a vec3 uniform. + /// 设置vec3 uniform。 + pub fn set_vec3(&mut self, name: &str, x: f32, y: f32, z: f32) -> &mut Self { + self.uniforms.set_vec3(name, x, y, z); + self + } + + /// Set a vec4 uniform. + /// 设置vec4 uniform。 + pub fn set_vec4(&mut self, name: &str, x: f32, y: f32, z: f32, w: f32) -> &mut Self { + self.uniforms.set_vec4(name, x, y, z, w); + self + } + + /// Set a color uniform (RGBA, 0.0-1.0). + /// 设置颜色uniform(RGBA,0.0-1.0)。 + pub fn set_color(&mut self, name: &str, r: f32, g: f32, b: f32, a: f32) -> &mut Self { + self.uniforms.set_color(name, r, g, b, a); + self + } +} + +// ============= Built-in material presets ============= +// ============= 内置材质预设 ============= + +impl Material { + /// Create a standard sprite material. + /// 创建标准精灵材质。 + pub fn sprite() -> Self { + Self::new("Sprite") + } + + /// Create an additive (glow) material. + /// 创建加法(发光)材质。 + pub fn additive() -> Self { + let mut mat = Self::new("Additive"); + mat.blend_mode = BlendMode::Additive; + mat + } + + /// Create a multiply (shadow) material. + /// 创建乘法(阴影)材质。 + pub fn multiply() -> Self { + let mut mat = Self::new("Multiply"); + mat.blend_mode = BlendMode::Multiply; + mat + } + + /// Create an unlit/opaque material. + /// 创建无光照/不透明材质。 + pub fn unlit() -> Self { + let mut mat = Self::new("Unlit"); + mat.blend_mode = BlendMode::None; + mat + } +} diff --git a/packages/engine/src/renderer/material/mod.rs b/packages/engine/src/renderer/material/mod.rs new file mode 100644 index 00000000..68bfa464 --- /dev/null +++ b/packages/engine/src/renderer/material/mod.rs @@ -0,0 +1,10 @@ +//! Material system for 2D rendering. +//! 2D渲染的材质系统。 + +mod material; +mod manager; +mod uniform; + +pub use material::{Material, BlendMode, CullMode}; +pub use manager::MaterialManager; +pub use uniform::{UniformValue, MaterialUniforms}; diff --git a/packages/engine/src/renderer/material/uniform.rs b/packages/engine/src/renderer/material/uniform.rs new file mode 100644 index 00000000..de7a7cf5 --- /dev/null +++ b/packages/engine/src/renderer/material/uniform.rs @@ -0,0 +1,185 @@ +//! Material uniform values and types. +//! 材质uniform值和类型。 + +use std::collections::HashMap; +use web_sys::{WebGl2RenderingContext, WebGlUniformLocation}; + +use crate::renderer::shader::ShaderProgram; + +/// Uniform value types supported by the material system. +/// 材质系统支持的uniform值类型。 +#[derive(Clone, Debug)] +pub enum UniformValue { + /// Single float value | 单精度浮点值 + Float(f32), + /// Two component vector | 二维向量 + Vec2([f32; 2]), + /// Three component vector | 三维向量 + Vec3([f32; 3]), + /// Four component vector (also used for colors) | 四维向量(也用于颜色) + Vec4([f32; 4]), + /// Single integer value | 整数值 + Int(i32), + /// 3x3 matrix | 3x3矩阵 + Mat3([f32; 9]), + /// 4x4 matrix | 4x4矩阵 + Mat4([f32; 16]), + /// Texture sampler slot | 纹理采样器槽位 + Sampler(i32), +} + +impl UniformValue { + /// Apply this uniform value to a shader. + /// 将此uniform值应用到着色器。 + pub fn apply(&self, gl: &WebGl2RenderingContext, location: &WebGlUniformLocation) { + match self { + UniformValue::Float(v) => { + gl.uniform1f(Some(location), *v); + } + UniformValue::Vec2(v) => { + gl.uniform2f(Some(location), v[0], v[1]); + } + UniformValue::Vec3(v) => { + gl.uniform3f(Some(location), v[0], v[1], v[2]); + } + UniformValue::Vec4(v) => { + gl.uniform4f(Some(location), v[0], v[1], v[2], v[3]); + } + UniformValue::Int(v) => { + gl.uniform1i(Some(location), *v); + } + UniformValue::Mat3(v) => { + gl.uniform_matrix3fv_with_f32_array(Some(location), false, v); + } + UniformValue::Mat4(v) => { + gl.uniform_matrix4fv_with_f32_array(Some(location), false, v); + } + UniformValue::Sampler(slot) => { + gl.uniform1i(Some(location), *slot); + } + } + } +} + +/// Collection of material uniform values. +/// 材质uniform值集合。 +#[derive(Clone, Debug, Default)] +pub struct MaterialUniforms { + /// Named uniform values | 命名的uniform值 + values: HashMap, +} + +impl MaterialUniforms { + /// Create empty uniforms collection. + /// 创建空的uniform集合。 + pub fn new() -> Self { + Self { + values: HashMap::new(), + } + } + + /// Set a uniform value. + /// 设置uniform值。 + pub fn set(&mut self, name: &str, value: UniformValue) { + self.values.insert(name.to_string(), value); + } + + /// Get a uniform value. + /// 获取uniform值。 + pub fn get(&self, name: &str) -> Option<&UniformValue> { + self.values.get(name) + } + + /// Remove a uniform value. + /// 移除uniform值。 + pub fn remove(&mut self, name: &str) -> Option { + self.values.remove(name) + } + + /// Check if a uniform exists. + /// 检查uniform是否存在。 + pub fn has(&self, name: &str) -> bool { + self.values.contains_key(name) + } + + /// Apply all uniforms to a shader program. + /// 将所有uniform应用到着色器程序。 + pub fn apply_to_shader(&self, gl: &WebGl2RenderingContext, shader: &ShaderProgram) { + for (name, value) in &self.values { + if let Some(location) = shader.get_uniform_location(gl, name) { + value.apply(gl, &location); + } + } + } + + /// Get all uniform names. + /// 获取所有uniform名称。 + pub fn names(&self) -> Vec<&String> { + self.values.keys().collect() + } + + /// Clear all uniforms. + /// 清除所有uniform。 + pub fn clear(&mut self) { + self.values.clear(); + } + + /// Get uniform count. + /// 获取uniform数量。 + pub fn len(&self) -> usize { + self.values.len() + } + + /// Check if empty. + /// 检查是否为空。 + pub fn is_empty(&self) -> bool { + self.values.is_empty() + } +} + +// ============= Convenience setters ============= +// ============= 便捷设置方法 ============= + +impl MaterialUniforms { + /// Set a float uniform. + /// 设置浮点uniform。 + pub fn set_float(&mut self, name: &str, value: f32) { + self.set(name, UniformValue::Float(value)); + } + + /// Set a vec2 uniform. + /// 设置vec2 uniform。 + pub fn set_vec2(&mut self, name: &str, x: f32, y: f32) { + self.set(name, UniformValue::Vec2([x, y])); + } + + /// Set a vec3 uniform. + /// 设置vec3 uniform。 + pub fn set_vec3(&mut self, name: &str, x: f32, y: f32, z: f32) { + self.set(name, UniformValue::Vec3([x, y, z])); + } + + /// Set a vec4 uniform (also used for colors). + /// 设置vec4 uniform(也用于颜色)。 + pub fn set_vec4(&mut self, name: &str, x: f32, y: f32, z: f32, w: f32) { + self.set(name, UniformValue::Vec4([x, y, z, w])); + } + + /// Set a color uniform (RGBA, 0.0-1.0). + /// 设置颜色uniform(RGBA,0.0-1.0)。 + pub fn set_color(&mut self, name: &str, r: f32, g: f32, b: f32, a: f32) { + self.set(name, UniformValue::Vec4([r, g, b, a])); + } + + /// Set an integer uniform. + /// 设置整数uniform。 + pub fn set_int(&mut self, name: &str, value: i32) { + self.set(name, UniformValue::Int(value)); + } + + /// Set a texture sampler uniform. + /// 设置纹理采样器uniform。 + pub fn set_sampler(&mut self, name: &str, slot: i32) { + self.set(name, UniformValue::Sampler(slot)); + } +} diff --git a/packages/engine/src/renderer/mod.rs b/packages/engine/src/renderer/mod.rs index cef5c6d9..1054cd2d 100644 --- a/packages/engine/src/renderer/mod.rs +++ b/packages/engine/src/renderer/mod.rs @@ -4,6 +4,7 @@ pub mod batch; pub mod shader; pub mod texture; +pub mod material; mod renderer2d; mod camera; @@ -18,3 +19,5 @@ pub use texture::{Texture, TextureManager}; pub use grid::GridRenderer; pub use gizmo::{GizmoRenderer, TransformMode}; pub use viewport::{RenderTarget, ViewportManager, ViewportConfig}; +pub use shader::{ShaderManager, ShaderProgram, SHADER_ID_DEFAULT_SPRITE}; +pub use material::{Material, MaterialManager, BlendMode, CullMode, UniformValue, MaterialUniforms}; diff --git a/packages/engine/src/renderer/renderer2d.rs b/packages/engine/src/renderer/renderer2d.rs index 1a55e148..0348865f 100644 --- a/packages/engine/src/renderer/renderer2d.rs +++ b/packages/engine/src/renderer/renderer2d.rs @@ -8,7 +8,8 @@ use crate::core::error::Result; use crate::resource::TextureManager; use super::batch::SpriteBatch; use super::camera::Camera2D; -use super::shader::{ShaderProgram, SPRITE_VERTEX_SHADER, SPRITE_FRAGMENT_SHADER}; +use super::shader::ShaderManager; +use super::material::MaterialManager; /// 2D renderer with batched sprite rendering. /// 带批处理精灵渲染的2D渲染器。 @@ -20,9 +21,13 @@ pub struct Renderer2D { /// 精灵批处理渲染器。 sprite_batch: SpriteBatch, - /// Sprite shader program. - /// 精灵Shader程序。 - shader: ShaderProgram, + /// Shader manager. + /// 着色器管理器。 + shader_manager: ShaderManager, + + /// Material manager. + /// 材质管理器。 + material_manager: MaterialManager, /// 2D camera. /// 2D相机。 @@ -31,6 +36,16 @@ pub struct Renderer2D { /// Clear color (RGBA). /// 清除颜色 (RGBA)。 clear_color: [f32; 4], + + /// Current active shader ID. + /// 当前激活的着色器ID。 + #[allow(dead_code)] + current_shader_id: u32, + + /// Current active material ID. + /// 当前激活的材质ID。 + #[allow(dead_code)] + current_material_id: u32, } impl Renderer2D { @@ -42,7 +57,8 @@ impl Renderer2D { /// * `max_sprites` - Maximum sprites per batch | 每批次最大精灵数 pub fn new(gl: &WebGl2RenderingContext, max_sprites: usize) -> Result { let sprite_batch = SpriteBatch::new(gl, max_sprites)?; - let shader = ShaderProgram::new(gl, SPRITE_VERTEX_SHADER, SPRITE_FRAGMENT_SHADER)?; + let shader_manager = ShaderManager::new(gl)?; + let material_manager = MaterialManager::new(); // Get canvas size for camera | 获取canvas尺寸用于相机 let canvas = gl.canvas() @@ -59,9 +75,12 @@ impl Renderer2D { Ok(Self { sprite_batch, - shader, + shader_manager, + material_manager, camera, clear_color: [0.1, 0.1, 0.12, 1.0], + current_shader_id: 0, + current_material_id: 0, }) } @@ -73,6 +92,7 @@ impl Renderer2D { /// * `texture_ids` - Texture ID for each sprite | 每个精灵的纹理ID /// * `uvs` - UV coordinates for each sprite | 每个精灵的UV坐标 /// * `colors` - Packed color for each sprite | 每个精灵的打包颜色 + /// * `material_ids` - Material ID for each sprite (0 = default) | 每个精灵的材质ID(0 = 默认) /// * `texture_manager` - Texture manager | 纹理管理器 pub fn submit_batch( &mut self, @@ -80,6 +100,7 @@ impl Renderer2D { texture_ids: &[u32], uvs: &[f32], colors: &[u32], + material_ids: &[u32], texture_manager: &TextureManager, ) -> Result<()> { self.sprite_batch.add_sprites( @@ -87,6 +108,7 @@ impl Renderer2D { texture_ids, uvs, colors, + material_ids, texture_manager, ) } @@ -94,34 +116,61 @@ impl Renderer2D { /// Render the current frame. /// 渲染当前帧。 pub fn render(&mut self, gl: &WebGl2RenderingContext, texture_manager: &TextureManager) -> Result<()> { + use super::batch::BatchKey; + if self.sprite_batch.sprite_count() == 0 { return Ok(()); } - // Bind shader | 绑定Shader - self.shader.bind(gl); - - // Set projection matrix | 设置投影矩阵 - let projection = self.camera.projection_matrix(); - self.shader.set_uniform_mat3(gl, "u_projection", &projection.to_cols_array()); - - // Set texture sampler | 设置纹理采样器 - self.shader.set_uniform_i32(gl, "u_texture", 0); - - // Render each texture batch | 渲染每个纹理批次 - // Only collect non-empty batches | 只收集非空批次 - let texture_ids: Vec = self.sprite_batch.texture_batches() + // Collect non-empty batch keys | 收集非空批次键 + let batch_keys: Vec = self.sprite_batch.batches() .iter() .filter(|(_, vertices)| !vertices.is_empty()) - .map(|(id, _)| *id) + .map(|(key, _)| *key) .collect(); - for texture_id in texture_ids { - // Bind texture for this batch | 绑定此批次的纹理 - texture_manager.bind_texture(texture_id, 0); + // Track current state to minimize state changes | 跟踪当前状态以最小化状态切换 + let mut current_material_id: u32 = u32::MAX; + let mut current_texture_id: u32 = u32::MAX; - // Flush this texture's sprites | 刷新此纹理的精灵 - self.sprite_batch.flush_for_texture(gl, texture_id); + // Get projection matrix once | 一次性获取投影矩阵 + let projection = self.camera.projection_matrix(); + + for batch_key in batch_keys { + // Switch material if needed | 如需切换材质 + if batch_key.material_id != current_material_id { + current_material_id = batch_key.material_id; + + // Get material (fallback to default if not found) | 获取材质(未找到则回退到默认) + let material = self.material_manager.get_material(batch_key.material_id) + .unwrap_or_else(|| self.material_manager.get_default_material()); + + // Bind shader | 绑定Shader + let shader = self.shader_manager.get_shader(material.shader_id) + .unwrap_or_else(|| self.shader_manager.get_default_shader()); + shader.bind(gl); + + // Apply blend mode | 应用混合模式 + MaterialManager::apply_blend_mode(gl, material.blend_mode); + + // Set projection matrix | 设置投影矩阵 + shader.set_uniform_mat3(gl, "u_projection", &projection.to_cols_array()); + + // Set texture sampler | 设置纹理采样器 + shader.set_uniform_i32(gl, "u_texture", 0); + + // Apply material uniforms | 应用材质uniform + material.uniforms.apply_to_shader(gl, shader); + } + + // Switch texture if needed | 如需切换纹理 + if batch_key.texture_id != current_texture_id { + current_texture_id = batch_key.texture_id; + texture_manager.bind_texture(batch_key.texture_id, 0); + } + + // Flush this batch | 刷新此批次 + self.sprite_batch.flush_for_batch(gl, &batch_key); } // Clear batch for next frame | 清空批处理以供下一帧使用 @@ -161,4 +210,123 @@ impl Renderer2D { pub fn resize(&mut self, width: f32, height: f32) { self.camera.set_viewport(width, height); } + + // ============= Shader Management ============= + // ============= 着色器管理 ============= + + /// Compile and register a custom shader. + /// 编译并注册自定义着色器。 + /// + /// # Returns | 返回 + /// The shader ID for referencing this shader | 用于引用此着色器的ID + pub fn compile_shader( + &mut self, + gl: &WebGl2RenderingContext, + vertex_source: &str, + fragment_source: &str, + ) -> Result { + self.shader_manager.compile_shader(gl, vertex_source, fragment_source) + } + + /// Compile a shader with a specific ID. + /// 使用特定ID编译着色器。 + pub fn compile_shader_with_id( + &mut self, + gl: &WebGl2RenderingContext, + shader_id: u32, + vertex_source: &str, + fragment_source: &str, + ) -> Result<()> { + self.shader_manager.compile_shader_with_id(gl, shader_id, vertex_source, fragment_source) + } + + /// Check if a shader exists. + /// 检查着色器是否存在。 + pub fn has_shader(&self, shader_id: u32) -> bool { + self.shader_manager.has_shader(shader_id) + } + + /// Remove a shader. + /// 移除着色器。 + pub fn remove_shader(&mut self, shader_id: u32) -> bool { + self.shader_manager.remove_shader(shader_id) + } + + /// Get shader manager reference. + /// 获取着色器管理器引用。 + pub fn shader_manager(&self) -> &ShaderManager { + &self.shader_manager + } + + /// Get mutable shader manager reference. + /// 获取可变着色器管理器引用。 + pub fn shader_manager_mut(&mut self) -> &mut ShaderManager { + &mut self.shader_manager + } + + // ============= Material Management ============= + // ============= 材质管理 ============= + + /// Register a custom material. + /// 注册自定义材质。 + /// + /// # Returns | 返回 + /// The material ID for referencing this material | 用于引用此材质的ID + pub fn register_material(&mut self, material: super::material::Material) -> u32 { + self.material_manager.register_material(material) + } + + /// Register a material with a specific ID. + /// 使用特定ID注册材质。 + pub fn register_material_with_id(&mut self, material_id: u32, material: super::material::Material) { + self.material_manager.register_material_with_id(material_id, material); + } + + /// Get a material by ID. + /// 按ID获取材质。 + pub fn get_material(&self, material_id: u32) -> Option<&super::material::Material> { + self.material_manager.get_material(material_id) + } + + /// Get a mutable material by ID. + /// 按ID获取可变材质。 + pub fn get_material_mut(&mut self, material_id: u32) -> Option<&mut super::material::Material> { + self.material_manager.get_material_mut(material_id) + } + + /// Check if a material exists. + /// 检查材质是否存在。 + pub fn has_material(&self, material_id: u32) -> bool { + self.material_manager.has_material(material_id) + } + + /// Remove a material. + /// 移除材质。 + pub fn remove_material(&mut self, material_id: u32) -> bool { + self.material_manager.remove_material(material_id) + } + + /// Set a material's float uniform. + /// 设置材质的浮点uniform。 + pub fn set_material_float(&mut self, material_id: u32, name: &str, value: f32) -> bool { + self.material_manager.set_material_float(material_id, name, value) + } + + /// Set a material's vec4 uniform. + /// 设置材质的vec4 uniform。 + pub fn set_material_vec4(&mut self, material_id: u32, name: &str, x: f32, y: f32, z: f32, w: f32) -> bool { + self.material_manager.set_material_vec4(material_id, name, x, y, z, w) + } + + /// Get material manager reference. + /// 获取材质管理器引用。 + pub fn material_manager(&self) -> &MaterialManager { + &self.material_manager + } + + /// Get mutable material manager reference. + /// 获取可变材质管理器引用。 + pub fn material_manager_mut(&mut self) -> &mut MaterialManager { + &mut self.material_manager + } } diff --git a/packages/engine/src/renderer/shader/manager.rs b/packages/engine/src/renderer/shader/manager.rs new file mode 100644 index 00000000..1c4115cd --- /dev/null +++ b/packages/engine/src/renderer/shader/manager.rs @@ -0,0 +1,172 @@ +//! Shader manager for runtime shader compilation and caching. +//! 着色器管理器,用于运行时着色器编译和缓存。 + +use std::collections::HashMap; +use web_sys::WebGl2RenderingContext; + +use crate::core::error::Result; +use super::program::ShaderProgram; +use super::builtin::{SPRITE_VERTEX_SHADER, SPRITE_FRAGMENT_SHADER}; + +/// Reserved shader IDs for built-in shaders. +/// 内置着色器的保留ID。 +pub const SHADER_ID_DEFAULT_SPRITE: u32 = 0; + +/// Shader manager for compiling and caching shader programs. +/// 着色器管理器,用于编译和缓存着色器程序。 +/// +/// Manages multiple shader programs, allowing runtime compilation of custom shaders. +/// 管理多个着色器程序,允许运行时编译自定义着色器。 +pub struct ShaderManager { + /// Compiled shader programs indexed by ID. + /// 按ID索引的已编译着色器程序。 + shaders: HashMap, + + /// Next available shader ID for custom shaders. + /// 下一个可用的自定义着色器ID。 + next_shader_id: u32, + + /// Shader source cache for hot-reloading (optional). + /// 着色器源代码缓存,用于热重载(可选)。 + shader_sources: HashMap, +} + +impl ShaderManager { + /// Create a new shader manager with built-in shaders. + /// 创建带有内置着色器的新着色器管理器。 + /// + /// # Arguments | 参数 + /// * `gl` - WebGL2 context | WebGL2上下文 + pub fn new(gl: &WebGl2RenderingContext) -> Result { + let mut manager = Self { + shaders: HashMap::new(), + next_shader_id: 100, // Reserve 0-99 for built-in shaders + shader_sources: HashMap::new(), + }; + + // Compile built-in sprite shader | 编译内置精灵着色器 + let default_shader = ShaderProgram::new(gl, SPRITE_VERTEX_SHADER, SPRITE_FRAGMENT_SHADER)?; + manager.shaders.insert(SHADER_ID_DEFAULT_SPRITE, default_shader); + manager.shader_sources.insert( + SHADER_ID_DEFAULT_SPRITE, + (SPRITE_VERTEX_SHADER.to_string(), SPRITE_FRAGMENT_SHADER.to_string()), + ); + + log::info!("ShaderManager initialized with {} built-in shaders | 着色器管理器初始化完成,内置着色器数量: {}", + manager.shaders.len(), manager.shaders.len()); + + Ok(manager) + } + + /// Compile and register a custom shader program. + /// 编译并注册自定义着色器程序。 + /// + /// # Arguments | 参数 + /// * `gl` - WebGL2 context | WebGL2上下文 + /// * `vertex_source` - Vertex shader GLSL source | 顶点着色器GLSL源代码 + /// * `fragment_source` - Fragment shader GLSL source | 片段着色器GLSL源代码 + /// + /// # Returns | 返回 + /// The shader ID for referencing this shader | 用于引用此着色器的ID + pub fn compile_shader( + &mut self, + gl: &WebGl2RenderingContext, + vertex_source: &str, + fragment_source: &str, + ) -> Result { + let shader = ShaderProgram::new(gl, vertex_source, fragment_source)?; + let shader_id = self.next_shader_id; + self.next_shader_id += 1; + + self.shaders.insert(shader_id, shader); + self.shader_sources.insert( + shader_id, + (vertex_source.to_string(), fragment_source.to_string()), + ); + + log::debug!("Custom shader compiled with ID: {} | 自定义着色器编译完成,ID: {}", shader_id, shader_id); + + Ok(shader_id) + } + + /// Compile and register a shader with a specific ID. + /// 使用特定ID编译并注册着色器。 + /// + /// # Arguments | 参数 + /// * `gl` - WebGL2 context | WebGL2上下文 + /// * `shader_id` - Desired shader ID | 期望的着色器ID + /// * `vertex_source` - Vertex shader GLSL source | 顶点着色器GLSL源代码 + /// * `fragment_source` - Fragment shader GLSL source | 片段着色器GLSL源代码 + pub fn compile_shader_with_id( + &mut self, + gl: &WebGl2RenderingContext, + shader_id: u32, + vertex_source: &str, + fragment_source: &str, + ) -> Result<()> { + let shader = ShaderProgram::new(gl, vertex_source, fragment_source)?; + + // Remove old shader if exists | 如果存在则移除旧着色器 + self.shaders.remove(&shader_id); + + self.shaders.insert(shader_id, shader); + self.shader_sources.insert( + shader_id, + (vertex_source.to_string(), fragment_source.to_string()), + ); + + log::debug!("Shader compiled with ID: {} | 着色器编译完成,ID: {}", shader_id, shader_id); + + Ok(()) + } + + /// Get a shader program by ID. + /// 按ID获取着色器程序。 + #[inline] + pub fn get_shader(&self, shader_id: u32) -> Option<&ShaderProgram> { + self.shaders.get(&shader_id) + } + + /// Get the default sprite shader. + /// 获取默认精灵着色器。 + #[inline] + pub fn get_default_shader(&self) -> &ShaderProgram { + self.shaders.get(&SHADER_ID_DEFAULT_SPRITE) + .expect("Default shader should always exist | 默认着色器应该始终存在") + } + + /// Check if a shader exists. + /// 检查着色器是否存在。 + #[inline] + pub fn has_shader(&self, shader_id: u32) -> bool { + self.shaders.contains_key(&shader_id) + } + + /// Remove a shader program. + /// 移除着色器程序。 + /// + /// Note: Cannot remove built-in shaders (ID < 100). + /// 注意:无法移除内置着色器(ID < 100)。 + pub fn remove_shader(&mut self, shader_id: u32) -> bool { + if shader_id < 100 { + log::warn!("Cannot remove built-in shader: {} | 无法移除内置着色器: {}", shader_id, shader_id); + return false; + } + + self.shader_sources.remove(&shader_id); + self.shaders.remove(&shader_id).is_some() + } + + /// Get all shader IDs. + /// 获取所有着色器ID。 + pub fn shader_ids(&self) -> Vec { + self.shaders.keys().copied().collect() + } + + /// Get shader count. + /// 获取着色器数量。 + #[inline] + pub fn shader_count(&self) -> usize { + self.shaders.len() + } +} diff --git a/packages/engine/src/renderer/shader/mod.rs b/packages/engine/src/renderer/shader/mod.rs index dfbd5f42..f8b23278 100644 --- a/packages/engine/src/renderer/shader/mod.rs +++ b/packages/engine/src/renderer/shader/mod.rs @@ -3,6 +3,8 @@ mod program; mod builtin; +mod manager; pub use program::ShaderProgram; pub use builtin::{SPRITE_VERTEX_SHADER, SPRITE_FRAGMENT_SHADER}; +pub use manager::{ShaderManager, SHADER_ID_DEFAULT_SPRITE};