mirror of
https://github.com/galacean/engine.git
synced 2026-06-09 17:23:29 +08:00
refactor: scene parser split parse component and entity (#1334)
This commit is contained in:
@@ -1,8 +1,28 @@
|
||||
import type { BackgroundMode } from "@oasis-engine/core";
|
||||
import { IRefObject } from "@oasis-engine/core/types/asset/IRefObject";
|
||||
import { IColor } from "../mesh/IModelMesh";
|
||||
|
||||
export interface IPrefabFile {
|
||||
entities: Array<IEntity>;
|
||||
}
|
||||
|
||||
export interface IScene extends IPrefabFile {}
|
||||
export interface IScene extends IPrefabFile {
|
||||
scene: {
|
||||
background: {
|
||||
mode: BackgroundMode;
|
||||
color: IColor;
|
||||
texture?: IRefObject;
|
||||
sky?: IRefObject;
|
||||
};
|
||||
ambient: {
|
||||
ambientLight: IRefObject;
|
||||
diffuseSolidColor: IColor;
|
||||
diffuseIntensity: number;
|
||||
specularIntensity: number;
|
||||
};
|
||||
};
|
||||
files: Array<{ id: string; type: string; virtualPath: string; path: string }>;
|
||||
}
|
||||
|
||||
export interface IVector3 {
|
||||
x: number;
|
||||
@@ -40,6 +60,6 @@ export type IClassObject = {
|
||||
props?: { [key: string]: IBasicType | IMethodParams };
|
||||
};
|
||||
|
||||
export type IBasicType = string | number | boolean | null | undefined | IReferenceType | IClassObject | IMethodParams;
|
||||
export type IBasicType = string | number | boolean | null | undefined | IAssetRef | IClassObject | IMethodParams;
|
||||
|
||||
export type IReferenceType = { key?: string; refId: string };
|
||||
export type IAssetRef = { key?: string; refId: string };
|
||||
|
||||
@@ -3,39 +3,13 @@ import type { IEntity, IPrefabFile } from "./PrefabDesign";
|
||||
import { ReflectionParser } from "./ReflectionParser";
|
||||
|
||||
export class PrefabParser {
|
||||
constructor(private _engine: Engine) {}
|
||||
|
||||
parse(data: IPrefabFile): Promise<Entity> {
|
||||
const entitiesMap: Record<string, Entity> = {};
|
||||
const entitiesConfigMap: Record<string, IEntity> = {};
|
||||
const promises: Promise<Entity>[] = [];
|
||||
const entitiesConfig = data.entities;
|
||||
for (const entity of entitiesConfig) {
|
||||
entitiesConfigMap[entity.id] = entity;
|
||||
promises.push(ReflectionParser.parseEntity(entity, this._engine));
|
||||
}
|
||||
|
||||
return Promise.all(promises).then((entities) => {
|
||||
const rootId = entitiesConfig[0].id;
|
||||
entities.forEach((entity, index) => {
|
||||
entitiesMap[entitiesConfig[index].id] = entity;
|
||||
});
|
||||
PrefabParser.parseChildren(entitiesConfigMap, entitiesMap, rootId);
|
||||
return entitiesMap[rootId];
|
||||
});
|
||||
}
|
||||
|
||||
static parseChildren(
|
||||
entitiesConfig: { [key: string]: IEntity },
|
||||
entities: { [key: string]: Entity },
|
||||
parentId: string
|
||||
) {
|
||||
const children = entitiesConfig[parentId].children;
|
||||
static parseChildren(entitiesConfig: Map<string, IEntity>, entities: Map<string, Entity>, parentId: string) {
|
||||
const children = entitiesConfig.get(parentId).children;
|
||||
if (children && children.length > 0) {
|
||||
const parent = entities[parentId];
|
||||
const parent = entities.get(parentId);
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const childId = children[i];
|
||||
const entity = entities[childId];
|
||||
const entity = entities.get(childId);
|
||||
parent.addChild(entity);
|
||||
this.parseChildren(entitiesConfig, entities, childId);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Engine, Entity, Loader } from "@oasis-engine/core";
|
||||
import { IBasicType, IClassObject, IEntity, IReferenceType } from "./PrefabDesign";
|
||||
import { IBasicType, IClassObject, IEntity, IAssetRef } from "./PrefabDesign";
|
||||
|
||||
export class ReflectionParser {
|
||||
static customParseComponentHandles = new Map<string, Function>();
|
||||
@@ -12,30 +12,10 @@ export class ReflectionParser {
|
||||
return ReflectionParser.getEntityByConfig(entityConfig, engine).then((entity) => {
|
||||
entity.isActive = entityConfig.isActive ?? true;
|
||||
const { position, rotation, scale } = entityConfig;
|
||||
if (position) {
|
||||
entity.transform.setPosition(position.x, position.y, position.z);
|
||||
}
|
||||
if (rotation) {
|
||||
entity.transform.setRotation(rotation.x, rotation.y, rotation.z);
|
||||
}
|
||||
if (scale) {
|
||||
entity.transform.setScale(scale.x, scale.y, scale.z);
|
||||
}
|
||||
const promises = [];
|
||||
for (let i = 0; i < entityConfig.components.length; i++) {
|
||||
const componentConfig = entityConfig.components[i];
|
||||
const key = !componentConfig.refId ? componentConfig.class : componentConfig.refId;
|
||||
let component;
|
||||
if (key === "Animator") {
|
||||
component = entity.getComponent(Loader.getClass(key));
|
||||
}
|
||||
component = component || entity.addComponent(Loader.getClass(key));
|
||||
const promise = this.parsePropsAndMethods(component, componentConfig, engine);
|
||||
promises.push(promise);
|
||||
}
|
||||
return Promise.all(promises).then(() => {
|
||||
return entity;
|
||||
});
|
||||
if (position) entity.transform.position.copyFrom(position);
|
||||
if (rotation) entity.transform.rotation.copyFrom(rotation);
|
||||
if (scale) entity.transform.scale.copyFrom(scale);
|
||||
return entity;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -78,13 +58,13 @@ export class ReflectionParser {
|
||||
return Promise.all(value.map((item) => this.parseBasicType(item, engine, resourceManager)));
|
||||
} else if (typeof value === "object" && value != null) {
|
||||
if (this._isClass(value)) {
|
||||
// 类对象
|
||||
// class object
|
||||
return this.parseClassObject(value, engine, resourceManager);
|
||||
} else if (this._isRef(value)) {
|
||||
// 引用对象
|
||||
// reference object
|
||||
return resourceManager.getResourceByRef(value);
|
||||
} else {
|
||||
// 基础类型
|
||||
// basic type
|
||||
return Promise.resolve(value);
|
||||
}
|
||||
} else {
|
||||
@@ -120,17 +100,10 @@ export class ReflectionParser {
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
Promise.all(promises).then(() => {
|
||||
const handle = this.customParseComponentHandles[instance.constructor.name];
|
||||
if (handle) {
|
||||
handle(instance, item, engine).then(() => {
|
||||
resolve(instance);
|
||||
});
|
||||
} else {
|
||||
resolve(instance);
|
||||
}
|
||||
}).catch(reject)
|
||||
return Promise.all(promises).then(() => {
|
||||
const handle = this.customParseComponentHandles[instance.constructor.name];
|
||||
if (handle) return handle(instance, item, engine);
|
||||
else return instance;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -152,7 +125,7 @@ export class ReflectionParser {
|
||||
return value["class"] != undefined;
|
||||
}
|
||||
|
||||
private static _isRef(value: any): value is IReferenceType {
|
||||
private static _isRef(value: any): value is IAssetRef {
|
||||
return value["refId"] != undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,113 @@
|
||||
import { Engine, Entity, Scene } from "@oasis-engine/core";
|
||||
import { Engine, Entity, Loader, Scene } from "@oasis-engine/core";
|
||||
import { IEntity, IScene } from "../prefab/PrefabDesign";
|
||||
import { PrefabParser } from "../prefab/PrefabParser";
|
||||
import { ReflectionParser } from "../prefab/ReflectionParser";
|
||||
import { SceneParserContext } from "./SceneParserContext";
|
||||
|
||||
/** @Internal */
|
||||
export class SceneParser {
|
||||
/**
|
||||
* Parse scene data.
|
||||
* @param engine - the engine of the parser context
|
||||
* @param sceneData - scene data which is exported by editor
|
||||
* @returns a promise of scene
|
||||
*/
|
||||
static parse(engine: Engine, sceneData: IScene): Promise<Scene> {
|
||||
const scene = new Scene(engine);
|
||||
const entitiesMap: Record<string, Entity> = {};
|
||||
const entitiesConfigMap: Record<string, IEntity> = {};
|
||||
const promises: Promise<Entity>[] = [];
|
||||
const entitiesConfig = sceneData.entities;
|
||||
for (const entity of entitiesConfig) {
|
||||
entitiesConfigMap[entity.id] = entity;
|
||||
promises.push(ReflectionParser.parseEntity(entity, engine));
|
||||
}
|
||||
const context = new SceneParserContext(sceneData, scene);
|
||||
const parser = new SceneParser(context);
|
||||
parser.start();
|
||||
return parser.promise;
|
||||
}
|
||||
|
||||
return Promise.all(promises).then((entities) => {
|
||||
const rootIds = [];
|
||||
entities.forEach((entity, index) => {
|
||||
entitiesMap[entitiesConfig[index].id] = entity;
|
||||
if (!entitiesConfig[index].parent) {
|
||||
rootIds.push(entitiesConfig[index].id);
|
||||
}
|
||||
});
|
||||
for (const rootId of rootIds) {
|
||||
PrefabParser.parseChildren(entitiesConfigMap, entitiesMap, rootId);
|
||||
}
|
||||
const rootEntities = rootIds.map((id) => entitiesMap[id]);
|
||||
for (let i = 0; i < rootEntities.length; i++) {
|
||||
scene.addRootEntity(rootEntities[i]);
|
||||
}
|
||||
return scene;
|
||||
/**
|
||||
* The promise of parsed scene.
|
||||
*/
|
||||
readonly promise: Promise<Scene>;
|
||||
|
||||
private _resolve: (scene: Scene) => void;
|
||||
private _reject: (reason: any) => void;
|
||||
private _engine: Engine;
|
||||
|
||||
constructor(public readonly context: SceneParserContext) {
|
||||
this._engine = this.context.scene.engine;
|
||||
this._organizeEntities = this._organizeEntities.bind(this);
|
||||
this._parseComponents = this._parseComponents.bind(this);
|
||||
this._clearAndResolveScene = this._clearAndResolveScene.bind(this);
|
||||
this.promise = new Promise<Scene>((resolve, reject) => {
|
||||
this._reject = reject;
|
||||
this._resolve = resolve;
|
||||
});
|
||||
}
|
||||
|
||||
/** start parse the scene */
|
||||
start() {
|
||||
this._parseEntities()
|
||||
.then(this._organizeEntities)
|
||||
.then(this._parseComponents)
|
||||
.then(this._clearAndResolveScene)
|
||||
.then(this._resolve)
|
||||
.catch(this._reject);
|
||||
}
|
||||
|
||||
private _parseEntities(): Promise<Entity[]> {
|
||||
const entitiesConfig = this.context.originalData.entities;
|
||||
const entityConfigMap = this.context.entityConfigMap;
|
||||
const entitiesMap = this.context.entityMap;
|
||||
const rootIds = this.context.rootIds;
|
||||
const engine = this._engine;
|
||||
const promises = entitiesConfig.map((entityConfig) => {
|
||||
entityConfigMap.set(entityConfig.id, entityConfig);
|
||||
// record root entities
|
||||
if (!entityConfig.parent) rootIds.push(entityConfig.id);
|
||||
return ReflectionParser.parseEntity(entityConfig, engine);
|
||||
});
|
||||
|
||||
return Promise.all(promises).then((entities) => {
|
||||
for (let i = 0, l = entities.length; i < l; i++) {
|
||||
entitiesMap.set(entitiesConfig[i].id, entities[i]);
|
||||
}
|
||||
return entities;
|
||||
});
|
||||
}
|
||||
|
||||
private _organizeEntities() {
|
||||
const { entityConfigMap, entityMap, scene, rootIds } = this.context;
|
||||
for (const rootId of rootIds) {
|
||||
PrefabParser.parseChildren(entityConfigMap, entityMap, rootId);
|
||||
}
|
||||
const rootEntities = rootIds.map((id) => entityMap.get(id));
|
||||
for (let i = 0; i < rootEntities.length; i++) {
|
||||
scene.addRootEntity(rootEntities[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private _parseComponents() {
|
||||
const entitiesConfig = this.context.originalData.entities;
|
||||
const entityMap = this.context.entityMap;
|
||||
|
||||
const promises = [];
|
||||
for (let i = 0, l = entitiesConfig.length; i < l; i++) {
|
||||
const entityConfig = entitiesConfig[i];
|
||||
const entity = entityMap.get(entityConfig.id);
|
||||
for (let i = 0; i < entityConfig.components.length; i++) {
|
||||
const componentConfig = entityConfig.components[i];
|
||||
const key = !componentConfig.refId ? componentConfig.class : componentConfig.refId;
|
||||
let component;
|
||||
// TODO: remove hack code when support additional edit
|
||||
if (key === "Animator") {
|
||||
component = entity.getComponent(Loader.getClass(key));
|
||||
}
|
||||
component = component || entity.addComponent(Loader.getClass(key));
|
||||
const promise = ReflectionParser.parsePropsAndMethods(component, componentConfig, entity.engine);
|
||||
promises.push(promise);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _clearAndResolveScene() {
|
||||
const scene = this.context.scene;
|
||||
this.context.destroy();
|
||||
return scene;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Component, Entity, Scene } from "@oasis-engine/core";
|
||||
import { IEntity, IScene } from "../prefab/PrefabDesign";
|
||||
|
||||
export class SceneParserContext {
|
||||
entityMap: Map<string, Entity> = new Map();
|
||||
components: Map<string, Component> = new Map();
|
||||
assets: Map<string, any> = new Map();
|
||||
entityConfigMap: Map<string, IEntity> = new Map();
|
||||
rootIds: string[] = [];
|
||||
constructor(public readonly originalData: IScene, public readonly scene: Scene) {}
|
||||
|
||||
destroy() {
|
||||
this.entityMap.clear();
|
||||
this.components.clear();
|
||||
this.assets.clear();
|
||||
this.entityConfigMap.clear();
|
||||
this.rootIds.length = 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user