Files
esengine/packages/asset-system/src/integration/EngineIntegration.ts

541 lines
16 KiB
TypeScript
Raw Normal View History

/**
* Engine integration for asset system
*
*/
import { AssetManager } from '../core/AssetManager';
import { AssetGUID } from '../types/AssetTypes';
import { ITextureAsset, IAudioAsset, IJsonAsset } from '../interfaces/IAssetLoader';
import { globalPathResolver } from '../core/AssetPathResolver';
/**
* Engine bridge interface
*
*/
export interface IEngineBridge {
/**
* Load texture to GPU
* GPU
*/
loadTexture(id: number, url: string): Promise<void>;
/**
* Load multiple textures
*
*/
loadTextures(requests: Array<{ id: number; url: string }>): Promise<void>;
/**
* Unload texture from GPU
* GPU卸载纹理
*/
unloadTexture(id: number): void;
/**
* Get texture info
*
*/
getTextureInfo(id: number): { width: number; height: number } | null;
}
/**
* Audio asset with runtime ID
* ID
*/
interface AudioAssetEntry {
id: number;
asset: IAudioAsset;
path: string;
}
/**
* Data asset with runtime ID
* ID
*/
interface DataAssetEntry {
id: number;
data: unknown;
path: string;
}
/**
* Asset system engine integration
*
*/
export class EngineIntegration {
private _assetManager: AssetManager;
private _engineBridge?: IEngineBridge;
private _textureIdMap = new Map<AssetGUID, number>();
private _pathToTextureId = new Map<string, number>();
// Audio resource mappings | 音频资源映射
private _audioIdMap = new Map<AssetGUID, number>();
private _pathToAudioId = new Map<string, number>();
private _audioAssets = new Map<number, AudioAssetEntry>();
private static _nextAudioId = 1;
// Data resource mappings | 数据资源映射
private _dataIdMap = new Map<AssetGUID, number>();
private _pathToDataId = new Map<string, number>();
private _dataAssets = new Map<number, DataAssetEntry>();
private static _nextDataId = 1;
constructor(assetManager: AssetManager, engineBridge?: IEngineBridge) {
this._assetManager = assetManager;
this._engineBridge = engineBridge;
}
/**
* Set engine bridge
*
*/
setEngineBridge(bridge: IEngineBridge): void {
this._engineBridge = bridge;
}
/**
* Load texture for component
*
*
feat: 添加跨平台运行时、资产系统和UI适配功能 (#256) * feat(platform-common): 添加WASM加载器和环境检测API * feat(rapier2d): 新增Rapier2D WASM绑定包 * feat(physics-rapier2d): 添加跨平台WASM加载器 * feat(asset-system): 添加运行时资产目录和bundle格式 * feat(asset-system-editor): 新增编辑器资产管理包 * feat(editor-core): 添加构建系统和模块管理 * feat(editor-app): 重构浏览器预览使用import maps * feat(platform-web): 添加BrowserRuntime和资产读取 * feat(engine): 添加材质系统和着色器管理 * feat(material): 新增材质系统和着色器编辑器 * feat(tilemap): 增强tilemap编辑器和动画系统 * feat(modules): 添加module.json配置 * feat(core): 添加module.json和类型定义更新 * chore: 更新依赖和构建配置 * refactor(plugins): 更新插件模板使用ModuleManifest * chore: 添加第三方依赖库 * chore: 移除BehaviourTree-ai和ecs-astar子模块 * docs: 更新README和文档主题样式 * fix: 修复Rust文档测试和添加rapier2d WASM绑定 * fix(tilemap-editor): 修复画布高DPI屏幕分辨率适配问题 * feat(ui): 添加UI屏幕适配系统(CanvasScaler/SafeArea) * fix(ecs-engine-bindgen): 添加缺失的ecs-framework-math依赖 * fix: 添加缺失的包依赖修复CI构建 * fix: 修复CodeQL检测到的代码问题 * fix: 修复构建错误和缺失依赖 * fix: 修复类型检查错误 * fix(material-system): 修复tsconfig配置支持TypeScript项目引用 * fix(editor-core): 修复Rollup构建配置添加tauri external * fix: 修复CodeQL检测到的代码问题 * fix: 修复CodeQL检测到的代码问题
2025-12-03 22:15:22 +08:00
* AssetManager
* AssetManager handles path resolution internally, just pass the original path here.
*/
async loadTextureForComponent(texturePath: string): Promise<number> {
// 检查缓存(使用原始路径作为键)
// Check cache (using original path as key)
const existingId = this._pathToTextureId.get(texturePath);
if (existingId) {
return existingId;
}
feat: 添加跨平台运行时、资产系统和UI适配功能 (#256) * feat(platform-common): 添加WASM加载器和环境检测API * feat(rapier2d): 新增Rapier2D WASM绑定包 * feat(physics-rapier2d): 添加跨平台WASM加载器 * feat(asset-system): 添加运行时资产目录和bundle格式 * feat(asset-system-editor): 新增编辑器资产管理包 * feat(editor-core): 添加构建系统和模块管理 * feat(editor-app): 重构浏览器预览使用import maps * feat(platform-web): 添加BrowserRuntime和资产读取 * feat(engine): 添加材质系统和着色器管理 * feat(material): 新增材质系统和着色器编辑器 * feat(tilemap): 增强tilemap编辑器和动画系统 * feat(modules): 添加module.json配置 * feat(core): 添加module.json和类型定义更新 * chore: 更新依赖和构建配置 * refactor(plugins): 更新插件模板使用ModuleManifest * chore: 添加第三方依赖库 * chore: 移除BehaviourTree-ai和ecs-astar子模块 * docs: 更新README和文档主题样式 * fix: 修复Rust文档测试和添加rapier2d WASM绑定 * fix(tilemap-editor): 修复画布高DPI屏幕分辨率适配问题 * feat(ui): 添加UI屏幕适配系统(CanvasScaler/SafeArea) * fix(ecs-engine-bindgen): 添加缺失的ecs-framework-math依赖 * fix: 添加缺失的包依赖修复CI构建 * fix: 修复CodeQL检测到的代码问题 * fix: 修复构建错误和缺失依赖 * fix: 修复类型检查错误 * fix(material-system): 修复tsconfig配置支持TypeScript项目引用 * fix(editor-core): 修复Rollup构建配置添加tauri external * fix: 修复CodeQL检测到的代码问题 * fix: 修复CodeQL检测到的代码问题
2025-12-03 22:15:22 +08:00
// 通过资产系统加载AssetManager 内部会解析路径)
// Load through asset system (AssetManager resolves path internally)
const result = await this._assetManager.loadAssetByPath<ITextureAsset>(texturePath);
const textureAsset = result.asset;
feat: 添加跨平台运行时、资产系统和UI适配功能 (#256) * feat(platform-common): 添加WASM加载器和环境检测API * feat(rapier2d): 新增Rapier2D WASM绑定包 * feat(physics-rapier2d): 添加跨平台WASM加载器 * feat(asset-system): 添加运行时资产目录和bundle格式 * feat(asset-system-editor): 新增编辑器资产管理包 * feat(editor-core): 添加构建系统和模块管理 * feat(editor-app): 重构浏览器预览使用import maps * feat(platform-web): 添加BrowserRuntime和资产读取 * feat(engine): 添加材质系统和着色器管理 * feat(material): 新增材质系统和着色器编辑器 * feat(tilemap): 增强tilemap编辑器和动画系统 * feat(modules): 添加module.json配置 * feat(core): 添加module.json和类型定义更新 * chore: 更新依赖和构建配置 * refactor(plugins): 更新插件模板使用ModuleManifest * chore: 添加第三方依赖库 * chore: 移除BehaviourTree-ai和ecs-astar子模块 * docs: 更新README和文档主题样式 * fix: 修复Rust文档测试和添加rapier2d WASM绑定 * fix(tilemap-editor): 修复画布高DPI屏幕分辨率适配问题 * feat(ui): 添加UI屏幕适配系统(CanvasScaler/SafeArea) * fix(ecs-engine-bindgen): 添加缺失的ecs-framework-math依赖 * fix: 添加缺失的包依赖修复CI构建 * fix: 修复CodeQL检测到的代码问题 * fix: 修复构建错误和缺失依赖 * fix: 修复类型检查错误 * fix(material-system): 修复tsconfig配置支持TypeScript项目引用 * fix(editor-core): 修复Rollup构建配置添加tauri external * fix: 修复CodeQL检测到的代码问题 * fix: 修复CodeQL检测到的代码问题
2025-12-03 22:15:22 +08:00
// 如果有引擎桥接上传到GPU
// Upload to GPU if bridge exists
// 使用 globalPathResolver 将路径转换为引擎可用的 URL
// Use globalPathResolver to convert path to engine-compatible URL
if (this._engineBridge && textureAsset.data) {
feat: 添加跨平台运行时、资产系统和UI适配功能 (#256) * feat(platform-common): 添加WASM加载器和环境检测API * feat(rapier2d): 新增Rapier2D WASM绑定包 * feat(physics-rapier2d): 添加跨平台WASM加载器 * feat(asset-system): 添加运行时资产目录和bundle格式 * feat(asset-system-editor): 新增编辑器资产管理包 * feat(editor-core): 添加构建系统和模块管理 * feat(editor-app): 重构浏览器预览使用import maps * feat(platform-web): 添加BrowserRuntime和资产读取 * feat(engine): 添加材质系统和着色器管理 * feat(material): 新增材质系统和着色器编辑器 * feat(tilemap): 增强tilemap编辑器和动画系统 * feat(modules): 添加module.json配置 * feat(core): 添加module.json和类型定义更新 * chore: 更新依赖和构建配置 * refactor(plugins): 更新插件模板使用ModuleManifest * chore: 添加第三方依赖库 * chore: 移除BehaviourTree-ai和ecs-astar子模块 * docs: 更新README和文档主题样式 * fix: 修复Rust文档测试和添加rapier2d WASM绑定 * fix(tilemap-editor): 修复画布高DPI屏幕分辨率适配问题 * feat(ui): 添加UI屏幕适配系统(CanvasScaler/SafeArea) * fix(ecs-engine-bindgen): 添加缺失的ecs-framework-math依赖 * fix: 添加缺失的包依赖修复CI构建 * fix: 修复CodeQL检测到的代码问题 * fix: 修复构建错误和缺失依赖 * fix: 修复类型检查错误 * fix(material-system): 修复tsconfig配置支持TypeScript项目引用 * fix(editor-core): 修复Rollup构建配置添加tauri external * fix: 修复CodeQL检测到的代码问题 * fix: 修复CodeQL检测到的代码问题
2025-12-03 22:15:22 +08:00
const engineUrl = globalPathResolver.resolve(texturePath);
await this._engineBridge.loadTexture(textureAsset.textureId, engineUrl);
}
// 缓存映射(使用原始路径作为键,避免重复解析)
// Cache mapping (using original path as key to avoid re-resolving)
this._pathToTextureId.set(texturePath, textureAsset.textureId);
return textureAsset.textureId;
}
/**
* Load texture by GUID
* GUID加载纹理
*/
async loadTextureByGuid(guid: AssetGUID): Promise<number> {
// 检查是否已有纹理ID / Check if texture ID exists
const existingId = this._textureIdMap.get(guid);
if (existingId) {
return existingId;
}
// 通过资产系统加载 / Load through asset system
const result = await this._assetManager.loadAsset<ITextureAsset>(guid);
const textureAsset = result.asset;
// 如果有引擎桥接上传到GPU / Upload to GPU if bridge exists
if (this._engineBridge && textureAsset.data) {
const metadata = result.metadata;
await this._engineBridge.loadTexture(textureAsset.textureId, metadata.path);
}
// 缓存映射 / Cache mapping
this._textureIdMap.set(guid, textureAsset.textureId);
return textureAsset.textureId;
}
/**
* Batch load textures
*
*/
async loadTexturesBatch(paths: string[]): Promise<Map<string, number>> {
const results = new Map<string, number>();
// 收集需要加载的纹理 / Collect textures to load
const toLoad: string[] = [];
for (const path of paths) {
const existingId = this._pathToTextureId.get(path);
if (existingId) {
results.set(path, existingId);
} else {
toLoad.push(path);
}
}
if (toLoad.length === 0) {
return results;
}
// 并行加载所有纹理 / Load all textures in parallel
const loadPromises = toLoad.map(async (path) => {
try {
const id = await this.loadTextureForComponent(path);
results.set(path, id);
} catch (error) {
console.error(`Failed to load texture: ${path}`, error);
results.set(path, 0); // 使用默认纹理ID / Use default texture ID
}
});
await Promise.all(loadPromises);
return results;
}
/**
* IResourceLoader
* Load resources in batch (generic method for IResourceLoader interface)
*
* @param paths / Array of resource paths
* @param type / Resource type
* @returns ID / Map of paths to runtime IDs
*/
async loadResourcesBatch(paths: string[], type: 'texture' | 'audio' | 'font' | 'data'): Promise<Map<string, number>> {
switch (type) {
case 'texture':
return this.loadTexturesBatch(paths);
case 'audio':
return this.loadAudioBatch(paths);
case 'data':
return this.loadDataBatch(paths);
case 'font':
// 字体资源暂未实现 / Font resources not yet implemented
console.warn('[EngineIntegration] Font resource loading not yet implemented');
return new Map();
default:
console.warn(`[EngineIntegration] Unknown resource type '${type}'`);
return new Map();
}
}
// ============= Audio Resource Methods =============
// ============= 音频资源方法 =============
/**
* Load audio for component
*
*
* @param audioPath / Audio file path
* @returns ID / Runtime audio ID
*/
async loadAudioForComponent(audioPath: string): Promise<number> {
// 检查缓存 / Check cache
const existingId = this._pathToAudioId.get(audioPath);
if (existingId) {
return existingId;
}
// 通过资产系统加载 / Load through asset system
const result = await this._assetManager.loadAssetByPath<IAudioAsset>(audioPath);
const audioAsset = result.asset;
// 分配运行时 ID / Assign runtime ID
const audioId = EngineIntegration._nextAudioId++;
// 缓存映射 / Cache mapping
this._pathToAudioId.set(audioPath, audioId);
this._audioAssets.set(audioId, {
id: audioId,
asset: audioAsset,
path: audioPath
});
return audioId;
}
/**
* Batch load audio files
*
*/
async loadAudioBatch(paths: string[]): Promise<Map<string, number>> {
const results = new Map<string, number>();
// 收集需要加载的音频 / Collect audio to load
const toLoad: string[] = [];
for (const path of paths) {
const existingId = this._pathToAudioId.get(path);
if (existingId) {
results.set(path, existingId);
} else {
toLoad.push(path);
}
}
if (toLoad.length === 0) {
return results;
}
// 并行加载所有音频 / Load all audio in parallel
const loadPromises = toLoad.map(async (path) => {
try {
const id = await this.loadAudioForComponent(path);
results.set(path, id);
} catch (error) {
console.error(`Failed to load audio: ${path}`, error);
results.set(path, 0);
}
});
await Promise.all(loadPromises);
return results;
}
/**
* Get audio asset by ID
* ID
*/
getAudioAsset(audioId: number): IAudioAsset | null {
const entry = this._audioAssets.get(audioId);
return entry?.asset || null;
}
/**
* Get audio ID for path
* ID
*/
getAudioId(path: string): number | null {
return this._pathToAudioId.get(path) || null;
}
/**
* Unload audio
*
*/
unloadAudio(audioId: number): void {
const entry = this._audioAssets.get(audioId);
if (entry) {
this._pathToAudioId.delete(entry.path);
this._audioAssets.delete(audioId);
// 从 GUID 映射中清理 / Clean up GUID mapping
for (const [guid, id] of this._audioIdMap.entries()) {
if (id === audioId) {
this._audioIdMap.delete(guid);
this._assetManager.unloadAsset(guid);
break;
}
}
}
}
// ============= Data Resource Methods =============
// ============= 数据资源方法 =============
/**
* Load data (JSON) for component
* JSON
*
* @param dataPath / Data file path
* @returns ID / Runtime data ID
*/
async loadDataForComponent(dataPath: string): Promise<number> {
// 检查缓存 / Check cache
const existingId = this._pathToDataId.get(dataPath);
if (existingId) {
return existingId;
}
// 通过资产系统加载 / Load through asset system
const result = await this._assetManager.loadAssetByPath<IJsonAsset>(dataPath);
const jsonAsset = result.asset;
// 分配运行时 ID / Assign runtime ID
const dataId = EngineIntegration._nextDataId++;
// 缓存映射 / Cache mapping
this._pathToDataId.set(dataPath, dataId);
this._dataAssets.set(dataId, {
id: dataId,
data: jsonAsset.data,
path: dataPath
});
return dataId;
}
/**
* Batch load data files
*
*/
async loadDataBatch(paths: string[]): Promise<Map<string, number>> {
const results = new Map<string, number>();
// 收集需要加载的数据 / Collect data to load
const toLoad: string[] = [];
for (const path of paths) {
const existingId = this._pathToDataId.get(path);
if (existingId) {
results.set(path, existingId);
} else {
toLoad.push(path);
}
}
if (toLoad.length === 0) {
return results;
}
// 并行加载所有数据 / Load all data in parallel
const loadPromises = toLoad.map(async (path) => {
try {
const id = await this.loadDataForComponent(path);
results.set(path, id);
} catch (error) {
console.error(`Failed to load data: ${path}`, error);
results.set(path, 0);
}
});
await Promise.all(loadPromises);
return results;
}
/**
* Get data by ID
* ID
*/
getData<T = unknown>(dataId: number): T | null {
const entry = this._dataAssets.get(dataId);
return (entry?.data as T) || null;
}
/**
* Get data ID for path
* ID
*/
getDataId(path: string): number | null {
return this._pathToDataId.get(path) || null;
}
/**
* Unload data
*
*/
unloadData(dataId: number): void {
const entry = this._dataAssets.get(dataId);
if (entry) {
this._pathToDataId.delete(entry.path);
this._dataAssets.delete(dataId);
// 从 GUID 映射中清理 / Clean up GUID mapping
for (const [guid, id] of this._dataIdMap.entries()) {
if (id === dataId) {
this._dataIdMap.delete(guid);
this._assetManager.unloadAsset(guid);
break;
}
}
}
}
/**
* Unload texture
*
*/
unloadTexture(textureId: number): void {
// 从引擎卸载 / Unload from engine
if (this._engineBridge) {
this._engineBridge.unloadTexture(textureId);
}
// 清理映射 / Clean up mappings
for (const [path, id] of this._pathToTextureId.entries()) {
if (id === textureId) {
this._pathToTextureId.delete(path);
break;
}
}
for (const [guid, id] of this._textureIdMap.entries()) {
if (id === textureId) {
this._textureIdMap.delete(guid);
// 也从资产管理器卸载 / Also unload from asset manager
this._assetManager.unloadAsset(guid);
break;
}
}
}
/**
* Get texture ID for path
* ID
*/
getTextureId(path: string): number | null {
return this._pathToTextureId.get(path) || null;
}
/**
* Preload textures for scene
*
*/
async preloadSceneTextures(texturePaths: string[]): Promise<void> {
await this.loadTexturesBatch(texturePaths);
}
/**
* Clear all texture mappings
*
*/
clearTextureMappings(): void {
this._textureIdMap.clear();
this._pathToTextureId.clear();
}
/**
* Clear all audio mappings
*
*/
clearAudioMappings(): void {
this._audioIdMap.clear();
this._pathToAudioId.clear();
this._audioAssets.clear();
}
/**
* Clear all data mappings
*
*/
clearDataMappings(): void {
this._dataIdMap.clear();
this._pathToDataId.clear();
this._dataAssets.clear();
}
/**
* Clear all resource mappings
*
*/
clearAllMappings(): void {
this.clearTextureMappings();
this.clearAudioMappings();
this.clearDataMappings();
}
/**
* Get statistics
*
*/
getStatistics(): {
loadedTextures: number;
loadedAudio: number;
loadedData: number;
} {
return {
loadedTextures: this._pathToTextureId.size,
loadedAudio: this._audioAssets.size,
loadedData: this._dataAssets.size
};
}
}