Files
esengine/packages/material-system/src/MaterialManager.ts

848 lines
29 KiB
TypeScript
Raw Normal View History

feat: 添加跨平台运行时、资产系统和UI适配功能 (#256) * feat(platform-common): 添加WASM加载器和环境检测API * feat(rapier2d): 新增Rapier2D WASM绑定包 * feat(physics-rapier2d): 添加跨平台WASM加载器 * feat(asset-system): 添加运行时资产目录和bundle格式 * feat(asset-system-editor): 新增编辑器资产管理包 * feat(editor-core): 添加构建系统和模块管理 * feat(editor-app): 重构浏览器预览使用import maps * feat(platform-web): 添加BrowserRuntime和资产读取 * feat(engine): 添加材质系统和着色器管理 * feat(material): 新增材质系统和着色器编辑器 * feat(tilemap): 增强tilemap编辑器和动画系统 * feat(modules): 添加module.json配置 * feat(core): 添加module.json和类型定义更新 * chore: 更新依赖和构建配置 * refactor(plugins): 更新插件模板使用ModuleManifest * chore: 添加第三方依赖库 * chore: 移除BehaviourTree-ai和ecs-astar子模块 * docs: 更新README和文档主题样式 * fix: 修复Rust文档测试和添加rapier2d WASM绑定 * fix(tilemap-editor): 修复画布高DPI屏幕分辨率适配问题 * feat(ui): 添加UI屏幕适配系统(CanvasScaler/SafeArea) * fix(ecs-engine-bindgen): 添加缺失的ecs-framework-math依赖 * fix: 添加缺失的包依赖修复CI构建 * fix: 修复CodeQL检测到的代码问题 * fix: 修复构建错误和缺失依赖 * fix: 修复类型检查错误 * fix(material-system): 修复tsconfig配置支持TypeScript项目引用 * fix(editor-core): 修复Rollup构建配置添加tauri external * fix: 修复CodeQL检测到的代码问题 * fix: 修复CodeQL检测到的代码问题
2025-12-03 22:15:22 +08:00
/**
* Material manager service.
*
*
* Manages materials and shaders for the rendering system.
*
*/
import { Material } from './Material';
import {
Shader,
DEFAULT_VERTEX_SHADER,
DEFAULT_FRAGMENT_SHADER,
GRAYSCALE_FRAGMENT_SHADER,
TINT_FRAGMENT_SHADER,
FLASH_FRAGMENT_SHADER,
refactor(ui): UI 系统架构重构 (#309) * 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 的位置和锚点值映射
2025-12-19 15:33:36 +08:00
OUTLINE_FRAGMENT_SHADER,
SHINY_FRAGMENT_SHADER
feat: 添加跨平台运行时、资产系统和UI适配功能 (#256) * feat(platform-common): 添加WASM加载器和环境检测API * feat(rapier2d): 新增Rapier2D WASM绑定包 * feat(physics-rapier2d): 添加跨平台WASM加载器 * feat(asset-system): 添加运行时资产目录和bundle格式 * feat(asset-system-editor): 新增编辑器资产管理包 * feat(editor-core): 添加构建系统和模块管理 * feat(editor-app): 重构浏览器预览使用import maps * feat(platform-web): 添加BrowserRuntime和资产读取 * feat(engine): 添加材质系统和着色器管理 * feat(material): 新增材质系统和着色器编辑器 * feat(tilemap): 增强tilemap编辑器和动画系统 * feat(modules): 添加module.json配置 * feat(core): 添加module.json和类型定义更新 * chore: 更新依赖和构建配置 * refactor(plugins): 更新插件模板使用ModuleManifest * chore: 添加第三方依赖库 * chore: 移除BehaviourTree-ai和ecs-astar子模块 * docs: 更新README和文档主题样式 * fix: 修复Rust文档测试和添加rapier2d WASM绑定 * fix(tilemap-editor): 修复画布高DPI屏幕分辨率适配问题 * feat(ui): 添加UI屏幕适配系统(CanvasScaler/SafeArea) * fix(ecs-engine-bindgen): 添加缺失的ecs-framework-math依赖 * fix: 添加缺失的包依赖修复CI构建 * fix: 修复CodeQL检测到的代码问题 * fix: 修复构建错误和缺失依赖 * fix: 修复类型检查错误 * fix(material-system): 修复tsconfig配置支持TypeScript项目引用 * fix(editor-core): 修复Rollup构建配置添加tauri external * fix: 修复CodeQL检测到的代码问题 * fix: 修复CodeQL检测到的代码问题
2025-12-03 22:15:22 +08:00
} from './Shader';
import { BuiltInMaterials, BuiltInShaders, UniformType } from './types';
import type { IAssetManager } from '@esengine/asset-system';
import { AssetType } from '@esengine/asset-system';
import { MaterialLoader, type IMaterialAssetData } from './loaders/MaterialLoader';
import { ShaderLoader, type IShaderAssetData } from './loaders/ShaderLoader';
import { createLogger } from '@esengine/ecs-framework';
feat: 添加跨平台运行时、资产系统和UI适配功能 (#256) * feat(platform-common): 添加WASM加载器和环境检测API * feat(rapier2d): 新增Rapier2D WASM绑定包 * feat(physics-rapier2d): 添加跨平台WASM加载器 * feat(asset-system): 添加运行时资产目录和bundle格式 * feat(asset-system-editor): 新增编辑器资产管理包 * feat(editor-core): 添加构建系统和模块管理 * feat(editor-app): 重构浏览器预览使用import maps * feat(platform-web): 添加BrowserRuntime和资产读取 * feat(engine): 添加材质系统和着色器管理 * feat(material): 新增材质系统和着色器编辑器 * feat(tilemap): 增强tilemap编辑器和动画系统 * feat(modules): 添加module.json配置 * feat(core): 添加module.json和类型定义更新 * chore: 更新依赖和构建配置 * refactor(plugins): 更新插件模板使用ModuleManifest * chore: 添加第三方依赖库 * chore: 移除BehaviourTree-ai和ecs-astar子模块 * docs: 更新README和文档主题样式 * fix: 修复Rust文档测试和添加rapier2d WASM绑定 * fix(tilemap-editor): 修复画布高DPI屏幕分辨率适配问题 * feat(ui): 添加UI屏幕适配系统(CanvasScaler/SafeArea) * fix(ecs-engine-bindgen): 添加缺失的ecs-framework-math依赖 * fix: 添加缺失的包依赖修复CI构建 * fix: 修复CodeQL检测到的代码问题 * fix: 修复构建错误和缺失依赖 * fix: 修复类型检查错误 * fix(material-system): 修复tsconfig配置支持TypeScript项目引用 * fix(editor-core): 修复Rollup构建配置添加tauri external * fix: 修复CodeQL检测到的代码问题 * fix: 修复CodeQL检测到的代码问题
2025-12-03 22:15:22 +08:00
/** Logger instance for MaterialManager. | MaterialManager的日志实例。 */
const logger = createLogger('MaterialManager');
/**
* Engine bridge interface for communicating with Rust engine.
* Rust引擎通信的引擎桥接接口
*/
export interface IEngineBridge {
compileShader(vertexSource: string, fragmentSource: string): Promise<number>;
compileShaderWithId(shaderId: number, vertexSource: string, fragmentSource: string): Promise<void>;
hasShader(shaderId: number): boolean;
removeShader(shaderId: number): boolean;
createMaterial(name: string, shaderId: number, blendMode: number): number;
createMaterialWithId(materialId: number, name: string, shaderId: number, blendMode: number): void;
hasMaterial(materialId: number): boolean;
removeMaterial(materialId: number): boolean;
setMaterialFloat(materialId: number, name: string, value: number): boolean;
setMaterialVec2(materialId: number, name: string, x: number, y: number): boolean;
setMaterialVec3(materialId: number, name: string, x: number, y: number, z: number): boolean;
setMaterialVec4(materialId: number, name: string, x: number, y: number, z: number, w: number): boolean;
setMaterialColor(materialId: number, name: string, r: number, g: number, b: number, a: number): boolean;
setMaterialBlendMode(materialId: number, blendMode: number): boolean;
}
/**
* Material manager service.
*
*
* Manages materials, shaders, and their GPU resources.
* GPU资源
*/
export class MaterialManager {
/** Registered shaders. | 已注册的着色器。 */
private shaders: Map<number, Shader> = new Map();
/** Shader name to ID mapping. | 着色器名称到ID的映射。 */
private shaderNameToId: Map<string, number> = new Map();
/** Registered materials. | 已注册的材质。 */
private materials: Map<number, Material> = new Map();
/** Material name to ID mapping. | 材质名称到ID的映射。 */
private materialNameToId: Map<string, number> = new Map();
/** Material path to ID mapping. | 材质路径到ID的映射。 */
private materialPathToId: Map<string, number> = new Map();
/** Shader path to ID mapping. | 着色器路径到ID的映射。 */
private shaderPathToId: Map<string, number> = new Map();
/** Pending material loads (path -> promise). | 等待加载的材质(路径 -> Promise。 */
private pendingMaterialLoads: Map<string, Promise<number>> = new Map();
/** Pending shader loads (path -> promise). | 等待加载的着色器(路径 -> Promise。 */
private pendingShaderLoads: Map<string, Promise<number>> = new Map();
/** Next shader ID for custom shaders. | 下一个自定义着色器ID。 */
private nextShaderId: number = 100;
/** Next material ID for custom materials. | 下一个自定义材质ID。 */
private nextMaterialId: number = 100;
/** Engine bridge for GPU operations. | 用于GPU操作的引擎桥接。 */
private engineBridge: IEngineBridge | null = null;
/** Asset manager for loading material files. | 用于加载材质文件的资产管理器。 */
private assetManager: IAssetManager | null = null;
constructor() {
// Register built-in shaders and materials.
// 注册内置着色器和材质。
this.registerBuiltInAssets();
}
/**
* Set the engine bridge for GPU operations.
* GPU操作的引擎桥接
*
refactor(ui): UI 系统架构重构 (#309) * 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 的位置和锚点值映射
2025-12-19 15:33:36 +08:00
* When set, uploads all built-in shaders to the GPU.
* GPU
*
feat: 添加跨平台运行时、资产系统和UI适配功能 (#256) * feat(platform-common): 添加WASM加载器和环境检测API * feat(rapier2d): 新增Rapier2D WASM绑定包 * feat(physics-rapier2d): 添加跨平台WASM加载器 * feat(asset-system): 添加运行时资产目录和bundle格式 * feat(asset-system-editor): 新增编辑器资产管理包 * feat(editor-core): 添加构建系统和模块管理 * feat(editor-app): 重构浏览器预览使用import maps * feat(platform-web): 添加BrowserRuntime和资产读取 * feat(engine): 添加材质系统和着色器管理 * feat(material): 新增材质系统和着色器编辑器 * feat(tilemap): 增强tilemap编辑器和动画系统 * feat(modules): 添加module.json配置 * feat(core): 添加module.json和类型定义更新 * chore: 更新依赖和构建配置 * refactor(plugins): 更新插件模板使用ModuleManifest * chore: 添加第三方依赖库 * chore: 移除BehaviourTree-ai和ecs-astar子模块 * docs: 更新README和文档主题样式 * fix: 修复Rust文档测试和添加rapier2d WASM绑定 * fix(tilemap-editor): 修复画布高DPI屏幕分辨率适配问题 * feat(ui): 添加UI屏幕适配系统(CanvasScaler/SafeArea) * fix(ecs-engine-bindgen): 添加缺失的ecs-framework-math依赖 * fix: 添加缺失的包依赖修复CI构建 * fix: 修复CodeQL检测到的代码问题 * fix: 修复构建错误和缺失依赖 * fix: 修复类型检查错误 * fix(material-system): 修复tsconfig配置支持TypeScript项目引用 * fix(editor-core): 修复Rollup构建配置添加tauri external * fix: 修复CodeQL检测到的代码问题 * fix: 修复CodeQL检测到的代码问题
2025-12-03 22:15:22 +08:00
* @param bridge - Engine bridge instance. |
*/
setEngineBridge(bridge: IEngineBridge): void {
this.engineBridge = bridge;
refactor(ui): UI 系统架构重构 (#309) * 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 的位置和锚点值映射
2025-12-19 15:33:36 +08:00
// Upload all existing shaders to the engine
// 将所有现有着色器上传到引擎
this.uploadShadersToEngine();
}
/**
* Upload all registered shaders to the engine.
*
*
* Called automatically when engine bridge is set.
*
*/
private uploadShadersToEngine(): void {
if (!this.engineBridge) return;
let shadersUploaded = 0;
let materialsCreated = 0;
for (const [shaderId, shader] of this.shaders) {
// Skip if already compiled
// 跳过已编译的着色器
if (shader.compiled) continue;
try {
// Compile shader
// 编译着色器
this.engineBridge.compileShaderWithId(
shaderId,
shader.vertexSource,
shader.fragmentSource
);
shader.markCompiled();
shadersUploaded++;
logger.debug(`Uploaded shader ${shader.name} (ID: ${shaderId}) to engine`);
// Create a material for this shader if it doesn't exist in the engine
// 为此着色器创建材质(如果引擎中不存在)
// This allows sprites to reference the shader via materialId
// 这允许精灵通过 materialId 引用着色器
if (!this.engineBridge.hasMaterial(shaderId)) {
// Use shaderId as materialId for built-in shaders (1:1 mapping)
// 对于内置着色器,使用 shaderId 作为 materialId1:1 映射)
// BlendMode 1 = Alpha blending
this.engineBridge.createMaterialWithId(shaderId, shader.name, shaderId, 1);
materialsCreated++;
logger.debug(`Created material ${shader.name} (ID: ${shaderId}) for shader`);
}
} catch (e) {
logger.error(`Failed to upload shader ${shader.name} (ID: ${shaderId}):`, e);
}
}
logger.info(`Uploaded ${shadersUploaded} shaders and created ${materialsCreated} materials | 已上传 ${shadersUploaded} 个着色器,创建 ${materialsCreated} 个材质`);
feat: 添加跨平台运行时、资产系统和UI适配功能 (#256) * feat(platform-common): 添加WASM加载器和环境检测API * feat(rapier2d): 新增Rapier2D WASM绑定包 * feat(physics-rapier2d): 添加跨平台WASM加载器 * feat(asset-system): 添加运行时资产目录和bundle格式 * feat(asset-system-editor): 新增编辑器资产管理包 * feat(editor-core): 添加构建系统和模块管理 * feat(editor-app): 重构浏览器预览使用import maps * feat(platform-web): 添加BrowserRuntime和资产读取 * feat(engine): 添加材质系统和着色器管理 * feat(material): 新增材质系统和着色器编辑器 * feat(tilemap): 增强tilemap编辑器和动画系统 * feat(modules): 添加module.json配置 * feat(core): 添加module.json和类型定义更新 * chore: 更新依赖和构建配置 * refactor(plugins): 更新插件模板使用ModuleManifest * chore: 添加第三方依赖库 * chore: 移除BehaviourTree-ai和ecs-astar子模块 * docs: 更新README和文档主题样式 * fix: 修复Rust文档测试和添加rapier2d WASM绑定 * fix(tilemap-editor): 修复画布高DPI屏幕分辨率适配问题 * feat(ui): 添加UI屏幕适配系统(CanvasScaler/SafeArea) * fix(ecs-engine-bindgen): 添加缺失的ecs-framework-math依赖 * fix: 添加缺失的包依赖修复CI构建 * fix: 修复CodeQL检测到的代码问题 * fix: 修复构建错误和缺失依赖 * fix: 修复类型检查错误 * fix(material-system): 修复tsconfig配置支持TypeScript项目引用 * fix(editor-core): 修复Rollup构建配置添加tauri external * fix: 修复CodeQL检测到的代码问题 * fix: 修复CodeQL检测到的代码问题
2025-12-03 22:15:22 +08:00
}
/**
* Set the asset manager for loading material files.
*
*
* Also registers Material and Shader loaders with the asset manager.
*
*
* @param manager - Asset manager instance. |
*/
setAssetManager(manager: IAssetManager): void {
this.assetManager = manager;
// Register loaders with asset manager.
// 向资产管理器注册加载器。
if (manager.registerLoader) {
manager.registerLoader(AssetType.Material, new MaterialLoader());
manager.registerLoader(AssetType.Shader, new ShaderLoader());
logger.info('Registered Material and Shader loaders');
}
}
private registerBuiltInAssets(): void {
// Built-in shaders
const builtInShaders = [
{ id: BuiltInShaders.DefaultSprite, name: 'DefaultSprite', vertex: DEFAULT_VERTEX_SHADER, fragment: DEFAULT_FRAGMENT_SHADER },
{ id: BuiltInShaders.Grayscale, name: 'Grayscale', vertex: DEFAULT_VERTEX_SHADER, fragment: GRAYSCALE_FRAGMENT_SHADER },
{ id: BuiltInShaders.Tint, name: 'Tint', vertex: DEFAULT_VERTEX_SHADER, fragment: TINT_FRAGMENT_SHADER },
{ id: BuiltInShaders.Flash, name: 'Flash', vertex: DEFAULT_VERTEX_SHADER, fragment: FLASH_FRAGMENT_SHADER },
{ id: BuiltInShaders.Outline, name: 'Outline', vertex: DEFAULT_VERTEX_SHADER, fragment: OUTLINE_FRAGMENT_SHADER },
refactor(ui): UI 系统架构重构 (#309) * 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 的位置和锚点值映射
2025-12-19 15:33:36 +08:00
{ id: BuiltInShaders.Shiny, name: 'Shiny', vertex: DEFAULT_VERTEX_SHADER, fragment: SHINY_FRAGMENT_SHADER },
feat: 添加跨平台运行时、资产系统和UI适配功能 (#256) * feat(platform-common): 添加WASM加载器和环境检测API * feat(rapier2d): 新增Rapier2D WASM绑定包 * feat(physics-rapier2d): 添加跨平台WASM加载器 * feat(asset-system): 添加运行时资产目录和bundle格式 * feat(asset-system-editor): 新增编辑器资产管理包 * feat(editor-core): 添加构建系统和模块管理 * feat(editor-app): 重构浏览器预览使用import maps * feat(platform-web): 添加BrowserRuntime和资产读取 * feat(engine): 添加材质系统和着色器管理 * feat(material): 新增材质系统和着色器编辑器 * feat(tilemap): 增强tilemap编辑器和动画系统 * feat(modules): 添加module.json配置 * feat(core): 添加module.json和类型定义更新 * chore: 更新依赖和构建配置 * refactor(plugins): 更新插件模板使用ModuleManifest * chore: 添加第三方依赖库 * chore: 移除BehaviourTree-ai和ecs-astar子模块 * docs: 更新README和文档主题样式 * fix: 修复Rust文档测试和添加rapier2d WASM绑定 * fix(tilemap-editor): 修复画布高DPI屏幕分辨率适配问题 * feat(ui): 添加UI屏幕适配系统(CanvasScaler/SafeArea) * fix(ecs-engine-bindgen): 添加缺失的ecs-framework-math依赖 * fix: 添加缺失的包依赖修复CI构建 * fix: 修复CodeQL检测到的代码问题 * fix: 修复构建错误和缺失依赖 * fix: 修复类型检查错误 * fix(material-system): 修复tsconfig配置支持TypeScript项目引用 * fix(editor-core): 修复Rollup构建配置添加tauri external * fix: 修复CodeQL检测到的代码问题 * fix: 修复CodeQL检测到的代码问题
2025-12-03 22:15:22 +08:00
];
for (const { id, name, vertex, fragment } of builtInShaders) {
const shader = new Shader(name, vertex, fragment);
shader.id = id;
this.shaders.set(id, shader);
this.shaderNameToId.set(name, id);
}
// Built-in materials
const builtInMaterials = [
{ id: BuiltInMaterials.Default, material: Material.sprite() },
{ id: BuiltInMaterials.Additive, material: Material.additive() },
{ id: BuiltInMaterials.Multiply, material: Material.multiply() },
{ id: BuiltInMaterials.Unlit, material: Material.unlit() },
];
for (const { id, material } of builtInMaterials) {
material.id = id;
this.materials.set(id, material);
this.materialNameToId.set(material.name, id);
}
}
// ============= Shader Management =============
// ============= 着色器管理 =============
/**
* Register a shader.
*
*
* # Arguments |
* * `shader` - Shader instance to register. |
*
* # Returns |
* Shader ID for referencing this shader. | ID
*/
async registerShader(shader: Shader): Promise<number> {
const shaderId = this.nextShaderId++;
shader.id = shaderId;
// Compile on GPU if engine bridge is available
if (this.engineBridge) {
await this.engineBridge.compileShaderWithId(
shaderId,
shader.vertexSource,
shader.fragmentSource
);
shader.markCompiled();
}
this.shaders.set(shaderId, shader);
this.shaderNameToId.set(shader.name, shaderId);
return shaderId;
}
/**
* Get a shader by ID.
* ID获取着色器
*
* # Arguments |
* * `shaderId` - Shader ID to look up. | ID
*
* # Returns |
* Shader instance or undefined if not found. | undefined
*/
getShader(shaderId: number): Shader | undefined {
return this.shaders.get(shaderId);
}
/**
* Get a shader by name.
*
*
* # Arguments |
* * `name` - Shader name to look up. |
*
* # Returns |
* Shader instance or undefined if not found. | undefined
*/
getShaderByName(name: string): Shader | undefined {
const id = this.shaderNameToId.get(name);
return id !== undefined ? this.shaders.get(id) : undefined;
}
/**
* Check if a shader exists.
*
*
* # Arguments |
* * `shaderId` - Shader ID to check. | ID
*/
hasShader(shaderId: number): boolean {
return this.shaders.has(shaderId);
}
/**
* Remove a shader.
*
*
* # Arguments |
* * `shaderId` - Shader ID to remove. | ID
*
* # Returns |
* True if shader was removed, false if not found or is built-in. | true false
*/
removeShader(shaderId: number): boolean {
if (shaderId < 100) {
logger.warn('Cannot remove built-in shader:', shaderId);
return false;
}
const shader = this.shaders.get(shaderId);
if (shader) {
this.shaderNameToId.delete(shader.name);
this.shaders.delete(shaderId);
if (this.engineBridge) {
this.engineBridge.removeShader(shaderId);
}
return true;
}
return false;
}
// ============= Path-based Shader Loading =============
// ============= 基于路径的着色器加载 =============
/**
* Get shader ID by file path.
* ID
*
* Returns 0 (default shader) if not loaded.
* 0
*
* # Arguments |
* * `path` - Shader file path (.shader). | .shader
*
* # Returns |
* Shader ID or 0 if not found. | ID0
*/
getShaderIdByPath(path: string): number {
if (!path) return 0;
return this.shaderPathToId.get(path) ?? 0;
}
/**
* Check if a shader is loaded from a path.
*
*
* # Arguments |
* * `path` - Shader file path to check. |
*/
hasShaderByPath(path: string): boolean {
return this.shaderPathToId.has(path);
}
/**
* Load a shader from a .shader file path.
* .shader
*
* Uses asset-system for file loading and caches the result.
* 使 asset-system
*
* # Arguments |
* * `path` - Shader file path. |
*
* # Returns |
* Shader ID (0 if load failed). | ID0
*/
async loadShaderFromPath(path: string): Promise<number> {
// Return cached ID if already loaded.
// 如果已加载则返回缓存的ID。
const existingId = this.shaderPathToId.get(path);
if (existingId !== undefined) {
return existingId;
}
// Return pending promise if already loading.
// 如果正在加载则返回等待中的 Promise。
const pendingLoad = this.pendingShaderLoads.get(path);
if (pendingLoad) {
return pendingLoad;
}
// Create loading promise.
// 创建加载 Promise。
const loadPromise = this.doLoadShaderFromPath(path);
this.pendingShaderLoads.set(path, loadPromise);
try {
const shaderId = await loadPromise;
return shaderId;
} finally {
this.pendingShaderLoads.delete(path);
}
}
/**
* Internal method to load shader from path.
*
*
* # Arguments |
* * `path` - Shader file path. |
*
* # Returns |
* Shader ID, or 0 if load failed. | ID0
*/
private async doLoadShaderFromPath(path: string): Promise<number> {
if (!this.assetManager) {
logger.warn('No asset manager set, cannot load shader from path:', path);
return 0;
}
try {
// Use asset-system to load shader file.
// 使用 asset-system 加载着色器文件。
const result = await this.assetManager.loadAssetByPath<IShaderAssetData>(path);
// Get shader from asset data.
// 从资产数据获取着色器。
const shader = result.asset.shader;
if (!shader) {
logger.error('Shader asset is null for path:', path);
return 0;
}
// Register the shader.
// 注册着色器。
const shaderId = await this.registerShader(shader);
// Cache path -> ID mapping.
// 缓存路径到ID的映射。
this.shaderPathToId.set(path, shaderId);
return shaderId;
} catch (error) {
logger.error('Failed to load shader from path:', path, error);
return 0;
}
}
/**
* Unload a shader loaded from a path.
*
*
* # Arguments |
* * `path` - Shader file path to unload. |
*
* # Returns |
* True if unloaded successfully. | true
*/
unloadShaderByPath(path: string): boolean {
const shaderId = this.shaderPathToId.get(path);
if (shaderId === undefined) {
return false;
}
this.shaderPathToId.delete(path);
return this.removeShader(shaderId);
}
// ============= Material Management =============
// ============= 材质管理 =============
/**
* Register a material.
*
*
* # Arguments |
* * `material` - Material instance to register. |
*
* # Returns |
* Material ID for referencing this material. | ID
*/
registerMaterial(material: Material): number {
const materialId = this.nextMaterialId++;
material.id = materialId;
// Create on GPU if engine bridge is available
if (this.engineBridge) {
this.engineBridge.createMaterialWithId(
materialId,
material.name,
material.shaderId,
material.blendMode
);
this.syncMaterialUniforms(material);
}
this.materials.set(materialId, material);
this.materialNameToId.set(material.name, materialId);
return materialId;
}
/**
* Get a material by ID.
* ID获取材质
*
* # Arguments |
* * `materialId` - Material ID to look up. | ID
*
* # Returns |
* Material instance or undefined if not found. | undefined
*/
getMaterial(materialId: number): Material | undefined {
return this.materials.get(materialId);
}
/**
* Get a material by name.
*
*
* # Arguments |
* * `name` - Material name to look up. |
*
* # Returns |
* Material instance or undefined if not found. | undefined
*/
getMaterialByName(name: string): Material | undefined {
const id = this.materialNameToId.get(name);
return id !== undefined ? this.materials.get(id) : undefined;
}
/**
* Check if a material exists.
*
*
* # Arguments |
* * `materialId` - Material ID to check. | ID
*/
hasMaterial(materialId: number): boolean {
return this.materials.has(materialId);
}
/**
* Remove a material.
*
*
* # Arguments |
* * `materialId` - Material ID to remove. | ID
*
* # Returns |
* True if material was removed, false if not found or is built-in. | true false
*/
removeMaterial(materialId: number): boolean {
if (materialId < 100) {
logger.warn('Cannot remove built-in material:', materialId);
return false;
}
const material = this.materials.get(materialId);
if (material) {
this.materialNameToId.delete(material.name);
this.materials.delete(materialId);
if (this.engineBridge) {
this.engineBridge.removeMaterial(materialId);
}
return true;
}
return false;
}
/**
* Sync material uniforms to GPU.
* uniform GPU
*
* # Arguments |
* * `material` - Material to sync. |
*/
syncMaterialUniforms(material: Material): void {
if (!this.engineBridge || material.id < 0) return;
for (const [name, uniform] of material.getUniforms()) {
switch (uniform.type) {
case UniformType.Float:
this.engineBridge.setMaterialFloat(material.id, name, uniform.value as number);
break;
case UniformType.Vec2: {
const v2 = uniform.value as number[];
this.engineBridge.setMaterialVec2(material.id, name, v2[0], v2[1]);
break;
}
case UniformType.Vec3: {
const v3 = uniform.value as number[];
this.engineBridge.setMaterialVec3(material.id, name, v3[0], v3[1], v3[2]);
break;
}
case UniformType.Vec4: {
const v4 = uniform.value as number[];
this.engineBridge.setMaterialVec4(material.id, name, v4[0], v4[1], v4[2], v4[3]);
break;
}
case UniformType.Color: {
const c = uniform.value as number[];
this.engineBridge.setMaterialColor(material.id, name, c[0], c[1], c[2], c[3]);
break;
}
}
}
material.markClean();
}
/**
* Update all dirty materials.
*
*
* Syncs all materials that have been modified since last sync.
*
*/
syncDirtyMaterials(): void {
for (const material of this.materials.values()) {
if (material.dirty) {
this.syncMaterialUniforms(material);
}
}
}
// ============= Path-based Material Loading =============
// ============= 基于路径的材质加载 =============
/**
* Get material ID by file path.
* ID
*
* Returns 0 (default material) if not loaded.
* 0
*
* # Arguments |
* * `path` - Material file path (.mat). | .mat
*
* # Returns |
* Material ID or 0 if not found. | ID0
*/
getMaterialIdByPath(path: string): number {
if (!path) return 0;
return this.materialPathToId.get(path) ?? 0;
}
/**
* Check if a material is loaded from a path.
*
*
* # Arguments |
* * `path` - Material file path to check. |
*/
hasMaterialByPath(path: string): boolean {
return this.materialPathToId.has(path);
}
/**
* Load a material from a .mat file path.
* .mat
*
* Uses asset-system for file loading and caches the result.
* 使 asset-system
*
* # Arguments |
* * `path` - Material file path. |
*
* # Returns |
* Material ID (0 if load failed). | ID0
*/
async loadMaterialFromPath(path: string): Promise<number> {
// Return cached ID if already loaded
const existingId = this.materialPathToId.get(path);
if (existingId !== undefined) {
return existingId;
}
// Return pending promise if already loading
const pendingLoad = this.pendingMaterialLoads.get(path);
if (pendingLoad) {
return pendingLoad;
}
// Create loading promise
const loadPromise = this.doLoadMaterialFromPath(path);
this.pendingMaterialLoads.set(path, loadPromise);
try {
const materialId = await loadPromise;
return materialId;
} finally {
this.pendingMaterialLoads.delete(path);
}
}
/**
* Internal method to load material from path.
*
*
* @param path - Material file path. |
* @returns Material ID, or 0 if load failed. | ID0
*/
private async doLoadMaterialFromPath(path: string): Promise<number> {
if (!this.assetManager) {
logger.warn('No asset manager set, cannot load material from path:', path);
return 0;
}
try {
// Use asset-system to load material file.
// 使用 asset-system 加载材质文件。
const result = await this.assetManager.loadAssetByPath<IMaterialAssetData>(path);
// Get material from asset data.
// 从资产数据获取材质。
const material = result.asset.material;
if (!material) {
logger.error('Material asset is null for path:', path);
return 0;
}
// Register the material.
// 注册材质。
const materialId = this.registerMaterial(material);
// Cache path -> ID mapping.
// 缓存路径到ID的映射。
this.materialPathToId.set(path, materialId);
return materialId;
} catch (error) {
logger.error('Failed to load material from path:', path, error);
return 0;
}
}
/**
* Preload multiple materials from paths.
*
*
* Loads all materials in parallel for better performance.
*
*
* # Arguments |
* * `paths` - Array of material file paths. |
*
* # Returns |
* Map of path to material ID. | ID的映射
*/
async preloadMaterials(paths: string[]): Promise<Map<string, number>> {
const results = new Map<string, number>();
await Promise.all(
paths.map(async (path) => {
const id = await this.loadMaterialFromPath(path);
results.set(path, id);
})
);
return results;
}
/**
* Unload a material loaded from a path.
*
*
* # Arguments |
* * `path` - Material file path to unload. |
*
* # Returns |
* True if unloaded successfully. | true
*/
unloadMaterialByPath(path: string): boolean {
const materialId = this.materialPathToId.get(path);
if (materialId === undefined) {
return false;
}
this.materialPathToId.delete(path);
return this.removeMaterial(materialId);
}
// ============= Convenience Methods =============
// ============= 便捷方法 =============
/**
* Create a sprite material with optional tint.
*
*
* # Arguments |
* * `name` - Material name. |
* * `tintR` - Red tint (0-1). | 0-1
* * `tintG` - Green tint (0-1). | 绿0-1
* * `tintB` - Blue tint (0-1). | 0-1
*
* # Returns |
* New Material instance. |
*/
createSpriteMaterial(name: string, tintR: number = 1, tintG: number = 1, tintB: number = 1): Material {
const material = new Material(name, BuiltInShaders.DefaultSprite);
material.setColor('u_tint', tintR, tintG, tintB, 1.0);
return material;
}
/**
* Get all shader IDs.
* ID
*
* # Returns |
* Array of all registered shader IDs. | ID的数组
*/
getShaderIds(): number[] {
return Array.from(this.shaders.keys());
}
/**
* Get all material IDs.
* ID
*
* # Returns |
* Array of all registered material IDs. | ID的数组
*/
getMaterialIds(): number[] {
return Array.from(this.materials.keys());
}
}
// Singleton instance.
// 单例实例。
let materialManagerInstance: MaterialManager | null = null;
/**
* Get the global MaterialManager instance.
* MaterialManager
*
* Creates a new instance if one doesn't exist.
*
*
* # Returns |
* The global MaterialManager instance. | MaterialManager
*/
export function getMaterialManager(): MaterialManager {
if (!materialManagerInstance) {
materialManagerInstance = new MaterialManager();
}
return materialManagerInstance;
}