* feat(ui): 动态图集系统与渲染调试增强 ## 核心功能 ### 动态图集系统 (Dynamic Atlas) - 新增 DynamicAtlasManager:运行时纹理打包,支持 MaxRects 算法 - 新增 DynamicAtlasService:自动纹理加载与图集管理 - 新增 BinPacker:高效矩形打包算法 - 支持动态/固定两种扩展策略 - 自动 UV 重映射,实现 UI 元素合批渲染 ### Frame Debugger 增强 - 新增合批分析面板,显示批次中断原因 - 新增 UI 元素层级信息(depth, worldOrderInLayer) - 新增实体高亮功能,点击可在场景中定位 - 新增动态图集可视化面板 - 改进渲染原语详情展示 ### 闪光效果 (Shiny Effect) - 新增 UIShinyEffectComponent:UI 闪光参数配置 - 新增 UIShinyEffectSystem:材质覆盖驱动的闪光动画 - 新增 ShinyEffectComponent/System(Sprite 版本) ## 引擎层改进 ### Rust 纹理管理扩展 - create_blank_texture:创建空白 GPU 纹理 - update_texture_region:局部纹理更新 - 支持动态图集的 GPU 端操作 ### 材质系统 - 新增 effects/ 目录:ShinyEffect 等效果实现 - 新增 interfaces/ 目录:IMaterial 等接口定义 - 新增 mixins/ 目录:可组合的材质功能 ### EngineBridge 扩展 - 新增 createBlankTexture/updateTextureRegion 方法 - 改进纹理加载回调机制 ## UI 渲染改进 - UIRenderCollector:支持合批调试信息 - 稳定排序:addIndex 保证渲染顺序一致性 - 九宫格渲染优化 - 材质覆盖支持 ## 其他改进 - 国际化:新增 Frame Debugger 相关翻译 - 编辑器:新增渲染调试入口 - 文档:新增架构设计文档目录 * refactor(ui): 引入新基础组件架构与渲染工具函数 Phase 1 重构 - 组件职责分离与代码复用: 新增基础组件层: - UIGraphicComponent: 所有可视 UI 元素的基类(颜色、透明度、raycast) - UIImageComponent: 纹理显示组件(支持简单、切片、平铺、填充模式) - UISelectableComponent: 可交互元素的基类(状态管理、颜色过渡) 新增渲染工具: - UIRenderUtils: 提取共享的坐标计算、边框渲染、阴影渲染等工具函数 - getUIRenderTransform: 统一的变换数据提取 - renderBorder/renderShadow: 复用的边框和阴影渲染逻辑 新增渲染系统: - UIGraphicRenderSystem: 处理新基础组件的统一渲染器 重构现有系统: - UIRectRenderSystem: 使用新工具函数,移除重复代码 - UIButtonRenderSystem: 使用新工具函数,移除重复代码 这些改动为后续统一渲染系统奠定基础。 * refactor(ui): UIProgressBarRenderSystem 使用渲染工具函数 - 使用 getUIRenderTransform 替代手动变换计算 - 使用 renderBorder 工具函数替代重复的边框渲染 - 使用 lerpColor 工具函数替代重复的颜色插值 - 简化方法签名,使用 UIRenderTransform 类型 - 移除约 135 行重复代码 * refactor(ui): Slider 和 ScrollView 渲染系统使用工具函数 - UISliderRenderSystem: 使用 getUIRenderTransform,简化方法签名 - UIScrollViewRenderSystem: 使用 getUIRenderTransform,简化方法签名 - 统一使用 UIRenderTransform 类型减少参数传递 - 消除重复的变换计算代码 * refactor(ui): 使用 UIWidgetMarker 消除硬编码组件依赖 - 新增 UIWidgetMarker 标记组件 - UIRectRenderSystem 改为检查标记而非硬编码4种组件类型 - 各 Widget 渲染系统自动添加标记组件 - 减少模块间耦合,提高可扩展性 * feat(ui): 实现 Canvas 隔离机制 - 新增 UICanvasComponent 定义 Canvas 渲染组 - UITransformComponent 添加 Canvas 相关字段:canvasEntityId, worldSortingLayer, pixelPerfect - UILayoutSystem 传播 Canvas 设置给子元素 - UIRenderUtils 使用 Canvas 继承的排序层 - 支持嵌套 Canvas 和不同渲染模式 * refactor(ui): 统一纹理管理工具函数 Phase 4: 纹理管理统一 新增: - UITextureUtils.ts: 统一的纹理描述符接口和验证函数 - UITextureDescriptor: 支持 GUID/textureId/path 多种纹理源 - isValidTextureGuid: GUID 验证 - getTextureKey: 获取用于合批的纹理键 - normalizeTextureDescriptor: 规范化各种输入格式 - utils/index.ts: 工具函数导出 修改: - UIGraphicRenderSystem: 使用新的纹理工具函数 - index.ts: 导出纹理工具类型和函数 * refactor(ui): 实现统一的脏标记机制 Phase 5: Dirty 标记机制 新增: - UIDirtyFlags.ts: 位标记枚举和追踪工具 - UIDirtyFlags: Visual/Layout/Transform/Material/Text 标记 - IDirtyTrackable: 脏追踪接口 - DirtyTracker: 辅助工具类 - 帧级别脏状态追踪 (markFrameDirty, isFrameDirty) 修改: - UIGraphicComponent: 实现 IDirtyTrackable - 属性 setter 自动设置脏标记 - 保留 setDirty/clearDirty 向后兼容 - UIImageComponent: 所有属性支持脏追踪 - textureGuid/imageType/fillAmount 等变化自动标记 - UIGraphicRenderSystem: 使用 clearDirtyFlags() 导出: - UIDirtyFlags, IDirtyTrackable, DirtyTracker - markFrameDirty, isFrameDirty, clearFrameDirty * refactor(ui): 移除过时的 dirty flag API 移除 UIGraphicComponent 中的兼容性 API: - 移除 _isDirty getter/setter - 移除 setDirty() 方法 - 移除 clearDirty() 方法 现在统一使用新的 dirty flag 系统: - isDirty() / hasDirtyFlag(flags) - markDirty(flags) / clearDirtyFlags() * fix(ui): 修复两个 TODO 功能 1. 滑块手柄命中测试 (UIInputSystem) - UISliderComponent 添加 getHandleBounds() 计算手柄边界 - UISliderComponent 添加 isPointInHandle() 精确命中测试 - UIInputSystem.handleSlider() 使用精确测试更新悬停状态 2. 径向填充渲染 (UIGraphicRenderSystem) - 实现 renderRadialFill() 方法 - 支持 radial90/radial180/radial360 三种模式 - 支持 fillOrigin (top/right/bottom/left) 和 fillClockwise - 使用多段矩形近似饼形填充效果 * feat(ui): 完善 UI 系统架构和九宫格渲染 * fix(ui): 修复文本渲染层级问题并清理调试代码 - 修复纹理就绪后调用 invalidateUIRenderCaches() 导致的无限循环 - 移除 UITextRenderSystem、UIButtonRenderSystem、UIRectRenderSystem 中的首帧调试输出 - 移除 UILayoutSystem 中的布局调试日志 - 清理所有 __UI_RENDER_DEBUG__ 条件日志 * refactor(ui): 优化渲染批处理和输入框组件 渲染系统: - 修复 RenderBatcher 保持渲染顺序 - 优化 Rust SpriteBatch 避免合并非连续精灵 - 增强 EngineRenderSystem 纹理就绪检测 输入框组件: - 增强 UIInputFieldComponent 功能 - 改进 UIInputSystem 输入处理 - 新增 TextMeasureService 文本测量服务 * fix(ui): 修复九宫格首帧渲染和InputField输入问题 - 修复九宫格首帧 size=0x0 问题: - Viewport.tsx: 预览模式读取图片尺寸存储到 importSettings - AssetDatabase: ISpriteSettings 添加 width/height 字段 - AssetMetadataService: getTextureSpriteInfo 使用元数据尺寸作为后备 - UIRectRenderSystem: 当 atlasEntry 不存在时使用 spriteInfo 尺寸 - WebBuildPipeline: 构建时包含 importSettings - AssetManager: 从 catalog 初始化时复制 importSettings - AssetTypes: IAssetCatalogEntry 添加 importSettings 字段 - 修复 InputField 无法输入问题: - UIRuntimeModule: manifest 添加 pluginExport: 'UIPlugin' - 确保预览模式正确加载 UI 插件并绑定 UIInputSystem - 添加调试日志用于排查纹理加载问题 * fix(sprite): 修复类型导出错误 MaterialPropertyOverride 和 MaterialOverrides 应从 @esengine/material-system 导出 * fix(ui-editor): 补充 AnchorPreset 拉伸预设的映射 添加 StretchTop, StretchMiddle, StretchBottom, StretchLeft, StretchCenter, StretchRight 的位置和锚点值映射
800 lines
28 KiB
Rust
800 lines
28 KiB
Rust
//! ES Engine - High-performance 2D game engine for web and mobile platforms.
|
||
//! ES引擎 - 高性能2D游戏引擎,支持Web和移动平台。
|
||
//!
|
||
//! # Architecture | 架构
|
||
//!
|
||
//! The engine is designed with a modular architecture:
|
||
//! 引擎采用模块化架构设计:
|
||
//!
|
||
//! - `core` - Engine lifecycle and context management | 引擎生命周期和上下文管理
|
||
//! - `renderer` - 2D rendering with batch optimization | 2D渲染与批处理优化
|
||
//! - `math` - Mathematical primitives (vectors, matrices) | 数学基元(向量、矩阵)
|
||
//! - `resource` - Asset loading and management | 资源加载和管理
|
||
//! - `input` - Keyboard, mouse, and touch input | 键盘、鼠标和触摸输入
|
||
//! - `platform` - Platform abstraction layer | 平台抽象层
|
||
//!
|
||
//! # Example | 示例
|
||
//!
|
||
//! ```typescript
|
||
//! import { GameEngine } from 'es-engine';
|
||
//!
|
||
//! const engine = new GameEngine('canvas');
|
||
//! engine.loadTexture('player', 'assets/player.png');
|
||
//!
|
||
//! function gameLoop() {
|
||
//! engine.clear(0.0, 0.0, 0.0, 1.0);
|
||
//! engine.submitSpriteBatch(transforms, textureIds, uvs, colors);
|
||
//! engine.render();
|
||
//! requestAnimationFrame(gameLoop);
|
||
//! }
|
||
//! ```
|
||
|
||
#![warn(missing_docs)]
|
||
#![warn(rustdoc::missing_crate_level_docs)]
|
||
|
||
use wasm_bindgen::prelude::*;
|
||
|
||
// Module declarations | 模块声明
|
||
pub mod core;
|
||
pub mod math;
|
||
pub mod platform;
|
||
pub mod renderer;
|
||
pub mod resource;
|
||
pub mod input;
|
||
|
||
// Re-exports | 重新导出
|
||
pub use crate::core::{Engine, EngineConfig};
|
||
pub use crate::core::error::{EngineError, Result};
|
||
|
||
/// Initialize panic hook for better error messages in console.
|
||
/// 初始化panic hook以在控制台显示更好的错误信息。
|
||
#[wasm_bindgen(start)]
|
||
pub fn init() {
|
||
#[cfg(feature = "console_error_panic_hook")]
|
||
console_error_panic_hook::set_once();
|
||
|
||
// Initialize logger | 初始化日志
|
||
console_log::init_with_level(log::Level::Debug)
|
||
.expect("Failed to initialize logger | 日志初始化失败");
|
||
|
||
log::info!("ES Engine initialized | ES引擎初始化完成");
|
||
}
|
||
|
||
/// Game engine main interface exposed to JavaScript.
|
||
/// 暴露给JavaScript的游戏引擎主接口。
|
||
///
|
||
/// This is the primary entry point for the engine from TypeScript/JavaScript.
|
||
/// 这是从TypeScript/JavaScript访问引擎的主要入口点。
|
||
#[wasm_bindgen]
|
||
pub struct GameEngine {
|
||
engine: Engine,
|
||
}
|
||
|
||
#[wasm_bindgen]
|
||
impl GameEngine {
|
||
/// Create a new game engine instance.
|
||
/// 创建新的游戏引擎实例。
|
||
///
|
||
/// # Arguments | 参数
|
||
/// * `canvas_id` - The HTML canvas element ID | HTML canvas元素ID
|
||
///
|
||
/// # Returns | 返回
|
||
/// A new GameEngine instance or an error | 新的GameEngine实例或错误
|
||
#[wasm_bindgen(constructor)]
|
||
pub fn new(canvas_id: &str) -> std::result::Result<GameEngine, JsValue> {
|
||
let config = EngineConfig::default();
|
||
let engine = Engine::new(canvas_id, config)
|
||
.map_err(|e| JsValue::from_str(&e.to_string()))?;
|
||
|
||
Ok(GameEngine { engine })
|
||
}
|
||
|
||
/// Create a new game engine from external WebGL context.
|
||
/// 从外部 WebGL 上下文创建引擎。
|
||
///
|
||
/// This is designed for WeChat MiniGame and similar environments.
|
||
/// 适用于微信小游戏等环境。
|
||
#[wasm_bindgen(js_name = fromExternal)]
|
||
pub fn from_external(
|
||
gl_context: JsValue,
|
||
width: u32,
|
||
height: u32,
|
||
) -> std::result::Result<GameEngine, JsValue> {
|
||
let config = EngineConfig::default();
|
||
let engine = Engine::from_external(gl_context, width, height, config)
|
||
.map_err(|e| JsValue::from_str(&e.to_string()))?;
|
||
|
||
Ok(GameEngine { engine })
|
||
}
|
||
|
||
/// Clear the screen with specified color.
|
||
/// 使用指定颜色清除屏幕。
|
||
///
|
||
/// # Arguments | 参数
|
||
/// * `r` - Red component (0.0-1.0) | 红色分量
|
||
/// * `g` - Green component (0.0-1.0) | 绿色分量
|
||
/// * `b` - Blue component (0.0-1.0) | 蓝色分量
|
||
/// * `a` - Alpha component (0.0-1.0) | 透明度分量
|
||
pub fn clear(&self, r: f32, g: f32, b: f32, a: f32) {
|
||
self.engine.clear(r, g, b, a);
|
||
}
|
||
|
||
/// Get canvas width.
|
||
/// 获取画布宽度。
|
||
#[wasm_bindgen(getter)]
|
||
pub fn width(&self) -> u32 {
|
||
self.engine.width()
|
||
}
|
||
|
||
/// Get canvas height.
|
||
/// 获取画布高度。
|
||
#[wasm_bindgen(getter)]
|
||
pub fn height(&self) -> u32 {
|
||
self.engine.height()
|
||
}
|
||
|
||
/// Submit sprite batch data for rendering.
|
||
/// 提交精灵批次数据进行渲染。
|
||
///
|
||
/// # Arguments | 参数
|
||
/// * `transforms` - Float32Array [x, y, rotation, scaleX, scaleY, originX, originY] per sprite
|
||
/// 每个精灵的变换数据
|
||
/// * `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,
|
||
transforms: &[f32],
|
||
texture_ids: &[u32],
|
||
uvs: &[f32],
|
||
colors: &[u32],
|
||
material_ids: &[u32],
|
||
) -> std::result::Result<(), JsValue> {
|
||
self.engine
|
||
.submit_sprite_batch(transforms, texture_ids, uvs, colors, material_ids)
|
||
.map_err(|e| JsValue::from_str(&e.to_string()))
|
||
}
|
||
|
||
/// Render the current frame.
|
||
/// 渲染当前帧。
|
||
pub fn render(&mut self) -> std::result::Result<(), JsValue> {
|
||
self.engine
|
||
.render()
|
||
.map_err(|e| JsValue::from_str(&e.to_string()))
|
||
}
|
||
|
||
/// Render sprites as overlay (without clearing screen).
|
||
/// 渲染精灵作为叠加层(不清除屏幕)。
|
||
///
|
||
/// This is used for UI rendering on top of the world content.
|
||
/// 用于在世界内容上渲染 UI。
|
||
#[wasm_bindgen(js_name = renderOverlay)]
|
||
pub fn render_overlay(&mut self) -> std::result::Result<(), JsValue> {
|
||
self.engine
|
||
.render_overlay()
|
||
.map_err(|e| JsValue::from_str(&e.to_string()))
|
||
}
|
||
|
||
/// Load a texture from URL.
|
||
/// 从URL加载纹理。
|
||
///
|
||
/// # Arguments | 参数
|
||
/// * `id` - Unique texture identifier | 唯一纹理标识符
|
||
/// * `url` - Image URL to load | 要加载的图片URL
|
||
#[wasm_bindgen(js_name = loadTexture)]
|
||
pub fn load_texture(&mut self, id: u32, url: &str) -> std::result::Result<(), JsValue> {
|
||
self.engine
|
||
.load_texture(id, url)
|
||
.map_err(|e| JsValue::from_str(&e.to_string()))
|
||
}
|
||
|
||
/// Load texture by path, returning texture ID.
|
||
/// 按路径加载纹理,返回纹理ID。
|
||
///
|
||
/// # Arguments | 参数
|
||
/// * `path` - Image path/URL to load | 要加载的图片路径/URL
|
||
#[wasm_bindgen(js_name = loadTextureByPath)]
|
||
pub fn load_texture_by_path(&mut self, path: &str) -> std::result::Result<u32, JsValue> {
|
||
self.engine
|
||
.load_texture_by_path(path)
|
||
.map_err(|e| JsValue::from_str(&e.to_string()))
|
||
}
|
||
|
||
/// Get texture ID by path.
|
||
/// 按路径获取纹理ID。
|
||
///
|
||
/// # Arguments | 参数
|
||
/// * `path` - Image path to lookup | 要查找的图片路径
|
||
#[wasm_bindgen(js_name = getTextureIdByPath)]
|
||
pub fn get_texture_id_by_path(&self, path: &str) -> Option<u32> {
|
||
self.engine.get_texture_id_by_path(path)
|
||
}
|
||
|
||
/// Get texture size by path.
|
||
/// 按路径获取纹理尺寸。
|
||
///
|
||
/// Returns an array [width, height] or null if not found.
|
||
/// 返回数组 [width, height],如果未找到则返回 null。
|
||
///
|
||
/// # Arguments | 参数
|
||
/// * `path` - Image path to lookup | 要查找的图片路径
|
||
#[wasm_bindgen(js_name = getTextureSizeByPath)]
|
||
pub fn get_texture_size_by_path(&self, path: &str) -> Option<js_sys::Float32Array> {
|
||
self.engine.get_texture_size_by_path(path).map(|(w, h)| {
|
||
let arr = js_sys::Float32Array::new_with_length(2);
|
||
arr.set_index(0, w);
|
||
arr.set_index(1, h);
|
||
arr
|
||
})
|
||
}
|
||
|
||
/// Get or load texture by path.
|
||
/// 按路径获取或加载纹理。
|
||
///
|
||
/// # Arguments | 参数
|
||
/// * `path` - Image path/URL | 图片路径/URL
|
||
#[wasm_bindgen(js_name = getOrLoadTextureByPath)]
|
||
pub fn get_or_load_by_path(&mut self, path: &str) -> std::result::Result<u32, JsValue> {
|
||
self.engine
|
||
.get_or_load_by_path(path)
|
||
.map_err(|e| JsValue::from_str(&e.to_string()))
|
||
}
|
||
|
||
/// 获取纹理加载状态
|
||
/// Get texture loading state
|
||
///
|
||
/// # Arguments | 参数
|
||
/// * `id` - Texture ID | 纹理ID
|
||
///
|
||
/// # Returns | 返回
|
||
/// State string: "loading", "ready", or "failed:reason"
|
||
/// 状态字符串:"loading"、"ready" 或 "failed:原因"
|
||
#[wasm_bindgen(js_name = getTextureState)]
|
||
pub fn get_texture_state(&self, id: u32) -> String {
|
||
use crate::renderer::texture::TextureState;
|
||
match self.engine.get_texture_state(id) {
|
||
TextureState::Loading => "loading".to_string(),
|
||
TextureState::Ready => "ready".to_string(),
|
||
TextureState::Failed(reason) => format!("failed:{}", reason),
|
||
}
|
||
}
|
||
|
||
/// 检查纹理是否已就绪
|
||
/// Check if texture is ready to use
|
||
///
|
||
/// # Arguments | 参数
|
||
/// * `id` - Texture ID | 纹理ID
|
||
#[wasm_bindgen(js_name = isTextureReady)]
|
||
pub fn is_texture_ready(&self, id: u32) -> bool {
|
||
self.engine.is_texture_ready(id)
|
||
}
|
||
|
||
/// 获取正在加载中的纹理数量
|
||
/// Get the number of textures currently loading
|
||
#[wasm_bindgen(js_name = getTextureLoadingCount)]
|
||
pub fn get_texture_loading_count(&self) -> u32 {
|
||
self.engine.get_texture_loading_count()
|
||
}
|
||
|
||
/// Check if a key is currently pressed.
|
||
/// 检查某个键是否当前被按下。
|
||
///
|
||
/// # Arguments | 参数
|
||
/// * `key_code` - The key code to check | 要检查的键码
|
||
#[wasm_bindgen(js_name = isKeyDown)]
|
||
pub fn is_key_down(&self, key_code: &str) -> bool {
|
||
self.engine.is_key_down(key_code)
|
||
}
|
||
|
||
/// Update input state. Should be called once per frame.
|
||
/// 更新输入状态。应该每帧调用一次。
|
||
#[wasm_bindgen(js_name = updateInput)]
|
||
pub fn update_input(&mut self) {
|
||
self.engine.update_input();
|
||
}
|
||
|
||
/// Resize viewport.
|
||
/// 调整视口大小。
|
||
///
|
||
/// # Arguments | 参数
|
||
/// * `width` - New viewport width | 新视口宽度
|
||
/// * `height` - New viewport height | 新视口高度
|
||
pub fn resize(&mut self, width: u32, height: u32) {
|
||
self.engine.resize(width as f32, height as f32);
|
||
}
|
||
|
||
/// Set camera position, zoom, and rotation.
|
||
/// 设置相机位置、缩放和旋转。
|
||
///
|
||
/// # Arguments | 参数
|
||
/// * `x` - Camera X position | 相机X位置
|
||
/// * `y` - Camera Y position | 相机Y位置
|
||
/// * `zoom` - Zoom level | 缩放级别
|
||
/// * `rotation` - Rotation in radians | 旋转角度(弧度)
|
||
#[wasm_bindgen(js_name = setCamera)]
|
||
pub fn set_camera(&mut self, x: f32, y: f32, zoom: f32, rotation: f32) {
|
||
self.engine.set_camera(x, y, zoom, rotation);
|
||
}
|
||
|
||
/// Get camera state.
|
||
/// 获取相机状态。
|
||
///
|
||
/// # Returns | 返回
|
||
/// Array of [x, y, zoom, rotation] | 数组 [x, y, zoom, rotation]
|
||
#[wasm_bindgen(js_name = getCamera)]
|
||
pub fn get_camera(&self) -> Vec<f32> {
|
||
let (x, y, zoom, rotation) = self.engine.get_camera();
|
||
vec![x, y, zoom, rotation]
|
||
}
|
||
|
||
/// Convert screen coordinates to world coordinates.
|
||
/// 将屏幕坐标转换为世界坐标。
|
||
///
|
||
/// # Arguments | 参数
|
||
/// * `screen_x` - Screen X coordinate (0 = left edge of canvas)
|
||
/// * `screen_y` - Screen Y coordinate (0 = top edge of canvas)
|
||
///
|
||
/// # Returns | 返回
|
||
/// Array of [world_x, world_y] | 数组 [world_x, world_y]
|
||
#[wasm_bindgen(js_name = screenToWorld)]
|
||
pub fn screen_to_world(&self, screen_x: f32, screen_y: f32) -> Vec<f32> {
|
||
let (x, y) = self.engine.screen_to_world(screen_x, screen_y);
|
||
vec![x, y]
|
||
}
|
||
|
||
/// Convert world coordinates to screen coordinates.
|
||
/// 将世界坐标转换为屏幕坐标。
|
||
///
|
||
/// # Arguments | 参数
|
||
/// * `world_x` - World X coordinate
|
||
/// * `world_y` - World Y coordinate
|
||
///
|
||
/// # Returns | 返回
|
||
/// Array of [screen_x, screen_y] | 数组 [screen_x, screen_y]
|
||
#[wasm_bindgen(js_name = worldToScreen)]
|
||
pub fn world_to_screen(&self, world_x: f32, world_y: f32) -> Vec<f32> {
|
||
let (x, y) = self.engine.world_to_screen(world_x, world_y);
|
||
vec![x, y]
|
||
}
|
||
|
||
/// Set grid visibility.
|
||
/// 设置网格可见性。
|
||
#[wasm_bindgen(js_name = setShowGrid)]
|
||
pub fn set_show_grid(&mut self, show: bool) {
|
||
self.engine.set_show_grid(show);
|
||
}
|
||
|
||
/// Set clear color (background color).
|
||
/// 设置清除颜色(背景颜色)。
|
||
///
|
||
/// # Arguments | 参数
|
||
/// * `r`, `g`, `b`, `a` - Color components (0.0-1.0) | 颜色分量 (0.0-1.0)
|
||
#[wasm_bindgen(js_name = setClearColor)]
|
||
pub fn set_clear_color(&mut self, r: f32, g: f32, b: f32, a: f32) {
|
||
self.engine.set_clear_color(r, g, b, a);
|
||
}
|
||
|
||
/// Add a rectangle gizmo outline.
|
||
/// 添加矩形Gizmo边框。
|
||
///
|
||
/// # Arguments | 参数
|
||
/// * `x` - Center X position | 中心X位置
|
||
/// * `y` - Center Y position | 中心Y位置
|
||
/// * `width` - Rectangle width | 矩形宽度
|
||
/// * `height` - Rectangle height | 矩形高度
|
||
/// * `rotation` - Rotation in radians | 旋转角度(弧度)
|
||
/// * `origin_x` - Origin X (0-1) | 原点X (0-1)
|
||
/// * `origin_y` - Origin Y (0-1) | 原点Y (0-1)
|
||
/// * `r`, `g`, `b`, `a` - Color (0.0-1.0) | 颜色
|
||
/// * `show_handles` - Whether to show transform handles | 是否显示变换手柄
|
||
#[wasm_bindgen(js_name = addGizmoRect)]
|
||
pub fn add_gizmo_rect(
|
||
&mut self,
|
||
x: f32,
|
||
y: f32,
|
||
width: f32,
|
||
height: f32,
|
||
rotation: f32,
|
||
origin_x: f32,
|
||
origin_y: f32,
|
||
r: f32,
|
||
g: f32,
|
||
b: f32,
|
||
a: f32,
|
||
show_handles: bool,
|
||
) {
|
||
self.engine.add_gizmo_rect(x, y, width, height, rotation, origin_x, origin_y, r, g, b, a, show_handles);
|
||
}
|
||
|
||
/// Add a circle gizmo outline.
|
||
/// 添加圆形Gizmo边框。
|
||
#[wasm_bindgen(js_name = addGizmoCircle)]
|
||
pub fn add_gizmo_circle(
|
||
&mut self,
|
||
x: f32,
|
||
y: f32,
|
||
radius: f32,
|
||
r: f32,
|
||
g: f32,
|
||
b: f32,
|
||
a: f32,
|
||
) {
|
||
self.engine.add_gizmo_circle(x, y, radius, r, g, b, a);
|
||
}
|
||
|
||
/// Add a line gizmo.
|
||
/// 添加线条Gizmo。
|
||
#[wasm_bindgen(js_name = addGizmoLine)]
|
||
pub fn add_gizmo_line(
|
||
&mut self,
|
||
points: Vec<f32>,
|
||
r: f32,
|
||
g: f32,
|
||
b: f32,
|
||
a: f32,
|
||
closed: bool,
|
||
) {
|
||
self.engine.add_gizmo_line(points, r, g, b, a, closed);
|
||
}
|
||
|
||
/// Add a capsule gizmo outline.
|
||
/// 添加胶囊Gizmo边框。
|
||
#[wasm_bindgen(js_name = addGizmoCapsule)]
|
||
pub fn add_gizmo_capsule(
|
||
&mut self,
|
||
x: f32,
|
||
y: f32,
|
||
radius: f32,
|
||
half_height: f32,
|
||
rotation: f32,
|
||
r: f32,
|
||
g: f32,
|
||
b: f32,
|
||
a: f32,
|
||
) {
|
||
self.engine.add_gizmo_capsule(x, y, radius, half_height, rotation, r, g, b, a);
|
||
}
|
||
|
||
/// Set transform tool mode.
|
||
/// 设置变换工具模式。
|
||
///
|
||
/// # Arguments | 参数
|
||
/// * `mode` - 0=Select, 1=Move, 2=Rotate, 3=Scale
|
||
#[wasm_bindgen(js_name = setTransformMode)]
|
||
pub fn set_transform_mode(&mut self, mode: u8) {
|
||
self.engine.set_transform_mode(mode);
|
||
}
|
||
|
||
/// Set gizmo visibility.
|
||
/// 设置辅助工具可见性。
|
||
#[wasm_bindgen(js_name = setShowGizmos)]
|
||
pub fn set_show_gizmos(&mut self, show: bool) {
|
||
self.engine.set_show_gizmos(show);
|
||
}
|
||
|
||
/// Set editor mode.
|
||
/// 设置编辑器模式。
|
||
///
|
||
/// When false (runtime mode), editor-only UI like grid, gizmos,
|
||
/// and axis indicator are automatically hidden.
|
||
/// 当为 false(运行时模式)时,编辑器专用 UI(如网格、gizmos、坐标轴指示器)会自动隐藏。
|
||
#[wasm_bindgen(js_name = setEditorMode)]
|
||
pub fn set_editor_mode(&mut self, is_editor: bool) {
|
||
self.engine.set_editor_mode(is_editor);
|
||
}
|
||
|
||
/// Get editor mode.
|
||
/// 获取编辑器模式。
|
||
#[wasm_bindgen(js_name = isEditorMode)]
|
||
pub fn is_editor_mode(&self) -> bool {
|
||
self.engine.is_editor()
|
||
}
|
||
|
||
// ===== Multi-viewport API =====
|
||
// ===== 多视口 API =====
|
||
|
||
/// Register a new viewport.
|
||
/// 注册新视口。
|
||
///
|
||
/// # Arguments | 参数
|
||
/// * `id` - Unique viewport identifier | 唯一视口标识符
|
||
/// * `canvas_id` - HTML canvas element ID | HTML canvas元素ID
|
||
#[wasm_bindgen(js_name = registerViewport)]
|
||
pub fn register_viewport(&mut self, id: &str, canvas_id: &str) -> std::result::Result<(), JsValue> {
|
||
self.engine
|
||
.register_viewport(id, canvas_id)
|
||
.map_err(|e| JsValue::from_str(&e.to_string()))
|
||
}
|
||
|
||
/// Unregister a viewport.
|
||
/// 注销视口。
|
||
#[wasm_bindgen(js_name = unregisterViewport)]
|
||
pub fn unregister_viewport(&mut self, id: &str) {
|
||
self.engine.unregister_viewport(id);
|
||
}
|
||
|
||
/// Set the active viewport.
|
||
/// 设置活动视口。
|
||
#[wasm_bindgen(js_name = setActiveViewport)]
|
||
pub fn set_active_viewport(&mut self, id: &str) -> bool {
|
||
self.engine.set_active_viewport(id)
|
||
}
|
||
|
||
/// Set camera for a specific viewport.
|
||
/// 为特定视口设置相机。
|
||
#[wasm_bindgen(js_name = setViewportCamera)]
|
||
pub fn set_viewport_camera(&mut self, viewport_id: &str, x: f32, y: f32, zoom: f32, rotation: f32) {
|
||
self.engine.set_viewport_camera(viewport_id, x, y, zoom, rotation);
|
||
}
|
||
|
||
/// Get camera for a specific viewport.
|
||
/// 获取特定视口的相机。
|
||
#[wasm_bindgen(js_name = getViewportCamera)]
|
||
pub fn get_viewport_camera(&self, viewport_id: &str) -> Option<Vec<f32>> {
|
||
self.engine
|
||
.get_viewport_camera(viewport_id)
|
||
.map(|(x, y, zoom, rotation)| vec![x, y, zoom, rotation])
|
||
}
|
||
|
||
/// Set viewport configuration.
|
||
/// 设置视口配置。
|
||
#[wasm_bindgen(js_name = setViewportConfig)]
|
||
pub fn set_viewport_config(&mut self, viewport_id: &str, show_grid: bool, show_gizmos: bool) {
|
||
self.engine.set_viewport_config(viewport_id, show_grid, show_gizmos);
|
||
}
|
||
|
||
/// Resize a specific viewport.
|
||
/// 调整特定视口大小。
|
||
#[wasm_bindgen(js_name = resizeViewport)]
|
||
pub fn resize_viewport(&mut self, viewport_id: &str, width: u32, height: u32) {
|
||
self.engine.resize_viewport(viewport_id, width, height);
|
||
}
|
||
|
||
/// Render to a specific viewport.
|
||
/// 渲染到特定视口。
|
||
#[wasm_bindgen(js_name = renderToViewport)]
|
||
pub fn render_to_viewport(&mut self, viewport_id: &str) -> std::result::Result<(), JsValue> {
|
||
self.engine
|
||
.render_to_viewport(viewport_id)
|
||
.map_err(|e| JsValue::from_str(&e.to_string()))
|
||
}
|
||
|
||
/// Get all registered viewport IDs.
|
||
/// 获取所有已注册的视口ID。
|
||
#[wasm_bindgen(js_name = getViewportIds)]
|
||
pub fn get_viewport_ids(&self) -> Vec<String> {
|
||
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).
|
||
/// 设置材质的颜色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)
|
||
}
|
||
|
||
// ===== Texture Cache API =====
|
||
// ===== 纹理缓存 API =====
|
||
|
||
/// Clear the texture path cache.
|
||
/// 清除纹理路径缓存。
|
||
///
|
||
/// This should be called when restoring scene snapshots to ensure
|
||
/// textures are reloaded with correct IDs.
|
||
/// 在恢复场景快照时应调用此方法,以确保纹理使用正确的ID重新加载。
|
||
#[wasm_bindgen(js_name = clearTexturePathCache)]
|
||
pub fn clear_texture_path_cache(&mut self) {
|
||
self.engine.clear_texture_path_cache();
|
||
}
|
||
|
||
/// Clear all textures and reset state.
|
||
/// 清除所有纹理并重置状态。
|
||
///
|
||
/// This removes all loaded textures from GPU memory and resets
|
||
/// the ID counter. Use with caution as all texture references
|
||
/// will become invalid.
|
||
/// 这会从GPU内存中移除所有已加载的纹理并重置ID计数器。
|
||
/// 请谨慎使用,因为所有纹理引用都将变得无效。
|
||
#[wasm_bindgen(js_name = clearAllTextures)]
|
||
pub fn clear_all_textures(&mut self) {
|
||
self.engine.clear_all_textures();
|
||
}
|
||
|
||
// ===== Dynamic Atlas API =====
|
||
// ===== 动态图集 API =====
|
||
|
||
/// Create a blank texture for dynamic atlas.
|
||
/// 为动态图集创建空白纹理。
|
||
///
|
||
/// This creates a texture that can be filled later using `updateTextureRegion`.
|
||
/// Used for runtime atlas generation to batch UI elements with different textures.
|
||
/// 创建一个可以稍后使用 `updateTextureRegion` 填充的纹理。
|
||
/// 用于运行时图集生成,以批处理使用不同纹理的 UI 元素。
|
||
///
|
||
/// # Arguments | 参数
|
||
/// * `width` - Texture width in pixels (recommended: 2048) | 纹理宽度(推荐:2048)
|
||
/// * `height` - Texture height in pixels (recommended: 2048) | 纹理高度(推荐:2048)
|
||
///
|
||
/// # Returns | 返回
|
||
/// The texture ID for the created blank texture | 创建的空白纹理ID
|
||
#[wasm_bindgen(js_name = createBlankTexture)]
|
||
pub fn create_blank_texture(
|
||
&mut self,
|
||
width: u32,
|
||
height: u32,
|
||
) -> std::result::Result<u32, JsValue> {
|
||
self.engine
|
||
.create_blank_texture(width, height)
|
||
.map_err(|e| JsValue::from_str(&e.to_string()))
|
||
}
|
||
|
||
/// Update a region of an existing texture with pixel data.
|
||
/// 使用像素数据更新现有纹理的区域。
|
||
///
|
||
/// This is used for dynamic atlas to copy individual textures into the atlas.
|
||
/// 用于动态图集将单个纹理复制到图集纹理中。
|
||
///
|
||
/// # Arguments | 参数
|
||
/// * `id` - The texture ID to update | 要更新的纹理ID
|
||
/// * `x` - X offset in the texture | 纹理中的X偏移
|
||
/// * `y` - Y offset in the texture | 纹理中的Y偏移
|
||
/// * `width` - Width of the region to update | 要更新的区域宽度
|
||
/// * `height` - Height of the region to update | 要更新的区域高度
|
||
/// * `pixels` - RGBA pixel data (Uint8Array, 4 bytes per pixel) | RGBA像素数据(每像素4字节)
|
||
#[wasm_bindgen(js_name = updateTextureRegion)]
|
||
pub fn update_texture_region(
|
||
&self,
|
||
id: u32,
|
||
x: u32,
|
||
y: u32,
|
||
width: u32,
|
||
height: u32,
|
||
pixels: &[u8],
|
||
) -> std::result::Result<(), JsValue> {
|
||
self.engine
|
||
.update_texture_region(id, x, y, width, height, pixels)
|
||
.map_err(|e| JsValue::from_str(&e.to_string()))
|
||
}
|
||
}
|