feat(material): 新增材质系统和着色器编辑器

This commit is contained in:
yhh
2025-12-03 16:20:23 +08:00
parent 4a2362edf2
commit 243b929d5e
34 changed files with 5903 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
{
"id": "material-system",
"name": "@esengine/material-system",
"displayName": "Material System",
"description": "Material and shader system | 材质和着色器系统",
"version": "1.0.0",
"category": "Rendering",
"icon": "Palette",
"tags": ["material", "shader", "rendering"],
"isCore": true,
"defaultEnabled": true,
"isEngineModule": true,
"canContainContent": true,
"platforms": ["web", "desktop"],
"dependencies": ["core", "asset-system"],
"exports": {
"other": ["Material", "Shader", "MaterialManager"]
},
"editorPackage": "@esengine/material-editor",
"requiresWasm": false,
"outputPath": "dist/index.js",
"pluginExport": "MaterialSystemPlugin"
}

View File

@@ -0,0 +1,48 @@
{
"name": "@esengine/material-system",
"version": "1.0.0",
"description": "Material and shader system for ES Engine",
"esengine": {
"plugin": true,
"pluginExport": "MaterialSystemPlugin",
"category": "rendering",
"isEnginePlugin": true
},
"main": "dist/index.js",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"files": [
"dist"
],
"scripts": {
"build": "tsup",
"build:watch": "tsup --watch",
"type-check": "tsc --noEmit",
"clean": "rimraf dist"
},
"devDependencies": {
"@esengine/ecs-framework": "workspace:*",
"@esengine/asset-system": "workspace:*",
"@esengine/engine-core": "workspace:*",
"@esengine/build-config": "workspace:*",
"rimraf": "^5.0.5",
"tsup": "^8.0.0",
"typescript": "^5.3.3"
},
"keywords": [
"ecs",
"material",
"shader",
"webgl",
"rendering"
],
"author": "yhh",
"license": "MIT"
}

View 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).
* 设置颜色uniformRGBA0.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;
}
}

View File

@@ -0,0 +1,788 @@
/**
* 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
} 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操作的引擎桥接。
*
* @param bridge - Engine bridge instance. | 引擎桥接实例。
*/
setEngineBridge(bridge: IEngineBridge): void {
this.engineBridge = bridge;
}
/**
* 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 },
];
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;
}

View File

@@ -0,0 +1,96 @@
/**
* MaterialSystemPlugin for ES Engine.
* ES引擎的材质系统插件。
*
* 注意:材质系统不注册独立组件,材质作为渲染组件(如 SpriteComponent的属性使用
*/
import { MaterialManager, getMaterialManager } from './MaterialManager';
import { createLogger } from '@esengine/ecs-framework';
import type { IPlugin, 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: IPlugin = {
manifest,
runtimeModule: materialRuntimeModule as IRuntimeModule
};

View File

@@ -0,0 +1,311 @@
/**
* 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.
* 默认精灵顶点着色器源代码。
*/
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;
// Uniforms | 统一变量
uniform mat3 u_projection;
// Outputs to fragment shader | 输出到片段着色器
out vec2 v_texCoord;
out vec4 v_color;
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;
}
`;
/**
* 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;
// 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;
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;
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;
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;
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;
}
}
`;

View File

@@ -0,0 +1,56 @@
/**
* 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';
// 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
} 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';

View File

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

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

View File

@@ -0,0 +1,144 @@
/**
* 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
} 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;

View File

@@ -0,0 +1,12 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"composite": false,
"declaration": true,
"declarationMap": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts"]
}

View File

@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.build.json",
"compilerOptions": {
"composite": true,
"noEmit": true
}
}

View File

@@ -0,0 +1,7 @@
import { defineConfig } from 'tsup';
import { runtimeOnlyPreset } from '../build-config/src/presets/plugin-tsup';
export default defineConfig({
...runtimeOnlyPreset(),
tsconfig: 'tsconfig.build.json'
});