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:
YHH
2025-12-19 15:33:36 +08:00
committed by GitHub
parent 958933cd76
commit 536c4c5593
145 changed files with 18187 additions and 1543 deletions

View File

@@ -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 作为 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} 个材质`);
}
/**
@@ -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) {

View File

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

View File

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

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

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

View File

@@ -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.

View 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 覆盖RGBA0.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;
}

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

View 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 覆盖RGBA0.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;
}
}

View File

@@ -125,7 +125,8 @@ export const BuiltInShaders = {
Grayscale: 1,
Tint: 2,
Flash: 3,
Outline: 4
Outline: 4,
Shiny: 5
} as const;
/**