import { Utils } from '../common/Utils';
import { BaseEntity } from "./BaseEntity";
import { PrefabMixLoader } from '../loaders/PrefabMixLoader';
const __ = {
private: Symbol('private'),
}
// #region Private Functions
// Build params.
function _buildParams(param) {
let extras = param['extras'] || param['external'];
if (extras) {
let prefab = extras['prefab'];
if (prefab) {
param['url'] = param['url'] || prefab;
}
let autoCompile = extras['autoCompile'];
if (autoCompile) {
param['autoCompile'] = true;
}
}
return param;
}
// #endregion
/**
* @class Entity
* The entity object.
* @memberof THING
* @extends THING.BaseEntity
* @public
*/
class Entity extends BaseEntity {
static defaultTagArray = ['Entity'];
/**
* The prefabe object that load prefab resource in scene.
* @param {Object} param The initial parameters.
*/
constructor(param = {}) {
super(_buildParams(param));
this[__.private] = {};
let _private = this[__.private];
_private.bundle = null;
_private.isPrefabObject = false;
_private.prefabLoader = null;
}
static onParseCreateOptions(createOptions, options) {
[
'autoCompile',
'isEditor',
].forEach(key => {
if (Utils.isValid(options[key])) {
createOptions[key] = options[key];
}
});
}
// #region Private
_complete(resolve) {
this.onSyncComplete();
this.onDelayComplete();
resolve();
}
// #endregion
// #region Overrides
onCollectMesh() {
// All prefab object resource is external(outside), so skip to collect mesh when export
if (this.isPrefabObject || this.isSceneRoot) {
return false;
}
return true;
}
onLoadRenderableResource(options, resolve, reject) {
if (this.destroyed) {
reject(`The object had been destroyed, skip to load resources`);
}
else {
const _resolve = () => {
this.clearInternalAnimation();
resolve();
};
const isModelResource = Utils.parseBoolean(this.options.isModelResource, false);
if (isModelResource) {
super.onLoadRenderableResource(this.options, _resolve, reject);
return;
}
const url = this.resource.url;
// Determine the blob type
if (Utils.isBlobUrl(url)) {
super.onLoadRenderableResource(this.options, _resolve, reject);
return;
}
let _private = this[__.private];
_private.prefabLoader = new PrefabMixLoader();
_private.prefabLoader.load(this, options).then((bundle) => {
_private.prefabLoader = null;
if (bundle && bundle.isBundle) {
_private.bundle = bundle;
}
this._complete(resolve);
}, (e) => {
this.triggerError(e, reject);
});
}
}
onImportComponents(components, options, param) {
if (param) {
let extras = param['extras'] || param['external'];
if (extras && extras.prefab) {
return;
}
}
super.onImportComponents(components, options);
}
onNotifyComplete() {
let dynamic = this.options && this.options.dynamic;
if (!dynamic) {
this.waitForComplete().then(() => {
super.onNotifyComplete();
});
}
else {
super.onNotifyComplete();
}
}
onUnloadResource() {
let _private = this[__.private];
if (_private.bundle) {
this.app.unloadBundle(_private.bundle);
_private.bundle = null;
}
super.onUnloadResource();
}
onExportExternalData(options) {
let _private = this[__.private];
let data = super.onExportExternalData(options) || {};
// Compatibility bundle
if (_private.bundle) {
data.autoCompile = Utils.parseValue(this.options['autoCompile'], false);
data.prefab = this.resource.url;
}
return data;
}
/**
* Import external components (This is only used by bundles)
* @param {Object} externalComponents
* @returns
* @private
*/
onImportExternalComponents(externalComponents) {
return this.waitForComplete().then(() => {
// Import all children's components
for (let uuid in externalComponents) {
// Find the child object to import component
let child = this.find(`[external/uuid=${uuid}]`);
if (!child) {
continue;
}
// Import all components of child
let childExternalComponents = externalComponents[uuid];
childExternalComponents.forEach(externalComponent => {
let componentName = externalComponent.name;
let component = child[componentName];
if (component && component.onImport) {
component.onImport(externalComponent.external || externalComponent.params);
}
});
}
});
}
/**
* Export external components (This is only used by bundles)
* @returns
* @private
*/
onExportExternalComponents() {
if (!this.bundle) {
return null;
}
let suffixName = '_' + this.bundle.uuid;
let options = {
onGetComponentType: (name) => {
if (name._endsWith(suffixName)) {
return name._trimRight(suffixName);
}
else {
return name;
}
}
};
let externalComponents = super.onExportExternalComponents() || {};
this.children.traverse(child => {
let childComponents = child.onExportComponents(options);
if (!childComponents) {
return;
}
let external = child.external;
if (!external) {
return;
}
let uuid = external.uuid;
externalComponents[uuid] = childComponents;
});
return externalComponents;
}
onBeforeDestroy() {
let canDestroy = super.onBeforeDestroy();
if (canDestroy) {
let _private = this[__.private];
if (_private.prefabLoader) {
_private.prefabLoader.dispose();
_private.prefabLoader = null;
}
}
return canDestroy;
}
// #endregion
// #region Accessors
/**
* @typedef {Object} PrefabEntrance
* @property {Function} getBlueprintClasses Get the blueprint classes.
* @property {Function} getExportProperties Get the export properties.
* @property {Function} getExportFunctions Get the export functions.
*/
/**
* Get the entrance.
* @type {PrefabEntrance}
* @private
*/
get entrance() {
let _private = this[__.private];
let bundle = _private.bundle;
if (!bundle) {
return null;
}
let bundleInfo = bundle.prefab ? bundle.prefab : bundle.model;
return bundleInfo.entrance;
}
/**
* Get the bundle.
* @type {THING.Bundle}
* @private
*/
get bundle() {
let _private = this[__.private];
return _private.bundle;
}
/**
* Check whether it's PrefabObject type or inherit from it.
* @type {Boolean}
* @private
*/
get isPrefabObject() {
let _private = this[__.private];
return _private.isPrefabObject;
}
// #endregion
}
export { Entity }