import { Utils } from '../common/Utils';
import { Object3D } from './Object3D';
import { ModelAnimationComponent } from '../components/ModelAnimationComponent';
const __ = {
private: Symbol('private'),
}
const _internalAnimationComponentName = '_internalAnimation';
const registerComponentParam = { autoRegister: false, isResident: true };
/**
* @class BaseEntity
* The base entity object.
* @memberof THING
* @extends THING.Object3D
*/
class BaseEntity extends Object3D {
/**
* The base entity object that load 3D model resource in scene.
* @param {Object} param The initial parameters.
*/
constructor(param) {
super(param);
this._animation = this._animation || null;
}
// #region Private
_playDelayAnimation() {
// Make sure object is alive
if (this.destroyed) {
return;
}
// Get the renderable node
let node = this.bodyNode;
// If entity has animation then complete the delayed commands of it
if (node.hasAnimation()) {
this.animation.runDelayedCommands();
}
// If entity do not have any animations, then use the dummy animation component
else {
if (this._animation) {
this.removeComponent('animation');
}
this._animation = ModelAnimationComponent.cDummyAnimationInterface;
}
}
// #endregion
// #region Object3D overrides
onAfterSetup(param) {
if (param.extras && param.extras.animInfo) {
const animInfo = param.extras.animInfo;
this.playAnimation(animInfo);
}
}
onLoadComplete(options) {
super.onLoadComplete(options);
this._playDelayAnimation();
}
unloadResource(recursive = true) {
this.clearInternalAnimation();
return super.unloadResource(recursive);
}
clearInternalAnimation() {
if (this._animation) {
this._animation.stopAllAnimations();
this._animation = null;
this.removeComponent(_internalAnimationComponentName);
}
}
// #endregion
// #region Internal components attributes and functions
// #region Component - animation
get animation() {
if (!this._animation) {
this._animation = new ModelAnimationComponent();
this.addComponent(this._animation, _internalAnimationComponentName, registerComponentParam);
}
return this._animation;
}
/**
* Get all animations info.
* @type {Array<AnimationResult>}
* @example
* var UinoSpaceman = new THING.Entity({url:'.assets/models/UinoSpaceman/UinoSpaceman.gltf'});
* UinoSpaceman.waitForComplete().then(() => {
* let animations = UinoSpaceman.animations;
* let ret = animations[0].name == 'Walk';
* // @expect(ret == true);
* });
* @public
*/
get animations() { return this.animation.animations; }
/**
* Get the animation names.
* @type {Array<String>}
* @example
* var UinoSpaceman = new THING.Entity({url:'.assets/models/UinoSpaceman/UinoSpaceman.gltf'});
* UinoSpaceman.waitForComplete().then(() => {
* let animations = UinoSpaceman.animationNames;
* let ret = animations[0] == 'Walk';
* // @expect(ret == true);
* });
* @public
*/
get animationNames() { return this.animation.animationNames; }
/**
* @typedef {Object} PlayAnimationArgs
* @property {String} name The animation name.
* @property {Number} times? The loop times.
* @property {LoopType} loopType? The loop type.
* @property {Number} [speed=1] The playing speed.
* @property {Boolean} [reverse=false] True indicates to play in reverse mode.
* @property {Function} [onComplete=null] The callback function to receive complete event.
* @public
*/
/**
* Play animation, would stop all other animations.
* @param {PlayAnimationArgs} param The parameters.
* @example
* var UinoSpaceman = new THING.Entity({url:'.assets/models/UinoSpaceman/UinoSpaceman.gltf'});
* UinoSpaceman.waitForComplete().then(() => {
* UinoSpaceman.playAnimation({ name: 'Walk', loopType: "Repeat" });
* let ret = UinoSpaceman.isAnimationPlaying('Walk');
* // @expect(ret == true);
* });
* @public
*/
playAnimation() { return this.animation.playAnimation.apply(this.animation, arguments); }
/**
* Play animation in async mode, would stop all other animations.
* @param {PlayAnimationArgs} param The parameters.
* @returns {Promise<any>}
* @private
*/
playAnimationAsync() { return this.animation.playAnimationAsync.apply(this.animation, arguments); }
/**
* Blends animation, would not stop all other animations.
* @param {PlayAnimationArgs} param The parameters.
* @public
*/
blendAnimation() { return this.animation.blendAnimation.apply(this.animation, arguments); }
/**
* Blends animation in async mode, would not stop all other animations.
* @param {PlayAnimationArgs} param The parameters.
* @returns {Promise<any>}
* @private
*/
blendAnimationAsync() { return this.animation.blendAnimationAsync.apply(this.animation, arguments); }
/**
* Check whether has animation by name.
* @param {String} name The animation name
* @returns {Boolean}
* @example
* var UinoSpaceman = new THING.Entity({url:'.assets/models/UinoSpaceman/UinoSpaceman.gltf'});
* UinoSpaceman.waitForComplete().then(() => {
* let ret = UinoSpaceman.hasAnimation('Walk');
* // @expect(ret == true);
* });
* @public
*/
hasAnimation() { return this.animation.hasAnimation.apply(this.animation, arguments); }
/**
* Check whether animation is playing.
* @param {String} name The animation name
* @returns {Boolean}
* @example
* var UinoSpaceman = new THING.Entity({url:'.assets/models/UinoSpaceman/UinoSpaceman.gltf'});
* UinoSpaceman.waitForComplete().then(() => {
* UinoSpaceman.playAnimation({ name: 'Walk', loopType: "Repeat" });
* let ret = UinoSpaceman.isAnimationPlaying('Walk');
* // @expect(ret == true);
* });
* @public
*/
isAnimationPlaying() { return this.animation.isAnimationPlaying.apply(this.animation, arguments); }
/**
* Pause animation(s).
* @param {String|Array<String>} name The animation name or name list.
* @example
* var UinoSpaceman = new THING.Entity({url:'.assets/models/UinoSpaceman/UinoSpaceman.gltf'});
* UinoSpaceman.waitForComplete().then(() => {
* UinoSpaceman.playAnimation({ name: 'Walk', loopType: "Repeat" });
* let ret1 = UinoSpaceman.isAnimationPlaying('Walk');
* UinoSpaceman.pauseAnimation('Walk');
* let ret2 = UinoSpaceman.getAnimationState() == 'Paused';
* // @expect(ret1 == true && ret2 == true);
* });
* @public
*/
pauseAnimation() { return this.animation.pauseAnimation.apply(this.animation, arguments); }
/**
* Pause all animations.
* @example
* var UinoSpaceman = new THING.Entity({url:'.assets/models/UinoSpaceman/UinoSpaceman.gltf'});
* UinoSpaceman.waitForComplete().then(() => {
* UinoSpaceman.playAnimation({ name: 'Walk', loopType: "Repeat" });
* let ret1 = UinoSpaceman.isAnimationPlaying('Walk');
* UinoSpaceman.pauseAllAnimations();
* let ret2 = UinoSpaceman.getAnimationState() == 'Paused';
* // @expect(ret1 == true && ret2 == true);
* });
* @public
*/
pauseAllAnimations() { return this.animation.pauseAllAnimations.apply(this.animation, arguments); }
/**
* Resume animation(s).
* @param {String|Array<String>} name The animation name or name list.
* @example
* var UinoSpaceman = new THING.Entity({url:'.assets/models/UinoSpaceman/UinoSpaceman.gltf'});
* UinoSpaceman.waitForComplete().then(() => {
* UinoSpaceman.playAnimation({ name: 'Walk', loopType: "Repeat" });
* let ret1 = UinoSpaceman.isAnimationPlaying('Walk');
* UinoSpaceman.pauseAnimation('Walk');
* let ret2 = UinoSpaceman.getAnimationState() == 'Paused';
* UinoSpaceman.resumeAnimation('Walk');
* let ret3 = UinoSpaceman.isAnimationPlaying('Walk');
* // @expect(ret1 == true && ret2 == true && ret3 == true);
* });
* @public
*/
resumeAnimation() { return this.animation.resumeAnimation.apply(this.animation, arguments); }
/**
* Resume all animations.
* @example
* var UinoSpaceman = new THING.Entity({url:'.assets/models/UinoSpaceman/UinoSpaceman.gltf'});
* UinoSpaceman.waitForComplete().then(() => {
* UinoSpaceman.playAnimation({ name: 'Walk', loopType: "Repeat" });
* let ret1 = UinoSpaceman.isAnimationPlaying('Walk');
* UinoSpaceman.pauseAnimation('Walk');
* let ret2 = UinoSpaceman.getAnimationState() == 'Paused';
* UinoSpaceman.resumeAllAnimations();
* let ret3 = UinoSpaceman.isAnimationPlaying('Walk');
* // @expect(ret1 == true && ret2 == true && ret3 == true);
* });
* @public
*/
resumeAllAnimations() { return this.animation.resumeAllAnimations.apply(this.animation, arguments); }
/**
* Stop animation.
* @param {String} name The animation name.
* @example
* var UinoSpaceman = new THING.Entity({url:'.assets/models/UinoSpaceman/UinoSpaceman.gltf'});
* UinoSpaceman.waitForComplete().then(() => {
* UinoSpaceman.playAnimation({ name: 'Walk', loopType: "Repeat" });
* let ret1 = UinoSpaceman.isAnimationPlaying('Walk');
* UinoSpaceman.stopAnimation('Walk');
* let ret2 = UinoSpaceman.getAnimationState() == 'Stopped';
* // @expect(ret1 == true && ret2 == true);
* });
* @public
*/
stopAnimation() { return this.animation.stopAnimation.apply(this.animation, arguments); }
/**
* Stop all animations.
* @example
* var UinoSpaceman = new THING.Entity({url:'.assets/models/UinoSpaceman/UinoSpaceman.gltf'});
* UinoSpaceman.waitForComplete().then(() => {
* UinoSpaceman.playAnimation({ name: 'Walk', loopType: "Repeat" });
* let ret1 = UinoSpaceman.isAnimationPlaying('Walk');
* UinoSpaceman.stopAllAnimations();
* let ret2 = UinoSpaceman.getAnimationState() == 'Stopped';
* // @expect(ret1 == true && ret2 == true);
* });
* @public
*/
stopAllAnimations() { return this.animation.stopAllAnimations.apply(this.animation, arguments); }
/**
* @typedef {Object} AnimationResult
* @property {String} name The name.
* @property {Number} duration The duration in seconds.
* @property {Number} speed The playing speed.
* @property {PlayStateType} state The state type.
* @public
*/
/**
* Get animation info.
* @param {String} name The animation name.
* @returns {AnimationResult}
* @example
* var UinoSpaceman = new THING.Entity({url:'.assets/models/UinoSpaceman/UinoSpaceman.gltf'});
* UinoSpaceman.waitForComplete().then(() => {
* let animation = UinoSpaceman.getAnimation('Walk');
* let ret = animation.name == 'Walk';
* // @expect(ret == true);
* });
* @public
*/
getAnimation() { return this.animation.getAnimation.apply(this.animation, arguments); }
/**
* Get animation state.
* @param {String} name The animation name.
* @returns {PlayStateType}
* @example
* var UinoSpaceman = new THING.Entity({url:'.assets/models/UinoSpaceman/UinoSpaceman.gltf'});
* UinoSpaceman.waitForComplete().then(() => {
* UinoSpaceman.playAnimation({ name: 'Walk', loopType: "Repeat" });
* let state = UinoSpaceman.getAnimationState('Walk');
* let ret = state == 'Playing';
* // @expect(ret == true);
* });
* @public
*/
getAnimationState() { return this.animation.getAnimationState.apply(this.animation, arguments); }
/**
* Get playing animations.
* @returns {Array<AnimationResult>}
* @example
* var UinoSpaceman = new THING.Entity({url:'.assets/models/UinoSpaceman/UinoSpaceman.gltf'});
* UinoSpaceman.waitForComplete().then(() => {
* UinoSpaceman.playAnimation({ name: 'Walk', loopType: "Repeat" });
* let animations = UinoSpaceman.getPlayingAnimations();
* let ret = animations[0].state == 'Playing';
* // @expect(ret == true);
* });
* @public
*/
getPlayingAnimations() { return this.animation.getPlayingAnimations.apply(this.animation, arguments); }
/**
* Get animation direction type.
* @param {String} name The animation name.
* @returns {AnimationDirectionType}
* @public
*/
getAnimationDirectionType() { return this.animation.getAnimationDirectionType.apply(this.animation, arguments); }
/**
* Set animation direction type.
* @param {String} name The animation name.
* @param {AnimationDirectionType} value The direction type.
* @returns {Boolean}
* @public
*/
setAnimationDirectionType() { return this.animation.setAnimationDirectionType.apply(this.animation, arguments); }
/**
* Get animation speed.
* @param {String} name The animation name.
* @returns {Number}
* @example
* var UinoSpaceman = new THING.Entity({url:'.assets/models/UinoSpaceman/UinoSpaceman.gltf'});
* UinoSpaceman.waitForComplete().then(() => {
* UinoSpaceman.playAnimation({ name: 'Walk', loopType: "Repeat", speed: 10 });
* let speed = UinoSpaceman.getAnimationSpeed('Walk');
* let ret = speed == 10;
* // @expect(ret == true);
* });
* @public
*/
getAnimationSpeed() { return this.animation.getAnimationSpeed.apply(this.animation, arguments); }
/**
* Set animation speed.
* @param {String} name The animation name.
* @param {Number} value The speed.
* @returns {Boolean}
* @example
* var UinoSpaceman = new THING.Entity({url:'.assets/models/UinoSpaceman/UinoSpaceman.gltf'});
* UinoSpaceman.waitForComplete().then(() => {
* UinoSpaceman.playAnimation({ name: 'Walk', loopType: "Repeat", speed: 10 });
* UinoSpaceman.setAnimationSpeed('Walk', 20)
* let speed = UinoSpaceman.getAnimationSpeed('Walk');
* let ret = speed == 20;
* // @expect(ret == true);
* });
* @public
*/
setAnimationSpeed() { return this.animation.setAnimationSpeed.apply(this.animation, arguments); }
// #endregion
/**
* Promote node as child object.
* @param {String} name The node name.
* @param {THING.Object3D} parent The parent object, if it's null then indicates use current object as parent.
* @returns {THING.BaseEntity}
* @example
* var UinoSpaceman = new THING.Entity({url:'.assets/models/UinoSpaceman/UinoSpaceman.gltf'});
* UinoSpaceman.waitForComplete().then(() => {
* let obj = UinoSpaceman.promoteNode('pasted__head');
* let ret1 = obj instanceof THING.BaseEntity;
* let ret2 = obj.name == 'pasted__head';
* // @expect(ret1 == true && ret2 == true);
* });
*/
promoteNode(name, parent) {
if (!name) {
return null;
}
// We can not promote node when it's in instanced drawing mode
if (this.isInstancedDrawing) {
this.makeInstancedDrawing(false);
}
// 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');
// Promote node in body object
let node = this.body.promoteNode(name, parent.node);
if (!node) {
Utils.error(`Promote '${name}' node failed, due to node is not exist`);
return null;
}
// Create object
let object = new BaseEntity({
name,
renderableNode: node,
parent,
});
return object;
}
/**
* Check whether it's BaseEntity type or inherit from it.
* @type {Boolean}
* @example
* var UinoSpaceman = new THING.Entity({url:'.assets/models/UinoSpaceman/UinoSpaceman.gltf'});
* let ret = UinoSpaceman.isBaseEntity;
* // @expect(ret == true);
*/
get isBaseEntity() {
return true;
}
}
export { BaseEntity }