import { ResolvablePromise, TypedObject, ObjectAttributes, Flags } from '@uino/base-thing';
import { BaseObject } from './BaseObject';
import { BodyObject } from './BodyObject';
import { MathUtils } from '../math/MathUtils';
import { Utils } from '../common/Utils';
import { Style } from '../resources/Style';
import { HelperComponent } from '../components/HelperComponent';
import { TransformComponent } from '../components/TransformComponent';
import { RenderComponent } from '../components/RenderComponent';
import { LerpComponent } from '../components/LerpComponent';
import { BoundingComponent } from '../components/BoundingComponent';
import { LevelComponent } from '../components/LevelComponent';
import { ActionGroupComponent } from '../components/ActionGroupComponent';
import { BlueprintComponent } from '../components/BlueprintComponent';
import { ColliderComponent } from '../components/ColliderComponent';
import { RelationshipComponent } from '../components/RelationshipComponent';
import { TaskExecutorComponent } from '../components/TaskExecutorComponent';
import { EventType, InheritType, SpaceType, Transform } from '../const';
import { MonitorDataComponent } from '../components/MonitorDataComponent';
import { Tags } from '../common/Tags';

const __ = {
	private: Symbol('private'),
}

// [0-7] index is reserved for BaseObject, so index starts from 8
const Flag = {
	// Common
	Initializing: 1 << 8,
	Initialized: 1 << 9,
	// State
	Pickable: 1 << 10,
	Active: 1 << 11,
	FrustumCulled: 1 << 12,
	// On/Off
	KeepSize: 1 << 13,
	HadPromoted: 1 << 14,
	Instance: 1 << 15,
}

const BreakInstanceFlag = {
	AlwaysOnTop: 1 << 0,
	CastShadow: 1 << 1,
	ReceiveShadow: 1 << 2,
	LayerMask: 1 << 3
}

const registerComponentParam = { isResident: true };

const cTranslateByLerpName = '__translateByLerp__';

function _getStyleInheritance(object) {
	if (!object.hasInherit()) {
		return InheritType.Normal;
	}

	return object.inherit.style;
}

/**
 * @class Object3D
 * ThingJS中最基础的3d对象,所有三维场景中的物体都必须从此类派生
 * @memberof THING
 * @extends THING.BaseObject
 * @public
 */
class Object3D extends BaseObject {

	static defaultTagArray = ['Object3D'];

	// The resource state
	static ResourceState = {
		Error: -1,
		Ready: 1,
		Loaded: 2,
		Loading: 3,
	};

	/**
	 * 3D对象类,负责ThingJS中最基础的3D对象功能实现
	 * @param {object} param 初始化参数
	 * @param {string} [param.name=''] 物体名称
	 * @param {string} [param.id=''] 物体id
	 * @param {string} [param.uuid] 物体唯一标识
	 * @param {object} [param.userData] 设置/获取 物体的自定义属性
	 * @param {boolean} [param.queryabled=true] 物体是否可被查询 默认true
	 * @param {boolean} [param.destroyable=true] 物体是否可被销毁 默认true
	 * @param {Array<number>} [param.userData=''] 对象name
	 * @param {Array<number>} [param.localPosition=[0,0,0]] 相对父物体的位置
	 * @param {Array<number>} [param.localScale=[1,1,1]] 相对父物体的缩放
	 * @param {Array<number>} [param.localAngles=[0,0,0]] 相对父物体的旋转角度
	 * @param {Array<number>} [param.position=[0,0,0]] 世界坐标系下的位置
	 * @param {Array<number>} [param.scale=[1,1,1]] 世界坐标系下的缩放
	 * @param {Array<number>} [param.angles=[0,0,0]] T世界坐标系下的旋转角度
	 * @param {boolean} [param.pickable=true] 是否可拾取
	 * @param {boolean} [param.visible=true] 是否可见 详见visible的属性说明
	 * @param {boolean} [param.active=true] 是否激活 详见active的属性说明
	 * @param {boolean} [param.keepSize] 是否保持像素级别大小不变
	 * @param {boolean} [param.keepSizeUseBodyLocalScale=false] keepSize时是否使用 body.localScale(true)而不是 object.localScale(false)
	 * @param {Array<number>} [param.pivot] 轴心点在自身包围盒中的位置(数组每一个分量取值范围0-1)
	 * @param {object} [param.style] 样式设置
	 * @param {boolean} [param.castShadow] 是否投射阴影
	 * @param {boolean} [param.receiveShadow] 是否接收阴影
	 * @param {boolean} [param.alwaysOnTop] 是否始终在顶部
	 * @param {Function} [param.onError] 创建失败的回调
	 * @param {Function} [param.onComplete] 创建成功的回调
	 * @param {Function} [param.onSyncComplete] 同步创建成功的回调
	 * @param {Function} [param.onSyncBeforeDestroy] 同步销毁前的回调
	 * @example
	 * let obj = new THING.Object3D({
	 * 	name: 'box',
	 * 	position: [0, 0, 0],
	 * 	style: {
	 * 		color: 'red'
	 * 	},
	 * 	onError: function(error) {
	 * 		console.error(error);
	 * 	},
	 * 	onComplete: function() {
	 * 		console.log('创建成功');
	 * 	}
	 * @constructor
	 * @public
	 */
	constructor(param = {}) {
		super(param);

		this[__.private] = {};
		let _private = this[__.private];

		// Common
		_private.external = null;

		_private.styleTag = '';

		// Inheritance
		_private.inherit = null;
		const inherit = param.inherit;
		if (inherit) {
			if (inherit.visible) {
				this.inherit.visible = inherit.visible;
			}
			if (inherit.pickable) {
				this.inherit.pickable = inherit.pickable;
			}
			if (inherit.style) {
				this.inherit.style = inherit.style;
			}
			if (inherit.renderOrder) {
				this.inherit.renderOrder = inherit.renderOrder;
			}
		}

		// Render
		_private.style = undefined; // null indicates it's not renderable object
		_private.styleObjectAttributes = [];
		_private.renderLayout = null;

		// Resource
		_private.copyPromise = null;
		_private.resource = {
			url: '',
		};

		_private.options = Object.assign({}, param);

		// Setup callbacks
		_private.onError = Utils.parseValue(param['onError'], param['error']);
		_private.onComplete = Utils.parseValue(param['onComplete'], param['complete']);
		_private.onSyncComplete = Utils.parseValue(param['onSyncComplete'], param['syncComplete']);
		_private.onSyncBeforeDestroy = Utils.parseValue(param['onSyncBeforeDestroy'], param['syncBeforeDestroy']);

		// Sync complete callbacks
		_private.syncCompleteCallbacks = [];

		// Setup flags
		this._flags.enable(Flag.Initializing | Flag.Pickable | Flag.Active | Flag.FrustumCulled, true);

		// Some members must be initialized in constructor, so would declarate here
		this._resourceState = this._resourceState || Object3D.ResourceState.Ready;
		this._loadPromise = this._loadPromise || null;

		// Setup before add object
		this.onSetupAttributes(param);
		this.onSetupTransform(param);
		this.onSetupState(param);
		this.onSetupStyle(param);
		this.onSetupResource(param);

		// Setup completed
		this.onSetupComplete(param);

		// Notify completed
		this.onCreate(param);
	}
	// #region Private

	_createResourcePromise() {
		// Object has resources, unload it first
		if (this.loaded) {
			return this.unloadResource(false);
		}
		// Object is loading resources
		else if (this.loading) {
			return this.waitForComplete();
		}
		// Object is in unloaded state
		else {
			return Promise.resolve();
		}
	}

	_updateResource(value) {
		this.setResource(value);

		return this.reloadResource(false, { url: value.url });
	}

	_unloadResource(recursive) {
		// Get the initial local bounding box
		let initialLocalBoundingBox = this.initialLocalBoundingBox || this._body.getLocalBoundingBox();

		// Notify all unloadable components
		let unloadableComponents = this.unloadableComponents;
		if (unloadableComponents) {
			unloadableComponents.forEach(component => {
				component.onUnloadResource();
			});
		}

		// When unload resource
		this.onUnloadResource();

		// Unload body object resource
		this._body.unloadResource((type) => {
			return this.onCreateBodyNode(type);
		});

		// Continue to unload children resources
		if (recursive) {
			if (this.hasChildren()) {
				this.children.forEach(child => {
					child.unloadResource(recursive);
				});
			}
		}

		// Resume the initial local bounding box
		this.initialLocalBoundingBox = initialLocalBoundingBox;
	}

	_onLoadResource(options, callback, error) {
		// Had loaded resource
		if (this.loaded) {
			callback();
		}
		else {
			// Prevent object had been destroyed
			if (this.destroyed) {
				error({ object: this, desc: 'The object had been destroyed' });
			}
			else {
				this.onLoadResource(options, () => {
					if (!this.destroyed) {
						this.onSetupState(options);
						this.onTaskExecutorComplete(callback, error)
					}
				}, error);
			}
		}
	}

	_loadSelfResource(options, resolve, reject) {
		if (this.loading) {
			this.waitForComplete().then(() => {
				resolve();
			}).catch(ev => {
				reject(ev);
			});
		}
		else {
			this._onLoadResource(options, resolve, reject);
		}
	}

	_setChildrenAttributeState(inheritName, name, value, recursive) {
		let children = this.children;
		for (let i = 0; i < children.length; i++) {
			let child = children[i];

			if (child.hasInherit()) {
				let inheritType = child.inherit[inheritName];
				switch (inheritType) {
					case InheritType.Normal:
						child['set' + name](value, recursive);
						break;

					case InheritType.Break:
						child['set' + name](value, false);
						break;

					case InheritType.Jump:
						child.children.traverse(object => {
							object['set' + name](value, recursive);
						});
						break;

					default:
						break;
				}
			}
			else {
				child['set' + name](value, recursive);
			}
		}
	}

	_setAttributeState(inheritName, name, value, recursive = false) {
		let bodyNode = this.bodyNode;
		bodyNode.setAttribute(name, value);

		// Change all children if needed
		if (recursive && this.hasChildren()) {
			this._setChildrenAttributeState(inheritName, name, value, recursive);
		}
	}

	_syncAttributes() {
		let flags = this._flags;

		// Sync pickable state
		let pickable = flags.has(Flag.Pickable);
		if (!pickable) {
			this._setAttributeState('pickable', 'Pickable', pickable, false);
		}

		// Sync keep size state
		let keepSize = flags.has(Flag.KeepSize);
		if (keepSize) {
			this.transform.keepSize(true, this.app.camera);
		}

		// Sync renderable node state
		if (this.hasComponent('render')) {
			this.render.syncAttributes();
		}
	}

	_isSameResource(value) {
		let _private = this[__.private];

		if (Utils.isString(value)) {
			value = { url: value };
		}

		let resource = _private.resource;

		let children = resource.children;
		if (children && children.length) {
			return false;
		}

		if (resource.url != value.url) {
			return false;
		}

		return true;
	}

	_beforeSetAttribute(callback) {
		// Check whether skip to set attribute
		if (Utils.isFunction(callback)) {
			if (callback(this) === false) {
				return false;
			}
		}

		return true;
	}

	_needCopyComponent(isResident, hasInstance, classType) {
		if (!hasInstance) {
			// Skip for not resident component
			if (!isResident) {
				return false;
			}

			// Skip for not copy with instance component
			if (classType.mustCopyWithInstance) {
				return false;
			}
		}

		return true;
	}

	_copyStyleValues(values) {
		let style = this.body.style;

		for (let key in values) {
			let value = values[key];

			switch (key) {
				case 'attributes':
					for (let attributeKey in value) {
						style.setAttribute(attributeKey, value[attributeKey]);
					}
					break;

				case 'uniforms':
					for (let uniformKey in value) {
						style.setUniform(uniformKey, value[uniformKey]);
					}
					break;

				case 'macros':
					for (let macroKey in value) {
						style.setMacro(macroKey, value[macroKey]);
					}
					break;
				case 'uvData':
					let prevImageSlotType = style.imageSlotType;
					for (let uvKey in value) {
						style.imageSlotType = uvKey;
						style.uv = value[uvKey];
					}
					style.imageSlotType = prevImageSlotType;
					break;
				default:
					style[key] = value;
					break;
			}
		}
	}

	_renderComponents() {
		let renderableComponents = this.renderableComponents;
		if (renderableComponents) {
			let _private = this[__.private];

			if (!_private.renderLayout) {
				let that = this;

				_private.renderLayout = {
					getCenter: function () {
						return that.orientedBox.center;
					},
					selfToWorld: function () {
						return that.selfToWorld.apply(that, arguments);
					},
					worldToScreen: function () {
						let camera = that.app.camera;
						return camera.worldToScreen.apply(camera, arguments);
					}
				};
			}

			for (let i = 0, l = renderableComponents.length; i < l; i++) {
				let component = renderableComponents[i];
				if (component.active === false) {
					continue;
				}

				component.onRender(_private.renderLayout);
			}
		}
	}

	_traverseInheritance(callback) {
		this.traverseInheritance(
			(object) => {
				let style = object.body?.style;
				if (!style) {
					return;
				}

				callback(style);
			},
			_getStyleInheritance
		);
	}

	_createStyleAttributes(data, attributeKey) {
		let _private = this[__.private];

		let attributes = new ObjectAttributes({
			data,
			onChange: (ev) => {
				let key = ev.key;
				let propName = ev.propName;
				let value = Utils.parseValue(ev.value, null);

				this._traverseInheritance((bodyStyle) => {
					let attribute = bodyStyle[attributeKey];

					if (propName) {
						attribute[propName][key] = value;
					}
					else {
						attribute[key] = value;
					}
				});
			}
		});

		_private.styleObjectAttributes.push(attributes);

		return attributes.values;
	}

	_createStyle() {
		let objectBodyStyle = this.body.style;
		if (!objectBodyStyle) {
			return null;
		}

		let style = {};

		// #region Hook functions

		style.begin = () => {
			this._traverseInheritance((bodyStyle) => {
				bodyStyle.begin();
			});
		};

		style.end = () => {
			this._traverseInheritance((bodyStyle) => {
				bodyStyle.end();
			});
		};

		style.beginDefaultValues = () => {
			this._traverseInheritance((bodyStyle) => {
				bodyStyle.beginDefaultValues();
			});
		};

		style.endDefaultValues = () => {
			this._traverseInheritance((bodyStyle) => {
				bodyStyle.endDefaultValues();
			});
		};

		style.setAttribute = (type, value) => {
			this._traverseInheritance((bodyStyle) => {
				bodyStyle.setAttribute(type, value);
			});
		};

		style.getAttribute = (type) => {
			return this.body.style.getAttribute(type);
		};

		style.setUniform = (name, value) => {
			this._traverseInheritance((bodyStyle) => {
				bodyStyle.setUniform(name, value);
			});
		};

		style.getUniform = (name) => {
			return this.body.style.getUniform(name);
		};

		style.setMacro = (name, value) => {
			this._traverseInheritance((bodyStyle) => {
				bodyStyle.setMacro(name, value);
			});
		};

		style.getMacro = (name) => {
			return this.body.style.getMacro(name);
		};

		style.getUV = (type) => {
			return this.body.style.getUV(type);
		}

		style.getDiffValues = () => {
			return this.body.style.getDiffValues();
		}

		style.setUV = (type, key, value) => {
			this._traverseInheritance((bodyStyle) => {
				bodyStyle.setUV(type, key, value);
			});
		}

		style.copy = (from) => {
			if (from.isObjectStyle) {
				from = from.object.body.style;
			}

			this._traverseInheritance((bodyStyle) => {
				bodyStyle.copy(from);
			});
		}

		style.copyFromProperties = (values) => {
			this._traverseInheritance((bodyStyle) => {
				bodyStyle.copyFromProperties(values);
			});
		}

		style.getProperties = () => {
			return this.body.style.getProperties();
		}

		// #endregion

		// #region Hook attributes

		let edge, effect, attributes, uniforms, macros, uv;

		// Hook get accessor
		Object.defineProperties(style, {
			'isInstancedDrawing': {
				get: () => {
					return this.body.style.isInstancedDrawing;
				}
			},
			'object': {
				get: () => {
					return this;
				}
			},
			'isObjectStyle': {
				get: () => {
					return true;
				}
			},
			'edge': {
				get: () => {
					edge = edge || this._createStyleAttributes(objectBodyStyle.getData('edge'), 'edge');
					return edge;
				},
				set: (value) => {
					this._traverseInheritance((bodyStyle) => {
						bodyStyle.edge = value;
					});
				}
			},
			'effect': {
				get: () => {
					effect = effect || this._createStyleAttributes(objectBodyStyle.getData('effect'), 'effect');
					return effect;
				},
				set: (value) => {
					this._traverseInheritance((bodyStyle) => {
						bodyStyle.effect = value;
					});
				}
			},
			'attributes': {
				get: () => {
					attributes = attributes || this._createStyleAttributes(objectBodyStyle.getData('attributes'), 'attributes');
					return attributes;
				},
				set: (value) => {
					this._traverseInheritance((bodyStyle) => {
						bodyStyle.attributes = value;
					});
				}
			},
			'uniforms': {
				get: () => {
					uniforms = uniforms || this._createStyleAttributes(objectBodyStyle.getData('uniforms'), 'uniforms');
					return uniforms;
				},
				set: (value) => {
					this._traverseInheritance((bodyStyle) => {
						bodyStyle.uniforms = value;
					});
				}
			},
			'macros': {
				get: () => {
					macros = macros || this._createStyleAttributes(objectBodyStyle.getData('macros'), 'macros');
					return macros;
				},
				set: (value) => {
					this._traverseInheritance((bodyStyle) => {
						bodyStyle.macros = value;
					});
				}
			},
			'uv': {
				get: () => {
					uv = uv || this._createStyleAttributes(objectBodyStyle.getData('uv').map, 'uv');
					return uv;
				},
				set: (value) => {
					this._traverseInheritance((bodyStyle) => {
						bodyStyle.uv = value;
					});
				}
			}
		});

		// Collect common accessors keys
		let keys = Object.keys(Style.cDefaultValues);
		keys.push('imageSlotType');
		keys.push('image');
		keys.push('map');
		keys.push('envMap');
		keys.push('alphaMap');
		keys.push('emissiveMap');
		keys.push('normalMap');
		keys.push('colorMapping');
		keys.push('aoMap');

		// Hook common accessors
		keys.forEach(key => {
			Object.defineProperty(style, key, {
				get: () => {
					let value = this.body.style[key];
					if (Utils.isArray(value)) {
						return value.slice(0);
					}
					else {
						return value;
					}
				},
				set: (value) => {
					this._traverseInheritance((bodyStyle) => {
						bodyStyle[key] = value;
					});
				}
			});
		});

		// #endregion

		// #region JSON

		style.toJSON = () => {
			return this.body.style.toJSON();
		};

		// #endregion

		return style;
	}

	_getStyleValues(style) {
		if (style) {
			if (style.isObjectStyle || style.isStyle) {
				return JSON.parse(JSON.stringify(style));
			}
		}

		return style;
	}

	_updateInstanceState(flag, value) {
		this._breakInstanceFlags.enable(flag, value);

		if (!this._flags.has(Flag.Instance)) {
			return;
		}

		// 更新instance状态
		if (this._breakInstanceFlags.values === 0) {
			this.render.makeInstancedDrawing(true, this._makeInstancedDrawingOptions);
		}
		else {
			if (!this.instanceGroupName) {
				this.render.makeInstancedDrawing(false);
			}
		}
	}

	// #endregion

	// #region Overrides

	onSetupFlags(param) {
		super.onSetupFlags(param);
		this._breakInstanceFlags = new Flags();
	}

	onSetupParent(param) {
		this.onSetupComponent(param);
		this.onSetupBody(param);

		super.onSetupParent(param);
	}

	onSetupComponent(param) {
		this.addComponent(HelperComponent, 'helper', registerComponentParam);
		this.addComponent(LerpComponent, 'lerp', registerComponentParam);
		this.addComponent(BoundingComponent, 'bounding', registerComponentParam);
		this.addComponent(LevelComponent, 'level', registerComponentParam);
		this.addComponent(ActionGroupComponent, 'actionGroup', registerComponentParam);
		this.addComponent(BlueprintComponent, 'blueprint', registerComponentParam);
		this.addComponent(ColliderComponent, 'collider', registerComponentParam);
		this.addComponent(TransformComponent, 'transform', registerComponentParam);
		this.addComponent(RenderComponent, 'render', registerComponentParam);
		this.addComponent(RelationshipComponent, 'relationship', registerComponentParam);
		this.addComponent(TaskExecutorComponent, 'taskExecutor', registerComponentParam);
		this.addComponent(MonitorDataComponent, 'monitorData', registerComponentParam);
	}

	onSetupBody(param) {
		this._body = new BodyObject();
		this._body.init(this, param);

		// Sync body node name
		let name = param['name'] || this.name;
		this.node.setName(name);
		this._isDynamicLoad = Utils.parseValue(param['isDynamicLoad'], false) || Utils.parseValue(param['dynamic'], false);

		// If we provide root or renderable node then indicates resource is ready
		let rootNode = param['rootNode'];
		let renderableNode = param['renderableNode'];
		if (rootNode || renderableNode) {
			if (!this._isDynamicLoad) {
				this._resourceState = Object3D.ResourceState.Loaded;
			}
		}
	}

	onSetupAttributes(param) {
		let _private = this[__.private];

		const styleTag = param['styleTag'];
		if (styleTag) {
			_private.styleTag = styleTag;
		}

		// Setup extras
		let external = param['extras'] || param['external'];
		if (external) {
			_private.external = Object.assign({}, external);
		}

		const instanceGroupName = param['instanceGroupName'];
		if (instanceGroupName) {
			this.instanceGroupName = instanceGroupName;
		}
	}

	onSetupState(options) {
		// We are loading resource now
		if (!this._isDynamicLoad && this._resourceState != Object3D.ResourceState.Loaded) {
			this._resourceState = Object3D.ResourceState.Loading;
		}

		// Setup pickable
		let pickable = Utils.parseValue(options['pickable'], true);
		if (!pickable) {
			this.setPickable(false, false);
		}

		// Setup visibility
		let visible = Utils.parseValue(options['visible'], true);
		if (!visible) {
			this.setVisible(false, false);
		}

		// Setup active
		let active = Utils.parseValue(options['active'], true);
		if (!active) {
			this.setActive(false, false);
		}

		// Setup render attributes
		if (Utils.isNumber(options['renderOrder'])) {
			this.setRenderOrder(options['renderOrder'], false)
		}

		if (Utils.isBoolean(options['castShadow'])) {
			this.setCastShadow(options['castShadow'], false);
		}

		if (Utils.isBoolean(options['receiveShadow'])) {
			this.setReceiveShadow(options['receiveShadow'], false);
		}

		if (Utils.isBoolean(options['alwaysOnTop'])) {
			this.alwaysOnTop = options['alwaysOnTop'];
		}

		if (Utils.isNumber(options['layerMask'])) {
			this.layerMask = options['layerMask'];
		}
	}

	onSetupPosition(param) {
		if (param['localPosition']) {
			this.localPosition = param['localPosition'];
		}
		else if (param['position']) {
			this.position = param['position'];
		}
	}

	onSetupAngles(param) {
		if (param['localAngles']) {
			this.localAngles = param['localAngles'];
		}
		else if (param['localRotation']) {
			this.localRotation = param['localRotation'];
		}
		else if (param['localQuaternion']) {
			this.localQuaternion = param['localQuaternion'];
		}
		else if (param['angles']) {
			this.angles = param['angles'];
		}
		else if (param['rotation']) {
			this.rotation = param['rotation'];
		}
	}

	onSetupScale(param) {
		if (param['localScale']) {
			this.localScale = param['localScale'];
		}
		else if (param['scale']) {
			this.scale = param['scale'];
		}
	}

	onSetupMatrix(param) {
		if (param['matrix']) {
			this.matrix = param['matrix'];
		}
	}

	onSetupTransform(param) {
		this.onSetupPosition(param);
		this.onSetupAngles(param);
		this.onSetupScale(param);
		this.onSetupMatrix(param);

		// Setup keep size
		if (Utils.isValid(param['keepSizeDistance'])) {
			this.keepSizeDistance = param['keepSizeDistance'];
		}
		if (Utils.isValid(param['keepSizeDistanceLimited'])) {
			this.keepSizeDistanceLimited = param['keepSizeDistanceLimited'];
		}
		if (Utils.isValid(param['keepSizeUseBodyLocalScale'])) {
			this.keepSizeUseBodyLocalScale = param['keepSizeUseBodyLocalScale'];
		}

		if (param['keepSize']) {
			this.keepSize = param['keepSize'];
		}
	}

	onSetupStyle(param) {
		let styleValues = this._getStyleValues(param['style']);
		if (!styleValues) {
			return;
		}

		let style = this.body.style;
		if (!style) {
			return;
		}
		style.begin();

		this._copyStyleValues(styleValues);

		const defaultValues = styleValues['default'];
		if (defaultValues) {
			style.beginDefaultValues();

			this._copyStyleValues(defaultValues);

			style.endDefaultValues();
		}

		style.end();
	}

	onSetupURL(param) {
		let _private = this[__.private];

		// Load resource from URL if needed
		_private.resource.url = Utils.parseValue(param.url, '');

		_private.resource.isSceneRoot = Utils.parseBoolean(param.isSceneRoot, false)

		_private.resource.isSceneNode = Utils.parseBoolean(param.isSceneNode, false)

		if (param.nodeName) {
			_private.resource.nodeName = param.nodeName;
		}

		if (param.inverseRotationMode) {
			_private.resource.inverseRotationMode = param.inverseRotationMode;
		}

		if (param.excludeNodeNames) {
			_private.resource.excludeNodeNames = param.excludeNodeNames;
		}

		if (param.instanceId) {
			_private.resource.instanceId = param.instanceId;
		}

		if (param.instanceCount) {
			_private.resource.instanceCount = param.instanceCount;
		}
		if (param.instanceStyle) {
			_private.resource.instanceStyle = param.instanceStyle;
		}
	}

	onSetupResource(param) {
		this.onSetupURL(param);

		// Setup components
		const components = param['components'];
		let toolkit = param['toolkit'];
		if (components && toolkit) {
			toolkit['workPath'] = param['workPath'] || '';
			this.onImportComponents(components, toolkit, param);
		}

		// Start to load resource
		this._onLoadResource(param,
			// Resolve
			() => {
				if (!this._isDynamicLoad) {
					this.onLoadComplete();
				}
			},
			// Reject
			(ev) => {
				let _private = this[__.private];


				if (this._loadPromise) {
					this._loadPromise.reject(ev);
				}

				// Notify error
				if (_private.onError) {
					_private.onError(ev);
				}
			}
		);
	}

	onSetupComplete(param) {
		// Set pivot
		let pivot = param['pivot'];
		if (pivot) {
			this.pivot = pivot;
		}

		// Initialize done
		this._flags.enable(Flag.Initializing, false);
		this._flags.enable(Flag.Initialized, true);


		// Set ignore parent transform
		let ignoreParentTransform = param['ignoreParentTransform'];
		if (ignoreParentTransform) {
			this.ignoreParentTransform = ignoreParentTransform;
		}
	}

	/**
	 * When create.
	 * @param {object} param The options.
	 * @private
	 * @example
	 * // 创建一个对象
	 * const obj = new THING.Object3D();
	 * obj.onCreate({});
	 */
	onCreate(param) {
		// We have loaded resource
		if (this.loaded) {
			this.onNotifyComplete();
		}
		else {
			// If we do not need any dynamic then notify complete directly
			if (this._isDynamicLoad) {
				this.onNotifyComplete();
			}
		}

		// Notify create event in batch mode
		Utils.setBatchTimeout(() => {
			if (this.destroyed) {
				return;
			}

			this.trigger(EventType.Create, param);
		});

		// Notify create event in sync mode
		this.trigger(EventType.CreateSync, param);
	}

	clearInitialLocalBoundingBox() {
		// Clear initial bounding box
		if (this.hasComponent('bounding')) {
			if (this.extras?.tempInitBoundingBox) {
				this.initialLocalBoundingBox = null;
				delete this.extras.tempInitBoundingBox;
			}
		}
	}

	onLoadComplete() {
		if (this.destroyed) {
			return;
		}

		switch (this._resourceState) {
			// Load complete (loading->loaded)
			case Object3D.ResourceState.Loading:
			case Object3D.ResourceState.Loaded:
				// Update loaded flag
				this._resourceState = Object3D.ResourceState.Loaded;

				// Sync attributes
				this._syncAttributes();

				this.clearInitialLocalBoundingBox();

				// Load completed
				if (this._loadPromise) {
					this._loadPromise.resolve({ object: this });
				}

				// Notify load resource event in batch and delay mode to make sure event order is: create -> load
				Utils.setBatchTimeout(() => {
					Utils.setTimeout(() => {
						this.trigger(EventType.Load);
					});
				});

				// Notify outside if we have completed
				this.onNotifyComplete();
				break;

			// If it's not in loaded/loading state then indicates we have unload in during loading state
			case Object3D.ResourceState.Ready:
				// Unload resource what just loaded ... what a pity
				this._unloadResource(false);
				break;
			case Object3D.ResourceState.Error:
				// Load completed
				if (this._loadPromise) {
					this._loadPromise.resolve({ object: this });
				}
				this.onNotifyComplete();
				break;
			default:
				break;
		}
	}

	/**
	 * When notify sync-complete.
	 * @private
	 */
	onSyncComplete() {
		let _private = this[__.private];

		let onSyncComplete = _private.onSyncComplete;
		if (onSyncComplete) {
			onSyncComplete({ object: this });

			_private.onSyncComplete = null;
		}
	}

	onDelayComplete() {
		let _private = this[__.private];

		let onComplete = _private.onComplete;
		if (onComplete) {
			Utils.setTimeout(() => {
				if (this.destroyed) {
					return;
				}

				onComplete({ object: this });
			});

			_private.onComplete = null;
		}
	}

	onProcessSyncCompleteCallbacks() {
		let _private = this[__.private];

		let syncCompleteCallbacks = _private.syncCompleteCallbacks;
		if (syncCompleteCallbacks.length) {
			_private.syncCompleteCallbacks.forEach(callback => {
				callback();
			});

			_private.syncCompleteCallbacks.length = 0;
		}
	}

	/**
	 * When notify complete.
	 * @private
	 */
	onNotifyComplete() {
		this.onSyncComplete();
		this.onDelayComplete();
		this.onProcessSyncCompleteCallbacks();
	}

	/**
	 * When load reosurce.
	 * @param {object} options The options to load.
	 * @param {Function} resolve The promise resolve callback function.
	 * @param {Function} reject The promise reject callback function.
	 * @private
	 */
	onLoadResource(options, resolve, reject) {
		if (!this._flags.has(Flag.Initializing)) {
			this.onRefresh();
		}

		// Update status to loading
		let dynamic = Utils.parseValue(options['dynamic'], false);
		if (!dynamic) {
			this._resourceState = Object3D.ResourceState.Loading;
		}

		// Prevent call this function from parent class, so we delay it
		Utils.setTimeout(() => {
			if (this.destroyed) {
				return;
			}
			let dynamic = Utils.parseValue(options['dynamic'], false);
			if (!dynamic) {
				const loadComponentsResource = () => {
					// Notify all loadable components
					let loadableComponents = this.loadableComponents;
					if (loadableComponents) {
						loadableComponents.forEach(component => {
							const promise = component.onLoadResource();
							promise && this.taskExecutor.add(promise);
						});
					}
				}

				if (this.hasResource()) {
					this.onLoadRenderableResource(options, () => {
						loadComponentsResource();
						resolve();
					}, (error) => {
						this._resourceState = Object3D.ResourceState.Error;
						reject(error)
					});
				}
				// Here we do not proivde any URL, so let it to be loaded state
				else {
					loadComponentsResource();
					resolve();
				}
			}
			else {
				resolve();
			}
		});
	}

	onTaskExecutorComplete(callback, error) {
		this.taskExecutor.then(callback, error);
	}

	/**
	 * When unload resource.
	 * @private
	 */
	onUnloadResource() {
	}

	onAfterDestroy() {
		let _private = this[__.private];

		// Destroy body object
		if (this._body) {
			this._body.dispose();
			this._body = null;
		}

		_private.style = undefined;

		_private.styleObjectAttributes.forEach(objectAttribute => {
			objectAttribute.dispose();
		});

		_private.styleObjectAttributes.length = 0;

		_private.renderLayout = null;

		_private.onError = null;
		_private.onComplete = null;
		_private.onSyncComplete = null;

		_private.options = {};

		super.onAfterDestroy();
	}

	onAddToParentNode(node, parentNode, options) {
		// If node has not parent then we can not use attach mode, due to world position is always [0, 0, 0]
		let enableAttachMode = node.getParent() ? true : false;
		if (enableAttachMode) {
			let attachMode = Utils.parseValue(options['attachMode'], true);
			if (attachMode) {
				parentNode.attach(node);
			}
			else {
				parentNode.add(node);
			}
		}
		else {
			parentNode.add(node);
		}
	}

	onCopy(object) {
	}

	onCreateBodyNode(type) {
		return Utils.createObject(type, { app: this.app });
	}

	onGetPivot() {
		let tempData = this._getTempData();
		return tempData['pivot'];
	}

	onSetPivot(value) {
		let tempData = this._getTempData();

		const _setPivot = () => {
			if (!tempData['initPivot']) {
				tempData['initPivot'] = this.transform.getPivot();
			}

			if (value) {
				this.transform.setPivot(value);
			}
			else {
				// Reset pivot if it's already set
				if (!tempData['pivot']) {
					return;
				}
				this.transform.setPivot(tempData['initPivot']);
			}
			tempData['pivot'] = value;
		}

		if (this.loaded) {
			_setPivot();
		}
		else {
			this.addSyncCompleteCallback(() => {
				_setPivot();
			});
		}
	}

	onUpdate(deltaTime) {
		super.onUpdate(deltaTime);

		this._renderComponents();
	}

	onLoadRenderableResource(options, resolve, reject) {
		this.render.load(
			options,
			resolve,
			() => {
				if (this.options.error) {
					this.options.error();
				}
				reject();
			}
		);
	}

	onBeforeSetParent(parent) {
		// If the final parent is set to Object3D, you need to remove yourself from the parent.
		this._needRemove = false;
	}

	onAddChild(object, options) {
		this._body.createRootNode();

		object.onSetParent(this, (parentNode) => {
			this.onAddToParentNode(object.node, parentNode, options);

			let localPosition = options['localPosition'];
			let localAngles = options['localAngles'];

			// If we use any local arguments then means disable attach mode
			if (!localPosition && !localAngles) {
				// Only need to check attach mode when it's not local space
				let attachMode = Utils.parseBoolean(options['attachMode'], true);
				if (attachMode) {
					return false;
				}
			}

			let ignoreScale = Utils.parseValue(options['ignoreScale'], true);

			// Update position by sub node
			let subNodeName = options['subNodeName'];
			if (subNodeName) {
				let node = this._body.getNodeByName(subNodeName);
				if (!node) {
					Utils.error(`Sub node '${subNodeName}' is not existing when add object`, this);
					return false;
				}

				if (localPosition) {
					let subNodeLocalPosition = this.worldToSelf(node.position);
					let offset = MathUtils.addVector(subNodeLocalPosition, localPosition);

					object.position = this.selfToWorld(offset, ignoreScale);
				}
				else {
					object.position = node.position;
				}
			}
			// Update position without sub node
			else {
				if (localPosition) {
					object.position = this.selfToWorld(localPosition, ignoreScale);
				}
			}

			// Update angles
			if (localAngles) {
				object.localAngles = localAngles;
			}
		}, options);
	}

	// #endregion

	// #region Common


	/**
	 * 辅助组件 可显示坐标轴等
	 * @member {THING.HelperComponent} helper
	 * @memberof THING.Object3D
	 * @public
	 * @instance
	 */

	/**
	 * 物体的插值组件(可控制属性动态的修改)
	 * @member {THING.LerpComponent} lerp
	 * @memberof THING.Object3D
	 * @public
	 * @instance
	 */

	/**
	 * Get the BoundingComponent of object3D.
	 * @member {BoundingComponent} bounding
	 * @memberof THING.Object3D
	 * @instance
	 */

	/**
	 * 物体的层级组件
	 * @member {THING.LevelComponent} level
	 * @memberof THING.Object3D
	 * @instance
	 * @public
	 */

	/**
	 * Get the ActionGroupComponent of object3D.
	 * @member {ActionGroupComponent} actionGroup
	 * @memberof THING.Object3D
	 * @instance
	 */

	/**
	 * Get the BlueprintComponent of object3D.
	 * @member {BlueprintComponent} blueprint
	 * @memberof THING.Object3D
	 * @instance
	 */

	/**
	 * 物体的碰撞检测组件
	 * @member {ColliderComponent} collider
	 * @memberof THING.Object3D
	 * @instance
	 * @public
	 */

	/**
	 * Get the ActionGroupComponent of object3D.
	 * @member {ActionGroupComponent} actionGroup
	 * @memberof THING.Object3D
	 * @instance
	 */

	/**
	 * Get the TransformComponent of object3D.
	 * @member {TransformComponent} transform
	 * @memberof THING.Object3D
	 * @instance
	 */

	/**
	 * Get the RenderComponent of object3D.
	 * @member {RenderComponent} render
	 * @memberof THING.Object3D
	 * @instance
	 */

	/**
	 * Get the RelationshipComponent of object3D.
	 * @member {RelationshipComponent} relationship
	 * @memberof THING.Object3D
	 * @instance
	 */

	/**
	 * Get the TaskExecutorComponent of object3D.
	 * @member {TaskExecutorComponent} taskExecutor
	 * @memberof THING.Object3D
	 * @instance
	 */

	/**
	 * Get the MonitorDataComponent of object3D.
	 * @member {MonitorDataComponent} monitorData
	 * @memberof THING.Object3D
	 * @instance
	 */

	/**
	 * 设置/获取 对象名称
	 * @type {string}
	 * @example
	 * object.name = 'MyObject';
	 * @public
	 */
	get name() {
		return this._name;
	}
	set name(value) {
		super.name = value;

		this.node.setName(value);
	}

	/**
	 * 设置/获取 对象为动态加载
	 * @type {string}
	 * @default false
	 * @example
	 * object.isDynamicLoad = true;
	 * @public
	 */
	get isDynamicLoad() {
		return this._isDynamicLoad;
	}
	set isDynamicLoad(value) {
		this._isDynamicLoad = value;
	}

	/**
	 * Get/Set the external info.
	 * @type {object}
	 * @private
	 */
	get external() {
		let _private = this[__.private];

		return _private.external;
	}
	set external(value) {
		let _private = this[__.private];

		_private.external = value;
	}

	/**
	 * Get/Set the external info.
	 * @type {object}
	 * @private
	 */
	get extras() {
		let _private = this[__.private];

		return _private.external;
	}
	set extras(value) {
		let _private = this[__.private];

		_private.external = value;
	}

	// #region Render

	/**
	 * 取/设置遮罩层(LayerMask)。默认值为 1。通过"按位与"的计算,来判定物体在指定摄像机中的layerMask的值。
	 * @type {number}
	 * @example
	 * // Hide object by changing layer mask
	 * object.layerMask = 0;
	 * @public
	 */
	get layerMask() {
		let bodyNode = this.bodyNode;

		return bodyNode.getLayerMask();
	}
	set layerMask(value) {
		this.setLayerMask(value, true);
	}

	/**
	 * 样式标签
	 * @type {string}
	 * @public
	 */
	get styleTag() {
		let _private = this[__.private];
		return _private.styleTag;
	}
	set styleTag(value) {
		let _private = this[__.private];
		_private.styleTag = value;
	}
	// #endregion

	// #endregion

	// #region State

	/**
	 * The function to call when start to process some action with object(s).
	 * @callback ProcessObjectCallback
	 * @param {Object3D} object The object.
	 * @returns {boolean} False indicates reject to set attribute, otherwise continue to set attribute.
	 */

	/**
	 *
	 * 获取visible的状态
	 * @returns {boolean} 可见状态。
	 * @example
	 * 	let visible = object.getVisible();
	 * 	if (visible) {
	 * 		console.log('object is showing');
	 * 	}
	 * @public
	 */
	getVisible() {
		return this.bodyNode.getVisible();
	}

	/**
	 * 设置visible的状态
	 * @param {boolean} value 是否显示
	 * @param {boolean|ProcessObjectCallback} [recursive=false] 是否同时影响它的孩子
	 * @example
	 * 	// Hide object self only, exclude all children
	 * 	object.setVisible(false, false);
	 *
	 * 	// Hide object(s) but exclude children what name equals to 'stone'
	 * 	object.setVisible(false, (obj) => {
	 * 		if (obj.name == 'stone') {
	 * 			return false;
	 * 		}
	 * 	});
	 * @public
	 */
	setVisible(value, recursive = false) {
		if (this._beforeSetAttribute(recursive)) {
			// Update body visible state
			let bodyNode = this.bodyNode;
			if (bodyNode.getVisible() != value) {
				bodyNode.setVisible(value);

				this.trigger(EventType.VisibleChange, { value });
			}
			// If resource is not loaded then we would notify visible changed forcely
			// In order to let dynamic load component work
			else if (!this.loaded) {
				this.trigger(EventType.VisibleChange, { value });
			}

			// Notify all components
			let visibleComponents = this.visibleComponents;
			if (visibleComponents) {
				visibleComponents.forEach(component => {
					if (component.onVisibleChange) {
						component.onVisibleChange(value);
					}
				});
			}
		}

		// Change all children if needed
		// We make recursive as true value even though recursive is callback function
		if (recursive && this.hasChildren()) {
			this._setChildrenAttributeState('visible', 'Visible', value, recursive);
		}
	}

	/**
	 * 获取可拾取状态。
	 * @returns {boolean} 可拾取状态。
	 * @private
	 * @example
	 * 	let pickable = object.getPickable();
	 * 	if (pickable) {
	 * 		console.log('object is pickable');
	 * 	}
	 */
	getPickable() {
		return this._flags.has(Flag.Pickable);
	}

	/**
	 * 设置可拾取状态。
	 * @param {boolean} value 可拾取状态。
	 * @param {boolean} [recursive=false] 为真表示处理所有子对象。
	 * @public
	 * @example
	 * 	object.setPickable(true, true);
	 */
	setPickable(value, recursive = false) {
		this._flags.enable(Flag.Pickable, value);

		this._setAttributeState('pickable', 'Pickable', value, recursive);
	}

	/**
	 * 获取视锥剔除状态。
	 * @returns {boolean} 视锥剔除状态。
	 * @public
	 * @example
	 * 	let frustumCulled = object.getFrustumCulled();
	 * 	if (frustumCulled) {
	 * 		console.log('object is frustum culled');
	 * 	}
	 */
	getFrustumCulled() {
		return this._flags.has(Flag.FrustumCulled);
	}

	/**
	 * 设置视锥剔除状态。
	 * @param {boolean} value 为真表示启用视锥剔除,否则不启用。
	 * @param {boolean} [recursive=false] 为真表示处理所有子对象。
	 * @public
	 * @example
	 * 	object.setFrustumCulled(true, true);
	 */
	setFrustumCulled(value, recursive = false) {
		this._flags.enable(Flag.FrustumCulled, value);

		this._setAttributeState('frustumCulled', 'FrustumCulled', value, recursive);
	}

	/**
	 * 获取渲染状态。
	 * @returns {boolean} 渲染状态。
	 * @private
	 * @example
	 * 	let renderable = object.getRenderable();
	 * 	if (renderable) {
	 * 		console.log('object is renderable');
	 * 	}
	 */
	getRenderable() {
		console.warn('Please get visible with getVisible instead of getRenderable');
		return this.getVisible();
	}

	/**
	 * Set renderable state.
	 * @param {boolean} value True indicates render it, otherwise do not render it.
	 * @param {boolean} [recursive=false] True indicates process it with all children.
	 * @private
	 * @example
	 * 	object.setRenderable(true, true);
	 */
	setRenderable(value, recursive = false) {
		console.warn('Please set visible with setVisible instead of setRenderable');
		this.setVisible(value, recursive);
	}

	/**
	 * 获取激活状态。
	 * @returns {boolean} 激活状态。
	 * @private
	 * @example
	 * 	let active = object.getActive();
	 * 	if (active) {
	 * 		console.log('object is active');
	 * 	}
	 */
	getActive() {
		return this._flags.has(Flag.Active);
	}

	/**
	 * 设置激活状态。
	 * @param {boolean} value 激活状态。
	 * @param {boolean} [recursive=false] 为真表示处理所有子对象。
	 * @private
	 * @example
	 * 	object.setActive(true, true);
	 */
	setActive(value, recursive = false) {
		// Create root node to make sure active is different with visible
		this._body.createRootNode();

		// Update active state
		if (this._flags.enable(Flag.Active, value)) {
			// Update visible state of root node
			this._body.getRootNode().setVisible(value);

			// Update root node active state
			this.node.getUserData()['active'] = value;

			// Update all components
			this.getAllComponents().forEach(component => {
				component.active = value;
			});

			// Notify active changed
			this.trigger(EventType.ActiveChange, { value });
		}
		// Let the dynamic load component to work
		else if (!this.loaded) {
			this.trigger(EventType.ActiveChange, { value });
		}

		// Change all children if needed
		if (recursive) {
			this.children.forEach(child => {
				child.setActive(value, true);
			});
		}
	}

	setLayerMask(value, recursive = true) {
		this.bodyNode.setLayerMask(value);

		this._updateInstanceState(BreakInstanceFlag.LayerMask, value !== 1);

		if (recursive) {
			this.children.forEach(child => {
				child.setLayerMask(value, true);
			});
		}
	}

	hasInherit(key) {
		let _private = this[__.private];
		if (!_private.inherit) {
			return
		}
		if (Utils.isNull(key)) {
			return !!_private.inherit;
		}
		else {
			return !!_private.inherit && !Utils.isNull(_private.inherit[key])
		}
	}

	/**
	 * @typedef {object} InheritData
	 * @property {InheritType} style? 是否继承样式
	 * @property {InheritType} visible? 是否继承可见状态
	 * @property {InheritType} pickable? 是否继承拾取状态
	 * @public
	 */

	/**
	 * 获取继承信息
	 * @type {InheritData}
	 * @example
	 * 	object.inherit.style = THING.InheritType.Jump;
	 * 	object.inherit.visible = THING.InheritType.Break;
	 * 	object.inherit.pickable = THING.InheritType.Stop;
	 * @public
	 */
	get inherit() {
		let _private = this[__.private];

		if (!_private.inherit) {
			_private.inherit = new TypedObject({
				onConvertValue: (key, value) => {
					if (Utils.isBoolean(value)) {
						return value ? InheritType.Normal : InheritType.Stop;
					}

					return value;
				},
				values: {
					visible: InheritType.Normal,
					pickable: InheritType.Normal,
					style: InheritType.Normal,
					renderOrder: InheritType.Normal,
				}
			});
		}

		return _private.inherit;
	}

	/**
	 * 获取/设置显示状态,此属性操作会影响自身以及所有子对象。
	 * @type {boolean}
	 * @example
	 * 	let object = new THING.Box();
	 * 	// @expect(object.visible == true)
	 * 	object.visible = false;
	 * 	// @expect(object.visible == false)
	 * @public
	 */
	get visible() {
		return this.getVisible();
	}
	set visible(value) {
		this.setVisible(value, true);
	}

	/**
	 * Check whether it's invisible in scene.
	 * @type {boolean}
	 * @private
	 */
	get invisible() {
		return !this._body.getRenderableNode().getSceneVisible();
	}

	/**
	 * 获取/设置可拾取状态。此属性操作会影响自身以及所有子对象。
	 * @type {boolean}
	 * @example
	 * 	let object = new THING.Box();
	 * 	object.pickable = false;
	 * @public
	 */
	get pickable() {
		return this.getPickable();
	}
	set pickable(value) {
		this.setPickable(value, true);
	}

	/**
	 * Get/Set renderable state, would not change any children active.
	 * @type {boolean}
	 * @private
	 */
	get renderable() {
		return this.getRenderable();
	}
	set renderable(value) {
		this.setRenderable(value, false);
	}

	/**
	 * 激活状态主要影响可见性,类似与控制节点的可见性,与visible的区别是:<br>
	 * 父物体active为false,孩子的active为true,孩子不可见<br>
	 * 父物体visible为false,孩子的visible为true,孩子可见<br>
	 * @type {boolean}
	 * @example
	 *	let box = new THING.Box();
	 *	let box2 = new THING.Box({parent: box});
	 *	box.active = false;
	 *	// @expect(box2.active == true);
	 *	// @expect(box.active == false);
	 * @public
	 */
	get active() {
		return this.getActive();
	}
	set active(value) {
		this.setActive(value, false);
	}

	/**
	 * 获取/设置视锥剔除状态。
	 * @type {boolean}
	 * @public
	 */
	get frustumCulled() {
		return this.getFrustumCulled();
	}
	set frustumCulled(value) {
		this.setFrustumCulled(value, false);
	}

	get hadPromoted() {
		return this._flags.has(Flag.HadPromoted);
	}

	// #endregion

	// #region Transform

	/**
	 * 获取距离另一个Object3D对象或者一个三维坐标的距离
	 * @param {THING.Object3D|Array<number>} target 目标物体或者世界坐标
	 * @returns {number} 距离
	 * @example
	 * 	let distance = object.distanceTo([0, 10, 0]);
	 * 	if (distance > 5000) {
	 * 		console.log('object is so far from specified position');
	 * 	}
	 * @public
	 */
	distanceTo(target) {
		if (target.isObject3D) {
			return MathUtils.getDistance(this.position, target.position);
		}
		else {
			return MathUtils.getDistance(this.position, target);
		}
	}

	/**
	 * 是否自适应大小(缩放时保持像素级别大小不变)
	 * @type {boolean}
	 * @example
	 * 	let keepSize = object.keepSize;
	 * 	if (keepSize) {
	 * 		console.log('object is keep size to render');
	 * 	}
	 * @public
	 */
	get keepSize() {
		return this._flags.has(Flag.KeepSize);
	}
	set keepSize(value) {
		if (Utils.isValid(value)) {
			this._flags.enable(Flag.KeepSize, value);
			this.transform.keepSize(value, this.app.camera);
		}
	}

	/**
	 * 获取/设置轴心点信息。轴心点参考原点为自身包围盒的 [left, bottom, back] 的位置。数组每个分类的取值为0-1
	 * @type {Array<number>}
	 * @example
	 * 	// Make object origin to [right, top, front] position
	 * 	object.pivot = [1, 1, 1];
	 * @public
	 */
	get pivot() {
		return this.getPivot();
	}
	set pivot(value) {
		if (MathUtils.equalsVector3(this.pivot, value)) {
			return;
		}

		this.setPivot(value);
	}

	// #endregion

	// #region Nodes

	/**
	 * 设置父对象。
	 * @param {THING.BaseObject} parent 父对象。
	 * @param {Function} onAddNode 添加节点到父对象的回调函数。
	 * @param {object} options 选项。
	 * @private
	 */
	onSetParent(parent, onAddNode, options = {}) {
		// Attach to new parent
		if (parent) {
			if (onAddNode) {
				onAddNode(parent.node);
			}
		}
		// Just remove from current parent in render tree
		else if (this.parent && this.parent.node) {
			if (this._needRemove) {
				this.parent.node.remove(this.node);
			}
			else {
				// When modifying the parent, ensure that the parent is not empty
				// Otherwise, the "attach" will fail
				let attachMode = Utils.parseBoolean(options.attachMode, true);
				if (attachMode) {
					this.app.root.node.attach(this.node);
				}
				else {
					this.app.root.node.add(this.node);
				}
			}
		}

		this._needRemove = true;

		// Set the parent
		super.onSetParent(parent, options);

		// Notify all components
		let parentChangeComponents = this.parentChangeComponents;
		if (parentChangeComponents) {
			parentChangeComponents.forEach(component => {
				if (component.onParentChange) {
					component.onParentChange(parent);
				}
			});
		}
	}

	/**
	 * 添加对象作为自己的孩子
	 * @param {THING.Object3D} object 待添加对象
	 * @param {object} options 选项
	 * @param {string} options.subNodeName 子节点名称
	 * @param {Array<number>} options.localPosition  相对父物体的位置
	 * @param {Array<number>} options.localAngles 相对父物体的旋转角度
	 * @param {boolean} [options.attachMode=true] 表示是否保持世界坐标系下的位置不变
	 * @param {boolean} [options.ignoreScale=false] 是否忽略缩放
	 * @param {boolean} [options.indexOfParent] 对象在子节点中的索引
	 * @returns {boolean} 添加成功返回true,否则返回false
	 * @example
	 * 	// Keep local transform of box to be added to object
	 * 	object.add(new THING.Box({ localPosition: [0, 10, 0]}), { attachMode: false });
	 * @public
	 */
	add(object, options = {}) {
		return super.add(object, options);
	}

	/**
	 * 提升渲染节点(uNode)作为Object3D对象
	 * @param {string} name 节点名称
	 * @param {THING.Object3D} parent 父对象,如果为null,则表示使用当前对象作为父对象
	 * @param {*} cls 类
	 * @returns {THING.Object3D} 提升后的对象
	 * @example
	 * 	// Promote all sub nodes
	 * 	var nodeNames = entity.body.nodeNames;
	 * 	nodeNames.forEach(name => {
	 * 		entity.promoteNode(name);
	 * 	});
	 * @public
	 */
	promoteNode(name, parent, cls) {
		if (!name) {
			return null;
		}

		// if do not have the node,return null
		if (!this._body.hasNode(name)) {
			return null;
		}

		// We can not promote node when it's in instanced drawing mode
		if (this.isInstancedDrawing) {
			this.makeInstancedDrawing(false);
		}

		// Record it had been promoted
		this._flags.enable(Flag.HadPromoted, true);

		// Create root node to let sub node attach it
		this._body.createRootNode();

		// Get the parent
		parent = parent || this;

		// Remove some components, due to we can not work it together
		this.removeComponent('animation');

		// Create object
		cls = cls ? cls : Object3D;
		let object = new cls({
			name,
			parent
		});

		const style = object.body.style;
		style.copy(this.body.style);

		// Promote node in body object
		let node = this._body.promoteNode(name, parent.node, style.resource);

		object.matrixWorld = node.getMatrixWorld([]);
		object.body.setNode(node)

		return object;
	}

	/**
	 * Get pivot in self oriented box.
	 * @type {Array<number>}
	 * @private
	 */
	getPivot() {
		return this.onGetPivot();
	}

	/**
	 * Set pivot in self oriented box.
	 * @param {Array<number>} value The pivot value.
	 * @private
	 */
	setPivot(value) {
		this.onSetPivot(value);
	}

	/**
	 * Clear pivot node.
	 * @private
	 */
	clearPivot() {
		this.transform.clearPivot();

		this.setPivot(null);

		let tempData = this._getTempData();
		delete tempData['pivot'];
		delete tempData['initPivot'];
	}

	/**
	 * Get the attached points.
	 * @type {THING.Selector}
	 * @private
	 */
	get attachedPoints() {
		return this.query('.AttachedPoint', { recursive: false });
	}

	// #endregion

	// #region Picker

	/**
	 * @typedef {object} RaycastInfo
	 * @property {Array<number>} origin 射线起点位置
	 * @property {Array<number>} direction 射线在世界坐标系下的方向
	 * @public
	 */

	/**
	 * @typedef {object} RaycastResult
	 * @property {THING.Object3D} object 被射线检测到的对象
	 * @property {Array<number>} position 射线与对象相交的位置
	 * @property {number} distance 射线起点到相交点的距离
	 * @public
	 */

	/**
	 * 射线检测,检测射线是否与对象相交
	 * @param {RaycastInfo} ray 射线信息
	 * @param {boolean} [recursive=true] 是否对所有子对象进行检测
	 * @returns {Array<RaycastResult>} 检测结果(按距离从近到远排序)
	 * @example
	 * // 示例1: 创建一个向前的射线
	 * let results = object.raycast({
	 *   origin: [0, 0, 0],    // 起点
	 *   direction: [0, 0, 1]  // 向前
	 * });
	 * if (results.length) {
	 *   let nearest = results[0];  // 获取最近的相交点
	 *   console.log('击中物体:', nearest.object);
	 *   console.log('击中位置:', nearest.position);
	 *   console.log('击中距离:', nearest.distance);
	 * }
	 *
	 * // 示例2: 创建一个指向目标的射线
	 * let direction = THING.Math.normalizeVector(
	 *   THING.Math.subVector(targetPos, startPos)  // 目标点减起点
	 * );
	 * console.log('从起点:', startPos, '朝向:', direction, '进行射线检测');
	 * let hits = object.raycast({
	 *   origin: startPos,
	 *   direction: direction
	 * });
	 * console.log('射线检测结果:', hits);
	 * @public
	 */
	raycast(ray, recursive = true) {
		let node = recursive ? this.node : this.bodyNode;

		// Only works on renderable object
		if (node.isRenderableNode) {
			let targets = [];
			node.raycast(ray, targets);

			return targets.map(target => {
				return {
					node: target.node,
					distance: target.distance,
					position: target.position
				};
			}).sort((a, b) => a.distance - b.distance);
		}
		// We can not ray cast on none-renderable object
		else {
			return {
				node: null,
				distance: 0,
				position: null
			};
		}
	}

	/**
	 * Get/Set pick mode.
	 * @type {PickMode}
	 * @private
	 */
	get pickMode() {
		return this.bodyNode.getAttribute('PickMode');
	}
	set pickMode(value) {
		this.bodyNode.setAttribute('PickMode', value);
	}

	// #endregion

	// #region Style

	hasStyle() {
		return this.body.hasStyle();
	}

	getStyle() {
		let _private = this[__.private];

		if (_private.style === undefined) {
			_private.style = this._createStyle();
		}

		if (_private.style) {
			return _private.style;
		}
		else {
			return null;
		}
	}

	/**
	 * 整体设置样式
	 * @param {object} value 样式对象
	 * @param {boolean} [recursive=false] 是否影响孩子样式
	 * @example
	 * 	let box1 = new THING.Box();
	 * 	let box2 = new THING.Box({parent: box1});
	 * 	box1.setStyle({color: 'red'});
	 * 	// @expect(box1.style.color[0] == 1);
	 * 	// @expect(box1.style.color[1] == 0);
	 * 	// @expect(box1.style.color[2] == 0);
	 * 	// @expect(box2.style.color == null);
	 * @public
	 */
	setStyle(value, recursive = false) {
		if (recursive) {
			this.traverseInheritance(
				(object) => {
					object.body.setStyle(value);
				},
				_getStyleInheritance
			);
		}
		else {
			this.body.setStyle(value);
		}
	}

	/**
	 * 获取/设置样式
	 * @type {THING.Style}
	 * @example
	 * 	let style = object.style;
	 * 	style.color = 'red';
	 * 	style.opacity = 0.1;
	 * @public
	 */
	get style() {
		return this.getStyle();
	}
	set style(value) {
		this.setStyle(value, true);
	}

	// #endregion

	// #region Resources

	onBeforeDestroy() {
		if (!super.onBeforeDestroy()) {
			return false;
		}

		this.onSyncBeforeDestroy();

		// Disable instanced drawing first
		if (this.isInstancedDrawing) {
			this.makeInstancedDrawing(false);
		}

		// Skip matrix update to speed up render
		this.node.setAttribute('MatrixUpdateMode', 'SkipMatrixUpdate');

		return true;
	}

	/**
	 * When notify sync-before-destroy.
	 * @private
	 */
	onSyncBeforeDestroy() {
		let _private = this[__.private];

		let onSyncBeforeDestroy = _private.onSyncBeforeDestroy;
		if (onSyncBeforeDestroy) {
			onSyncBeforeDestroy({ object: this });

			_private.onSyncBeforeDestroy = null;
		}
	}

	/**
	 * 克隆对象
	 * @param {boolean} recursive 是否克隆所有的孩子
	 * @param {THING.BaseObject} parent 指定克隆出的对象父物体 默认用app.root作为父物体
	 * @param {object} options 选项
	 * @returns {THING.Object3D} 克隆出来的物体
	 * @example
	 * 	// Clone the object and move up
	 * 	let otherObject = object.clone();
	 * 	otherObject.translateY(10);
	 * @public
	 */
	clone(recursive, parent, options = {}) {
		let object = null;
		let attachMode = Utils.parseValue(options['attachMode'], true);
		let objParent = parent !== undefined ? parent : this.app.root;

		object = Utils.applyNew(this.constructor, {
			parent: objParent
		});

		object.options = Object.assign({}, options);
		object.copy(this);

		// Update the localPosition whit attachMode
		if (!attachMode && objParent != this.parent) {
			object.localPosition = this.localPosition;
		}

		recursive = Utils.parseBoolean(recursive, true);

		if (recursive) {
			this.children.forEach(child => {
				child.clone(true, object, { attachMode: false });
			});
		}

		return object;
	}

	/**
	 * 异步克隆
	 * @param {boolean} recursive 是否递归克隆
	 * @param {THING.BaseObject} parent 父对象,默认是app.root对象
	 * @param {object} options 选项
	 * @returns {Promise<any>}
	 * @private
	 * @example
	 * 	let object = await object.cloneAsync();
	 */
	cloneAsync(recursive, parent, options) {
		return new Promise((resolve, reject) => {
			let object = this.clone(recursive, parent, options);
			if (object) {
				object.copyPromise.then(() => {
					resolve(object);
				});
			}
			else {
				reject();
			}
		});
	}

	/**
	 * 等待物体资源加载完成 返回一个Promise对象
	 * @returns {Promise<any>} 返回一个Promise对象
	 * @example
	 * 	// 等待物体资源加载完成
	 * 	await object.waitForComplete();
	 * 	app.camera.fit(object);
	 * @public
	 */
	waitForComplete() {
		if (this.destroyed) {
			return Promise.resolve({ object: this });
		}

		if (this.loaded) {
			return Promise.resolve({ object: this });
		}
		else if (this._resourceState == Object3D.ResourceState.Error) {
			return Promise.reject({ object: this, desc: 'Object load failed' });
		}

		if (!this._loadPromise) {
			this._loadPromise = new ResolvablePromise();
		}

		return this._loadPromise;
	}

	/**
	 * @public
	 * @typedef {object} ResourceResult
	 * @property {string} url 资源路径
	 * @property {Array<number>} [localPosition] 相对父物体的位置
	 * @property {Array<number>} [localAngles] 相对父物体的旋转
	 * @property {Array<number>} [localScale] 相对父物体的缩放
	 * @property {Array<ResourceResult>} [children] 孩子的资源
	 */

	/**
	 * Set resource only, do not load resource.
	 * @param {ResourceResult} value The resource.
	 * @private
	 */
	setResource(value) {
		let _private = this[__.private];

		if (Utils.isString(value)) {
			value = { url: value };
		}

		_private.resource = value;
	}

	/**
	 * Get resource.
	 * @returns {ResourceResult}
	 * @private
	 */
	getResource() {
		let _private = this[__.private];

		return _private.resource;
	}

	/**
	 * Check whether has resource.
	 * @returns {boolean}
	 * @private
	 */
	hasResource() {
		let _private = this[__.private];

		let resource = _private.resource;
		if (resource.url) {
			return true;
		}

		let children = resource.children;
		if (children && children.length) {
			return true;
		}

		return false;
	}

	/**
	 * Reload resource.
	 * @param {boolean} [recursive=true] True indicates to load all children resources.
	 * @param {object} [options={}] The load options.
	 * @returns {Promise<any>}
	 * @private
	 */
	reloadResource(recursive = true, options = {}) {
		return new Promise((resolve, reject) => {
			this.unloadResource(recursive).then(() => {
				this.loadResource(recursive, options).then(() => {
					resolve()
				})
			});
		})
	}

	/**
	 * 加载资源 返回一个Promise对象
	 * @param {boolean} [recursive=true] 是否加载所有孩子的资源
	 * @param {ResourceResult} [options={}] 加载选项
	 * @returns {Promise<any>} 返回一个Promise对象
	 * @example
	 * 	// 等待物体加载资源完成
	 * 	await object.loadResource();
	 * @public
	 */
	loadResource(recursive = true, options = {}) {
		return new Promise((resolve, reject) => {
			// Load child resources
			if (recursive) {
				let promises = [];

				if (this.hasChildren()) {
					this.children.forEach(child => {
						let promise = child.loadResource(recursive, options);
						if (!promise) {
							Utils.error(`Load resource failed, due to promise is invalid`, child);
							return;
						}

						promises.push(promise);
					});
				}

				if (promises.length) {
					Promise.all(promises).then(() => {
						this._loadSelfResource(options, resolve, reject);
					}).catch(reject);
				}
				else {
					this._loadSelfResource(options, resolve, reject);
				}
			}
			else {
				this._loadSelfResource(options, resolve, reject);
			}
		}).then(
			// OK
			() => {
				this.onLoadComplete();
			},
			// Error
			(error) => {
				Utils.warn('Load resource failed', error);
				this.onLoadComplete();
			}
		);
	}

	/**
	 * 卸载资源 返回一个Promise对象
	 * @param {boolean} [recursive=true] 是否卸载所有孩子的资源
	 * @returns {Promise<any>} 返回一个Promise对象
	 * @example
	 * 	// 等待物体卸载资源完成
	 * 	await object.unloadResource();
	 * @public
	 */
	unloadResource(recursive = true) {
		// We can not unload resource when it's in instanced drawing mode
		this.makeInstancedDrawing(false);

		return new Promise((resolve, reject) => {
			// Loaded -> Unload
			if (this.loaded) {
				// Start to unload resource
				this._unloadResource(recursive);

				// We are in ready to load resource state now
				this._resourceState = Object3D.ResourceState.Ready;

				// Clear some promises
				this._loadPromise = null;

				resolve();
			}
			// Loading -> Wait for complete -> Unload
			else if (this.loading) {
				// Wait for complete
				this.waitForComplete().then(() => {
					this.unloadResource();

					resolve();
				});
			}
			else {
				resolve();
			}
		});
	}

	setBodyNode(node) {
		this.body.setNode(node);
	}

	getBodyNode() {
		return this.bodyNode;
	}

	/**
	 * Get/Set the options.
	 * @type {object}
	 * @private
	 */
	get options() {
		let _private = this[__.private];

		return _private.options;
	}
	set options(value) {
		let _private = this[__.private];

		if (value) {
			_private.options = Object.assign({}, value);
		}
		else {
			_private.options = {};
		}
	}

	get copyPromise() {
		return this[__.private].copyPromise;
	}

	/**
	 * Get/Set the loaded flag.
	 * @type {boolean}
	 * @private
	 */
	get loaded() {
		return this._resourceState == Object3D.ResourceState.Loaded;
	}
	set loaded(value) {
		if (value) {
			this._resourceState = Object3D.ResourceState.Loaded;
		}
		else {
			this._resourceState = Object3D.ResourceState.Ready;
		}
	}

	/**
	 * Get the loading flag.
	 * @type {boolean}
	 * @private
	 */
	get loading() {
		return this._resourceState == Object3D.ResourceState.Loading;
	}

	/**
	 * Get resource state
	 * @type {number}
	 * @private
	 */
	get resourceState() {
		return this._resourceState;
	}

	/**
	 * 获取body对象 body对象可以理解这个对象自身,不包含它的孩子.
	 * @type {THING.BodyObject}
	 * @public
	 */
	get body() {
		return this._body;
	}

	/**
	 * Get the root node.
	 * @type {object}
	 * @private
	 */
	get node() {
		return this._body.getNode();
	}

	/**
	 * Get the pivot node.
	 * @type {object}
	 * @private
	 */
	get pivotNode() {
		return this._body.getPivotNode();
	}

	/**
	 * Get the body node.
	 * @type {object}
	 * @private
	 */
	get bodyNode() {
		return this._body.getRenderableNode();
	}

	/**
	 * Get/Set resource.
	 * @type {ResourceResult}
	 * @private
	 */
	get resource() {
		return this[__.private].resource;
	}
	set resource(value) {
		if (!value) {
			return;
		}

		// Skip for the same resource
		if (this._isSameResource(value)) {
			return;
		}

		this._updateResource(value);
	}

	// #endregion

	// #region copy

	/**
	 * 拷贝对象(不拷贝uuid和parent),拷贝对象会重新加载资源
	 * @param {THING.BaseObject} object 源对象
	 * @param {object} options 选项
	 * @returns {Promise<any>} 返回一个Promise对象
	 * @example
	 * 	// 从查询结果拷贝对象
	 * 	let sourceObject = app.query('#master')[0];
	 * 	if (sourceObject) {
	 * 		object.copy(sourceObject);
	 * 	}
	 * @public
	 */
	copy(object, options = {}) {
		let _private = this[__.private];

		// Start to copy values
		this.onBeforeCopy(object, options);

		// Start to copy default style
		this.style.beginDefaultValues();
		object.style.beginDefaultValues();
		this.onCopyStyle(object, options);
		object.style.endDefaultValues();
		this.style.endDefaultValues();

		// Start to copy style
		this.onCopyStyle(object, options);

		// Create copy promise
		_private.copyPromise = new ResolvablePromise();

		// Wait resource promise to finish
		let promises = [];
		promises.push(this._createResourcePromise());
		promises.push(object.waitForComplete());

		return Promise.all(promises).then(() => {
			if (object.isInstancedDrawing === false && object.style.isInstancedDrawing === true) {
				let ustyle = object.bodyNode.getStyle();
				let curInstanceCount = ustyle.getInstancedCount();
				let maxInstanceCOunt = ustyle.getInstancedMaxCount();
				if (curInstanceCount >= maxInstanceCOunt) {
					ustyle.setInstancedMaxCount(maxInstanceCOunt * 2);
				}
			}

			// Create copy promise
			const cloneNode = object.bodyNode.clone();
			this.body.setNode(cloneNode);

			// Copy component
			this.onCopyComponents(object);

			// Continue to copy object on inherited class
			this.onCopy(object);

			// Make instanced drawing mode when it's needed
			if (object.isInstancedDrawing) {
				this.makeInstancedDrawing();
			}

			this._resourceState = Object3D.ResourceState.Loaded;
			this.onLoadComplete();

			// Copy finished
			_private.copyPromise.resolve();
		});
	}

	onCopyComponents(object) {
		// Copy components
		let accessorInfo = object.getAccessorInfo();
		for (let name in accessorInfo) {
			let { isResident, hasInstance, classType } = accessorInfo[name];

			// Check whether need to copy it
			if (!this._needCopyComponent(isResident, hasInstance, classType)) {
				continue;
			}

			let accessor = accessorInfo[name].accessor;
			let sourceComponent = accessor.get();

			let targetComponent = this[name];
			if (!targetComponent) {
				this.addComponent(classType, name, accessorInfo[name].args);

				targetComponent = this[name];
			}

			if (targetComponent.onCopy) {
				targetComponent.onCopy(sourceComponent);
			}
			else if (targetComponent.onImport) {
				if (sourceComponent && sourceComponent.onExport) {
					let data = sourceComponent.onExport();
					if (data) {
						targetComponent.onImport(Utils.cloneObject(data, false));
					}
				}
			}
		}
	}

	onCopyResource(object) {
		// Update resource
		this._updateResource(object.resource).then(() => {
			this.onCopyComponents(object);
		});
	}

	onBeforeCopy(object, options = {}) {
		// Common
		this.onCopyAttribute(object, options);

		// Update transform
		this.onCopyTransform(object, options);
	}

	copyCompnents(object) {
		this.onCopyComponents(object);
	}

	onCopyStyle(object, options = {}) {
		// Update style
		let style = object.style;
		if (style) {
			let styleData = options.onUpdateStyle ? options.onUpdateStyle(style.getProperties()) : style.getProperties();
			this.style.copyFromProperties(styleData);
		}
	}

	onCopyAttribute(object, options = {}) {
		let _private = this[__.private];

		// Common
		this.name = options.onUpdateValue ? options.onUpdateValue(object, 'name') : object.name;
		this.id = options.onUpdateValue ? options.onUpdateValue(object, 'id') : object.id;
		this.queryable = options.onUpdateValue ? options.onUpdateValue(object, 'queryable') : object.queryable;
		this.destroyable = options.onUpdateValue ? options.onUpdateValue(object, 'destroyable') : object.destroyable;
		this.active = options.onUpdateValue ? options.onUpdateValue(object, 'active') : object.active;
		this.visible = options.onUpdateValue ? options.onUpdateValue(object, 'visible') : object.visible;
		this.userData = options.onUpdateValue ? options.onUpdateValue(object, 'userData') : object.userData;
		this.alwaysOnTop = options.onUpdateValue ? options.onUpdateValue(object, 'alwaysOnTop') : object.alwaysOnTop;
		this.tags = new Tags(options.onUpdateValue ? options.onUpdateValue(object, 'tags') : object.tags);

		// resource
		this.setResource(Utils.cloneObject(object.resource));

		if (object.external) {
			_private.external = Object.assign({}, options.onUpdateValue ? options.onUpdateValue(object, 'external') : object.external);
		}
	}

	onCopyTransform(object, options = {}) {
		// We must keep root and body transform both, due to body and root would be 2 different nodes
		const position = options.onUpdateValue ? options.onUpdateValue(object, 'position') : object.position;
		const quaternion = options.onUpdateValue ? options.onUpdateValue(object, 'quaternion') : object.quaternion;
		const scale = options.onUpdateValue ? options.onUpdateValue(object, 'scale') : object.scale;

		const objectMatrixWorld = MathUtils.composeToMat4(position, quaternion, scale);
		const bodyMatrixWorld = MathUtils.composeToMat4(object.body.position, object.body.quaternion, object.body.scale);

		this.matrixWorld = objectMatrixWorld;
		this.body.matrixWorld = bodyMatrixWorld;
	}

	// #endregion

	// #region Notifier

	addSyncCompleteCallback(callback) {
		let _private = this[__.private];

		_private.syncCompleteCallbacks.push(callback);
	}

	// #endregion

	// #region Import/Export

	/**
	 * When check whether need to collect model.
	 * @returns {boolean}
	 * @private
	 */
	onCollectMesh() {
		// Skip to collect mesh for prefab
		if (this.isSubObject) {
			return false;
		}

		return true;
	}


	/**
	 * When import external data.
	 * @param {object} external The external data.
	 * @param {object} options The options
	 * @private
	 */
	onImportExternalData(external, options) {
		let _private = this[__.private];
		if (external) {
			_private.external = external;
		}
	}

	/**
	 * When export external data.
	 * @param {object} options The options.
	 * @returns {object}
	 * @private
	 */
	onExportExternalData(options) {
		const _private = this[__.private];
		const external = {};

		external['pivot'] && delete external['pivot']
		const pivot = this.pivot
		if (pivot) {
			external['pivot'] = pivot
		}

		external['inherit'] && delete external['inherit']
		const inherit = _private.inherit
		if (inherit) {
			const inheritExternal = {}

			inherit._keys.forEach((key) => {
				const value = inherit[key]
				if (value !== InheritType.Normal) {
					inheritExternal[key] = inherit[key]
				}
			})

			if (Object.keys(inheritExternal).length) {
				external['inherit'] = inheritExternal
			}
		}

		if (!Object.keys(external).length) {
			return null;
		}

		return external;
	}

	// #endregion

	// #region Internal components attributes and functions

	// #region Component - render

	/**
	 * Check whether it's in instanced drawing mode.
	 * @type {boolean}
	 * @private
	 */
	get isInstancedDrawing() { return this.render.isInstancedDrawing; }

	/**
	 * Get/Set the instance group name.
	 * @type {string}
	 * @private
	 */
	get instanceGroupName() { return this.render.instanceGroupName; }
	set instanceGroupName(value) { this.render.instanceGroupName = value; }

	/**
	 * 获取/设置渲染顺序,绘制顺序从低到高。
	 * @type {number}
	 * @public
	 */
	get renderOrder() { return this.render.renderOrder; }
	set renderOrder(value) { this.render.renderOrder = value; }

	/**
	 * Get/Set the cast shadow.
	 * @type {boolean}
	 * @private
	 */
	get castShadow() { return this.render.castShadow; }
	set castShadow(value) {
		this.setCastShadow(value, true);
	}

	/**
	 * Get/Set the receive shadow.
	 * @type {boolean}
	 * @private
	 */
	get receiveShadow() { return this.render.receiveShadow; }
	set receiveShadow(value) {
		this.setReceiveShadow(value, true);
	}

	/**
	 * 设置/获取 是否开启渲染置顶
	 * @type {boolean}
	 * @example
	 * // Keep object render as the top of render layer
	 * object.alwaysOnTop = true;
	 * @public
	 */
	get alwaysOnTop() { return this.render.alwaysOnTop; }
	set alwaysOnTop(value) {
		this.render.alwaysOnTop = value;
		this._updateInstanceState(BreakInstanceFlag.AlwaysOnTop, value)
	}

	/**
	 * 获取渲染顺序。
	 * @returns {number} 渲染顺序
	 * @public
	 * @example
	 * 	let renderOrder = object.renderOrder;
	 * 	console.log(renderOrder);
	 */
	getRenderOrder() { return this.render.getRenderOrder.apply(this.render, arguments); }

	/**
	 * 设置渲染顺序。
	 * @param {number} value 渲染顺序的值,渲染顺序从低到高。
	 * @param {boolean} recursive 为真表示处理所有子对象。
	 * @returns {boolean} 是否设置成功
	 * @public
	 * @example
	 * 	object.setRenderOrder(10);
	 */
	setRenderOrder() { return this.render.setRenderOrder.apply(this.render, arguments); }

	/**
	 * 获取/设置 是否开启阴影。
	 * @returns {boolean} 是否开启阴影
	 * @private
	 */
	getCastShadow() { return this.render.getCastShadow.apply(this.render, arguments); }

	/**
	 * 设置/获取 是否开启阴影。
	 * @param {boolean} value 是否开启阴影
	 * @param {boolean} recursive 为真表示处理所有子对象。
	 * @private
	 */
	setCastShadow(value, recursive) {
		this.render.setCastShadow(value, recursive);
		this._updateInstanceState(BreakInstanceFlag.CastShadow, !value)
	}

	/**
	 * Get receive shadow.
	 * @returns {boolean}
	 * @private
	 */
	getReceiveShadow() { return this.render.getReceiveShadow.apply(this.render, arguments); }

	/**
	 * Set receive shadow.
	 * @param {boolean} value The value.
	 * @param {boolean} recursive True indicates process it with all children.
	 * @private
	 */
	setReceiveShadow(value, recursive) {
		this.render.setReceiveShadow(value, recursive);
		this._updateInstanceState(BreakInstanceFlag.ReceiveShadow, !value)
	}

	/**
	 * 开启/关闭批量渲染模式。
	 * @param {boolean} value 是否开启
	 * @param {object} options 选项
	 * @param {string} [options.renderMode='InstancedRendering'] 渲染模式 InstancedRendering/SharedRendering
	 * SharedRendering代表材质共用但是drawCall还是多个 InstancedRendering代表材质共用,drawCall只有一次
	 * @returns {boolean} 是否开启成功
	 * @example
	 * 	// 开启批量渲染模式
	 * 	if (object.makeInstancedDrawing()) {
	 * 		console.log('Enable instanced drawing');
	 * 	}
	 * @public
	 */
	makeInstancedDrawing(value = true, options = {}) {
		this._flags.enable(Flag.Instance, value);
		if (value) {
			// 更新instance状态
			if (this.instanceGroupName) {
				this._makeInstancedDrawingOptions = options;
				return this.render.makeInstancedDrawing(true, options);
			}

			if (this._breakInstanceFlags.values === 0) {
				this._makeInstancedDrawingOptions = options;
				return this.render.makeInstancedDrawing(true, options);
			}

			return false;
		}
		else {
			this._makeInstancedDrawingOptions = null;
			return this.render.makeInstancedDrawing(value, options);
		}
	}

	/**
	 * @typedef {object} InstancingNodeOptions
	 * @property {number} maxNumber 实例的最大数量。
	 * @property {number} number 当前渲染的实例数量。
	 * @property {Array<Array<number>>} matrices 实例的变换矩阵数组。
	 * @property {Array<number>} [pickedIds] (可选)实例的拾取 ID,在拾取事件中返回 pickId。
	 * @property {Array<Array<number>>} [colors] (可选)实例的颜色数组。
	 * @public
	 */

	/**
	 * 启用实例化节点(启用后将使主体节点不可见)。
	 * @param {InstancingNodeOptions} options 选项。
	 * @returns {Promise<boolean>} 是否启用成功
	 * @example
	 * // 启用实例化渲染,设置最大数量为31104个实例
	 * object.enableInstancing({
	 *   maxNumber: 31104,
	 *   number: 0,
	 *   matrices: [], // 包含所有实例的变换矩阵
	 *   pickedIds: pickedIds, // 可选,包含所有实例的拾取ID
	 *   colors: colors // 可选,包含所有实例的颜色
	 * });
	 * @public
	 */
	enableInstancing() { return this.render.enableInstancing.apply(this.render, arguments); }

	/**
	 * 禁用实例化节点。
	 * @returns {boolean} 是否禁用成功
	 * @example
	 * // 禁用实例化渲染
	 * object.disableInstancing();
	 * @public
	 */
	disableInstancing() { return this.render.disableInstancing.apply(this.render, arguments); }

	/**
	 * 设置/更新实例化节点选项。
	 * @param {InstancingNodeOptions} options 选项。
	 * @returns {boolean} 是否设置成功
	 * @example
	 * // 更新实例化渲染的参数
	 * object.setInstancing({
	 *   maxNumber: 31104,
	 *   number: 31104,
	 *   matrices: matrices, // 包含所有实例的变换矩阵
	 *   pickedIds: pickedIds, // 可选,包含所有实例的拾取ID
	 *   colors: colors // 可选,包含所有实例的颜色
	 * });
	 * @public
	 */
	setInstancing() { return this.render.setInstancing.apply(this.render, arguments); }

	/**
	 * 获取实例化节点信息。
	 * @returns {InstancingNodeOptions} 实例化节点信息
	 * @example
	 * // 获取实例化渲染的当前参数
	 * const info = object.getInstancing();
	 * console.log(info.number); // 当前实例数量
	 * console.log(info.maxNumber); // 最大实例数量
	 * @public
	 */
	getInstancing() { return this.render.getInstancing.apply(this.render, arguments); }

	/**
	 * 按索引设置实例化颜色。
	 * @param {number} index 索引。
	 * @param {Array<number>} color 颜色。
	 * @returns {boolean} 是否设置成功
	 * @example
	 * // 将第30个实例设置为绿色
	 * object.setInstancingColor(30, [0, 1, 0]);
	 *
	 * // 随机将一些实例设置为红色警告色
	 * for(let i = 0; i < warnCount; i++) {
	 *   const index = Math.floor(Math.random() * 31104);
	 *   object.setInstancingColor(index, [1, 0, 0]);
	 * }
	 * @public
	 */
	setInstancingColor() { return this.render.setInstancingColor.apply(this.render, arguments); }

	/**
	 * 按索引设置实例化清除颜色。
	 * @param {number} index 索引。
	 * @param {boolean} value 是否清除颜色。
	 * @returns {boolean} 是否设置成功
	 * @example
	 * // 清除第一个实例的颜色
	 * object.setInstancingClearOutColor(0, true);
	 * @public
	 */
	setInstancingClearOutColor() { return this.render.setInstancingClearOutColor.apply(this.render, arguments); }

	/**
	 * 获取实例化清除颜色。
	 * @param {number} index 索引。
	 * @returns {boolean} 是否获取成功
	 * @example
	 * // 获取第一个实例的清除颜色
	 * const clearOutColor = object.getInstancingClearOutColor(0);
	 * console.log('Instance 0 clear out color:', clearOutColor);
	 * @public
	 */
	getInstancingClearOutColor() { return this.render.getInstancingClearOutColor.apply(this.render, arguments); }

	/**
	 * 按索引设置实例化漫反射UV变换。
	 * @param {number} index 索引。
	 * @param {Array<number>} diffuseUVTransform 漫反射UV变换。
	 * @returns {boolean} 是否设置成功
	 * @example
	 * // 设置第一个实例的漫反射UV变换
	 * object.setInstancingDiffuseUVTransform(0, [0, 0, 1, 1]);
	 * @public
	 */
	setInstancingDiffuseUVTransform() { return this.render.setInstancingDiffuseUVTransform.apply(this.render, arguments); }

	/**
	 * 获取实例化漫反射UV变换。
	 * @param {number} index 索引。
	 * @param {Array<number>} target 目标。
	 * @returns {Array<number>} 漫反射UV变换
	 * @example
	 * // 获取第一个实例的漫反射UV变换
	 * const diffuseUVTransform = object.getInstancingDiffuseUVTransform(0);
	 * console.log('Instance 0 diffuse UV transform:', diffuseUVTransform);
	 * @public
	 */
	getInstancingDiffuseUVTransform() { return this.render.getInstancingDiffuseUVTransform.apply(this.render, arguments); }

	/**
	 * 按索引设置实例化可见性。
	 * @param {number} index 索引。
	 * @param {boolean} visible 可见性。
	 * @returns {boolean} 是否设置成功
	 * @example
	 * // 隐藏第一个实例
	 * object.setInstancingVisible(0, false);
	 * @public
	 */
	setInstancingVisible() { return this.render.setInstancingVisible.apply(this.render, arguments); }

	/**
	 * 获取按索引的实例化可见性。
	 * @param {number} index 索引。
	 * @returns {boolean} 是否获取成功
	 * @example
	 * // 检查第一个实例是否可见
	 * const visible = object.getInstancingVisible(0);
	 * console.log('Instance 0 visible:', visible);
	 * @public
	 */
	getInstancingVisible() { return this.render.getInstancingVisible.apply(this.render, arguments); }

	/**
	 * 获取实例化数量。
	 * @returns {number} 实例化数量
	 * @example
	 * // 获取当前实例的数量
	 * const count = object.getInstancedCount();
	 * console.log('Current instance count:', count);
	 * @public
	 */
	getInstancedCount() { return this.render.getInstancedCount.apply(this.render, arguments); }

	/**
	 * 按索引设置实例化矩阵。
	 * @param {number} index 索引。
	 * @param {Array<number>} matrix 矩阵。
	 * @returns {boolean} 是否设置成功
	 * @example
	 * // 修改第30个实例的变换矩阵,将其Y轴位置上移1个单位
	 * const matrices = options[0].matrices[30];
	 * object.setInstancingMatrix(30, [
	 *   1, 0, 0, matrices[3],
	 *   0, 1, 0, matrices[7] + 1,
	 *   0, 0, 1, matrices[11],
	 *   0, 0, 0, 1
	 * ]);
	 * @public
	 */
	setInstancingMatrix() { return this.render.setInstancingMatrix.apply(this.render, arguments); }

	/**
	 * 获取按索引的实例化矩阵。
	 * @param {number} index 索引。
	 * @param {Array<number>} target? 目标。
	 * @returns {Array<number>} 实例化矩阵
	 * @example
	 * // 获取第一个实例的变换矩阵
	 * const matrix = object.getInstancingMatrix(0);
	 * console.log('Instance 0 matrix:', matrix);
	 * @public
	 */
	getInstancingMatrix() { return this.render.getInstancingMatrix.apply(this.render, arguments); }

	/**
	 * 设置实例化属性。
	 * @param {string} name 名称。
	 * @param {any} value 值。
	 * @returns {boolean} 是否设置成功
	 * @example
	 * // 启用颜色属性
	 * object.setInstancingAttribute('Colors', true);
	 * @public
	 */
	setInstancingAttribute() { return this.render.setInstancingAttribute.apply(this.render, arguments); }

	/**
	 * 获取实例化属性。
	 * @param {string} name 名称。
	 * @returns {any} 实例化属性
	 * @example
	 * // 获取颜色属性的状态
	 * const useColors = object.getInstancingAttribute('Colors');
	 * console.log('Using colors:', useColors);
	 * @public
	 */
	getInstancingAttribute() { return this.render.getInstancingAttribute.apply(this.render, arguments); }

	/**
	 * @typedef {object} RenderableNodeGeometryInfo
	 * @property {number} vertices The number of vertices.
	 * @property {number} triangles The number of triangles.
	 * @property {number} materials The number of materials.
	 * @property {number} textures The number of textures.
	 */

	/**
	 * 获取几何体信息。
	 * @returns {RenderableNodeGeometryInfo} 几何体信息
	 * @private
	 */
	getGeometryInfo() { return this.render.getGeometryInfo.apply(this.render, arguments); }

	// #endregion

	// #region Component - level

	/**
	 * Check whether it's current level.
	 * @type {boolean}
	 * @deprecated Deprecated, call it with LevelComponent.
	 * @private
	 */
	get isCurrentLevel() { return this.level.isCurrentLevel; }

	// #endregion

	// #region Component - bounding

	/**
	 * 获取世界坐标系下的包围盒数据  等同于getAABB(true,true,false)
	 * @type {THING.Box3}
	 * @example
	 * 	let boundingBox = object.boundingBox;
	 * 	if (boundingBox.halfSize[1] > 100) {
	 * 		console.log('The object is so tall');
	 * 	}
	 * @public
	 */
	get boundingBox() { return this.bounding.boundingBox; }

	/**
	 *  获取旋转后的包围盒数据 包围盒坐标轴一般是物体自身坐标轴 等同于getOBB(true,true,false)
	 * @type {OrientedBoxResult}
	 * @example
	 * 	let orientedBox = object.orientedBox;
	 * 	if (orientedBox.angles[1] > 0) {
	 * 		console.log('The object has rotated by Y-axis');
	 * 	}
	 * @public
	 */
	get orientedBox() { return this.bounding.orientedBox; }

	/**
	 * Get/Set the initial local bounding box.
	 * @type {BoundingBoxResult}
	 * @private
	 */
	get initialLocalBoundingBox() { return this.bounding.initialLocalBoundingBox; }
	set initialLocalBoundingBox(value) { this.bounding.initialLocalBoundingBox = value; }

	/**
	 * 获取包围盒
	 * @param {boolean} [recursive=true] 计算aabb时是否考虑孩子
	 * @param {boolean} [updateMatrix=true] 计算aabb时是否更新自己和孩子的矩阵
	 * @param {boolean} [local=false] 计算出的aabb的轴向是基于父物体坐标系,还是相对于世界坐标系(计算出的包围盒是不同的)
	 * @returns {THING.Box3} 包围盒对象
	 * @example
	 * 	let boundingBox = object.getAABB();
	 * 	console.log(boundingBox);
	 * @public
	 */
	getAABB() { return this.bounding.getAABB.apply(this.bounding, arguments); }

	/**
	 * @typedef {object} OrientedBoxResult 旋转包围盒对象
	 * @property {Array<number>} angles 中心点旋转角度
	 * @property {Array<number>} center 中心点世界坐标
	 * @property {Array<number>} size 包围盒尺寸
	 * @property {Array<number>} halfSize 包围盒尺寸x,y,z三个方向分别除以2可得到该参数
	 * @property {number} radius 包围盒最小点和最大点的距离的一半
	 * @public
	 */

	/**
	 * 获取旋转后的包围盒
	 * @param {boolean} [recursive=true] 计算obb时是否考虑孩子
	 * @param {boolean} [updateMatrix=true] 计算obb时是否更新自己和孩子的矩阵
	 * @param {boolean} [local=false] 描述obb时的参数是基于父物体还是世界坐标系(算出的包围盒是一样的,只是描述不同)
	 * @returns {OrientedBoxResult} 旋转包围盒对象
	 * @example
	 * 	let orientedBox = object.getOBB();
	 * 	console.log(orientedBox);
	 * @public
	 */
	getOBB() { return this.bounding.getOBB.apply(this.bounding, arguments); }

	// #endregion

	// #region Component - lerp

	/**
	 * @public
	 * @typedef LerpArgs
	 * @property {object} from 源属性
	 * @property {object} to 目标属性
	 * @property {number} [times=-1] 循环次数 -1代表无限
	 * @property {number} [duration=1000] 时间 单位毫秒
	 * @property {number} [delayTime=0] 延时 单位毫秒
	 * @property {LerpType} [lerpType] 插值类型 见THING.LerpType
	 * @property {LoopType} [loopType] 循环类型 见THING.LoopType
	 * @property {boolean} [orientToPath] 移动时是随路径调整自身朝向
	 * @property {Function} [onRepeat] 每一次插值结束,下一次插值开始时触发
	 * @property {Function} [onStart] 开始时触发
	 * @property {Function} [onStop] 停止时触发
	 * @property {Function} [onUpdate] 插值过程每帧触发
	 * @property {Function} [onComplete] 所有插值结束时触发
	 */

	/**
	 * @public
	 * @typedef LerpWithSpaceTypeArgs
	 * @property {object} from 源属性
	 * @property {object} to 目标属性
	 * @property {number} [times=-1] 循环次数 -1代表无限
	 * @property {number} [duration=1000] 时间 单位毫秒
	 * @property {number} [delayTime=0] 延时 单位毫秒
	 * @property {LerpType} [lerpType] 插值类型 见THING.LerpType
	 * @property {LoopType} [loopType] 循环类型 见THING.LoopType
	 * @property {boolean} [orientToPath] 移动时是随路径调整自身朝向
	 * @property {Function} [onRepeat] 每一次插值结束,下一次插值开始时触发
	 * @property {Function} [onStart] 开始时触发
	 * @property {Function} [onStop] 停止时触发
	 * @property {Function} [onUpdate] 插值过程每帧触发
	 * @property {Function} [onComplete] 所有插值结束时触发
	 * @property {THING.SpaceType} [spaceType=THING.SpaceType.World] 参数中的位置是在世界空间还是自身空间
	 */
	/**
	 * @public
	 * @typedef MovePathLerpArgs
	 * @property {object} from 源属性
	 * @property {object} to 目标属性
	 * @property {number} [times=-1] 循环次数 -1代表无限
	 * @property {number} [duration=1000] 时间 单位毫秒
	 * @property {number} [delayTime=0] 延时 单位毫秒
	 * @property {LerpType} lerpType? 插值类型 见THING.LerpType
	 * @property {LoopType} loopType? 循环类型 见THING.LoopType
	 * @property {boolean} orientToPath? 移动时是随路径调整自身朝向
	 * @property {Function} onRepeat 每一次插值结束,下一次插值开始时触发
	 * @property {Function} onStart 开始时触发
	 * @property {Function} onStop 停止时触发
	 * @property {Function} onUpdate 插值过程每帧触发
	 * @property {Function} onComplete 所有插值结束时触发
	 * @property {THING.SpaceType} [spaceType=THING.SpaceType.World] 参数中的位置是在世界空间还是自身空间
	 * @property {Array<number>} up 向上的方向
	 * @property {boolean} [closure=false] 路径是否闭合,即移动到最后一个点之后是否要返回第一个点
	 */

	/**
	 * 获取飞行状态
	 * @type {boolean}
	 * @public
	 */
	get isFlying() { return this.lerp.isFlying; }

	/**
	 * Lerp points.
	 * @private
	 */
	lerpPoints() { return this.lerp.lerpPoints.apply(this.lerp, arguments); }

	/**
	 * Lerp points in async mode.
	 * @private
	 */
	lerpPointsAsync() { return this.lerp.lerpPointsAsync.apply(this.lerp, arguments); }

	/**
	 * 停止移动
	 * @example
	 * 	object.stopMoving();
	 * @public
	 */
	stopMoving() { return this.lerp.stopMoving.apply(this.lerp, arguments); }

	/**
	 * 暂停移动
	 * @example
	 * 	object.pauseMoving();
	 * @public
	 */
	pauseMoving() { return this.lerp.pauseMoving.apply(this.lerp, arguments); }

	/**
	 * 恢复移动
	 * @example
	 * 	object.resumeMoving();
	 * @public
	 */
	resumeMoving() { return this.lerp.resumeMoving.apply(this.lerp, arguments); }

	/**
	 * 对象移动到目标位置
	 * @param {Array<number>} value 目标位置
	 * @param {MovePathLerpArgs} param 移动的参数
	 * @returns {boolean} 是否移动成功
	 * @example
	 * 	object.moveTo(object.selfToWorld(THING.Math.randomVector([-200, -5, -200], [200, 5, 200])), {
	 * 		loopType: THING.LoopType.PingPong,
	 * 		duration: THING.Math.randomInt(1000, 5000)
	 * });
	 * object.moveTo({
	 * 	to: object.selfToWorld(THING.Math.randomVector([-200, -5, -200], [200, 5, 200])),
	 * 	duration: THING.Math.randomInt(1000, 5000)
	 * })
	 * @public
	 */
	moveTo() { return this.lerp.moveTo.apply(this.lerp, arguments); }

	/**
	 * Move object in duration (async).
	 * @param {Array<number>} value The position.
	 * @param {MovePathLerpArgs} param The parameters.
	 * @returns {Promise<any>}
	 * @example
	 * 	await object.moveToAsync(object.selfToWorld(THING.Math.randomVector([-200, -5, -200], [200, 5, 200])), {
	 * 		duration: THING.Math.randomInt(1000, 5000)
	 * });
	 * @private
	 */
	moveToAsync() { return this.lerp.moveToAsync.apply(this.lerp, arguments); }

	/**
	 * 对象沿路径移动
	 * @param {Array<Array<number>>} value 组成路径的坐标的集合
	 * @param {MovePathLerpArgs} param 移动参数
	 * @returns {boolean} 是否移动成功
	 * @example
	 * let path = [
	 * 	[100, 0, 0],
	 * 	[100, 0, 100],
	 * 	[0, 0, 100],
	 * [0, 0, 0],
	 * ];
	 *
	 * object.movePath(path.map(point => object.selfToWorld(point)), {
	 * 	duration: 5 * 1000,
	 * 	loopType: THING.LoopType.Repeat,
	 * });
	 * object.movePath({
	 * 	path: path.map(point => object.selfToWorld(point)),
	 * 	duration: 5 * 1000,
	 * 	loopType: THING.LoopType.Repeat,
	 * });
	 * @public
	 */
	movePath() { return this.lerp.movePath.apply(this.lerp, arguments); }

	/**
	 * 对象沿路径移动 返回一个Promise对象
	 * @param {Array<Array<number>>} value 组成路径的坐标的集合
	 * @param {MovePathLerpArgs} param 移动参数
	 * @returns {Promise<any>} 返回一个Promise对象
	 * @example
	 * let path = [
	 * 	[100, 0, 0],
	 * 	[100, 0, 100],
	 * 	[0, 0, 100],
	 * [0, 0, 0],
	 * ];
	 *
	 * await object.movePathAsync(path.map(point => object.selfToWorld(point)), {
	 * 	duration: 5 * 1000
	 * });
	 * await object.movePathAsync({
	 * 	path: path.map(point => object.selfToWorld(point)),
	 * 	duration: 5 * 1000
	 * });
	 * @public
	 */
	movePathAsync() { return this.lerp.movePathAsync.apply(this.lerp, arguments); }

	/**
	 * 停止缩放动画
	 * @returns {boolean} 是否停止成功
	 * @example
	 * object.stopScaling();
	 * @public
	 */
	stopScaling() { return this.lerp.stopScaling.apply(this.lerp, arguments); }

	/**
	 * 暂停缩放动画
	 * @returns {boolean} 是否暂停成功
	 * @example
	 * 	object.pauseScaling();
	 * @public
	 */
	pauseScaling() { return this.lerp.pauseScaling.apply(this.lerp, arguments); }

	/**
	 *  恢复缩放动画
	 * @returns {boolean} 是否恢复成功
	 * @example
	 * 	object.resumeScaling();
	 * @public
	 */
	resumeScaling() { return this.lerp.resumeScaling.apply(this.lerp, arguments); }

	/**
	 * 缩放动画
	 * @param {Array<number>} value 要缩放到三个轴向的倍数
	 * @param {LerpWithSpaceTypeArgs} param 插值参数
	 * @returns {boolean} 是否缩放成功
	 * @example
	 * object.scaleTo(THING.Math.randomVector([1, 1, 1], [3, 3, 3]), {
	 * 	loopType: THING.LoopType.PingPong,
	 * 	duration: THING.Math.randomInt(1000, 5000)
	 * });
	 * object.scaleTo({
	 * 	to: THING.Math.randomVector([1, 1, 1], [3, 3, 3]),
	 * 	duration: THING.Math.randomInt(1000, 5000)
	 * });
	 * @public
	 */
	scaleTo() { return this.lerp.scaleTo.apply(this.lerp, arguments); }

	/**
	 * Scale object in duration (async).
	 * @example
	 * await object.scaleToAsync(THING.Math.randomVector([1, 1, 1], [3, 3, 3]), {
	 * 	duration: THING.Math.randomInt(1000, 5000)
	 * });
	 * await object.scaleToAsync({
	 * 	to: THING.Math.randomVector([1, 1, 1], [3, 3, 3]),
	 * 	duration: THING.Math.randomInt(1000, 5000)
	 * });
	 * @private
	 */
	scaleToAsync() { return this.lerp.scaleToAsync.apply(this.lerp, arguments); }

	/**
	 * 停止旋转
	 * @example
	 * 	object.stopRotating();
	 * @public
	 */
	stopRotating() { return this.lerp.stopRotating.apply(this.lerp, arguments); }

	/**
	 * 暂停旋转
	 * @example
	 * 	object.pauseRotating();
	 * @public
	 */
	pauseRotating() { return this.lerp.pauseRotating.apply(this.lerp, arguments); }

	/**
	 * 恢复旋转
	 * @example
	 * 	object.resumeRotating();
	 * @public
	 */
	resumeRotating() { return this.lerp.resumeRotating.apply(this.lerp, arguments); }

	/**
	 * 旋转对象
	 * @param {Array<number>} value 三个轴向的旋转角度
	 * @param {LerpWithSpaceTypeArgs} param 插值参数
	 * @returns {boolean} 是否旋转成功
	 * @example
	 * object.rotateTo([0, 360, 0], {
	 * 	loopType: THING.LoopType.Repeat,
	 * 	duration: 10 * 1000
	 * });
	 * object.rotateTo({
	 * 	to: [0, 360, 0],
	 * 	duration: 10 * 1000
	 * });
	 * @public
	 */
	rotateTo() { return this.lerp.rotateTo.apply(this.lerp, arguments); }
	// 沿特定轴旋转 可插值
	rotateOnAxisByLerp(axis, angle, options = {}) {
		const currentRotation = this.localQuaternion;
		const rotation = MathUtils.getQuatFromAxisRadian(axis, angle * Math.PI / 180);
		const newRotation = [];
		MathUtils.quat.multiply(newRotation, currentRotation, rotation);
		// 旋转一定是绕自身轴
		options.space = SpaceType.Self;
		return this.rotateTo(newRotation, options)
	}
	// 沿x轴旋转 可插值
	rotateXByLerp(angle, options) {
		let localXAxis = [this.matrix[0], this.matrix[1], this.matrix[2]];
		MathUtils.normalizeVector(localXAxis);
		return this.rotateOnAxisByLerp(localXAxis, angle, options);
	}
	// 沿y轴旋转 可插值
	rotateYByLerp(angle, options) {
		let localYAxis = [this.matrix[4], this.matrix[5], this.matrix[6]];
		localYAxis = MathUtils.normalizeVector(localYAxis);
		return this.rotateOnAxisByLerp(localYAxis, angle, options);
	}
	// 沿z轴旋转 可插值
	rotateZByLerp(angle, options) {
		let localZAxis = [this.matrix[8], this.matrix[9], this.matrix[10]];
		localZAxis = MathUtils.normalizeVector(localZAxis);
		return this.rotateOnAxisByLerp(localZAxis, angle, options);
	}
	/**
	 * Rotate object in duration (async).
	 * @example
	 * await object.rotateToAsync([0, 360, 0], {
	 * 	duration: 10 * 1000
	 * });
	 * @private
	 */
	rotateToAsync() { return this.lerp.rotateToAsync.apply(this.lerp, arguments); }

	/**
	 * 停止淡入淡出动作。
	 * @example
	 * object.stopFading();
	 * @public
	 */
	stopFading() { return this.lerp.stopFading.apply(this.lerp, arguments); }

	/**
	 * 暂停淡入淡出动作。
	 * @example
	 * object.pauseFading();
	 * @public
	 */
	pauseFading() { return this.lerp.pauseFading.apply(this.lerp, arguments); }

	/**
	 * 恢复淡入淡出动作。
	 * @example
	 * object.resumeFading();
	 * @public
	 */
	resumeFading() { return this.lerp.resumeFading.apply(this.lerp, arguments); }

	/**
	 * 对象渐现
	 * @param {LerpArgs} param The parameters. 插值参数
	 * @example
	 * object.fadeIn({
	 *   duration: 2000,
	 *   onComplete: () => {
	 *     console.log('Fade in completed');
	 *   }
	 * });
	 * @public
	 */
	fadeIn() { return this.lerp.fadeIn.apply(this.lerp, arguments); }

	/**
	 * 对象渐隐
	 * @param {LerpArgs} param The parameters. 插值参数
	 * @example
	 * object.fadeOut({
	 *   duration: 2000,
	 *   onComplete: () => {
	 *     console.log('Fade out completed');
	 *   }
	 * });
	 * @public
	 */
	fadeOut() { return this.lerp.fadeOut.apply(this.lerp, arguments); }

	/**
	 * 异步将对象渐现。
	 * @param {LerpArgs} param The parameters. 插值参数
	 * @returns {Promise} 一个在渐现完成时解析的承诺。
	 * @example
	 * await object.fadeInAsync({
	 *   duration: 2000
	 * });
	 * @public
	 */
	fadeInAsync() { return this.lerp.fadeInAsync.apply(this.lerp, arguments); }

	/**
	 * 异步将对象渐隐。
	 * @param {LerpArgs} param The parameters. 插值参数
	 * @returns {Promise} 一个在渐隐完成时解析的承诺。
	 * @example
	 * await object.fadeOutAsync({
	 *   duration: 2000
	 * });
	 * @public
	 */
	fadeOutAsync() { return this.lerp.fadeOutAsync.apply(this.lerp, arguments); }

	/**
	 * 停止飞行
	 * @example
	 * 	object.stopFlying();
	 * @public
	 */
	stopFlying() { return this.lerp.stopFlying.apply(this.lerp, arguments); }

	/**
	 * 暂停飞行
	 * @example
	 * 	object.pauseFlying();
	 * @public
	 */
	pauseFlying() { return this.lerp.pauseFlying.apply(this.lerp, arguments); }

	/**
	 * 恢复飞行
	 * @example
	 * 	object.resumeFlying();
	 * @public
	 */
	resumeFlying() { return this.lerp.resumeFlying.apply(this.lerp, arguments); }

	/**
	 * @typedef {object} LerpFlyToArgs 飞行的插值参数
	 * @property {Array<number>} [position] 要飞到的位置
	 * @property {Array<number>|THING.BaseObject} target
	 * </br>飞到目标位置之后物体的target,即物体target和position的差代表对象的方向,
	 * </br>如果传入一个BaseObject,target就是目标对象的位置,如果没有传position,会根据距离目标对象的距离,水平角,垂直角来计算position
	 * </br>参见 https://en.wikipedia.org/wiki/Spherical_coordinate_system
	 * @property {Array<number>} [up] 飞到目标点后对象的up朝向
	 * @property {number} [duration] 飞行时间
	 * @property {number} [delayTime] 延迟时间
	 * @property {number} [distance] 如果目标点是一个Object3D,且没传position,代表距离目标对象的距离,用于计算position
	 * @property {number} [horzAngle] 如果目标点是一个Object3D,且没传position,代表与目标对象的水平夹角,用于计算position
	 * @property {number} [vertAngle] 如果目标点是一个Object3D,且没传position,代表与目标对象的垂直夹角,用于计算position
	 * @property {LerpType} [lerpType] 插值类型
	 * @property {LerpType} [positionLerpType] 飞行时positoin的插值类型
	 * @property {LerpType} [targetLerpType] 飞行时target的插值类型
	 * @property {LerpType} [upLerpType] 飞行时up的插值类型
	 * @property {Function} [onStart] 开始飞行时的回调
	 * @property {Function} [onStop] 停止飞行时的回调
	 * @property {Function} [onResume] 恢复飞行时的回调
	 * @property {Function} [onPause] 暂停飞行时的回调
	 * @property {Function} [onUpdate] 飞行时每帧回调
	 * @property {Function} [onComplete] 飞行完成时的回调
	 * @public
	 */

	/**
	 * 物体在特定时间内飞行到特定位置
	 * @param {THING.BaseObject|LerpFlyToArgs} param 可传入BaseObject或飞行参数
	 * @returns {boolean} 是否飞行成功
	 * @example
	 *  object.flyTo(baseObject);//飞到物体
	 *
	 * 	object.flyTo({
	 * 		target: otherTarget,
	 * 		horzAngle: 0,
	 * 		vertAngle: 45
	 * 	});
	 *
	 * 	object.flyTo({
	 * 		position: [0,10,0],
	 *    target:[0,0,0],
	 * 		up:[0,0,1]
	 * 	});
	 * @public
	 */
	flyTo() { return this.lerp.flyTo.apply(this.lerp, arguments); }

	/**
	 * 物体在特定时间内飞行到特定位置
	 * @param {THING.BaseObject|LerpFlyToArgs} param 可传入BaseObject或飞行参数
	 * @returns {Promise<any>} 返回一个Promise对象
	 * @example
	 * 	await object.flyToAsync({
	 * 		target: otherTarget,
	 * 		horzAngle: 0,
	 * 		vertAngle: 45
	 * 	});
	 * @private
	 */
	flyToAsync() { return this.lerp.flyToAsync.apply(this.lerp, arguments); }

	/**
	 * 根据目标对象计算最佳位置 一般用于调整摄像机最佳视角 <br>
	 * 注意参数中 times 和 duration 不起作用
	 * @param {THING.BaseObject|LerpFlyToArgs} param 可传入BaseObject或参数
	 * @returns {boolean} 是否计算成功
	 * @example
	 * app.camera.fit(otherTarget);
	 * @public
	 */
	fit() { return this.lerp.fit.apply(this.lerp, arguments); }

	/**
	 * 停止UV变化 主要用于贴图动画
	 * @param {ImageSlotType} slotType 指定纹理类型
	 * @returns {boolean} 是否停止成功
	 * @example
	 * 	object.stopUVTransform(THING.ImageSlotType.Map);
	 * @public
	 */
	stopUVTransform() { return this.lerp.stopUVTransform.apply(this.lerp, arguments); }

	/**
	 * 暂停UV变化
	 * @param {ImageSlotType} slotType 指定纹理类型
	 * @returns {boolean} 是否暂停成功
	 * @example
	 * 	object.pauseUVTransform(THING.ImageSlotType.Map);
	 * @public
	 */
	pauseUVTransform() { return this.lerp.pauseUVTransform.apply(this.lerp, arguments); }

	/**
	 * 恢复UV变化
	 * @param {ImageSlotType} slotType 指定纹理类型
	 * @returns {boolean} 是否恢复成功
	 * @example
	 * 	object.resumeUVTransform(THING.ImageSlotType.Map);
	 * @public
	 */
	resumeUVTransform() { return this.lerp.resumeUVTransform.apply(this.lerp, arguments); }

	/**
	 * 开始UV变化
	 * @param {ImageSlotType} slotType 指定纹理类型 (diffuseMap,alphaMap,emissiveMap等)
	 * @param {LerpArgs} value 插值参数 参数中需指定UV对象中要变化哪个属性
	 * </br>包含以下四种 {number[]} offset,{number[]} repeat,{number[]} center(number[]),{number} rotation
	 * @returns {boolean} 是否开始成功
	 * @example
	 * // Lerp UV offset from [0, 0] to [-1, 0] in 2 seconds by repeat mode
	 * object.uvTransformTo(THING.ImageSlotType.Map, {
	 * 	from: { offset: [0, 0] },
	 * 	to: { offset: [-1, 0] },
	 * 	duration: 2000,
	 * 	loopType: THING.LoopType.Repeat,
	 * 	times: 3
	 * });
	 * @public
	 */
	uvTransformTo() { return this.lerp.uvTransformTo.apply(this.lerp, arguments); }

	/**
	 * 开始UV变化
	 * @param {ImageSlotType} slotType 指定纹理类型 (diffuseMap,alphaMap,emissiveMap等)
	 * @param {LerpArgs} value 插值参数 参数中需指定UV对象中要变化哪个属性
	 * </br>包含以下四种 {number[]} offset,{number[]} repeat,{number[]} center(number[]),{number} rotation
	 * @returns {Promise<any>} 返回一个Promise对象
	 * @example
	 * // Lerp UV offset from [0, 0] to [-1, 0] in 2 seconds by repeat mode and wait for complete
	 * await object.uvTransformToAsync(THING.ImageSlotType.Map, {
	 * 	from: { offset: [0, 0] },
	 * 	to: { offset: [-1, 0] },
	 * 	duration: 2000,
	 * 	loopType: THING.LoopType.Repeat,
	 * 	times: 3
	 * });
	 * @private
	 */
	uvTransformToAsync() { return this.lerp.uvTransformToAsync.apply(this.lerp, arguments); }

	// #endregion

	// #region Component - transform

	/**
	 * 设置/获取 相对于父物体坐标系的位置(偏移)
	 * @type {Array<number>}
	 * @public
	 */
	get localPosition() { return this.transform.getLocalPosition(); }
	set localPosition(value) { this.transform.setLocalPosition(value); }

	/**
	 * 设置/获取 相对于父物体坐标系的旋转角度
	 * 欧拉角('XYZ'),单位为 degree(度)。
	 * @type {Array<number>}
	 * @public
	 */
	get localAngles() { return this.transform.getLocalAngles(); }
	set localAngles(value) { this.transform.setLocalAngles(value); }

	/**
	 * 等同于localAngles
	 * 欧拉角('XYZ'),单位为 degree(度)。
	 * @type {Array<number>}
	 * @public
	 */
	get localRotation() { return this.transform.getLocalAngles(); }
	set localRotation(value) { this.transform.setLocalAngles(value); }

	/**
	 * 设置/获取 相对于父物体坐标系的四元数
	 * @type {Array<number>}
	 * @public
	 */
	get localQuaternion() { return this.transform.getLocalQuaternion(); }
	set localQuaternion(value) { this.transform.setLocalQuaternion(value); }

	/**
	 * 设置/获取 父物体坐标系下的缩放
	 * @type {Array<number>}
	 * @public
	 */
	get localScale() { return this.transform.getLocalScale(); }
	set localScale(value) { this.transform.setLocalScale(value); }

	/**
	 * 设置/获取 世界坐标系下的位置
	 * @type {Array<number>}
	 * @public
	 */
	get position() { return this.transform.getWorldPosition(); }
	set position(value) { this.transform.setWorldPosition(value); }

	/**
	 * 设置/获取 世界坐标系下的旋转角度
	 * 欧拉角('XYZ'),单位为 degree(度)。
	 * @type {Array<number>}
	 * @public
	 */
	get angles() { return this.transform.getWorldAngles(); }
	set angles(value) { this.transform.setWorldAngles(value); }

	/**
	 * 等同于angles
	 * 欧拉角('XYZ'),单位为 degree(度)。
	 * @type {Array<number>}
	 * @public
	 */
	get rotation() { return this.transform.getWorldAngles(); }
	set rotation(value) { this.transform.setWorldAngles(value); }

	/**
	 * 设置/获取 世界坐标系下的四元数
	 * @type {Array<number>}
	 * @public
	 */
	get quaternion() { return this.transform.getWorldQuaternion(); }
	set quaternion(value) { this.transform.setWorldQuaternion(value); }

	/**
	 * 设置/获取 世界坐标系下的缩放
	 * @type {Array<number>}
	 * @public
	 */
	get scale() { return this.transform.getWorldScale(); }
	set scale(value) { this.transform.setWorldScale(value); }

	/**
	 * 设置/获取 自身的矩阵
	 * @type {Array<number>}
	 * @private
	 */
	get matrix() { return this.transform.getMatrix(); }
	set matrix(value) { this.transform.setMatrix(value); }

	/**
	 * 设置/获取 在世界坐标系下的矩阵
	 * @type {Array<number>}
	 * @private
	 */
	get matrixWorld() { return this.transform.getMatrixWorld([], true); }
	set matrixWorld(value) { this.transform.setMatrixWorld(value); }

	/**
	 * Get the inversed matrix.
	 * @type {Array<number>}
	 * @private
	 */
	get inversedMatrix() { return this.transform.inversedMatrix; }

	/**
	 * Get the inversed matrix world.
	 * @type {Array<number>}
	 * @private
	 */
	get inversedMatrixWorld() { return this.transform.inversedMatrixWorld; }

	/**
	 * Get the up direction of world space.
	 * @type {Array<number>}
	 * @private
	 */
	get up() { return this.transform.up; }

	/**
	 * Get the forward direction in world space.
	 * @type {Array<number>}
	 * @private
	 */
	get forward() { return this.transform.forward; }

	/**
	 * Get the cross direction in world space.
	 * @type {Array<number>}
	 * @private
	 */
	get cross() { return this.transform.cross; }

	/**
	 * Get/Set keep size distance, null indicates use the current camera position to calculate distance.
	 * @type {number}
	 * @private
	 */
	get keepSizeDistance() { return this.transform.keepSizeDistance; }
	set keepSizeDistance(value) { this.transform.keepSizeDistance = value; }

	/**
	 * Gets/sets the distance limit to keep the size, with null indicates no limit
	 * @type {number}
	 * @private
	 */
	get keepSizeDistanceLimited() { return this.transform.keepSizeDistanceLimited; }
	set keepSizeDistanceLimited(value) { this.transform.keepSizeDistanceLimited = value; }

	/**
	 * 获取/设置 keepSize 时是否使用 body.localScale(true)而不是 object.localScale(false,默认)。
	 * @type {boolean}
	 * @default false
	 * @example
	 * // 使用 body.localScale 来保持大小
	 * object.keepSizeUseBodyLocalScale = true;
	 * object.keepSize = true;
	 * @public
	 */
	get keepSizeUseBodyLocalScale() { return this.transform.keepSizeUseBodyLocalScale; }
	set keepSizeUseBodyLocalScale(value) { this.transform.keepSizeUseBodyLocalScale = value; }

	/**
	 * 获取/设置是否忽略父物体的变换
	 * @type {THING.Transform}
	 * @example
	 * 	object.ignoreParentTransform = THING.Transform.Rotation | THING.Transform.Scale;
	 * @public
	 */
	get ignoreParentTransform() {
		return this._ignoreParentTransform;
	}
	set ignoreParentTransform(value) {
		const modeDefault = 'Default';
		const modeR = 'EliminateParentR';
		const modeS = 'EliminateParentS';
		const modeRS = 'EliminateParentRS';

		if (value == null || value === 0) {
			this.node.setAttribute('MatrixUpdateMode', modeDefault);
		}
		else {
			const hasR = (value & Transform.Rotation) !== 0;
			const hasS = (value & Transform.Scale) !== 0;

			if (hasR && hasS) {
				this.node.setAttribute('MatrixUpdateMode', modeRS);
			}
			else if (hasR) {
				this.node.setAttribute('MatrixUpdateMode', modeR);
			}
			else if (hasS) {
				this.node.setAttribute('MatrixUpdateMode', modeS);
			}
			else {
				this.node.setAttribute('MatrixUpdateMode', modeDefault);
			}
		}

		this.updateWorldMatrix();
		this._ignoreParentTransform = value;
	}

	/**
	 * Get local position.
	 * @param {Array<number>} target? The target to save result.
	 * @returns {Array<number>} The reference of target.
	 * @private
	 */
	getLocalPosition() { return this.transform.getLocalPosition.apply(this.transform, arguments); }

	/**
	 * Get quaternion of the inertial space.
	 * @param {Array<number>} target? The target to save result.
	 * @returns {Array<number>} The reference of target.
	 * @private
	 */
	getLocalQuaternion() { return this.transform.getLocalQuaternion.apply(this.transform, arguments); }

	/**
	 * Get scale of the self coordinate system.
	 * @param {Array<number>} target? The target to save result.
	 * @returns {Array<number>} The reference of target.
	 * @private
	 */
	getLocalScale() { return this.transform.getLocalScale.apply(this.transform, arguments); }

	/**
	 * Get world position.
	 * @param {Array<number>} target? The target to save result.
	 * @returns {Array<number>} The reference of target.
	 * @private
	 */
	getWorldPosition() { return this.transform.getWorldPosition.apply(this.transform, arguments); }

	/**
	 * Get quaternion of the world space.
	 * @param {Array<number>} target? The target to save result.
	 * @returns {Array<number>} The reference of target.
	 * @private
	 */
	getWorldQuaternion() { return this.transform.getWorldQuaternion.apply(this.transform, arguments); }

	/**
	 * Get scale of the world coordinate system.
	 * @param {Array<number>} target? The target to save result.
	 * @returns {Array<number>} The reference of target.
	 * @private
	 */
	getWorldScale() { return this.transform.getWorldScale.apply(this.transform, arguments); }

	/**
	 * Get the forward direction in world space.
	 * @param {Array<number>} target? The target to save result.
	 * @returns {Array<number>} The reference of target.
	 * @private
	 */
	getForward() { return this.transform.getForward.apply(this.transform, arguments); }

	/**
	 * 等同于rotateOnAxis
	 * @param {Array<number>} axis  在自身坐标系下的轴向信息
	 * @param {number} angle 旋转角度
	 * @param {LerpArgs} lerpParam 插值信息
	 * @returns {boolean} 是否旋转成功
	 * @example
	 * 	let box = new THING.Box();
	 * 	box.rotate(THING.Utils.xAxis, 45);
	 * 	// @expect(THING.Math.equalsVector3([45, 0, 0], box.angles) == true);
	 * @public
	 */
	rotate() { return this.transform.rotateOnAxis.apply(this.transform, arguments); }

	/**
	 * 绕固定轴旋转特定角度
	 * @param {Array<number>} axis 在自身坐标系下的轴向信息
	 * @param {number} angle 旋转角度
	 * @param {LerpArgs} lerpParam 插值信息
	 * @returns {boolean} 是否旋转成功
	 * @example
	 * 	let box = new THING.Box();
	 * 	box.rotateOnAxis(THING.Utils.xAxis, 45);
	 * 	//@expect(THING.Math.equalsVector3([45, 0, 0], box.angles) == true);
	 * @public
	 */
	rotateOnAxis() {
		if (arguments.length === 3) {
			return this.rotateOnAxisByLerp.apply(this, arguments);
		}
		return this.transform.rotateOnAxis.apply(this.transform, arguments);
	}

	/**
	 * 绕自身坐标系下的x轴进行旋转
	 * @param {number} angle 旋转角度
	 * @param {LerpArgs} lerpParam 插值信息
	 * @returns {boolean} 是否旋转成功
	 * @public
	 */
	rotateX() {
		if (arguments.length === 2) {
			return this.rotateXByLerp.apply(this, arguments);
		}
		return this.transform.rotateX.apply(this.transform, arguments);
	}

	/**
	 * 绕自身坐标系下的y轴进行旋转
	 * @param {number} angle 旋转角度
	 * @param {LerpArgs} lerpParam 插值信息
	 * @returns {boolean} 是否旋转成功
	 * @public
	 */
	rotateY() {
		if (arguments.length === 2) {
			return this.rotateYByLerp.apply(this, arguments);
		}
		return this.transform.rotateY.apply(this.transform, arguments);
	}

	/**
	 * 绕自身坐标系下的z轴进行旋转
	 * @param {number} angle 旋转角度
	 * @param {LerpArgs} lerpParam 插值信息
	 * @returns {boolean} 是否旋转成功
	 * @public
	 */
	rotateZ() {
		if (arguments.length === 2) {
			return this.rotateZByLerp.apply(this, arguments);
		}
		return this.transform.rotateZ.apply(this.transform, arguments);
	}

	/**
	 * 沿自身坐标系指定轴进行平移
	 * @param {Array<number>} axis 在自身坐标系下的轴向信息
	 * @param {number} distance 移动距离
	 * @param {LerpArgs} lerpParam 插值信息
	 * @returns {boolean} 是否平移成功
	 * @private
	 */
	translateOnAxis() {
		if (arguments.length === 3) {
			return this.translateOnAxisByLerp.apply(this, arguments);
		}
		return this.transform.translateOnAxis.apply(this.transform, arguments);
	}

	/**
	 * 沿着自身X轴对物体做偏移
	 * @param {number} distance 偏移距离 单位米
	 * @param {LerpArgs} [lerpParam] 插值信息
	 * @returns {boolean} 是否平移成功
	 * @public
	 * @example
	 * 	object.translateX(10, {time: 1000})
	 */
	translateX() {
		if (arguments.length === 2) {
			return this.translateXByLerp.apply(this, arguments);
		}
		return this.transform.translateX.apply(this.transform, arguments);
	}

	/**
	 * 沿着自身Y轴对物体做偏移
	 * @param {number} distance 偏移距离 单位米
	 * @param {LerpArgs} [lerpParam] 插值信息
	 * @returns {boolean} 是否平移成功
	 * @public
	 * @example
	 * 	object.translateY(10, {time: 1000})
	 */
	translateY() {
		if (arguments.length === 2) {
			return this.translateYByLerp.apply(this, arguments);
		}
		return this.transform.translateY.apply(this.transform, arguments);
	}

	/**
	 * 沿着自身Z轴对物体做偏移
	 * @param {number} distance 偏移距离 单位米
	 * @param {LerpArgs} [lerpParam] 插值信息
	 * @returns {boolean} 是否平移成功
	 * @public
	 * @example
	 * 	object.translateZ(10, {time: 1000})
	 */
	translateZ() {
		if (arguments.length === 2) {
			return this.translateZByLerp.apply(this, arguments);
		}
		return this.transform.translateZ.apply(this.transform, arguments);
	}

	/**
	 * 沿三个轴向对物体做偏移
	 * @param {Array<number>} offset 三个轴向的偏移 单位米
	 * @param {LerpArgs} [lerpParam] 插值信息
	 * @returns {boolean} 是否平移成功
	 * @public
	 * @example
	 * 	object.translate([10, 10, 10], {time: 1000})
	 */
	translate() {
		if (arguments.length === 2) {
			return this.translateByLerp.apply(this, arguments);
		}
		return this.transform.translate.apply(this.transform, arguments);
	}

	/**
	 * 沿自身坐标系指定轴进行平移通过插值的方式
	 * @param {Array<number>} axis 移动轴方向
	 * @param {number} distance 移动距离
	 * @param {LerpArgs} options 插值参数
	 * @private
	 * @example
	 * app.camera.translateOnAxisByLerp([1, 0, 0], 10, {time: 1000})
	 */
	translateOnAxisByLerp(axis, distance, options) {
		const localPosition = this.localPosition.slice()

		let _axis = [0, 0, 0];
		let _vector3 = [0, 0, 0]

		MathUtils.vec3.normalize(_axis, axis);
		MathUtils.vec3.transformQuat(_vector3, _axis, this.localQuaternion);
		MathUtils.vec3.scale(_vector3, _vector3, distance);

		const newLocalPosition = MathUtils.addVector(localPosition, _vector3)

		const lerpParam = {
			from: { localPosition },
			to: {
				localPosition: newLocalPosition
			},
		}

		Object.assign(lerpParam, options);

		this.lerp.to(lerpParam, cTranslateByLerpName);
	}

	/**
	 * 沿自身坐标系x轴方向进行平移通过插值的方式
	 * @param {number} distance 移动距离
	 * @param {LerpArgs} options 插值参数
	 * @private
	 * @example
	 * app.camera.translateXByLerp(10, {time: 1000})
	 */
	translateXByLerp(distance, options) {
		this.translateOnAxisByLerp(Utils.xAxis, distance, options);
	}

	/**
	 * 沿自身坐标系y轴方向进行平移通过插值的方式
	 * @param {number} distance 移动距离
	 * @param {LerpArgs} options 插值参数
	 * @private
	 */
	translateYByLerp(distance, options) {
		this.translateOnAxisByLerp(Utils.yAxis, distance, options);
	}

	/**
	 * 沿自身坐标系z轴方向进行平移通过插值的方式
	 * @param {number} distance 移动距离
	 * @param {LerpArgs} options 插值参数
	 * @private
	 */
	translateZByLerp(distance, options) {
		this.translateOnAxisByLerp(Utils.zAxis, distance, options);
	}

	/**
	 * 沿自身坐标系平移指定位置通过插值的方式
	 * @param {Array<number>} offset 移动位置
	 * @param {LerpArgs} options 插值参数
	 * @private
	 * @example
	 * app.camera.translateByLerp([10,10,0], {time: 1000})
	 */
	translateByLerp(offset, options) {
		const position = this.position.slice()

		let newPosition = this.selfToWorld(offset, false)

		const lerpParam = {
			from: {
				position: position
			},
			to: {
				position: newPosition
			},
		}

		Object.assign(lerpParam, options);

		this.lerp.to(lerpParam, cTranslateByLerpName);
	}

	/**
	 * @public
	 * @typedef {object} LookAtArgs lookAt的参数
	 * @property {Array<number>} up lookAt时 物体的up方向
	 * @property {AxisType} [lockAxis] 锁定自身的轴向(x,y,z)
	 * @property {boolean} [always=false] 是否一直看目标 默认false 代表只看一次,如果传true,观察者或被观察者移动时也会看向目标
	 * @property {boolean} [lookOnPlane=false] 是看向目标物体还是看向目标物体的垂直平面 默认false 如果为true时,看的方向是当前位置与目标物体垂直平面的垂线方向
	 */

	/**
	 * 看对象或一个目标位置
	 * @param {Array<number>|THING.Object3D} target 要看的对象或目标位置
	 * @param {LookAtArgs} param 参数列表
	 * @returns {boolean} 是否看成功
	 * @public
	 */
	lookAt() { return this.transform.lookAt.apply(this.transform, arguments); }

	/**
	 * Convert local position to self position.
	 * @param {Array<number>} position The local position.
	 * @param {boolean} [ignoreScale=false] True indicates ignore scale factor.
	 * @returns {Array<number>}
	 * @private
	 */
	localToSelf() { return this.transform.localToSelf.apply(this.transform, arguments); }

	/**
	 * Convert self position to local position.
	 * @param {Array<number>} position The self position.
	 * @param {boolean} [ignoreScale=false] True indicates ignore scale factor.
	 * @returns {Array<number>}
	 * @private
	 */
	selfToLocal() { return this.transform.selfToLocal.apply(this.transform, arguments); }

	/**
	 * 世界坐标转自身坐标
	 * @param {Array<number>} position 世界坐标
	 * @param {boolean} [ignoreScale=false] 转换时是否忽略缩放 默认不忽略
	 * @returns {Array<number>}
	 * @example
	 * let worldPos = [5, 10, 0];
	 * let obj = new THING.Object3D({position: worldPos});
	 * let selfPos = obj.worldToSelf(worldPos);
	 * // print [0, 0, 0]
	 * console.log(selfPos);
	 * @public
	 */
	worldToSelf() { return this.transform.worldToSelf.apply(this.transform, arguments); }

	/**
	 * 自身坐标转世界坐标
	 * @param {Array<number>} position 自身坐标系下的坐标
	 * @param {boolean} [ignoreScale=false] 转换时是否忽略缩放 默认不忽略
	 * @returns {Array<number>} 返回世界坐标
	 * @example
	 * let obj = new THING.Object3D({ position: [0, 15, 0] });
	 * let worldPos = obj.selfToWorld([0, -15, 0]);
	 * // print [0, 0, 0]
	 * console.log(worldPos);
	 * @public
	 */
	selfToWorld() { return this.transform.selfToWorld.apply(this.transform, arguments); }

	/**
	 * 相对于父物体的坐标转为世界坐标
	 * @param {Array<number>} position 相对于父物体的坐标
	 * @param {boolean} [ignoreScale=false] 转换时是否忽略缩放 默认不忽略
	 * @returns {Array<number>} 返回世界坐标
	 * @example
	 * let parentObj = new THING.Object3D({
	 * 	position: [0, 5, 0]
	 * });
	 *
	 * let childObj = new THING.Object3D({
	 * 	localPosition: [0, 10, 0],
	 *  parent: parentObj
	 * });
	 *
	 * let worldPos = childObj.localToWorld([0, -5, 0]);
	 *
	 * // print [0, 0, 0]
	 * console.log(worldPos);
	 * @public
	 */
	localToWorld() { return this.transform.localToWorld.apply(this.transform, arguments); }

	/**
	 * 世界坐标转为相对于父物体的坐标
	 * @param {Array<number>} position 世界坐标
	 * @param {boolean} [ignoreScale=false] 转换时是否忽略缩放 默认不忽略
	 * @returns {Array<number>} 返回相对于父物体的坐标
	 * @example
	 * let parentObj = new THING.Object3D({
	 * 	position: [0, 5, 0]
	 * });
	 *
	 * let childObj = new THING.Object3D({
	 * 	localPosition: [0, 10, 0],
	 * 	parent: parentObj
	 * });
	 *
	 * let localPosition = childObj.worldToLocal([0, 5, 0]);
	 *
	 * // print [0, 0, 0]
	 * console.log(localPosition);
	 * @public
	 */
	worldToLocal() { return this.transform.worldToLocal.apply(this.transform, arguments); }

	/**
	 * Get self quaternion from target position.
	 * @param {Array<number>} target The target position.
	 * @returns {Array<number>}
	 * @private
	 */
	getSelfQuaternionFromTarget() { return this.transform.getSelfQuaternionFromTarget.apply(this.transform, arguments); }

	/**
	 * Get self angles from target position.
	 * @param {Array<number>} target The target position.
	 * @returns {Array<number>}
	 * @private
	 */
	getSelfAnglesFromTarget() { return this.transform.getSelfAnglesFromTarget.apply(this.transform, arguments); }

	/**
	 * Get world quaternion from target position.
	 * @param {Array<number>} target The target position.
	 * @returns {Array<number>}
	 * @private
	 */
	getWorldQuaternionFromTarget() { return this.transform.getWorldQuaternionFromTarget.apply(this.transform, arguments); }

	/**
	 * Get world angles from target position.
	 * @param {Array<number>} target The target position.
	 * @returns {Array<number>}
	 * @private
	 */
	getWorldAnglesFromTarget() { return this.transform.getWorldAnglesFromTarget.apply(this.transform, arguments); }

	/**
	 * Get the world position from angles in self space.
	 * @param {Array<number>} value The angles in self space.
	 * @param {number} distance The distance in meter(s).
	 * @returns {Array<number>}
	 * @private
	 */
	getWorldPositionFromSelfAngles() { return this.transform.getWorldPositionFromSelfAngles.apply(this.transform, arguments); }

	/**
	 * Get matrix world.
	 * @returns {Array<number>}
	 * @private
	 */
	getMatrixWorld() { return this.transform.getMatrixWorld.apply(this.transform, arguments); }

	/**
	 * Set matrix world.
	 * @param {Array<number>} value The matrix value.
	 * @param {boolean} [updateMatrix=true] True indicates update all parents matrix world.
	 * @private
	 */
	setMatrixWorld() { return this.transform.setMatrixWorld.apply(this.transform, arguments); }

	/**
	 * Get the local matrix of target object, it would return the relative matrix of target space world.
	 * @param {THING.Object3D} target The target object.
	 * @param {boolean} [updateMatrix=false] True indicates to update matrix world forcely.
	 * @returns {Array<number>}
	 * @example
	 * 	let box1 = new THING.Box(10, 5, 2.5);
	 * 	let box2 = new THING.Box(10, 5, 2.5, { position: [0, 10, 0] });
	 * 	THING.Math.mat4.equals(box1.matrixWorld, THING.Math.mat4.mul([], box2.matrixWorld, box1.getLocalMatrix(box2)));
	 * @private
	 */
	getLocalMatrix() { return this.transform.getLocalMatrix.apply(this.transform, arguments); }

	/**
	 * Update self and all parents and children matrix world.
	 * @param {boolean} updateParents True indicates update all parents.
	 * @param {boolean} updateChildren True indicates update all children.
	 * @private
	 */
	updateWorldMatrix() { return this.transform.updateWorldMatrix.apply(this.transform, arguments); }

	/**
	 * Update self and all children matrix world.
	 * @private
	 */
	updateMatrixWorld() { return this.transform.updateMatrixWorld.apply(this.transform, arguments); }

	/**
	 * 绑定子节点,绑定后将自动根据子节点更新世界坐标。
	 * 绑定后位置、角度和缩放会锁定。
	 * @param {THING.Object3D} target 目标对象。
	 * @param {string} subNodeName 子节点名称。
	 * @returns {boolean} 是否绑定成功。
	 * @example
	 * 	let box = new THING.Box();
	 * 	box.bindSubNode(car, 'chair');
	 * @public
	 */
	bindSubNode() { return this.transform.bindSubNode.apply(this.transform, arguments); }

	/**
	 * 解绑子节点。
	 * @example
	 * 	let box = new THING.Box();
	 * 	box.bindSubNode(car, 'chair');
	 * 	box.unbindSubNode();
	 * @public
	 */
	unbindSubNode() { this.transform.unbindSubNode.apply(this.transform, arguments); }

	// #endregion

	// #endregion

	get isInitializing() {
		return this._flags.has(Flag.Initializing);
	}

	get Initialized() {
		return this._flags.has(Flag.Initialized);
	}

	get isObject3D() {
		return true;
	}

	get isBaseObject3D() {
		console.warn('Plase use isObject3D instead of isBaseObject3D');
		return true;
	}

	get isSceneRoot() {
		let _private = this[__.private];
		return _private.resource.isSceneRoot;
	}

	get isSceneNode() {
		let _private = this[__.private];
		return _private.resource.isSceneNode;
	}

	/**
	 * 修正 keepSize 计算得到的缩放值的钩子,子类可根据自身需求进行重写。
	 * 例如 `Marker` 在开启 `autoFitBodyScale` 模式时,可以在此对缩放做额外比例修复。
	 * @param {Array<number>} scale 由 `TransformComponent` 计算出的缩放值 `[sx, sy, sz]`。
	 * @returns {Array<number>} 修正后的缩放值,默认直接返回原值。
	 * @protected
	 */
	onFixKeepSizeScale(scale) {
		return scale;
	}

}

export { Object3D }