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
/**
* Entity - 实体
*
* 在场景中加载 3D 模型资源的物体,支持动画播放,可加载预制件。
* @class Entity
* @memberof THING
* @extends THING.BaseEntity
* @public
* @example
const entity = new THING.Entity({
url: './models/robot.glb',
position: [0, 0, 0],
onComplete: (e) => {
console.log('模型加载完成', e);
},
});
*/
class Entity extends BaseEntity {
static defaultTagArray = ['Entity'];
/**
* Entity的构造函数
* 也可用于在场景中加载预制资源的预制对象。
* @constructor
* @public
* @param {object} param 初始化参数 这里支持Object3D初始化的所有参数,此外可接受url作为资源路径
* @param {string} [param.url] 资源路径
*/
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 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) {
if (bundle.isBundle) {
_private.bundle = bundle;
}
else {
_private.loadResult = bundle;
}
}
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;
}
else if (_private.loadResult) {
const map = _private.loadResult.data;
if (map) {
map.forEach(element => {
element.unload && element.unload();
});
}
}
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 {Promise<void>} 导入外部组件的Promise。
* @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 {object} 外部组件。
* @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;
}
promoteNode(name, parent, cls = Entity) {
return super.promoteNode(name, parent, cls);
}
// #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. 获取输出函数的方法
*/
/**
预制件入口
* @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 }