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 的位置和锚点值映射
This commit is contained in:
@@ -14,7 +14,8 @@ import {
|
||||
GRAYSCALE_FRAGMENT_SHADER,
|
||||
TINT_FRAGMENT_SHADER,
|
||||
FLASH_FRAGMENT_SHADER,
|
||||
OUTLINE_FRAGMENT_SHADER
|
||||
OUTLINE_FRAGMENT_SHADER,
|
||||
SHINY_FRAGMENT_SHADER
|
||||
} from './Shader';
|
||||
import { BuiltInMaterials, BuiltInShaders, UniformType } from './types';
|
||||
import type { IAssetManager } from '@esengine/asset-system';
|
||||
@@ -103,10 +104,67 @@ export class MaterialManager {
|
||||
* Set the engine bridge for GPU operations.
|
||||
* 设置用于GPU操作的引擎桥接。
|
||||
*
|
||||
* When set, uploads all built-in shaders to the GPU.
|
||||
* 设置后,将所有内置着色器上传到GPU。
|
||||
*
|
||||
* @param bridge - Engine bridge instance. | 引擎桥接实例。
|
||||
*/
|
||||
setEngineBridge(bridge: IEngineBridge): void {
|
||||
this.engineBridge = bridge;
|
||||
|
||||
// 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 作为 materialId(1: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} 个材质`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,6 +196,7 @@ export class MaterialManager {
|
||||
{ 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 },
|
||||
{ id: BuiltInShaders.Shiny, name: 'Shiny', vertex: DEFAULT_VERTEX_SHADER, fragment: SHINY_FRAGMENT_SHADER },
|
||||
];
|
||||
|
||||
for (const { id, name, vertex, fragment } of builtInShaders) {
|
||||
|
||||
@@ -2,7 +2,13 @@
|
||||
* MaterialSystemPlugin for ES Engine.
|
||||
* ES引擎的材质系统插件。
|
||||
*
|
||||
* 注意:材质系统不注册独立组件,材质作为渲染组件(如 SpriteComponent)的属性使用
|
||||
* Provides:
|
||||
* - Material and Shader management
|
||||
* - Built-in shaders (Default, Grayscale, Tint, Flash, Outline, Shiny)
|
||||
*
|
||||
* 提供:
|
||||
* - 材质和着色器管理
|
||||
* - 内置着色器
|
||||
*/
|
||||
|
||||
import { MaterialManager, getMaterialManager } from './MaterialManager';
|
||||
@@ -82,7 +88,9 @@ const manifest: ModuleManifest = {
|
||||
defaultEnabled: true,
|
||||
isEngineModule: true,
|
||||
dependencies: ['core', 'asset-system'],
|
||||
exports: { other: ['Material', 'Shader', 'MaterialManager'] },
|
||||
exports: {
|
||||
other: ['Material', 'Shader', 'MaterialManager']
|
||||
},
|
||||
requiresWasm: false
|
||||
};
|
||||
|
||||
|
||||
@@ -120,6 +120,13 @@ export class Shader {
|
||||
/**
|
||||
* Default sprite vertex shader source.
|
||||
* 默认精灵顶点着色器源代码。
|
||||
*
|
||||
* Vertex layout (9 floats per vertex):
|
||||
* 顶点布局(每顶点 9 个浮点数):
|
||||
* - location 0: position (2 floats)
|
||||
* - location 1: tex_coord (2 floats)
|
||||
* - location 2: color (4 floats)
|
||||
* - location 3: aspect_ratio (1 float)
|
||||
*/
|
||||
export const DEFAULT_VERTEX_SHADER = `#version 300 es
|
||||
precision highp float;
|
||||
@@ -128,6 +135,7 @@ precision highp float;
|
||||
layout(location = 0) in vec2 a_position;
|
||||
layout(location = 1) in vec2 a_texCoord;
|
||||
layout(location = 2) in vec4 a_color;
|
||||
layout(location = 3) in float a_aspectRatio;
|
||||
|
||||
// Uniforms | 统一变量
|
||||
uniform mat3 u_projection;
|
||||
@@ -135,6 +143,7 @@ uniform mat3 u_projection;
|
||||
// Outputs to fragment shader | 输出到片段着色器
|
||||
out vec2 v_texCoord;
|
||||
out vec4 v_color;
|
||||
out float v_aspectRatio;
|
||||
|
||||
void main() {
|
||||
// Apply projection matrix | 应用投影矩阵
|
||||
@@ -144,6 +153,7 @@ void main() {
|
||||
// Pass through to fragment shader | 传递到片段着色器
|
||||
v_texCoord = a_texCoord;
|
||||
v_color = a_color;
|
||||
v_aspectRatio = a_aspectRatio;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -157,6 +167,7 @@ precision highp float;
|
||||
// Inputs from vertex shader | 来自顶点着色器的输入
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
in float v_aspectRatio;
|
||||
|
||||
// Texture sampler | 纹理采样器
|
||||
uniform sampler2D u_texture;
|
||||
@@ -185,6 +196,7 @@ precision highp float;
|
||||
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
in float v_aspectRatio;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform float u_grayscale; // 0.0 = full color, 1.0 = full grayscale
|
||||
@@ -215,6 +227,7 @@ precision highp float;
|
||||
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
in float v_aspectRatio;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform vec4 u_tintColor; // Tint color to apply
|
||||
@@ -243,6 +256,7 @@ precision highp float;
|
||||
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
in float v_aspectRatio;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform vec4 u_flashColor; // Flash color
|
||||
@@ -273,6 +287,7 @@ precision highp float;
|
||||
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
in float v_aspectRatio;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform vec4 u_outlineColor;
|
||||
@@ -309,3 +324,98 @@ void main() {
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* Shiny/Shimmer effect fragment shader.
|
||||
* 闪光效果片段着色器。
|
||||
*
|
||||
* Uses v_aspectRatio from vertex attribute for aspect-ratio-aware rotation.
|
||||
* 使用顶点属性中的 v_aspectRatio 进行宽高比感知的旋转。
|
||||
*/
|
||||
export const SHINY_FRAGMENT_SHADER = `#version 300 es
|
||||
precision highp float;
|
||||
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
in float v_aspectRatio;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
|
||||
// Shiny effect uniforms | 闪光效果 uniform 变量
|
||||
uniform float u_shinyProgress; // Animation progress (0-1) | 动画进度
|
||||
uniform float u_shinyWidth; // Width of shine band (0-1) | 闪光带宽度
|
||||
uniform float u_shinyRotation; // Rotation in radians | 旋转角度(弧度)
|
||||
uniform float u_shinySoftness; // Edge softness (0-1) | 边缘柔和度
|
||||
uniform float u_shinyBrightness; // Brightness multiplier | 亮度倍数
|
||||
uniform float u_shinyGloss; // Gloss intensity (0=white, 1=color-tinted) | 光泽度
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec4 texColor = texture(u_texture, v_texCoord);
|
||||
float originAlpha = texColor.a;
|
||||
vec4 color = texColor * v_color;
|
||||
|
||||
// Early discard for transparent pixels
|
||||
if (color.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
|
||||
// Calculate rotated position for the sweep (0 to 1 range)
|
||||
// 计算旋转后的扫描位置(0 到 1 范围)
|
||||
//
|
||||
// 1. 计算基础方向向量 dir = (cos(θ), sin(θ))
|
||||
// 2. 宽高比校正:dir.x *= height/width = 1/aspectRatio
|
||||
// 3. 归一化方向向量
|
||||
// 4. 计算扫描位置(考虑纹理坐标 Y 轴方向)
|
||||
//
|
||||
// 1. Calculate base direction vector dir = (cos(θ), sin(θ))
|
||||
// 2. Aspect ratio correction: dir.x *= height/width = 1/aspectRatio
|
||||
// 3. Normalize direction vector
|
||||
// 4. Calculate sweep position (accounting for texture Y-axis direction)
|
||||
//
|
||||
vec2 center = v_texCoord - vec2(0.5);
|
||||
float cosR = cos(u_shinyRotation);
|
||||
float sinR = sin(u_shinyRotation);
|
||||
|
||||
// Aspect ratio correction: scale X by 1/aspectRatio (height/width)
|
||||
// v_aspectRatio is passed from vertex attribute, calculated at render time
|
||||
// 宽高比校正:X 分量乘以 1/aspectRatio(即 height/width)
|
||||
// v_aspectRatio 从顶点属性传入,在渲染时计算
|
||||
float adjCosR = cosR / max(v_aspectRatio, 0.001);
|
||||
|
||||
// Normalize the direction vector
|
||||
// 归一化方向向量
|
||||
float len = sqrt(adjCosR * adjCosR + sinR * sinR);
|
||||
float dirX = adjCosR / len;
|
||||
float dirY = sinR / len;
|
||||
|
||||
// Sweep position: project onto perpendicular direction
|
||||
// Y-axis flip: texture coords have Y pointing up, but we want top-to-bottom sweep
|
||||
// 扫描位置:投影到垂直方向
|
||||
// Y 轴翻转:纹理坐标 Y 向上,但我们需要从上到下扫描
|
||||
float rotatedPos = (center.x * dirY - center.y * dirX) + 0.5;
|
||||
|
||||
// Map progress to location (-0.5 to 1.5 range for smooth entry/exit)
|
||||
float location = u_shinyProgress * 2.0 - 0.5;
|
||||
|
||||
// Calculate normalized distance (1 at center, 0 at edges)
|
||||
// 计算归一化距离(中心为1,边缘为0)
|
||||
float normalized = 1.0 - clamp(abs((rotatedPos - location) / max(u_shinyWidth, 0.001)), 0.0, 1.0);
|
||||
|
||||
// Apply softness with smoothstep
|
||||
// 使用 smoothstep 应用柔和度
|
||||
float shinePower = smoothstep(0.0, u_shinySoftness * 2.0, normalized);
|
||||
|
||||
// Calculate reflect color: lerp between white and bright original color
|
||||
// 计算反射颜色:在白色和明亮的原色之间插值
|
||||
vec3 reflectColor = mix(vec3(1.0), color.rgb * 10.0, u_shinyGloss);
|
||||
|
||||
// Apply shine: additive blend with halved intensity
|
||||
// 应用高光:半强度加性混合
|
||||
vec3 shineAdd = originAlpha * (shinePower * 0.5) * u_shinyBrightness * reflectColor;
|
||||
vec3 finalColor = color.rgb + shineAdd;
|
||||
|
||||
fragColor = vec4(finalColor, color.a);
|
||||
}
|
||||
`;
|
||||
|
||||
189
packages/material-system/src/effects/BaseShinyEffect.ts
Normal file
189
packages/material-system/src/effects/BaseShinyEffect.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* Base shiny effect component for ES Engine.
|
||||
* ES引擎基础闪光效果组件。
|
||||
*
|
||||
* This abstract base class provides shared shiny effect properties and methods
|
||||
* that can be extended by both SpriteShinyEffectComponent and UIShinyEffectComponent.
|
||||
* 此抽象基类提供可由 SpriteShinyEffectComponent 和 UIShinyEffectComponent 扩展的
|
||||
* 共享闪光效果属性和方法。
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base interface for shiny effect configuration.
|
||||
* 闪光效果配置的基础接口。
|
||||
*
|
||||
* This interface defines all properties needed for the shiny effect animation.
|
||||
* 此接口定义了闪光效果动画所需的所有属性。
|
||||
*/
|
||||
export interface IShinyEffect {
|
||||
// ============= Effect Parameters =============
|
||||
// ============= 效果参数 =============
|
||||
|
||||
/**
|
||||
* Width of the shiny band (0.0 - 1.0).
|
||||
* 闪光带宽度 (0.0 - 1.0)。
|
||||
*/
|
||||
width: number;
|
||||
|
||||
/**
|
||||
* Rotation angle in degrees.
|
||||
* 旋转角度(度)。
|
||||
*/
|
||||
rotation: number;
|
||||
|
||||
/**
|
||||
* Edge softness (0.0 - 1.0).
|
||||
* 边缘柔和度 (0.0 - 1.0)。
|
||||
*/
|
||||
softness: number;
|
||||
|
||||
/**
|
||||
* Brightness multiplier.
|
||||
* 亮度倍增器。
|
||||
*/
|
||||
brightness: number;
|
||||
|
||||
/**
|
||||
* Gloss intensity.
|
||||
* 光泽度。
|
||||
*/
|
||||
gloss: number;
|
||||
|
||||
// ============= Animation Settings =============
|
||||
// ============= 动画设置 =============
|
||||
|
||||
/**
|
||||
* Whether the animation is playing.
|
||||
* 动画是否正在播放。
|
||||
*/
|
||||
play: boolean;
|
||||
|
||||
/**
|
||||
* Whether to loop the animation.
|
||||
* 是否循环动画。
|
||||
*/
|
||||
loop: boolean;
|
||||
|
||||
/**
|
||||
* Animation duration in seconds.
|
||||
* 动画持续时间(秒)。
|
||||
*/
|
||||
duration: number;
|
||||
|
||||
/**
|
||||
* Delay between loops in seconds.
|
||||
* 循环之间的延迟(秒)。
|
||||
*/
|
||||
loopDelay: number;
|
||||
|
||||
/**
|
||||
* Initial delay before first play in seconds.
|
||||
* 首次播放前的初始延迟(秒)。
|
||||
*/
|
||||
initialDelay: number;
|
||||
|
||||
// ============= Runtime State =============
|
||||
// ============= 运行时状态 =============
|
||||
|
||||
/** Current animation progress (0.0 - 1.0). | 当前动画进度。 */
|
||||
progress: number;
|
||||
|
||||
/** Current elapsed time in the animation cycle. | 当前周期已用时间。 */
|
||||
elapsedTime: number;
|
||||
|
||||
/** Whether currently in delay phase. | 是否处于延迟阶段。 */
|
||||
inDelay: boolean;
|
||||
|
||||
/** Remaining delay time. | 剩余延迟时间。 */
|
||||
delayRemaining: number;
|
||||
|
||||
/** Whether the initial delay has been processed. | 初始延迟是否已处理。 */
|
||||
initialDelayProcessed: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default values for shiny effect properties.
|
||||
* 闪光效果属性的默认值。
|
||||
*/
|
||||
export const SHINY_EFFECT_DEFAULTS = {
|
||||
width: 0.25,
|
||||
rotation: 129,
|
||||
softness: 1.0,
|
||||
brightness: 1.0,
|
||||
gloss: 1.0,
|
||||
play: true,
|
||||
loop: true,
|
||||
duration: 2.0,
|
||||
loopDelay: 2.0,
|
||||
initialDelay: 0,
|
||||
progress: 0,
|
||||
elapsedTime: 0,
|
||||
inDelay: false,
|
||||
delayRemaining: 0,
|
||||
initialDelayProcessed: false
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Property metadata for shiny effect Inspector.
|
||||
* 闪光效果 Inspector 的属性元数据。
|
||||
*/
|
||||
export const SHINY_EFFECT_PROPERTIES = {
|
||||
width: { type: 'number', label: 'Width', min: 0, max: 1, step: 0.01 },
|
||||
rotation: { type: 'number', label: 'Rotation', min: 0, max: 360, step: 1 },
|
||||
softness: { type: 'number', label: 'Softness', min: 0, max: 1, step: 0.01 },
|
||||
brightness: { type: 'number', label: 'Brightness', min: 0, max: 2, step: 0.01 },
|
||||
gloss: { type: 'number', label: 'Gloss', min: 0, max: 2, step: 0.01 },
|
||||
play: { type: 'boolean', label: 'Play' },
|
||||
loop: { type: 'boolean', label: 'Loop' },
|
||||
duration: { type: 'number', label: 'Duration', min: 0.1, step: 0.1 },
|
||||
loopDelay: { type: 'number', label: 'Loop Delay', min: 0, step: 0.1 },
|
||||
initialDelay: { type: 'number', label: 'Initial Delay', min: 0, step: 0.1 }
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Reset shiny effect runtime state.
|
||||
* 重置闪光效果运行时状态。
|
||||
*
|
||||
* @param effect - The shiny effect to reset | 要重置的闪光效果
|
||||
*/
|
||||
export function resetShinyEffect(effect: IShinyEffect): void {
|
||||
effect.progress = 0;
|
||||
effect.elapsedTime = 0;
|
||||
effect.inDelay = false;
|
||||
effect.delayRemaining = 0;
|
||||
effect.initialDelayProcessed = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start playing the shiny effect.
|
||||
* 开始播放闪光效果。
|
||||
*
|
||||
* @param effect - The shiny effect to start | 要开始的闪光效果
|
||||
*/
|
||||
export function startShinyEffect(effect: IShinyEffect): void {
|
||||
resetShinyEffect(effect);
|
||||
effect.play = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the shiny effect.
|
||||
* 停止闪光效果。
|
||||
*
|
||||
* @param effect - The shiny effect to stop | 要停止的闪光效果
|
||||
*/
|
||||
export function stopShinyEffect(effect: IShinyEffect): void {
|
||||
effect.play = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rotation in radians for shader use.
|
||||
* 获取弧度制的旋转角度供着色器使用。
|
||||
*
|
||||
* @param effect - The shiny effect | 闪光效果
|
||||
* @returns Rotation in radians | 弧度制的旋转角度
|
||||
*/
|
||||
export function getShinyRotationRadians(effect: IShinyEffect): number {
|
||||
return effect.rotation * Math.PI / 180;
|
||||
}
|
||||
153
packages/material-system/src/effects/ShinyEffectAnimator.ts
Normal file
153
packages/material-system/src/effects/ShinyEffectAnimator.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* Shiny effect animator for ES Engine.
|
||||
* ES引擎闪光效果动画器。
|
||||
*
|
||||
* This module provides shared animation logic for shiny effects that can be used
|
||||
* by both SpriteShinyEffectSystem and UIShinyEffectSystem.
|
||||
* 此模块提供可由 SpriteShinyEffectSystem 和 UIShinyEffectSystem 使用的
|
||||
* 共享闪光效果动画逻辑。
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
import type { IShinyEffect } from './BaseShinyEffect';
|
||||
import { getShinyRotationRadians } from './BaseShinyEffect';
|
||||
import type { IMaterialOverridable } from '../interfaces/IMaterialOverridable';
|
||||
import { BuiltInShaders } from '../types';
|
||||
|
||||
/**
|
||||
* Shared animator logic for shiny effect.
|
||||
* 闪光效果共享的动画逻辑。
|
||||
*
|
||||
* This class provides static methods for updating animation state and
|
||||
* applying material overrides, eliminating code duplication between
|
||||
* sprite and UI shiny effect systems.
|
||||
* 此类提供用于更新动画状态和应用材质覆盖的静态方法,
|
||||
* 消除精灵和 UI 闪光效果系统之间的代码重复。
|
||||
*/
|
||||
export class ShinyEffectAnimator {
|
||||
/**
|
||||
* Update animation state.
|
||||
* 更新动画状态。
|
||||
*
|
||||
* This method handles:
|
||||
* - Initial delay processing
|
||||
* - Delay phase countdown
|
||||
* - Progress calculation
|
||||
* - Loop handling
|
||||
*
|
||||
* 此方法处理:
|
||||
* - 初始延迟处理
|
||||
* - 延迟阶段倒计时
|
||||
* - 进度计算
|
||||
* - 循环处理
|
||||
*
|
||||
* @param shiny - The shiny effect component | 闪光效果组件
|
||||
* @param deltaTime - Time elapsed since last frame (seconds) | 上一帧以来经过的时间(秒)
|
||||
*/
|
||||
static updateAnimation(shiny: IShinyEffect, deltaTime: number): void {
|
||||
// Handle initial delay
|
||||
// 处理初始延迟
|
||||
if (!shiny.initialDelayProcessed && shiny.initialDelay > 0) {
|
||||
shiny.delayRemaining = shiny.initialDelay;
|
||||
shiny.inDelay = true;
|
||||
shiny.initialDelayProcessed = true;
|
||||
}
|
||||
|
||||
// Handle delay phase
|
||||
// 处理延迟阶段
|
||||
if (shiny.inDelay) {
|
||||
shiny.delayRemaining -= deltaTime;
|
||||
if (shiny.delayRemaining <= 0) {
|
||||
shiny.inDelay = false;
|
||||
shiny.elapsedTime = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Update elapsed time
|
||||
// 更新已用时间
|
||||
shiny.elapsedTime += deltaTime;
|
||||
|
||||
// Calculate progress (0 to 1)
|
||||
// 计算进度(0 到 1)
|
||||
shiny.progress = Math.min(shiny.elapsedTime / shiny.duration, 1.0);
|
||||
|
||||
// Check if animation completed
|
||||
// 检查动画是否完成
|
||||
if (shiny.progress >= 1.0) {
|
||||
if (shiny.loop) {
|
||||
// Start loop delay
|
||||
// 开始循环延迟
|
||||
shiny.inDelay = true;
|
||||
shiny.delayRemaining = shiny.loopDelay;
|
||||
shiny.progress = 0;
|
||||
shiny.elapsedTime = 0;
|
||||
} else {
|
||||
// Stop animation
|
||||
// 停止动画
|
||||
shiny.play = false;
|
||||
shiny.progress = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply shiny effect material overrides to a renderable component.
|
||||
* 将闪光效果材质覆盖应用到可渲染组件。
|
||||
*
|
||||
* This method:
|
||||
* - Sets the Shiny shader if not already set
|
||||
* - Applies all uniform overrides for the shiny effect
|
||||
*
|
||||
* Note: aspectRatio is passed via vertex attribute from the rendering pipeline,
|
||||
* calculated from sprite's scaleX/scaleY in the Rust engine.
|
||||
*
|
||||
* 此方法:
|
||||
* - 如果尚未设置,则设置 Shiny 着色器
|
||||
* - 应用闪光效果的所有 uniform 覆盖
|
||||
*
|
||||
* 注意:宽高比通过渲染管线的顶点属性传递,在 Rust 引擎中从精灵的 scaleX/scaleY 计算。
|
||||
*
|
||||
* @param shiny - The shiny effect component | 闪光效果组件
|
||||
* @param target - The target component implementing IMaterialOverridable | 实现 IMaterialOverridable 的目标组件
|
||||
*/
|
||||
static applyMaterialOverrides(shiny: IShinyEffect, target: IMaterialOverridable): void {
|
||||
// Ensure target uses Shiny shader
|
||||
// 确保目标使用 Shiny 着色器
|
||||
if (target.getMaterialId() === 0) {
|
||||
target.setMaterialId(BuiltInShaders.Shiny);
|
||||
}
|
||||
|
||||
// Apply uniform overrides (aspectRatio is from vertex attribute v_aspectRatio)
|
||||
// 应用 uniform 覆盖(宽高比来自顶点属性 v_aspectRatio)
|
||||
target.setOverrideFloat('u_shinyProgress', shiny.progress);
|
||||
target.setOverrideFloat('u_shinyWidth', shiny.width);
|
||||
target.setOverrideFloat('u_shinyRotation', getShinyRotationRadians(shiny));
|
||||
target.setOverrideFloat('u_shinySoftness', shiny.softness);
|
||||
target.setOverrideFloat('u_shinyBrightness', shiny.brightness);
|
||||
target.setOverrideFloat('u_shinyGloss', shiny.gloss);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single entity with shiny effect.
|
||||
* 处理单个带有闪光效果的实体。
|
||||
*
|
||||
* This is a convenience method that combines updateAnimation and applyMaterialOverrides.
|
||||
* 这是一个结合了 updateAnimation 和 applyMaterialOverrides 的便捷方法。
|
||||
*
|
||||
* @param shiny - The shiny effect component | 闪光效果组件
|
||||
* @param target - The target component implementing IMaterialOverridable | 实现 IMaterialOverridable 的目标组件
|
||||
* @param deltaTime - Time elapsed since last frame (seconds) | 上一帧以来经过的时间(秒)
|
||||
* @returns True if the effect was processed, false if skipped | 如果效果已处理则返回 true,如果跳过则返回 false
|
||||
*/
|
||||
static processEffect(shiny: IShinyEffect, target: IMaterialOverridable, deltaTime: number): boolean {
|
||||
if (!shiny.play) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.updateAnimation(shiny, deltaTime);
|
||||
this.applyMaterialOverrides(shiny, target);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,46 @@
|
||||
// 类型。
|
||||
export * from './types';
|
||||
|
||||
// Interfaces.
|
||||
// 接口。
|
||||
export type {
|
||||
MaterialPropertyType,
|
||||
MaterialPropertyOverride,
|
||||
MaterialOverrides,
|
||||
IMaterialOverridable
|
||||
} from './interfaces/IMaterialOverridable';
|
||||
|
||||
export type {
|
||||
ShaderPropertyType,
|
||||
ShaderPropertyHint,
|
||||
ShaderPropertyMeta,
|
||||
ShaderAssetDefinition,
|
||||
ShaderAssetFile
|
||||
} from './interfaces/IShaderProperty';
|
||||
|
||||
export {
|
||||
BUILTIN_SHADER_PROPERTIES,
|
||||
getShaderProperties,
|
||||
getShaderPropertiesById
|
||||
} from './interfaces/IShaderProperty';
|
||||
|
||||
// Mixins.
|
||||
// Mixin。
|
||||
export { MaterialOverridableMixin, MaterialOverrideHelper } from './mixins/MaterialOverridableMixin';
|
||||
|
||||
// Effects.
|
||||
// 效果。
|
||||
export type { IShinyEffect } from './effects/BaseShinyEffect';
|
||||
export {
|
||||
SHINY_EFFECT_DEFAULTS,
|
||||
SHINY_EFFECT_PROPERTIES,
|
||||
resetShinyEffect,
|
||||
startShinyEffect,
|
||||
stopShinyEffect,
|
||||
getShinyRotationRadians
|
||||
} from './effects/BaseShinyEffect';
|
||||
export { ShinyEffectAnimator } from './effects/ShinyEffectAnimator';
|
||||
|
||||
// Core classes.
|
||||
// 核心类。
|
||||
export { Material } from './Material';
|
||||
@@ -35,7 +75,8 @@ export {
|
||||
GRAYSCALE_FRAGMENT_SHADER,
|
||||
TINT_FRAGMENT_SHADER,
|
||||
FLASH_FRAGMENT_SHADER,
|
||||
OUTLINE_FRAGMENT_SHADER
|
||||
OUTLINE_FRAGMENT_SHADER,
|
||||
SHINY_FRAGMENT_SHADER
|
||||
} from './Shader';
|
||||
|
||||
// Manager.
|
||||
|
||||
176
packages/material-system/src/interfaces/IMaterialOverridable.ts
Normal file
176
packages/material-system/src/interfaces/IMaterialOverridable.ts
Normal file
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* Material override interfaces for ES Engine.
|
||||
* ES引擎材质覆盖接口。
|
||||
*
|
||||
* This module provides a unified interface for components that support
|
||||
* material property overrides (SpriteComponent, UIRenderComponent, etc.).
|
||||
* 此模块为支持材质属性覆盖的组件提供统一接口。
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
/**
|
||||
* Material property override value types.
|
||||
* 材质属性覆盖值类型。
|
||||
*/
|
||||
export type MaterialPropertyType = 'float' | 'vec2' | 'vec3' | 'vec4' | 'color' | 'int';
|
||||
|
||||
/**
|
||||
* Material property override definition.
|
||||
* 材质属性覆盖定义。
|
||||
*/
|
||||
export interface MaterialPropertyOverride {
|
||||
/** Property type | 属性类型 */
|
||||
type: MaterialPropertyType;
|
||||
|
||||
/** Property value | 属性值 */
|
||||
value: number | number[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Material overrides record type.
|
||||
* 材质覆盖记录类型。
|
||||
*/
|
||||
export type MaterialOverrides = Record<string, MaterialPropertyOverride>;
|
||||
|
||||
/**
|
||||
* Interface for components that support material property overrides.
|
||||
* 支持材质属性覆盖的组件接口。
|
||||
*
|
||||
* Both SpriteComponent and UIRenderComponent implement this interface,
|
||||
* allowing unified handling by material systems and inspectors.
|
||||
* SpriteComponent 和 UIRenderComponent 都实现此接口,
|
||||
* 允许材质系统和检查器统一处理。
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* function applyShinyEffect(target: IMaterialOverridable, progress: number): void {
|
||||
* target.setMaterialId(BuiltInShaders.Shiny);
|
||||
* target.setOverrideFloat('u_shinyProgress', progress);
|
||||
* }
|
||||
*
|
||||
* // Works with both SpriteComponent and UIRenderComponent
|
||||
* applyShinyEffect(spriteComponent, 0.5);
|
||||
* applyShinyEffect(uiRenderComponent, 0.5);
|
||||
* ```
|
||||
*/
|
||||
export interface IMaterialOverridable {
|
||||
/**
|
||||
* Material GUID for asset reference.
|
||||
* 材质资产引用的 GUID。
|
||||
*/
|
||||
materialGuid: string;
|
||||
|
||||
/**
|
||||
* Current material overrides (read-only access).
|
||||
* 当前材质覆盖(只读访问)。
|
||||
*/
|
||||
readonly materialOverrides: MaterialOverrides;
|
||||
|
||||
/**
|
||||
* Get current material ID.
|
||||
* 获取当前材质 ID。
|
||||
*/
|
||||
getMaterialId(): number;
|
||||
|
||||
/**
|
||||
* Set material ID.
|
||||
* 设置材质 ID。
|
||||
*
|
||||
* @param id - Material/Shader ID from BuiltInShaders or custom shader
|
||||
* 来自 BuiltInShaders 或自定义着色器的材质/着色器 ID
|
||||
*/
|
||||
setMaterialId(id: number): void;
|
||||
|
||||
/**
|
||||
* Set a float uniform override.
|
||||
* 设置浮点 uniform 覆盖。
|
||||
*
|
||||
* @param name - Uniform name (e.g., 'u_shinyProgress')
|
||||
* @param value - Float value
|
||||
*/
|
||||
setOverrideFloat(name: string, value: number): this;
|
||||
|
||||
/**
|
||||
* Set a vec2 uniform override.
|
||||
* 设置 vec2 uniform 覆盖。
|
||||
*
|
||||
* @param name - Uniform name
|
||||
* @param x - X component
|
||||
* @param y - Y component
|
||||
*/
|
||||
setOverrideVec2(name: string, x: number, y: number): this;
|
||||
|
||||
/**
|
||||
* Set a vec3 uniform override.
|
||||
* 设置 vec3 uniform 覆盖。
|
||||
*
|
||||
* @param name - Uniform name
|
||||
* @param x - X component
|
||||
* @param y - Y component
|
||||
* @param z - Z component
|
||||
*/
|
||||
setOverrideVec3(name: string, x: number, y: number, z: number): this;
|
||||
|
||||
/**
|
||||
* Set a vec4 uniform override.
|
||||
* 设置 vec4 uniform 覆盖。
|
||||
*
|
||||
* @param name - Uniform name
|
||||
* @param x - X component
|
||||
* @param y - Y component
|
||||
* @param z - Z component
|
||||
* @param w - W component
|
||||
*/
|
||||
setOverrideVec4(name: string, x: number, y: number, z: number, w: number): this;
|
||||
|
||||
/**
|
||||
* Set a color uniform override (RGBA, 0.0-1.0).
|
||||
* 设置颜色 uniform 覆盖(RGBA,0.0-1.0)。
|
||||
*
|
||||
* @param name - Uniform name
|
||||
* @param r - Red component (0-1)
|
||||
* @param g - Green component (0-1)
|
||||
* @param b - Blue component (0-1)
|
||||
* @param a - Alpha component (0-1), defaults to 1.0
|
||||
*/
|
||||
setOverrideColor(name: string, r: number, g: number, b: number, a?: number): this;
|
||||
|
||||
/**
|
||||
* Set an integer uniform override.
|
||||
* 设置整数 uniform 覆盖。
|
||||
*
|
||||
* @param name - Uniform name
|
||||
* @param value - Integer value
|
||||
*/
|
||||
setOverrideInt(name: string, value: number): this;
|
||||
|
||||
/**
|
||||
* Get a specific override value.
|
||||
* 获取特定覆盖值。
|
||||
*
|
||||
* @param name - Uniform name
|
||||
* @returns Override value or undefined if not set
|
||||
*/
|
||||
getOverride(name: string): MaterialPropertyOverride | undefined;
|
||||
|
||||
/**
|
||||
* Remove a specific override.
|
||||
* 移除特定覆盖。
|
||||
*
|
||||
* @param name - Uniform name to remove
|
||||
*/
|
||||
removeOverride(name: string): this;
|
||||
|
||||
/**
|
||||
* Clear all overrides.
|
||||
* 清除所有覆盖。
|
||||
*/
|
||||
clearOverrides(): this;
|
||||
|
||||
/**
|
||||
* Check if any overrides are set.
|
||||
* 检查是否设置了任何覆盖。
|
||||
*/
|
||||
hasOverrides(): boolean;
|
||||
}
|
||||
369
packages/material-system/src/interfaces/IShaderProperty.ts
Normal file
369
packages/material-system/src/interfaces/IShaderProperty.ts
Normal file
@@ -0,0 +1,369 @@
|
||||
/**
|
||||
* Shader property interfaces for ES Engine.
|
||||
* ES引擎着色器属性接口。
|
||||
*
|
||||
* This module provides interfaces for defining shader property metadata,
|
||||
* enabling automatic Inspector UI generation for material editing.
|
||||
* 此模块提供用于定义着色器属性元数据的接口,
|
||||
* 实现材质编辑的自动 Inspector UI 生成。
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
/**
|
||||
* Shader property types.
|
||||
* 着色器属性类型。
|
||||
*/
|
||||
export type ShaderPropertyType =
|
||||
| 'float'
|
||||
| 'int'
|
||||
| 'vec2'
|
||||
| 'vec3'
|
||||
| 'vec4'
|
||||
| 'color'
|
||||
| 'texture';
|
||||
|
||||
/**
|
||||
* UI hint for property display.
|
||||
* 属性显示的 UI 提示。
|
||||
*/
|
||||
export type ShaderPropertyHint =
|
||||
| 'range' // Show as slider | 显示为滑块
|
||||
| 'angle' // Show as angle picker (degrees) | 显示为角度选择器(度)
|
||||
| 'hdr' // HDR color picker | HDR 颜色选择器
|
||||
| 'normal' // Normal map preview | 法线贴图预览
|
||||
| 'default'; // Default input | 默认输入
|
||||
|
||||
/**
|
||||
* Shader property UI metadata.
|
||||
* 着色器属性 UI 元数据。
|
||||
*
|
||||
* This interface defines all metadata needed to generate an Inspector UI
|
||||
* for editing shader uniform values.
|
||||
* 此接口定义生成用于编辑着色器 uniform 值的 Inspector UI 所需的所有元数据。
|
||||
*/
|
||||
export interface ShaderPropertyMeta {
|
||||
/**
|
||||
* Property type.
|
||||
* 属性类型。
|
||||
*/
|
||||
type: ShaderPropertyType;
|
||||
|
||||
/**
|
||||
* Display label (supports i18n key format "中文 | English").
|
||||
* 显示标签(支持国际化键格式 "中文 | English")。
|
||||
*/
|
||||
label: string;
|
||||
|
||||
/**
|
||||
* Property group for organization in Inspector.
|
||||
* Inspector 中用于组织的属性分组。
|
||||
*
|
||||
* Properties with the same group will be displayed together under a collapsible header.
|
||||
* 具有相同分组的属性将在可折叠标题下一起显示。
|
||||
*/
|
||||
group?: string;
|
||||
|
||||
/**
|
||||
* Default value.
|
||||
* 默认值。
|
||||
*/
|
||||
default?: number | number[] | string;
|
||||
|
||||
/**
|
||||
* Minimum value (for numeric types).
|
||||
* 最小值(用于数值类型)。
|
||||
*/
|
||||
min?: number;
|
||||
|
||||
/**
|
||||
* Maximum value (for numeric types).
|
||||
* 最大值(用于数值类型)。
|
||||
*/
|
||||
max?: number;
|
||||
|
||||
/**
|
||||
* Step value for numeric inputs.
|
||||
* 数值输入的步长值。
|
||||
*/
|
||||
step?: number;
|
||||
|
||||
/**
|
||||
* UI hint for specialized display.
|
||||
* 用于特殊显示的 UI 提示。
|
||||
*/
|
||||
hint?: ShaderPropertyHint;
|
||||
|
||||
/**
|
||||
* Tooltip description (supports i18n).
|
||||
* 工具提示描述(支持国际化)。
|
||||
*/
|
||||
tooltip?: string;
|
||||
|
||||
/**
|
||||
* Whether to hide in Inspector.
|
||||
* 是否在 Inspector 中隐藏。
|
||||
*
|
||||
* Hidden properties are typically controlled by scripts or systems.
|
||||
* 隐藏的属性通常由脚本或系统控制。
|
||||
*/
|
||||
hidden?: boolean;
|
||||
|
||||
/**
|
||||
* Texture filter options (for texture type).
|
||||
* 纹理过滤选项(用于纹理类型)。
|
||||
*/
|
||||
textureFilter?: 'linear' | 'nearest';
|
||||
|
||||
/**
|
||||
* Texture wrap options (for texture type).
|
||||
* 纹理包裹选项(用于纹理类型)。
|
||||
*/
|
||||
textureWrap?: 'repeat' | 'clamp' | 'mirror';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extended shader definition with property metadata.
|
||||
* 带属性元数据的扩展着色器定义。
|
||||
*
|
||||
* This interface extends the basic shader definition with UI metadata
|
||||
* for Inspector generation and asset serialization.
|
||||
* 此接口使用 UI 元数据扩展基本着色器定义,
|
||||
* 用于 Inspector 生成和资产序列化。
|
||||
*/
|
||||
export interface ShaderAssetDefinition {
|
||||
/**
|
||||
* Shader name (unique identifier).
|
||||
* 着色器名称(唯一标识符)。
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Display name for UI.
|
||||
* UI 显示名称。
|
||||
*/
|
||||
displayName?: string;
|
||||
|
||||
/**
|
||||
* Shader description.
|
||||
* 着色器描述。
|
||||
*/
|
||||
description?: string;
|
||||
|
||||
/**
|
||||
* Vertex shader source (inline GLSL or relative path).
|
||||
* 顶点着色器源(内联 GLSL 或相对路径)。
|
||||
*/
|
||||
vertexSource: string;
|
||||
|
||||
/**
|
||||
* Fragment shader source (inline GLSL or relative path).
|
||||
* 片段着色器源(内联 GLSL 或相对路径)。
|
||||
*/
|
||||
fragmentSource: string;
|
||||
|
||||
/**
|
||||
* Property metadata for Inspector.
|
||||
* Inspector 属性元数据。
|
||||
*
|
||||
* Key is the uniform name (e.g., 'u_shinyProgress').
|
||||
* 键是 uniform 名称(例如 'u_shinyProgress')。
|
||||
*/
|
||||
properties?: Record<string, ShaderPropertyMeta>;
|
||||
|
||||
/**
|
||||
* Render queue / order.
|
||||
* 渲染队列/顺序。
|
||||
*
|
||||
* Lower values render first. Default is 2000 (opaque).
|
||||
* 较低的值先渲染。默认为 2000(不透明)。
|
||||
*/
|
||||
renderQueue?: number;
|
||||
|
||||
/**
|
||||
* Preset blend mode.
|
||||
* 预设混合模式。
|
||||
*/
|
||||
blendMode?: 'alpha' | 'additive' | 'multiply' | 'opaque';
|
||||
|
||||
/**
|
||||
* Whether this shader requires depth testing.
|
||||
* 此着色器是否需要深度测试。
|
||||
*/
|
||||
depthTest?: boolean;
|
||||
|
||||
/**
|
||||
* Whether this shader writes to depth buffer.
|
||||
* 此着色器是否写入深度缓冲区。
|
||||
*/
|
||||
depthWrite?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shader asset file format (.shader).
|
||||
* 着色器资产文件格式 (.shader)。
|
||||
*/
|
||||
export interface ShaderAssetFile {
|
||||
/**
|
||||
* Schema version for format evolution.
|
||||
* 用于格式演进的模式版本。
|
||||
*/
|
||||
version: number;
|
||||
|
||||
/**
|
||||
* Shader definition.
|
||||
* 着色器定义。
|
||||
*/
|
||||
shader: ShaderAssetDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Built-in shader property definitions.
|
||||
* 内置着色器属性定义。
|
||||
*
|
||||
* These are the property metadata for built-in shaders.
|
||||
* 这些是内置着色器的属性元数据。
|
||||
*/
|
||||
export const BUILTIN_SHADER_PROPERTIES: Record<string, Record<string, ShaderPropertyMeta>> = {
|
||||
Shiny: {
|
||||
u_shinyProgress: {
|
||||
type: 'float',
|
||||
label: '进度 | Progress',
|
||||
group: 'Animation',
|
||||
default: 0,
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
hidden: true
|
||||
},
|
||||
u_shinyWidth: {
|
||||
type: 'float',
|
||||
label: '宽度 | Width',
|
||||
group: 'Effect',
|
||||
default: 0.25,
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
tooltip: '闪光带宽度 | Width of the shiny band'
|
||||
},
|
||||
u_shinyRotation: {
|
||||
type: 'float',
|
||||
label: '角度 | Rotation',
|
||||
group: 'Effect',
|
||||
default: 0.524, // 30 degrees in radians | 30度的弧度值
|
||||
min: 0,
|
||||
max: 6.28, // 360 degrees | 360度
|
||||
step: 0.01,
|
||||
hint: 'angle',
|
||||
tooltip: '闪光扫过的角度 | Angle of shine sweep'
|
||||
},
|
||||
u_shinySoftness: {
|
||||
type: 'float',
|
||||
label: '柔和度 | Softness',
|
||||
group: 'Effect',
|
||||
default: 1.0,
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01
|
||||
},
|
||||
u_shinyBrightness: {
|
||||
type: 'float',
|
||||
label: '亮度 | Brightness',
|
||||
group: 'Effect',
|
||||
default: 1.0,
|
||||
min: 0,
|
||||
max: 2,
|
||||
step: 0.01
|
||||
},
|
||||
u_shinyGloss: {
|
||||
type: 'float',
|
||||
label: '光泽度 | Gloss',
|
||||
group: 'Effect',
|
||||
default: 1.0,
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
tooltip: '0=白色高光, 1=带颜色 | 0=white shine, 1=color-tinted'
|
||||
}
|
||||
},
|
||||
Grayscale: {
|
||||
u_grayscale: {
|
||||
type: 'float',
|
||||
label: '灰度 | Grayscale',
|
||||
default: 1.0,
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
hint: 'range',
|
||||
tooltip: '0=彩色, 1=完全灰度 | 0=full color, 1=full grayscale'
|
||||
}
|
||||
},
|
||||
Tint: {
|
||||
u_tintColor: {
|
||||
type: 'color',
|
||||
label: '着色 | Tint Color',
|
||||
default: [1, 1, 1, 1]
|
||||
}
|
||||
},
|
||||
Flash: {
|
||||
u_flashColor: {
|
||||
type: 'color',
|
||||
label: '闪光颜色 | Flash Color',
|
||||
default: [1, 1, 1, 1]
|
||||
},
|
||||
u_flashAmount: {
|
||||
type: 'float',
|
||||
label: '闪光强度 | Flash Amount',
|
||||
default: 0,
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
hint: 'range'
|
||||
}
|
||||
},
|
||||
Outline: {
|
||||
u_outlineColor: {
|
||||
type: 'color',
|
||||
label: '描边颜色 | Outline Color',
|
||||
default: [0, 0, 0, 1]
|
||||
},
|
||||
u_outlineWidth: {
|
||||
type: 'float',
|
||||
label: '描边宽度 | Outline Width',
|
||||
default: 1,
|
||||
min: 0,
|
||||
max: 10,
|
||||
step: 0.5
|
||||
},
|
||||
u_texelSize: {
|
||||
type: 'vec2',
|
||||
label: '纹素大小 | Texel Size',
|
||||
default: [0.01, 0.01],
|
||||
hidden: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get shader property metadata by shader name.
|
||||
* 通过着色器名称获取着色器属性元数据。
|
||||
*
|
||||
* @param shaderName - Name of the shader | 着色器名称
|
||||
* @returns Property metadata or undefined | 属性元数据或 undefined
|
||||
*/
|
||||
export function getShaderProperties(shaderName: string): Record<string, ShaderPropertyMeta> | undefined {
|
||||
return BUILTIN_SHADER_PROPERTIES[shaderName];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get shader property metadata by shader ID.
|
||||
* 通过着色器 ID 获取着色器属性元数据。
|
||||
*
|
||||
* @param shaderId - ID of the shader (from BuiltInShaders) | 着色器 ID(来自 BuiltInShaders)
|
||||
* @returns Property metadata or undefined | 属性元数据或 undefined
|
||||
*/
|
||||
export function getShaderPropertiesById(shaderId: number): Record<string, ShaderPropertyMeta> | undefined {
|
||||
const shaderNames = ['DefaultSprite', 'Grayscale', 'Tint', 'Flash', 'Outline', 'Shiny'];
|
||||
const name = shaderNames[shaderId];
|
||||
return name ? BUILTIN_SHADER_PROPERTIES[name] : undefined;
|
||||
}
|
||||
268
packages/material-system/src/mixins/MaterialOverridableMixin.ts
Normal file
268
packages/material-system/src/mixins/MaterialOverridableMixin.ts
Normal file
@@ -0,0 +1,268 @@
|
||||
/**
|
||||
* Material overridable mixin for ES Engine.
|
||||
* ES引擎材质覆盖 Mixin。
|
||||
*
|
||||
* This mixin provides material override functionality that can be mixed into
|
||||
* any component class (SpriteComponent, UIRenderComponent, etc.).
|
||||
* 此 Mixin 提供材质覆盖功能,可混入任何组件类。
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
import type {
|
||||
MaterialPropertyOverride,
|
||||
MaterialOverrides,
|
||||
IMaterialOverridable
|
||||
} from '../interfaces/IMaterialOverridable';
|
||||
|
||||
/**
|
||||
* Constructor type for mixin base class.
|
||||
* Mixin 基类的构造函数类型。
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type Constructor<T = object> = new (...args: any[]) => T;
|
||||
|
||||
/**
|
||||
* Mixin that provides material override functionality.
|
||||
* 提供材质覆盖功能的 Mixin。
|
||||
*
|
||||
* This mixin adds all material override methods to a base class,
|
||||
* implementing the IMaterialOverridable interface.
|
||||
* 此 Mixin 将所有材质覆盖方法添加到基类,实现 IMaterialOverridable 接口。
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Apply mixin to a component class
|
||||
* class MySpriteComponent extends MaterialOverridableMixin(Component) {
|
||||
* // ... other properties
|
||||
* }
|
||||
*
|
||||
* // The class now has all material override methods
|
||||
* const sprite = new MySpriteComponent();
|
||||
* sprite.setMaterialId(BuiltInShaders.Shiny);
|
||||
* sprite.setOverrideFloat('u_shinyProgress', 0.5);
|
||||
* ```
|
||||
*
|
||||
* @param Base - Base class to extend
|
||||
* @returns Class with material override functionality
|
||||
*/
|
||||
export function MaterialOverridableMixin<TBase extends Constructor>(Base: TBase) {
|
||||
return class MaterialOverridableClass extends Base implements IMaterialOverridable {
|
||||
/**
|
||||
* Material GUID for asset reference.
|
||||
* 材质资产引用的 GUID。
|
||||
*/
|
||||
materialGuid: string = '';
|
||||
|
||||
/**
|
||||
* Current material ID.
|
||||
* 当前材质 ID。
|
||||
* @internal - Use getMaterialId() and setMaterialId() instead
|
||||
*/
|
||||
__materialId: number = 0;
|
||||
|
||||
/**
|
||||
* Material property overrides.
|
||||
* 材质属性覆盖。
|
||||
* @internal - Use materialOverrides getter instead
|
||||
*/
|
||||
__materialOverrides: MaterialOverrides = {};
|
||||
|
||||
/**
|
||||
* Get current material overrides.
|
||||
* 获取当前材质覆盖。
|
||||
*/
|
||||
get materialOverrides(): MaterialOverrides {
|
||||
return this.__materialOverrides;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current material ID.
|
||||
* 获取当前材质 ID。
|
||||
*/
|
||||
getMaterialId(): number {
|
||||
return this.__materialId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set material ID.
|
||||
* 设置材质 ID。
|
||||
*/
|
||||
setMaterialId(id: number): void {
|
||||
this.__materialId = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a float uniform override.
|
||||
* 设置浮点 uniform 覆盖。
|
||||
*/
|
||||
setOverrideFloat(name: string, value: number): this {
|
||||
this.__materialOverrides[name] = { type: 'float', value };
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a vec2 uniform override.
|
||||
* 设置 vec2 uniform 覆盖。
|
||||
*/
|
||||
setOverrideVec2(name: string, x: number, y: number): this {
|
||||
this.__materialOverrides[name] = { type: 'vec2', value: [x, y] };
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a vec3 uniform override.
|
||||
* 设置 vec3 uniform 覆盖。
|
||||
*/
|
||||
setOverrideVec3(name: string, x: number, y: number, z: number): this {
|
||||
this.__materialOverrides[name] = { type: 'vec3', value: [x, y, z] };
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a vec4 uniform override.
|
||||
* 设置 vec4 uniform 覆盖。
|
||||
*/
|
||||
setOverrideVec4(name: string, x: number, y: number, z: number, w: number): this {
|
||||
this.__materialOverrides[name] = { type: 'vec4', value: [x, y, z, w] };
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a color uniform override (RGBA, 0.0-1.0).
|
||||
* 设置颜色 uniform 覆盖(RGBA,0.0-1.0)。
|
||||
*/
|
||||
setOverrideColor(name: string, r: number, g: number, b: number, a: number = 1.0): this {
|
||||
this.__materialOverrides[name] = { type: 'color', value: [r, g, b, a] };
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an integer uniform override.
|
||||
* 设置整数 uniform 覆盖。
|
||||
*/
|
||||
setOverrideInt(name: string, value: number): this {
|
||||
this.__materialOverrides[name] = { type: 'int', value: Math.floor(value) };
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific override value.
|
||||
* 获取特定覆盖值。
|
||||
*/
|
||||
getOverride(name: string): MaterialPropertyOverride | undefined {
|
||||
return this.__materialOverrides[name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a specific override.
|
||||
* 移除特定覆盖。
|
||||
*/
|
||||
removeOverride(name: string): this {
|
||||
delete this.__materialOverrides[name];
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all overrides.
|
||||
* 清除所有覆盖。
|
||||
*/
|
||||
clearOverrides(): this {
|
||||
this.__materialOverrides = {};
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if any overrides are set.
|
||||
* 检查是否设置了任何覆盖。
|
||||
*/
|
||||
hasOverrides(): boolean {
|
||||
return Object.keys(this.__materialOverrides).length > 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class that can be used for composition instead of mixin.
|
||||
* 可用于组合而非 Mixin 的辅助类。
|
||||
*
|
||||
* Use this when you cannot use mixins (e.g., class already extends another class).
|
||||
* 当无法使用 Mixin 时使用此类(例如,类已继承其他类)。
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* class MyComponent extends Component {
|
||||
* private _materialHelper = new MaterialOverrideHelper();
|
||||
*
|
||||
* get materialOverrides() { return this._materialHelper.materialOverrides; }
|
||||
* getMaterialId() { return this._materialHelper.getMaterialId(); }
|
||||
* setMaterialId(id: number) { this._materialHelper.setMaterialId(id); }
|
||||
* // ... delegate other methods
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export class MaterialOverrideHelper implements IMaterialOverridable {
|
||||
materialGuid: string = '';
|
||||
private _materialId: number = 0;
|
||||
private _materialOverrides: MaterialOverrides = {};
|
||||
|
||||
get materialOverrides(): MaterialOverrides {
|
||||
return this._materialOverrides;
|
||||
}
|
||||
|
||||
getMaterialId(): number {
|
||||
return this._materialId;
|
||||
}
|
||||
|
||||
setMaterialId(id: number): void {
|
||||
this._materialId = id;
|
||||
}
|
||||
|
||||
setOverrideFloat(name: string, value: number): this {
|
||||
this._materialOverrides[name] = { type: 'float', value };
|
||||
return this;
|
||||
}
|
||||
|
||||
setOverrideVec2(name: string, x: number, y: number): this {
|
||||
this._materialOverrides[name] = { type: 'vec2', value: [x, y] };
|
||||
return this;
|
||||
}
|
||||
|
||||
setOverrideVec3(name: string, x: number, y: number, z: number): this {
|
||||
this._materialOverrides[name] = { type: 'vec3', value: [x, y, z] };
|
||||
return this;
|
||||
}
|
||||
|
||||
setOverrideVec4(name: string, x: number, y: number, z: number, w: number): this {
|
||||
this._materialOverrides[name] = { type: 'vec4', value: [x, y, z, w] };
|
||||
return this;
|
||||
}
|
||||
|
||||
setOverrideColor(name: string, r: number, g: number, b: number, a: number = 1.0): this {
|
||||
this._materialOverrides[name] = { type: 'color', value: [r, g, b, a] };
|
||||
return this;
|
||||
}
|
||||
|
||||
setOverrideInt(name: string, value: number): this {
|
||||
this._materialOverrides[name] = { type: 'int', value: Math.floor(value) };
|
||||
return this;
|
||||
}
|
||||
|
||||
getOverride(name: string): MaterialPropertyOverride | undefined {
|
||||
return this._materialOverrides[name];
|
||||
}
|
||||
|
||||
removeOverride(name: string): this {
|
||||
delete this._materialOverrides[name];
|
||||
return this;
|
||||
}
|
||||
|
||||
clearOverrides(): this {
|
||||
this._materialOverrides = {};
|
||||
return this;
|
||||
}
|
||||
|
||||
hasOverrides(): boolean {
|
||||
return Object.keys(this._materialOverrides).length > 0;
|
||||
}
|
||||
}
|
||||
@@ -125,7 +125,8 @@ export const BuiltInShaders = {
|
||||
Grayscale: 1,
|
||||
Tint: 2,
|
||||
Flash: 3,
|
||||
Outline: 4
|
||||
Outline: 4,
|
||||
Shiny: 5
|
||||
} as const;
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user