Feature/render pipeline (#232)

* refactor(engine): 重构2D渲染管线坐标系统

* feat(engine): 完善2D渲染管线和编辑器视口功能

* feat(editor): 实现Viewport变换工具系统

* feat(editor): 优化Inspector渲染性能并修复Gizmo变换工具显示

* feat(editor): 实现Run on Device移动预览功能

* feat(editor): 添加组件属性控制和依赖关系系统

* feat(editor): 实现动画预览功能和优化SpriteAnimator编辑器

* feat(editor): 修复SpriteAnimator动画预览功能并迁移CI到pnpm

* feat(editor): 修复SpriteAnimator动画预览并迁移到pnpm

* feat(editor): 修复SpriteAnimator动画预览并迁移到pnpm

* feat(editor): 修复SpriteAnimator动画预览并迁移到pnpm

* feat(editor): 修复SpriteAnimator动画预览并迁移到pnpm

* feat(ci): 迁移项目到pnpm并修复CI构建问题

* chore: 迁移CI工作流到pnpm并添加WASM构建支持

* chore: 迁移CI工作流到pnpm并添加WASM构建支持

* chore: 迁移CI工作流到pnpm并添加WASM构建支持

* chore: 迁移CI工作流到pnpm并添加WASM构建支持

* chore: 迁移CI工作流到pnpm并添加WASM构建支持

* chore: 迁移CI工作流到pnpm并添加WASM构建支持

* chore: 移除 network 相关包

* chore: 移除 network 相关包
This commit is contained in:
YHH
2025-11-23 14:49:37 +08:00
committed by GitHub
parent b15cbab313
commit a3f7cc38b1
247 changed files with 33561 additions and 52047 deletions

View File

@@ -0,0 +1,431 @@
/**
* Asset database for managing asset metadata
* 用于管理资产元数据的资产数据库
*/
import {
AssetGUID,
AssetType,
IAssetMetadata,
IAssetCatalogEntry
} from '../types/AssetTypes';
/**
* Asset database implementation
* 资产数据库实现
*/
export class AssetDatabase {
private readonly _metadata = new Map<AssetGUID, IAssetMetadata>();
private readonly _pathToGuid = new Map<string, AssetGUID>();
private readonly _typeToGuids = new Map<AssetType, Set<AssetGUID>>();
private readonly _labelToGuids = new Map<string, Set<AssetGUID>>();
private readonly _dependencies = new Map<AssetGUID, Set<AssetGUID>>();
private readonly _dependents = new Map<AssetGUID, Set<AssetGUID>>();
/**
* Add asset to database
* 添加资产到数据库
*/
addAsset(metadata: IAssetMetadata): void {
const { guid, path, type, labels, dependencies } = metadata;
// 存储元数据 / Store metadata
this._metadata.set(guid, metadata);
this._pathToGuid.set(path, guid);
// 按类型索引 / Index by type
if (!this._typeToGuids.has(type)) {
this._typeToGuids.set(type, new Set());
}
this._typeToGuids.get(type)!.add(guid);
// 按标签索引 / Index by labels
labels.forEach((label) => {
if (!this._labelToGuids.has(label)) {
this._labelToGuids.set(label, new Set());
}
this._labelToGuids.get(label)!.add(guid);
});
// 建立依赖关系 / Establish dependencies
this.updateDependencies(guid, dependencies);
}
/**
* Remove asset from database
* 从数据库移除资产
*/
removeAsset(guid: AssetGUID): void {
const metadata = this._metadata.get(guid);
if (!metadata) return;
// 清理元数据 / Clean up metadata
this._metadata.delete(guid);
this._pathToGuid.delete(metadata.path);
// 清理类型索引 / Clean up type index
const typeSet = this._typeToGuids.get(metadata.type);
if (typeSet) {
typeSet.delete(guid);
if (typeSet.size === 0) {
this._typeToGuids.delete(metadata.type);
}
}
// 清理标签索引 / Clean up label indices
metadata.labels.forEach((label) => {
const labelSet = this._labelToGuids.get(label);
if (labelSet) {
labelSet.delete(guid);
if (labelSet.size === 0) {
this._labelToGuids.delete(label);
}
}
});
// 清理依赖关系 / Clean up dependencies
this.clearDependencies(guid);
}
/**
* Update asset metadata
* 更新资产元数据
*/
updateAsset(guid: AssetGUID, updates: Partial<IAssetMetadata>): void {
const metadata = this._metadata.get(guid);
if (!metadata) return;
// 如果路径改变,更新索引 / Update index if path changed
if (updates.path && updates.path !== metadata.path) {
this._pathToGuid.delete(metadata.path);
this._pathToGuid.set(updates.path, guid);
}
// 如果类型改变,更新索引 / Update index if type changed
if (updates.type && updates.type !== metadata.type) {
const oldTypeSet = this._typeToGuids.get(metadata.type);
if (oldTypeSet) {
oldTypeSet.delete(guid);
}
if (!this._typeToGuids.has(updates.type)) {
this._typeToGuids.set(updates.type, new Set());
}
this._typeToGuids.get(updates.type)!.add(guid);
}
// 如果依赖改变,更新关系 / Update relations if dependencies changed
if (updates.dependencies) {
this.updateDependencies(guid, updates.dependencies);
}
// 合并更新 / Merge updates
Object.assign(metadata, updates);
metadata.lastModified = Date.now();
metadata.version++;
}
/**
* Get asset metadata
* 获取资产元数据
*/
getMetadata(guid: AssetGUID): IAssetMetadata | undefined {
return this._metadata.get(guid);
}
/**
* Get metadata by path
* 通过路径获取元数据
*/
getMetadataByPath(path: string): IAssetMetadata | undefined {
const guid = this._pathToGuid.get(path);
return guid ? this._metadata.get(guid) : undefined;
}
/**
* Find assets by type
* 按类型查找资产
*/
findAssetsByType(type: AssetType): AssetGUID[] {
const guids = this._typeToGuids.get(type);
return guids ? Array.from(guids) : [];
}
/**
* Find assets by label
* 按标签查找资产
*/
findAssetsByLabel(label: string): AssetGUID[] {
const guids = this._labelToGuids.get(label);
return guids ? Array.from(guids) : [];
}
/**
* Find assets by multiple labels (AND operation)
* 按多个标签查找资产AND操作
*/
findAssetsByLabels(labels: string[]): AssetGUID[] {
if (labels.length === 0) return [];
let result: Set<AssetGUID> | null = null;
for (const label of labels) {
const labelGuids = this._labelToGuids.get(label);
if (!labelGuids || labelGuids.size === 0) return [];
if (!result) {
result = new Set(labelGuids);
} else {
// 交集 / Intersection
const intersection = new Set<AssetGUID>();
labelGuids.forEach((guid) => {
if (result!.has(guid)) {
intersection.add(guid);
}
});
result = intersection;
}
}
return result ? Array.from(result) : [];
}
/**
* Search assets by query
* 通过查询搜索资产
*/
searchAssets(query: {
name?: string;
type?: AssetType;
labels?: string[];
path?: string;
}): AssetGUID[] {
let results = Array.from(this._metadata.keys());
// 按名称过滤 / Filter by name
if (query.name) {
const nameLower = query.name.toLowerCase();
results = results.filter((guid) => {
const metadata = this._metadata.get(guid)!;
return metadata.name.toLowerCase().includes(nameLower);
});
}
// 按类型过滤 / Filter by type
if (query.type) {
const typeGuids = this._typeToGuids.get(query.type);
if (!typeGuids) return [];
results = results.filter((guid) => typeGuids.has(guid));
}
// 按标签过滤 / Filter by labels
if (query.labels && query.labels.length > 0) {
const labelResults = this.findAssetsByLabels(query.labels);
const labelSet = new Set(labelResults);
results = results.filter((guid) => labelSet.has(guid));
}
// 按路径过滤 / Filter by path
if (query.path) {
const pathLower = query.path.toLowerCase();
results = results.filter((guid) => {
const metadata = this._metadata.get(guid)!;
return metadata.path.toLowerCase().includes(pathLower);
});
}
return results;
}
/**
* Get asset dependencies
* 获取资产依赖
*/
getDependencies(guid: AssetGUID): AssetGUID[] {
const deps = this._dependencies.get(guid);
return deps ? Array.from(deps) : [];
}
/**
* Get asset dependents (assets that depend on this one)
* 获取资产的依赖者(依赖此资产的其他资产)
*/
getDependents(guid: AssetGUID): AssetGUID[] {
const deps = this._dependents.get(guid);
return deps ? Array.from(deps) : [];
}
/**
* Get all dependencies recursively
* 递归获取所有依赖
*/
getAllDependencies(guid: AssetGUID, visited = new Set<AssetGUID>()): AssetGUID[] {
if (visited.has(guid)) return [];
visited.add(guid);
const result: AssetGUID[] = [];
const directDeps = this.getDependencies(guid);
for (const dep of directDeps) {
result.push(dep);
const transitiveDeps = this.getAllDependencies(dep, visited);
result.push(...transitiveDeps);
}
return result;
}
/**
* Check for circular dependencies
* 检查循环依赖
*/
hasCircularDependency(guid: AssetGUID): boolean {
const visited = new Set<AssetGUID>();
const recursionStack = new Set<AssetGUID>();
const checkCycle = (current: AssetGUID): boolean => {
visited.add(current);
recursionStack.add(current);
const deps = this.getDependencies(current);
for (const dep of deps) {
if (!visited.has(dep)) {
if (checkCycle(dep)) return true;
} else if (recursionStack.has(dep)) {
return true;
}
}
recursionStack.delete(current);
return false;
};
return checkCycle(guid);
}
/**
* Update dependencies
* 更新依赖关系
*/
private updateDependencies(guid: AssetGUID, newDependencies: AssetGUID[]): void {
// 清除旧的依赖关系 / Clear old dependencies
this.clearDependencies(guid);
// 建立新的依赖关系 / Establish new dependencies
if (newDependencies.length > 0) {
this._dependencies.set(guid, new Set(newDependencies));
// 更新被依赖关系 / Update dependent relations
newDependencies.forEach((dep) => {
if (!this._dependents.has(dep)) {
this._dependents.set(dep, new Set());
}
this._dependents.get(dep)!.add(guid);
});
}
}
/**
* Clear dependencies
* 清除依赖关系
*/
private clearDependencies(guid: AssetGUID): void {
// 清除依赖 / Clear dependencies
const deps = this._dependencies.get(guid);
if (deps) {
deps.forEach((dep) => {
const dependents = this._dependents.get(dep);
if (dependents) {
dependents.delete(guid);
if (dependents.size === 0) {
this._dependents.delete(dep);
}
}
});
this._dependencies.delete(guid);
}
// 清除被依赖 / Clear dependents
const dependents = this._dependents.get(guid);
if (dependents) {
dependents.forEach((dependent) => {
const dependencies = this._dependencies.get(dependent);
if (dependencies) {
dependencies.delete(guid);
if (dependencies.size === 0) {
this._dependencies.delete(dependent);
}
}
});
this._dependents.delete(guid);
}
}
/**
* Get database statistics
* 获取数据库统计
*/
getStatistics(): {
totalAssets: number;
assetsByType: Map<AssetType, number>;
totalDependencies: number;
assetsWithDependencies: number;
circularDependencies: number;
} {
const assetsByType = new Map<AssetType, number>();
this._typeToGuids.forEach((guids, type) => {
assetsByType.set(type, guids.size);
});
let circularDependencies = 0;
this._metadata.forEach((_, guid) => {
if (this.hasCircularDependency(guid)) {
circularDependencies++;
}
});
return {
totalAssets: this._metadata.size,
assetsByType,
totalDependencies: Array.from(this._dependencies.values()).reduce(
(sum, deps) => sum + deps.size,
0
),
assetsWithDependencies: this._dependencies.size,
circularDependencies
};
}
/**
* Export to catalog entries
* 导出为目录条目
*/
exportToCatalog(): IAssetCatalogEntry[] {
const entries: IAssetCatalogEntry[] = [];
this._metadata.forEach((metadata) => {
entries.push({
guid: metadata.guid,
path: metadata.path,
type: metadata.type,
size: metadata.size,
hash: metadata.hash
});
});
return entries;
}
/**
* Clear database
* 清空数据库
*/
clear(): void {
this._metadata.clear();
this._pathToGuid.clear();
this._typeToGuids.clear();
this._labelToGuids.clear();
this._dependencies.clear();
this._dependents.clear();
}
}