refactor: reorganize package structure and decouple framework packages (#338)
* refactor: reorganize package structure and decouple framework packages ## Package Structure Reorganization - Reorganized 55 packages into categorized subdirectories: - packages/framework/ - Generic framework (Laya/Cocos compatible) - packages/engine/ - ESEngine core modules - packages/rendering/ - Rendering modules (WASM dependent) - packages/physics/ - Physics modules - packages/streaming/ - World streaming - packages/network-ext/ - Network extensions - packages/editor/ - Editor framework and plugins - packages/rust/ - Rust WASM engine - packages/tools/ - Build tools and SDK ## Framework Package Decoupling - Decoupled behavior-tree and blueprint packages from ESEngine dependencies - Created abstracted interfaces (IBTAssetManager, IBehaviorTreeAssetContent) - ESEngine-specific code moved to esengine/ subpath exports - Framework packages now usable with Cocos/Laya without ESEngine ## CI Configuration - Updated CI to only type-check and lint framework packages - Added type-check:framework and lint:framework scripts ## Breaking Changes - Package import paths changed due to directory reorganization - ESEngine integrations now use subpath imports (e.g., '@esengine/behavior-tree/esengine') * fix: update es-engine file path after directory reorganization * docs: update README to focus on framework over engine * ci: only build framework packages, remove Rust/WASM dependencies * fix: remove esengine subpath from behavior-tree and blueprint builds ESEngine integration code will only be available in full engine builds. Framework packages are now purely engine-agnostic. * fix: move network-protocols to framework, build both in CI * fix: update workflow paths from packages/core to packages/framework/core * fix: exclude esengine folder from type-check in behavior-tree and blueprint * fix: update network tsconfig references to new paths * fix: add test:ci:framework to only test framework packages in CI * fix: only build core and math npm packages in CI * fix: exclude test files from CodeQL and fix string escaping security issue
This commit is contained in:
276
packages/engine/material-system/src/Material.ts
Normal file
276
packages/engine/material-system/src/Material.ts
Normal file
@@ -0,0 +1,276 @@
|
||||
/**
|
||||
* Material class for managing rendering properties.
|
||||
* 用于管理渲染属性的材质类。
|
||||
*/
|
||||
|
||||
import { BlendMode, CullMode, UniformType, UniformValue, MaterialDefinition } from './types';
|
||||
|
||||
/**
|
||||
* Material class that holds shader reference and uniform values.
|
||||
* 持有着色器引用和uniform值的材质类。
|
||||
*/
|
||||
export class Material {
|
||||
/** Material ID assigned by the engine | 引擎分配的材质ID */
|
||||
private _id: number = -1;
|
||||
|
||||
/** Material name for debugging | 材质名称(用于调试) */
|
||||
public name: string;
|
||||
|
||||
/** Shader ID to use | 使用的着色器ID */
|
||||
public shaderId: number;
|
||||
|
||||
/** Blend mode | 混合模式 */
|
||||
public blendMode: BlendMode;
|
||||
|
||||
/** Cull mode | 剔除模式 */
|
||||
public cullMode: CullMode;
|
||||
|
||||
/** Depth test enabled | 是否启用深度测试 */
|
||||
public depthTest: boolean;
|
||||
|
||||
/** Depth write enabled | 是否启用深度写入 */
|
||||
public depthWrite: boolean;
|
||||
|
||||
/** Material uniform values | 材质uniform值 */
|
||||
private uniforms: Map<string, UniformValue> = new Map();
|
||||
|
||||
/** Texture references by uniform name | 按uniform名称引用的纹理 */
|
||||
private textures: Map<string, string> = new Map();
|
||||
|
||||
/** Whether the material needs to sync with engine | 材质是否需要与引擎同步 */
|
||||
private _dirty: boolean = true;
|
||||
|
||||
constructor(name: string = 'Material', shaderId: number = 0) {
|
||||
this.name = name;
|
||||
this.shaderId = shaderId;
|
||||
this.blendMode = BlendMode.Alpha;
|
||||
this.cullMode = CullMode.None;
|
||||
this.depthTest = false;
|
||||
this.depthWrite = false;
|
||||
}
|
||||
|
||||
/** Get the material ID | 获取材质ID */
|
||||
get id(): number {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
/** Set the material ID (called by MaterialManager) | 设置材质ID(由MaterialManager调用) */
|
||||
set id(value: number) {
|
||||
this._id = value;
|
||||
}
|
||||
|
||||
/** Check if material is dirty | 检查材质是否为脏 */
|
||||
get dirty(): boolean {
|
||||
return this._dirty;
|
||||
}
|
||||
|
||||
/** Mark material as clean | 标记材质为干净 */
|
||||
markClean(): void {
|
||||
this._dirty = false;
|
||||
}
|
||||
|
||||
// ============= Uniform Setters =============
|
||||
// ============= Uniform设置方法 =============
|
||||
|
||||
/**
|
||||
* Set a float uniform.
|
||||
* 设置浮点uniform。
|
||||
*/
|
||||
setFloat(name: string, value: number): this {
|
||||
this.uniforms.set(name, { type: UniformType.Float, value });
|
||||
this._dirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a vec2 uniform.
|
||||
* 设置vec2 uniform。
|
||||
*/
|
||||
setVec2(name: string, x: number, y: number): this {
|
||||
this.uniforms.set(name, { type: UniformType.Vec2, value: [x, y] });
|
||||
this._dirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a vec3 uniform.
|
||||
* 设置vec3 uniform。
|
||||
*/
|
||||
setVec3(name: string, x: number, y: number, z: number): this {
|
||||
this.uniforms.set(name, { type: UniformType.Vec3, value: [x, y, z] });
|
||||
this._dirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a vec4 uniform.
|
||||
* 设置vec4 uniform。
|
||||
*/
|
||||
setVec4(name: string, x: number, y: number, z: number, w: number): this {
|
||||
this.uniforms.set(name, { type: UniformType.Vec4, value: [x, y, z, w] });
|
||||
this._dirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a color uniform (RGBA, 0.0-1.0).
|
||||
* 设置颜色uniform(RGBA,0.0-1.0)。
|
||||
*/
|
||||
setColor(name: string, r: number, g: number, b: number, a: number = 1.0): this {
|
||||
this.uniforms.set(name, { type: UniformType.Color, value: [r, g, b, a] });
|
||||
this._dirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an integer uniform.
|
||||
* 设置整数uniform。
|
||||
*/
|
||||
setInt(name: string, value: number): this {
|
||||
this.uniforms.set(name, { type: UniformType.Int, value: Math.floor(value) });
|
||||
this._dirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a texture uniform.
|
||||
* 设置纹理uniform。
|
||||
*/
|
||||
setTexture(name: string, texturePath: string): this {
|
||||
this.textures.set(name, texturePath);
|
||||
this._dirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
// ============= Uniform Getters =============
|
||||
// ============= Uniform获取方法 =============
|
||||
|
||||
/**
|
||||
* Get a uniform value.
|
||||
* 获取uniform值。
|
||||
*/
|
||||
getUniform(name: string): UniformValue | undefined {
|
||||
return this.uniforms.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a texture path.
|
||||
* 获取纹理路径。
|
||||
*/
|
||||
getTexture(name: string): string | undefined {
|
||||
return this.textures.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all uniforms.
|
||||
* 获取所有uniform。
|
||||
*/
|
||||
getUniforms(): Map<string, UniformValue> {
|
||||
return this.uniforms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all textures.
|
||||
* 获取所有纹理。
|
||||
*/
|
||||
getTextures(): Map<string, string> {
|
||||
return this.textures;
|
||||
}
|
||||
|
||||
// ============= Serialization =============
|
||||
// ============= 序列化 =============
|
||||
|
||||
/**
|
||||
* Export to material definition.
|
||||
* 导出为材质定义。
|
||||
*/
|
||||
toDefinition(): MaterialDefinition {
|
||||
const uniformsObj: Record<string, UniformValue> = {};
|
||||
for (const [key, value] of this.uniforms) {
|
||||
uniformsObj[key] = value;
|
||||
}
|
||||
|
||||
const texturesObj: Record<string, string> = {};
|
||||
for (const [key, value] of this.textures) {
|
||||
texturesObj[key] = value;
|
||||
}
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
shader: this.shaderId,
|
||||
blendMode: this.blendMode,
|
||||
cullMode: this.cullMode,
|
||||
depthTest: this.depthTest,
|
||||
depthWrite: this.depthWrite,
|
||||
uniforms: Object.keys(uniformsObj).length > 0 ? uniformsObj : undefined,
|
||||
textures: Object.keys(texturesObj).length > 0 ? texturesObj : undefined
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Import from material definition.
|
||||
* 从材质定义导入。
|
||||
*/
|
||||
static fromDefinition(def: MaterialDefinition): Material {
|
||||
const material = new Material(def.name, typeof def.shader === 'number' ? def.shader : 0);
|
||||
material.blendMode = def.blendMode ?? BlendMode.Alpha;
|
||||
material.cullMode = def.cullMode ?? CullMode.None;
|
||||
material.depthTest = def.depthTest ?? false;
|
||||
material.depthWrite = def.depthWrite ?? false;
|
||||
|
||||
if (def.uniforms) {
|
||||
for (const [key, value] of Object.entries(def.uniforms)) {
|
||||
material.uniforms.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
if (def.textures) {
|
||||
for (const [key, value] of Object.entries(def.textures)) {
|
||||
material.textures.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
return material;
|
||||
}
|
||||
|
||||
// ============= Static Factory Methods =============
|
||||
// ============= 静态工厂方法 =============
|
||||
|
||||
/**
|
||||
* Create a default sprite material.
|
||||
* 创建默认精灵材质。
|
||||
*/
|
||||
static sprite(): Material {
|
||||
return new Material('Sprite', 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an additive (glow) material.
|
||||
* 创建加法(发光)材质。
|
||||
*/
|
||||
static additive(): Material {
|
||||
const mat = new Material('Additive', 0);
|
||||
mat.blendMode = BlendMode.Additive;
|
||||
return mat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a multiply (shadow) material.
|
||||
* 创建乘法(阴影)材质。
|
||||
*/
|
||||
static multiply(): Material {
|
||||
const mat = new Material('Multiply', 0);
|
||||
mat.blendMode = BlendMode.Multiply;
|
||||
return mat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an unlit/opaque material.
|
||||
* 创建无光照/不透明材质。
|
||||
*/
|
||||
static unlit(): Material {
|
||||
const mat = new Material('Unlit', 0);
|
||||
mat.blendMode = BlendMode.None;
|
||||
return mat;
|
||||
}
|
||||
}
|
||||
847
packages/engine/material-system/src/MaterialManager.ts
Normal file
847
packages/engine/material-system/src/MaterialManager.ts
Normal file
@@ -0,0 +1,847 @@
|
||||
/**
|
||||
* 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,
|
||||
OUTLINE_FRAGMENT_SHADER,
|
||||
SHINY_FRAGMENT_SHADER
|
||||
} 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';
|
||||
|
||||
/** 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操作的引擎桥接。
|
||||
*
|
||||
* 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} 个材质`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 },
|
||||
{ id: BuiltInShaders.Shiny, name: 'Shiny', vertex: DEFAULT_VERTEX_SHADER, fragment: SHINY_FRAGMENT_SHADER },
|
||||
];
|
||||
|
||||
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. | 着色器ID,未找到则返回0。
|
||||
*/
|
||||
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). | 着色器ID(加载失败返回0)。
|
||||
*/
|
||||
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. | 着色器ID,加载失败返回0。
|
||||
*/
|
||||
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. | 材质ID,未找到则返回0。
|
||||
*/
|
||||
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). | 材质ID(加载失败返回0)。
|
||||
*/
|
||||
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. | 材质ID,加载失败返回0。
|
||||
*/
|
||||
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;
|
||||
}
|
||||
104
packages/engine/material-system/src/MaterialSystemPlugin.ts
Normal file
104
packages/engine/material-system/src/MaterialSystemPlugin.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* MaterialSystemPlugin for ES Engine.
|
||||
* ES引擎的材质系统插件。
|
||||
*
|
||||
* Provides:
|
||||
* - Material and Shader management
|
||||
* - Built-in shaders (Default, Grayscale, Tint, Flash, Outline, Shiny)
|
||||
*
|
||||
* 提供:
|
||||
* - 材质和着色器管理
|
||||
* - 内置着色器
|
||||
*/
|
||||
|
||||
import { MaterialManager, getMaterialManager } from './MaterialManager';
|
||||
import { createLogger } from '@esengine/ecs-framework';
|
||||
import type { IRuntimePlugin, ModuleManifest, IRuntimeModule } from '@esengine/engine-core';
|
||||
|
||||
/** Logger instance for MaterialRuntimeModule. | MaterialRuntimeModule的日志实例。 */
|
||||
const logger = createLogger('MaterialRuntimeModule');
|
||||
|
||||
/**
|
||||
* Runtime module interface for Material system.
|
||||
* 材质系统的运行时模块接口。
|
||||
*/
|
||||
export interface IMaterialRuntimeModule {
|
||||
onInitialize?(): Promise<void>;
|
||||
onDestroy?(): void;
|
||||
getMaterialManager(): MaterialManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runtime module that provides material and shader functionality.
|
||||
* 提供材质和着色器功能的运行时模块。
|
||||
*
|
||||
* 该模块提供:
|
||||
* - MaterialManager: 材质资产管理
|
||||
* - 材质文件加载和缓存
|
||||
* - 与 Rust 引擎的材质/着色器桥接
|
||||
*/
|
||||
export class MaterialRuntimeModule implements IMaterialRuntimeModule {
|
||||
private materialManager: MaterialManager;
|
||||
|
||||
constructor() {
|
||||
this.materialManager = getMaterialManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the material system.
|
||||
* 初始化材质系统。
|
||||
*/
|
||||
async onInitialize(): Promise<void> {
|
||||
logger.info('Initialized');
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup the material system.
|
||||
* 清理材质系统。
|
||||
*/
|
||||
onDestroy(): void {
|
||||
logger.info('Destroyed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the material manager.
|
||||
* 获取材质管理器。
|
||||
*/
|
||||
getMaterialManager(): MaterialManager {
|
||||
return this.materialManager;
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
export const materialRuntimeModule = new MaterialRuntimeModule();
|
||||
|
||||
/**
|
||||
* Plugin manifest for Material System.
|
||||
* 材质系统的插件清单。
|
||||
*/
|
||||
const manifest: ModuleManifest = {
|
||||
id: 'material-system',
|
||||
name: '@esengine/material-system',
|
||||
displayName: 'Material System',
|
||||
version: '1.0.0',
|
||||
description: '材质和着色器系统',
|
||||
category: 'Rendering',
|
||||
icon: 'Palette',
|
||||
isCore: true,
|
||||
defaultEnabled: true,
|
||||
isEngineModule: true,
|
||||
dependencies: ['core', 'asset-system'],
|
||||
exports: {
|
||||
other: ['Material', 'Shader', 'MaterialManager']
|
||||
},
|
||||
requiresWasm: false
|
||||
};
|
||||
|
||||
/**
|
||||
* Material System Plugin.
|
||||
* 材质系统插件。
|
||||
*/
|
||||
export const MaterialSystemPlugin: IRuntimePlugin = {
|
||||
manifest,
|
||||
runtimeModule: materialRuntimeModule as IRuntimeModule
|
||||
};
|
||||
421
packages/engine/material-system/src/Shader.ts
Normal file
421
packages/engine/material-system/src/Shader.ts
Normal file
@@ -0,0 +1,421 @@
|
||||
/**
|
||||
* Shader class for managing GLSL shaders.
|
||||
* 用于管理GLSL着色器的类。
|
||||
*/
|
||||
|
||||
import { ShaderDefinition, UniformValue, UniformType } from './types';
|
||||
|
||||
/**
|
||||
* Shader class that holds vertex and fragment shader source.
|
||||
* 持有顶点和片段着色器源代码的着色器类。
|
||||
*/
|
||||
export class Shader {
|
||||
/** Shader ID assigned by the engine | 引擎分配的着色器ID */
|
||||
private _id: number = -1;
|
||||
|
||||
/** Shader name for reference | 着色器名称用于引用 */
|
||||
public name: string;
|
||||
|
||||
/** Vertex shader GLSL source | 顶点着色器GLSL源代码 */
|
||||
public vertexSource: string;
|
||||
|
||||
/** Fragment shader GLSL source | 片段着色器GLSL源代码 */
|
||||
public fragmentSource: string;
|
||||
|
||||
/** Shader uniforms with default values | 着色器uniform及其默认值 */
|
||||
private uniforms: Map<string, UniformValue> = new Map();
|
||||
|
||||
/** Whether the shader has been compiled | 着色器是否已编译 */
|
||||
private _compiled: boolean = false;
|
||||
|
||||
constructor(name: string, vertexSource: string, fragmentSource: string) {
|
||||
this.name = name;
|
||||
this.vertexSource = vertexSource;
|
||||
this.fragmentSource = fragmentSource;
|
||||
}
|
||||
|
||||
/** Get the shader ID | 获取着色器ID */
|
||||
get id(): number {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
/** Set the shader ID (called by ShaderManager) | 设置着色器ID(由ShaderManager调用) */
|
||||
set id(value: number) {
|
||||
this._id = value;
|
||||
}
|
||||
|
||||
/** Check if shader is compiled | 检查着色器是否已编译 */
|
||||
get compiled(): boolean {
|
||||
return this._compiled;
|
||||
}
|
||||
|
||||
/** Mark shader as compiled | 标记着色器为已编译 */
|
||||
markCompiled(): void {
|
||||
this._compiled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a uniform with default value.
|
||||
* 定义带有默认值的uniform。
|
||||
*/
|
||||
defineUniform(name: string, type: UniformType, defaultValue: number | number[] | string): this {
|
||||
this.uniforms.set(name, { type, value: defaultValue });
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get uniform definition.
|
||||
* 获取uniform定义。
|
||||
*/
|
||||
getUniform(name: string): UniformValue | undefined {
|
||||
return this.uniforms.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all uniform definitions.
|
||||
* 获取所有uniform定义。
|
||||
*/
|
||||
getUniforms(): Map<string, UniformValue> {
|
||||
return this.uniforms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export to shader definition.
|
||||
* 导出为着色器定义。
|
||||
*/
|
||||
toDefinition(): ShaderDefinition {
|
||||
const uniformsObj: Record<string, UniformValue> = {};
|
||||
for (const [key, value] of this.uniforms) {
|
||||
uniformsObj[key] = value;
|
||||
}
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
vertexSource: this.vertexSource,
|
||||
fragmentSource: this.fragmentSource,
|
||||
uniforms: Object.keys(uniformsObj).length > 0 ? uniformsObj : undefined
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Import from shader definition.
|
||||
* 从着色器定义导入。
|
||||
*/
|
||||
static fromDefinition(def: ShaderDefinition): Shader {
|
||||
const shader = new Shader(def.name, def.vertexSource, def.fragmentSource);
|
||||
|
||||
if (def.uniforms) {
|
||||
for (const [key, value] of Object.entries(def.uniforms)) {
|
||||
shader.uniforms.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
}
|
||||
|
||||
// ============= Built-in Shaders =============
|
||||
// ============= 内置着色器 =============
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
// Vertex attributes | 顶点属性
|
||||
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;
|
||||
|
||||
// Outputs to fragment shader | 输出到片段着色器
|
||||
out vec2 v_texCoord;
|
||||
out vec4 v_color;
|
||||
out float v_aspectRatio;
|
||||
|
||||
void main() {
|
||||
// Apply projection matrix | 应用投影矩阵
|
||||
vec3 pos = u_projection * vec3(a_position, 1.0);
|
||||
gl_Position = vec4(pos.xy, 0.0, 1.0);
|
||||
|
||||
// Pass through to fragment shader | 传递到片段着色器
|
||||
v_texCoord = a_texCoord;
|
||||
v_color = a_color;
|
||||
v_aspectRatio = a_aspectRatio;
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* Default sprite fragment shader source.
|
||||
* 默认精灵片段着色器源代码。
|
||||
*/
|
||||
export const DEFAULT_FRAGMENT_SHADER = `#version 300 es
|
||||
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;
|
||||
|
||||
// Output color | 输出颜色
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
// Sample texture and multiply by vertex color | 采样纹理并乘以顶点颜色
|
||||
vec4 texColor = texture(u_texture, v_texCoord);
|
||||
fragColor = texColor * v_color;
|
||||
|
||||
// Discard fully transparent pixels | 丢弃完全透明的像素
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* Grayscale fragment shader for desaturation effect.
|
||||
* 灰度片段着色器用于去饱和效果。
|
||||
*/
|
||||
export const GRAYSCALE_FRAGMENT_SHADER = `#version 300 es
|
||||
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
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec4 texColor = texture(u_texture, v_texCoord);
|
||||
vec4 color = texColor * v_color;
|
||||
|
||||
float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
|
||||
vec3 grayscaleColor = vec3(gray);
|
||||
|
||||
fragColor = vec4(mix(color.rgb, grayscaleColor, u_grayscale), color.a);
|
||||
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* Color tint fragment shader.
|
||||
* 颜色着色片段着色器。
|
||||
*/
|
||||
export const TINT_FRAGMENT_SHADER = `#version 300 es
|
||||
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
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec4 texColor = texture(u_texture, v_texCoord);
|
||||
vec4 color = texColor * v_color;
|
||||
|
||||
// Apply tint by multiplying RGB and keeping alpha
|
||||
fragColor = vec4(color.rgb * u_tintColor.rgb, color.a * u_tintColor.a);
|
||||
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* Flash/hit effect fragment shader.
|
||||
* 闪白/受击效果片段着色器。
|
||||
*/
|
||||
export const FLASH_FRAGMENT_SHADER = `#version 300 es
|
||||
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
|
||||
uniform float u_flashAmount; // 0.0 = no flash, 1.0 = full flash
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec4 texColor = texture(u_texture, v_texCoord);
|
||||
vec4 color = texColor * v_color;
|
||||
|
||||
// Mix original color with flash color
|
||||
vec3 flashedColor = mix(color.rgb, u_flashColor.rgb, u_flashAmount);
|
||||
fragColor = vec4(flashedColor, color.a);
|
||||
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* Outline fragment shader.
|
||||
* 描边片段着色器。
|
||||
*/
|
||||
export const OUTLINE_FRAGMENT_SHADER = `#version 300 es
|
||||
precision highp float;
|
||||
|
||||
in vec2 v_texCoord;
|
||||
in vec4 v_color;
|
||||
in float v_aspectRatio;
|
||||
|
||||
uniform sampler2D u_texture;
|
||||
uniform vec4 u_outlineColor;
|
||||
uniform float u_outlineWidth;
|
||||
uniform vec2 u_texelSize; // 1.0 / textureSize
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec4 texColor = texture(u_texture, v_texCoord);
|
||||
vec4 color = texColor * v_color;
|
||||
|
||||
// Check if this pixel should be outline
|
||||
if (color.a < 0.1) {
|
||||
// Sample neighbors
|
||||
float a = 0.0;
|
||||
for (float x = -1.0; x <= 1.0; x += 1.0) {
|
||||
for (float y = -1.0; y <= 1.0; y += 1.0) {
|
||||
vec2 offset = vec2(x, y) * u_texelSize * u_outlineWidth;
|
||||
a += texture(u_texture, v_texCoord + offset).a;
|
||||
}
|
||||
}
|
||||
|
||||
if (a > 0.0) {
|
||||
fragColor = u_outlineColor;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fragColor = color;
|
||||
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* 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/engine/material-system/src/effects/BaseShinyEffect.ts
Normal file
189
packages/engine/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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
102
packages/engine/material-system/src/index.ts
Normal file
102
packages/engine/material-system/src/index.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Material System for ES Engine.
|
||||
* ES引擎材质系统。
|
||||
*
|
||||
* This system provides:
|
||||
* 该系统提供:
|
||||
*
|
||||
* - Material: Material definition class with shader and uniform parameters.
|
||||
* 材质定义类,包含着色器和 uniform 参数。
|
||||
* - Shader: Shader definition class with vertex and fragment shader code.
|
||||
* 着色器定义类,包含顶点和片段着色器代码。
|
||||
* - MaterialManager: Material asset manager.
|
||||
* 材质资产管理器。
|
||||
* - MaterialLoader: Asset loader for .mat files.
|
||||
* .mat 文件的资产加载器。
|
||||
*
|
||||
* Note: Materials are not standalone components, but used as properties of
|
||||
* render components (such as SpriteComponent).
|
||||
* 注意:材质不是独立组件,而是作为渲染组件(如 SpriteComponent)的属性使用。
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
// Types.
|
||||
// 类型。
|
||||
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';
|
||||
export {
|
||||
Shader,
|
||||
DEFAULT_VERTEX_SHADER,
|
||||
DEFAULT_FRAGMENT_SHADER,
|
||||
GRAYSCALE_FRAGMENT_SHADER,
|
||||
TINT_FRAGMENT_SHADER,
|
||||
FLASH_FRAGMENT_SHADER,
|
||||
OUTLINE_FRAGMENT_SHADER,
|
||||
SHINY_FRAGMENT_SHADER
|
||||
} from './Shader';
|
||||
|
||||
// Manager.
|
||||
// 管理器。
|
||||
export { MaterialManager, getMaterialManager } from './MaterialManager';
|
||||
export type { IEngineBridge } from './MaterialManager';
|
||||
|
||||
// Loaders.
|
||||
// 加载器。
|
||||
export { MaterialLoader } from './loaders/MaterialLoader';
|
||||
export type { IMaterialAssetData } from './loaders/MaterialLoader';
|
||||
export { ShaderLoader } from './loaders/ShaderLoader';
|
||||
export type { IShaderAssetData, ShaderFileFormat } from './loaders/ShaderLoader';
|
||||
|
||||
// Runtime Module.
|
||||
// 运行时模块。
|
||||
export { MaterialRuntimeModule, materialRuntimeModule, MaterialSystemPlugin } from './MaterialSystemPlugin';
|
||||
export type { IMaterialRuntimeModule } from './MaterialSystemPlugin';
|
||||
|
||||
// Service Tokens.
|
||||
// 服务令牌。
|
||||
export { MaterialManagerToken } from './tokens';
|
||||
export type { IMaterialManager } from './tokens';
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Material asset loader.
|
||||
* 材质资产加载器。
|
||||
*/
|
||||
|
||||
import {
|
||||
AssetType,
|
||||
IAssetContent,
|
||||
IAssetParseContext
|
||||
} from '@esengine/asset-system';
|
||||
import type { IAssetLoader, AssetContentType } from '@esengine/asset-system';
|
||||
import { Material } from '../Material';
|
||||
|
||||
/**
|
||||
* Material asset data structure.
|
||||
* 材质资产数据结构。
|
||||
*/
|
||||
export interface IMaterialAssetData {
|
||||
/** Material instance. | 材质实例。 */
|
||||
material: Material;
|
||||
/** Material definition data. | 材质定义数据。 */
|
||||
definition: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Material file loader.
|
||||
* 材质文件加载器。
|
||||
*/
|
||||
export class MaterialLoader implements IAssetLoader<IMaterialAssetData> {
|
||||
readonly supportedType = AssetType.Material;
|
||||
readonly supportedExtensions = ['.mat'];
|
||||
readonly contentType: AssetContentType = 'text';
|
||||
|
||||
/**
|
||||
* Parse material from content.
|
||||
* 从内容解析材质。
|
||||
*/
|
||||
async parse(content: IAssetContent, context: IAssetParseContext): Promise<IMaterialAssetData> {
|
||||
if (!content.text) {
|
||||
throw new Error('Material content is empty');
|
||||
}
|
||||
|
||||
const data = JSON.parse(content.text);
|
||||
|
||||
// Support wrapper format: { material: {...} }
|
||||
const materialDef = data.material || data;
|
||||
|
||||
// Create material from definition.
|
||||
const material = Material.fromDefinition(materialDef);
|
||||
|
||||
return {
|
||||
material,
|
||||
definition: materialDef
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose material asset.
|
||||
* 释放材质资产。
|
||||
*/
|
||||
dispose(_asset: IMaterialAssetData): void {
|
||||
// Material cleanup if needed.
|
||||
}
|
||||
}
|
||||
87
packages/engine/material-system/src/loaders/ShaderLoader.ts
Normal file
87
packages/engine/material-system/src/loaders/ShaderLoader.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Shader asset loader.
|
||||
* 着色器资产加载器。
|
||||
*/
|
||||
|
||||
import {
|
||||
AssetType,
|
||||
IAssetContent,
|
||||
IAssetParseContext
|
||||
} from '@esengine/asset-system';
|
||||
import type { IAssetLoader, AssetContentType } from '@esengine/asset-system';
|
||||
import { Shader } from '../Shader';
|
||||
import type { ShaderDefinition } from '../types';
|
||||
|
||||
/**
|
||||
* Shader asset data structure.
|
||||
* 着色器资产数据结构。
|
||||
*/
|
||||
export interface IShaderAssetData {
|
||||
/** Shader instance. | 着色器实例。 */
|
||||
shader: Shader;
|
||||
/** Shader definition data. | 着色器定义数据。 */
|
||||
definition: ShaderDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shader file format.
|
||||
* 着色器文件格式。
|
||||
*
|
||||
* ```json
|
||||
* {
|
||||
* "version": 1,
|
||||
* "shader": {
|
||||
* "name": "CustomShader",
|
||||
* "vertexSource": "...",
|
||||
* "fragmentSource": "...",
|
||||
* "uniforms": { ... }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export interface ShaderFileFormat {
|
||||
version: number;
|
||||
shader: ShaderDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shader file loader.
|
||||
* 着色器文件加载器。
|
||||
*/
|
||||
export class ShaderLoader implements IAssetLoader<IShaderAssetData> {
|
||||
readonly supportedType = AssetType.Shader;
|
||||
readonly supportedExtensions = ['.shader'];
|
||||
readonly contentType: AssetContentType = 'text';
|
||||
|
||||
/**
|
||||
* Parse shader from content.
|
||||
* 从内容解析着色器。
|
||||
*/
|
||||
async parse(content: IAssetContent, context: IAssetParseContext): Promise<IShaderAssetData> {
|
||||
if (!content.text) {
|
||||
throw new Error('Shader content is empty');
|
||||
}
|
||||
|
||||
const data = JSON.parse(content.text) as ShaderFileFormat;
|
||||
|
||||
if (!data.shader) {
|
||||
throw new Error('Invalid shader file: missing shader definition');
|
||||
}
|
||||
|
||||
const shaderDef = data.shader;
|
||||
const shader = Shader.fromDefinition(shaderDef);
|
||||
|
||||
return {
|
||||
shader,
|
||||
definition: shaderDef
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose shader asset.
|
||||
* 释放着色器资产。
|
||||
*/
|
||||
dispose(_asset: IShaderAssetData): void {
|
||||
// Shader cleanup if needed.
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
174
packages/engine/material-system/src/tokens.ts
Normal file
174
packages/engine/material-system/src/tokens.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* Material System Service Tokens
|
||||
* 材质系统服务令牌
|
||||
*
|
||||
* 遵循"谁定义接口,谁导出 Token"原则。
|
||||
* Following "who defines interface, who exports Token" principle.
|
||||
*/
|
||||
|
||||
import { createServiceToken } from '@esengine/ecs-framework';
|
||||
import type { Material } from './Material';
|
||||
import type { Shader } from './Shader';
|
||||
import type { IEngineBridge } from './MaterialManager';
|
||||
import type { IAssetManager } from '@esengine/asset-system';
|
||||
|
||||
// ============================================================================
|
||||
// Material Manager Interface
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* MaterialManager 接口
|
||||
* MaterialManager interface
|
||||
*
|
||||
* 提供材质和着色器管理功能。
|
||||
* Provides material and shader management functionality.
|
||||
*/
|
||||
export interface IMaterialManager {
|
||||
// ========== Initialization | 初始化 ==========
|
||||
|
||||
/**
|
||||
* 设置引擎桥接
|
||||
* Set engine bridge for Rust communication
|
||||
*/
|
||||
setEngineBridge(bridge: IEngineBridge): void;
|
||||
|
||||
/**
|
||||
* 设置资产管理器
|
||||
* Set asset manager for loading assets
|
||||
*/
|
||||
setAssetManager(assetManager: IAssetManager): void;
|
||||
|
||||
/**
|
||||
* 初始化内置材质
|
||||
* Initialize built-in materials
|
||||
*/
|
||||
initializeBuiltInMaterials(): Promise<void>;
|
||||
|
||||
// ========== Shader Management | 着色器管理 ==========
|
||||
|
||||
/**
|
||||
* 注册着色器
|
||||
* Register a shader
|
||||
*/
|
||||
registerShader(shader: Shader): Promise<number>;
|
||||
|
||||
/**
|
||||
* 通过 ID 获取着色器
|
||||
* Get shader by ID
|
||||
*/
|
||||
getShader(id: number): Shader | undefined;
|
||||
|
||||
/**
|
||||
* 通过名称获取着色器
|
||||
* Get shader by name
|
||||
*/
|
||||
getShaderByName(name: string): Shader | undefined;
|
||||
|
||||
/**
|
||||
* 移除着色器
|
||||
* Remove a shader
|
||||
*/
|
||||
removeShader(id: number): boolean;
|
||||
|
||||
/**
|
||||
* 从路径加载着色器
|
||||
* Load shader from path
|
||||
*/
|
||||
loadShaderByPath(path: string): Promise<number>;
|
||||
|
||||
// ========== Material Management | 材质管理 ==========
|
||||
|
||||
/**
|
||||
* 注册材质
|
||||
* Register a material
|
||||
*/
|
||||
registerMaterial(material: Material): Promise<number>;
|
||||
|
||||
/**
|
||||
* 通过 ID 获取材质
|
||||
* Get material by ID
|
||||
*/
|
||||
getMaterial(id: number): Material | undefined;
|
||||
|
||||
/**
|
||||
* 通过名称获取材质
|
||||
* Get material by name
|
||||
*/
|
||||
getMaterialByName(name: string): Material | undefined;
|
||||
|
||||
/**
|
||||
* 移除材质
|
||||
* Remove a material
|
||||
*/
|
||||
removeMaterial(id: number): boolean;
|
||||
|
||||
/**
|
||||
* 从路径加载材质
|
||||
* Load material from path
|
||||
*/
|
||||
loadMaterialByPath(path: string): Promise<number>;
|
||||
|
||||
/**
|
||||
* 克隆材质
|
||||
* Clone a material
|
||||
*/
|
||||
cloneMaterial(materialId: number, newName?: string): Promise<Material | null>;
|
||||
|
||||
// ========== Built-in Materials | 内置材质 ==========
|
||||
|
||||
/**
|
||||
* 获取默认材质 ID
|
||||
* Get default material ID
|
||||
*/
|
||||
getDefaultMaterialId(): number;
|
||||
|
||||
/**
|
||||
* 获取灰度材质 ID
|
||||
* Get grayscale material ID
|
||||
*/
|
||||
getGrayscaleMaterialId(): number;
|
||||
|
||||
/**
|
||||
* 获取着色材质 ID
|
||||
* Get tint material ID
|
||||
*/
|
||||
getTintMaterialId(): number;
|
||||
|
||||
/**
|
||||
* 获取闪烁材质 ID
|
||||
* Get flash material ID
|
||||
*/
|
||||
getFlashMaterialId(): number;
|
||||
|
||||
/**
|
||||
* 获取轮廓材质 ID
|
||||
* Get outline material ID
|
||||
*/
|
||||
getOutlineMaterialId(): number;
|
||||
|
||||
// ========== Uniform Management | Uniform 管理 ==========
|
||||
|
||||
/**
|
||||
* 设置材质 uniform 值
|
||||
* Set material uniform value
|
||||
*/
|
||||
setMaterialUniform(materialId: number, name: string, value: any): boolean;
|
||||
|
||||
// ========== Lifecycle | 生命周期 ==========
|
||||
|
||||
/**
|
||||
* 销毁管理器,释放所有资源
|
||||
* Destroy manager and release all resources
|
||||
*/
|
||||
destroy(): void;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Service Token
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* MaterialManager 服务令牌
|
||||
* MaterialManager service token
|
||||
*/
|
||||
export const MaterialManagerToken = createServiceToken<IMaterialManager>('materialManager');
|
||||
145
packages/engine/material-system/src/types.ts
Normal file
145
packages/engine/material-system/src/types.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* Material and shader types for ES Engine
|
||||
* ES引擎的材质和着色器类型
|
||||
*/
|
||||
|
||||
/**
|
||||
* Blend modes for material rendering.
|
||||
* 材质渲染的混合模式。
|
||||
*/
|
||||
export enum BlendMode {
|
||||
/** No blending, fully opaque | 无混合,完全不透明 */
|
||||
None = 0,
|
||||
/** Standard alpha blending | 标准透明度混合 */
|
||||
Alpha = 1,
|
||||
/** Additive blending (good for glow effects) | 加法混合(适用于发光效果) */
|
||||
Additive = 2,
|
||||
/** Multiplicative blending (good for shadows) | 乘法混合(适用于阴影) */
|
||||
Multiply = 3,
|
||||
/** Screen blending (opposite of multiply) | 滤色混合(与乘法相反) */
|
||||
Screen = 4,
|
||||
/** Premultiplied alpha | 预乘透明度 */
|
||||
PremultipliedAlpha = 5
|
||||
}
|
||||
|
||||
/**
|
||||
* Cull modes for material rendering.
|
||||
* 材质渲染的剔除模式。
|
||||
*/
|
||||
export enum CullMode {
|
||||
/** No face culling | 不剔除 */
|
||||
None = 0,
|
||||
/** Cull front faces | 剔除正面 */
|
||||
Front = 1,
|
||||
/** Cull back faces | 剔除背面 */
|
||||
Back = 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Uniform value types supported by the material system.
|
||||
* 材质系统支持的uniform值类型。
|
||||
*/
|
||||
export enum UniformType {
|
||||
Float = 'float',
|
||||
Vec2 = 'vec2',
|
||||
Vec3 = 'vec3',
|
||||
Vec4 = 'vec4',
|
||||
Color = 'color',
|
||||
Int = 'int',
|
||||
Mat3 = 'mat3',
|
||||
Mat4 = 'mat4',
|
||||
Sampler = 'sampler'
|
||||
}
|
||||
|
||||
/**
|
||||
* Uniform value definition.
|
||||
* Uniform值定义。
|
||||
*/
|
||||
export interface UniformValue {
|
||||
type: UniformType;
|
||||
value: number | number[] | string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shader definition.
|
||||
* 着色器定义。
|
||||
*/
|
||||
export interface ShaderDefinition {
|
||||
/** Shader name for reference | 着色器名称用于引用 */
|
||||
name: string;
|
||||
/** Vertex shader GLSL source | 顶点着色器GLSL源代码 */
|
||||
vertexSource: string;
|
||||
/** Fragment shader GLSL source | 片段着色器GLSL源代码 */
|
||||
fragmentSource: string;
|
||||
/** Shader uniforms with default values | 着色器uniform及其默认值 */
|
||||
uniforms?: Record<string, UniformValue>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Material definition.
|
||||
* 材质定义。
|
||||
*/
|
||||
export interface MaterialDefinition {
|
||||
/** Material name | 材质名称 */
|
||||
name: string;
|
||||
/** Shader ID or name to use | 使用的着色器ID或名称 */
|
||||
shader: number | string;
|
||||
/** Blend mode | 混合模式 */
|
||||
blendMode?: BlendMode;
|
||||
/** Cull mode | 剔除模式 */
|
||||
cullMode?: CullMode;
|
||||
/** Depth test enabled | 是否启用深度测试 */
|
||||
depthTest?: boolean;
|
||||
/** Depth write enabled | 是否启用深度写入 */
|
||||
depthWrite?: boolean;
|
||||
/** Material uniform values | 材质uniform值 */
|
||||
uniforms?: Record<string, UniformValue>;
|
||||
/** Texture references by uniform name | 按uniform名称引用的纹理 */
|
||||
textures?: Record<string, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Material asset data for serialization.
|
||||
* 用于序列化的材质资产数据。
|
||||
*/
|
||||
export interface MaterialAssetData {
|
||||
version: number;
|
||||
material: MaterialDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shader asset data for serialization.
|
||||
* 用于序列化的着色器资产数据。
|
||||
*/
|
||||
export interface ShaderAssetData {
|
||||
version: number;
|
||||
shader: ShaderDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Built-in shader IDs.
|
||||
* 内置着色器ID。
|
||||
*/
|
||||
export const BuiltInShaders = {
|
||||
DefaultSprite: 0,
|
||||
Grayscale: 1,
|
||||
Tint: 2,
|
||||
Flash: 3,
|
||||
Outline: 4,
|
||||
Shiny: 5
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Built-in material IDs.
|
||||
* 内置材质ID。
|
||||
*/
|
||||
export const BuiltInMaterials = {
|
||||
/** Default sprite material | 默认精灵材质 */
|
||||
Default: 0,
|
||||
/** Additive blend material | 加法混合材质 */
|
||||
Additive: 1,
|
||||
/** Multiply blend material | 乘法混合材质 */
|
||||
Multiply: 2,
|
||||
/** Unlit/opaque material | 无光照/不透明材质 */
|
||||
Unlit: 3
|
||||
} as const;
|
||||
Reference in New Issue
Block a user