feat(engine): 添加材质系统和着色器管理

This commit is contained in:
yhh
2025-12-03 16:20:13 +08:00
parent 0c590d7c12
commit 4a2362edf2
12 changed files with 1321 additions and 50 deletions

View File

@@ -185,6 +185,7 @@ impl Engine {
texture_ids: &[u32], texture_ids: &[u32],
uvs: &[f32], uvs: &[f32],
colors: &[u32], colors: &[u32],
material_ids: &[u32],
) -> Result<()> { ) -> Result<()> {
// Debug: log once // Debug: log once
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
@@ -199,6 +200,7 @@ impl Engine {
texture_ids, texture_ids,
uvs, uvs,
colors, colors,
material_ids,
&self.texture_manager, &self.texture_manager,
) )
} }
@@ -533,4 +535,176 @@ impl Engine {
pub fn viewport_ids(&self) -> Vec<String> { pub fn viewport_ids(&self) -> Vec<String> {
self.viewport_manager.viewport_ids().into_iter().cloned().collect() 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<u32> {
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
}
}
} }

View File

@@ -142,6 +142,7 @@ impl GameEngine {
/// * `texture_ids` - Uint32Array of texture IDs | 纹理ID数组 /// * `texture_ids` - Uint32Array of texture IDs | 纹理ID数组
/// * `uvs` - Float32Array [u0, v0, u1, v1] per sprite | 每个精灵的UV坐标 /// * `uvs` - Float32Array [u0, v0, u1, v1] per sprite | 每个精灵的UV坐标
/// * `colors` - Uint32Array of packed RGBA colors | 打包的RGBA颜色数组 /// * `colors` - Uint32Array of packed RGBA colors | 打包的RGBA颜色数组
/// * `material_ids` - Uint32Array of material IDs (0 = default) | 材质ID数组0 = 默认)
#[wasm_bindgen(js_name = submitSpriteBatch)] #[wasm_bindgen(js_name = submitSpriteBatch)]
pub fn submit_sprite_batch( pub fn submit_sprite_batch(
&mut self, &mut self,
@@ -149,9 +150,10 @@ impl GameEngine {
texture_ids: &[u32], texture_ids: &[u32],
uvs: &[f32], uvs: &[f32],
colors: &[u32], colors: &[u32],
material_ids: &[u32],
) -> std::result::Result<(), JsValue> { ) -> std::result::Result<(), JsValue> {
self.engine 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())) .map_err(|e| JsValue::from_str(&e.to_string()))
} }
@@ -463,4 +465,150 @@ impl GameEngine {
pub fn get_viewport_ids(&self) -> Vec<String> { pub fn get_viewport_ids(&self) -> Vec<String> {
self.engine.viewport_ids() 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<u32, JsValue> {
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).
/// 设置材质的颜色uniformRGBA0.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)
}
} }

View File

@@ -4,5 +4,5 @@
mod sprite_batch; mod sprite_batch;
mod vertex; mod vertex;
pub use sprite_batch::SpriteBatch; pub use sprite_batch::{BatchKey, SpriteBatch};
pub use vertex::{SpriteVertex, VERTEX_SIZE}; pub use vertex::{SpriteVertex, VERTEX_SIZE};

View File

@@ -27,6 +27,18 @@ const TRANSFORM_STRIDE: usize = 7;
/// UV数据步长。 /// UV数据步长。
const UV_STRIDE: usize = 4; 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).
/// 材质ID0 = 默认材质)。
pub material_id: u32,
/// Texture ID.
/// 纹理ID。
pub texture_id: u32,
}
/// Sprite batch renderer. /// Sprite batch renderer.
/// 精灵批处理渲染器。 /// 精灵批处理渲染器。
/// ///
@@ -35,7 +47,7 @@ const UV_STRIDE: usize = 4;
/// ///
/// # Performance | 性能 /// # Performance | 性能
/// - Uses dynamic vertex buffer for efficient updates | 使用动态顶点缓冲区以高效更新 /// - 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+精灵 /// - Supports up to 10000+ sprites per batch | 每批次支持10000+精灵
pub struct SpriteBatch { pub struct SpriteBatch {
/// Vertex array object. /// Vertex array object.
@@ -54,9 +66,9 @@ pub struct SpriteBatch {
/// 最大精灵数。 /// 最大精灵数。
max_sprites: usize, max_sprites: usize,
/// Per-texture vertex data buffers. /// Per-material-texture vertex data buffers.
/// 按纹理分组的顶点数据缓冲区。 /// 按材质和纹理分组的顶点数据缓冲区。
texture_batches: HashMap<u32, Vec<f32>>, batches: HashMap<BatchKey, Vec<f32>>,
/// Total sprite count across all batches. /// Total sprite count across all batches.
/// 所有批次的总精灵数。 /// 所有批次的总精灵数。
@@ -124,7 +136,7 @@ impl SpriteBatch {
vbo, vbo,
ibo, ibo,
max_sprites, max_sprites,
texture_batches: HashMap::new(), batches: HashMap::new(),
sprite_count: 0, sprite_count: 0,
}) })
} }
@@ -192,7 +204,7 @@ impl SpriteBatch {
/// Clear the batch for a new frame. /// Clear the batch for a new frame.
/// 为新帧清空批处理。 /// 为新帧清空批处理。
pub fn clear(&mut self) { pub fn clear(&mut self) {
for batch in self.texture_batches.values_mut() { for batch in self.batches.values_mut() {
batch.clear(); batch.clear();
} }
self.sprite_count = 0; self.sprite_count = 0;
@@ -206,6 +218,7 @@ impl SpriteBatch {
/// * `texture_ids` - Texture ID for each sprite | 每个精灵的纹理ID /// * `texture_ids` - Texture ID for each sprite | 每个精灵的纹理ID
/// * `uvs` - [u0, v0, u1, v1] per sprite | 每个精灵的UV坐标 /// * `uvs` - [u0, v0, u1, v1] per sprite | 每个精灵的UV坐标
/// * `colors` - Packed RGBA color per sprite | 每个精灵的打包RGBA颜色 /// * `colors` - Packed RGBA color per sprite | 每个精灵的打包RGBA颜色
/// * `material_ids` - Material ID for each sprite (0 = default) | 每个精灵的材质ID0 = 默认)
/// * `_texture_manager` - Texture manager for getting texture sizes | 纹理管理器 /// * `_texture_manager` - Texture manager for getting texture sizes | 纹理管理器
pub fn add_sprites( pub fn add_sprites(
&mut self, &mut self,
@@ -213,6 +226,7 @@ impl SpriteBatch {
texture_ids: &[u32], texture_ids: &[u32],
uvs: &[f32], uvs: &[f32],
colors: &[u32], colors: &[u32],
material_ids: &[u32],
_texture_manager: &TextureManager, _texture_manager: &TextureManager,
) -> Result<()> { ) -> Result<()> {
let sprite_count = texture_ids.len(); 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 | 检查容量 // Check capacity | 检查容量
if self.sprite_count + sprite_count > self.max_sprites { if self.sprite_count + sprite_count > self.max_sprites {
return Err(EngineError::InvalidBatchData(format!( 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 { for i in 0..sprite_count {
let t_offset = i * TRANSFORM_STRIDE; let t_offset = i * TRANSFORM_STRIDE;
let uv_offset = i * UV_STRIDE; let uv_offset = i * UV_STRIDE;
@@ -276,11 +298,14 @@ impl SpriteBatch {
let width = scale_x; let width = scale_x;
let height = scale_y; 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 | 获取或创建此纹理的批次 // Get or create batch for this material+texture combination | 获取或创建此材质+纹理组合的批次
let batch = self.texture_batches let batch = self.batches
.entry(texture_id) .entry(batch_key)
.or_insert_with(Vec::new); .or_insert_with(Vec::new);
// Calculate transformed vertices and add to batch | 计算变换后的顶点并添加到批次 // Calculate transformed vertices and add to batch | 计算变换后的顶点并添加到批次
@@ -367,9 +392,9 @@ impl SpriteBatch {
} }
} }
/// Flush a specific texture batch to GPU and render. /// Flush a batch to GPU and render.
/// 将特定纹理批次刷新到GPU并渲染。 /// 将批次刷新到GPU并渲染。
fn flush_texture_batch(&self, gl: &WebGl2RenderingContext, vertices: &[f32]) { fn flush_batch(&self, gl: &WebGl2RenderingContext, vertices: &[f32]) {
if vertices.is_empty() { if vertices.is_empty() {
return; return;
} }
@@ -403,17 +428,17 @@ impl SpriteBatch {
gl.bind_vertex_array(None); gl.bind_vertex_array(None);
} }
/// Get texture batches for rendering. /// Get all batches for rendering.
/// 获取用于渲染的纹理批次 /// 获取所有批次用于渲染。
pub fn texture_batches(&self) -> &HashMap<u32, Vec<f32>> { pub fn batches(&self) -> &HashMap<BatchKey, Vec<f32>> {
&self.texture_batches &self.batches
} }
/// Flush a specific texture batch. /// Flush a specific batch by key.
/// 刷新特定纹理批次。 /// 按键刷新特定批次。
pub fn flush_for_texture(&self, gl: &WebGl2RenderingContext, texture_id: u32) { pub fn flush_for_batch(&self, gl: &WebGl2RenderingContext, key: &BatchKey) {
if let Some(vertices) = self.texture_batches.get(&texture_id) { if let Some(vertices) = self.batches.get(key) {
self.flush_texture_batch(gl, vertices); self.flush_batch(gl, vertices);
} }
} }

View File

@@ -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<u32, Material>,
/// 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<u32> {
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()
}
}

View File

@@ -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).
/// 设置颜色uniformRGBA0.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
}
}

View File

@@ -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};

View File

@@ -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<String, UniformValue>,
}
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<UniformValue> {
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).
/// 设置颜色uniformRGBA0.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));
}
}

View File

@@ -4,6 +4,7 @@
pub mod batch; pub mod batch;
pub mod shader; pub mod shader;
pub mod texture; pub mod texture;
pub mod material;
mod renderer2d; mod renderer2d;
mod camera; mod camera;
@@ -18,3 +19,5 @@ pub use texture::{Texture, TextureManager};
pub use grid::GridRenderer; pub use grid::GridRenderer;
pub use gizmo::{GizmoRenderer, TransformMode}; pub use gizmo::{GizmoRenderer, TransformMode};
pub use viewport::{RenderTarget, ViewportManager, ViewportConfig}; pub use viewport::{RenderTarget, ViewportManager, ViewportConfig};
pub use shader::{ShaderManager, ShaderProgram, SHADER_ID_DEFAULT_SPRITE};
pub use material::{Material, MaterialManager, BlendMode, CullMode, UniformValue, MaterialUniforms};

View File

@@ -8,7 +8,8 @@ use crate::core::error::Result;
use crate::resource::TextureManager; use crate::resource::TextureManager;
use super::batch::SpriteBatch; use super::batch::SpriteBatch;
use super::camera::Camera2D; 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 renderer with batched sprite rendering.
/// 带批处理精灵渲染的2D渲染器。 /// 带批处理精灵渲染的2D渲染器。
@@ -20,9 +21,13 @@ pub struct Renderer2D {
/// 精灵批处理渲染器。 /// 精灵批处理渲染器。
sprite_batch: SpriteBatch, sprite_batch: SpriteBatch,
/// Sprite shader program. /// Shader manager.
/// 精灵Shader程序 /// 着色器管理器
shader: ShaderProgram, shader_manager: ShaderManager,
/// Material manager.
/// 材质管理器。
material_manager: MaterialManager,
/// 2D camera. /// 2D camera.
/// 2D相机。 /// 2D相机。
@@ -31,6 +36,16 @@ pub struct Renderer2D {
/// Clear color (RGBA). /// Clear color (RGBA).
/// 清除颜色 (RGBA)。 /// 清除颜色 (RGBA)。
clear_color: [f32; 4], 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 { impl Renderer2D {
@@ -42,7 +57,8 @@ impl Renderer2D {
/// * `max_sprites` - Maximum sprites per batch | 每批次最大精灵数 /// * `max_sprites` - Maximum sprites per batch | 每批次最大精灵数
pub fn new(gl: &WebGl2RenderingContext, max_sprites: usize) -> Result<Self> { pub fn new(gl: &WebGl2RenderingContext, max_sprites: usize) -> Result<Self> {
let sprite_batch = SpriteBatch::new(gl, max_sprites)?; 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尺寸用于相机 // Get canvas size for camera | 获取canvas尺寸用于相机
let canvas = gl.canvas() let canvas = gl.canvas()
@@ -59,9 +75,12 @@ impl Renderer2D {
Ok(Self { Ok(Self {
sprite_batch, sprite_batch,
shader, shader_manager,
material_manager,
camera, camera,
clear_color: [0.1, 0.1, 0.12, 1.0], 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 /// * `texture_ids` - Texture ID for each sprite | 每个精灵的纹理ID
/// * `uvs` - UV coordinates for each sprite | 每个精灵的UV坐标 /// * `uvs` - UV coordinates for each sprite | 每个精灵的UV坐标
/// * `colors` - Packed color for each sprite | 每个精灵的打包颜色 /// * `colors` - Packed color for each sprite | 每个精灵的打包颜色
/// * `material_ids` - Material ID for each sprite (0 = default) | 每个精灵的材质ID0 = 默认)
/// * `texture_manager` - Texture manager | 纹理管理器 /// * `texture_manager` - Texture manager | 纹理管理器
pub fn submit_batch( pub fn submit_batch(
&mut self, &mut self,
@@ -80,6 +100,7 @@ impl Renderer2D {
texture_ids: &[u32], texture_ids: &[u32],
uvs: &[f32], uvs: &[f32],
colors: &[u32], colors: &[u32],
material_ids: &[u32],
texture_manager: &TextureManager, texture_manager: &TextureManager,
) -> Result<()> { ) -> Result<()> {
self.sprite_batch.add_sprites( self.sprite_batch.add_sprites(
@@ -87,6 +108,7 @@ impl Renderer2D {
texture_ids, texture_ids,
uvs, uvs,
colors, colors,
material_ids,
texture_manager, texture_manager,
) )
} }
@@ -94,34 +116,61 @@ impl Renderer2D {
/// Render the current frame. /// Render the current frame.
/// 渲染当前帧。 /// 渲染当前帧。
pub fn render(&mut self, gl: &WebGl2RenderingContext, texture_manager: &TextureManager) -> Result<()> { pub fn render(&mut self, gl: &WebGl2RenderingContext, texture_manager: &TextureManager) -> Result<()> {
use super::batch::BatchKey;
if self.sprite_batch.sprite_count() == 0 { if self.sprite_batch.sprite_count() == 0 {
return Ok(()); return Ok(());
} }
// Bind shader | 绑定Shader // Collect non-empty batch keys | 收集非空批次键
self.shader.bind(gl); let batch_keys: Vec<BatchKey> = self.sprite_batch.batches()
// 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<u32> = self.sprite_batch.texture_batches()
.iter() .iter()
.filter(|(_, vertices)| !vertices.is_empty()) .filter(|(_, vertices)| !vertices.is_empty())
.map(|(id, _)| *id) .map(|(key, _)| *key)
.collect(); .collect();
for texture_id in texture_ids { // Track current state to minimize state changes | 跟踪当前状态以最小化状态切换
// Bind texture for this batch | 绑定此批次的纹理 let mut current_material_id: u32 = u32::MAX;
texture_manager.bind_texture(texture_id, 0); let mut current_texture_id: u32 = u32::MAX;
// Flush this texture's sprites | 刷新此纹理的精灵 // Get projection matrix once | 一次性获取投影矩阵
self.sprite_batch.flush_for_texture(gl, texture_id); 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 | 清空批处理以供下一帧使用 // Clear batch for next frame | 清空批处理以供下一帧使用
@@ -161,4 +210,123 @@ impl Renderer2D {
pub fn resize(&mut self, width: f32, height: f32) { pub fn resize(&mut self, width: f32, height: f32) {
self.camera.set_viewport(width, height); 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<u32> {
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
}
} }

View File

@@ -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<u32, ShaderProgram>,
/// Next available shader ID for custom shaders.
/// 下一个可用的自定义着色器ID。
next_shader_id: u32,
/// Shader source cache for hot-reloading (optional).
/// 着色器源代码缓存,用于热重载(可选)。
shader_sources: HashMap<u32, (String, String)>,
}
impl ShaderManager {
/// Create a new shader manager with built-in shaders.
/// 创建带有内置着色器的新着色器管理器。
///
/// # Arguments | 参数
/// * `gl` - WebGL2 context | WebGL2上下文
pub fn new(gl: &WebGl2RenderingContext) -> Result<Self> {
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<u32> {
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<u32> {
self.shaders.keys().copied().collect()
}
/// Get shader count.
/// 获取着色器数量。
#[inline]
pub fn shader_count(&self) -> usize {
self.shaders.len()
}
}

View File

@@ -3,6 +3,8 @@
mod program; mod program;
mod builtin; mod builtin;
mod manager;
pub use program::ShaderProgram; pub use program::ShaderProgram;
pub use builtin::{SPRITE_VERTEX_SHADER, SPRITE_FRAGMENT_SHADER}; pub use builtin::{SPRITE_VERTEX_SHADER, SPRITE_FRAGMENT_SHADER};
pub use manager::{ShaderManager, SHADER_ID_DEFAULT_SPRITE};