import Class from './Class';
import EventMixin from './EventMixin';
import Matrix4 from '../math/Matrix4';
import Matrix4Notifier from '../math/Matrix4Notifier';
import Vector3 from '../math/Vector3';
import Vector3Notifier from '../math/Vector3Notifier';
import EulerNotifier from '../math/EulerNotifier';
import QuaternionNotifier from '../math/QuaternionNotifier';
import math from '../math/math';
import log from '../utils/log';
const defaultUp = new Vector3(0, 1, 0);
const tempMatrix4 = new Matrix4();
const TRAVERSE_STOP_NONE = 0;
const TRAVERSE_STOP_CHILDREN = 1;
const TRAVERSE_STOP_ALL = 2;
/**
* 节点,3D场景中的元素,是大部分类的基类
* @class
* @mixes EventMixin
* @example
* const node = new Hilo3d.Node({
* name:'test',
* x:100,
* rotationX:30,
* onUpdate(){
* this.rotationY ++;
* }
* });
* node.scaleX = 0.3;
* stage.addChild(node);
*/
const Node = Class.create(/** @lends Node.prototype */ {
Statics: {
/**
* traverse callback 返回值,执行后不暂停 traverse
* @memberOf Node
* @type {number}
*/
TRAVERSE_STOP_NONE,
/**
* traverse callback 返回值,执行后暂停子元素 traverse
* @memberOf Node
* @type {number}
*/
TRAVERSE_STOP_CHILDREN,
/**
* traverse callback 返回值,执行后暂停所有 traverse
* @memberOf Node
* @type {number}
*/
TRAVERSE_STOP_ALL
},
Mixes: EventMixin,
/**
* @default true
* @type {boolean}
*/
isNode: true,
/**
* @default Node
* @type {string}
*/
className: 'Node',
/**
* Node 的名字,可以通过 getChildByName 查找
* @type {string}
*/
name: '',
/**
* 动画
* @type {Animation}
* @default null
*/
anim: null,
/**
* animation 查找 id
* @type {String}
* @default ''
*/
animationId: '',
/**
* 骨骼名称
* @type {String}
* @default ''
*/
jointName: '',
/**
* 是否自动更新世界矩阵
* @default true
* @type {boolean}
*/
autoUpdateWorldMatrix: true,
/**
* 是否自动更新子元素世界矩阵
* @default true
* @type {boolean}
*/
autoUpdateChildWorldMatrix: true,
/**
* 父节点
* @default null
* @type {Node}
*/
parent: null,
_quatDirty: false,
_matrixDirty: false,
/**
* 每次更新的时候是否调用子节点的 onUpdate 方法
* @default true
* @type {boolean}
*/
needCallChildUpdate: true,
/**
* 节点是否显示
* @default true
* @type {boolean}
*/
visible: true,
/**
* 可视对象是否接受交互事件。默认为接受交互事件,即true。
* @default true
* @type {Boolean}
*/
pointerEnabled: true,
/**
* 子元素是否接受交互事件。
* @default true
* @type {Boolean}
*/
pointerChildren: true,
/**
* 是否用鼠标指针
* @default false
* @type {Boolean}
*/
useHandCursor: false,
/**
* 用户数据
* @default null
* @type {any}
*/
userData: null,
/**
* update 回调
* @type {Function}
* @default null
*/
onUpdate: null,
/**
* 是否强制使用父元素 worldMatrix,供高级开发者使用
* @private
* @type {Boolean}
*/
__forceUseParentWorldMatrix: false,
/**
* 只同步四元数,不同步欧拉角
* @type {Boolean}
* @default false
*/
onlySyncQuaternion: false,
/**
* @constructs
* @param {object} [params] 初始化参数,所有params都会复制到实例上
*/
constructor(params) {
/**
* @type {string}
*/
this.id = math.generateUUID(this.className);
/**
* 元素的up向量
* @type {Vector3}
*/
this.up = defaultUp.clone();
/**
* 元素直接点数组
* @type {Node[]}
*/
this.children = [];
/**
* 元素的世界矩阵
* @type {Matrix4}
*/
this.worldMatrix = new Matrix4();
this._matrix = new Matrix4Notifier();
this._position = new Vector3Notifier(0, 0, 0);
this._scale = new Vector3Notifier(1, 1, 1);
this._pivot = new Vector3Notifier(0, 0, 0);
this._rotation = new EulerNotifier();
this._quaternion = new QuaternionNotifier();
this._matrix.onUpdate = this._onMatrixUpdate.bind(this);
this._position.onUpdate = this._onPositionUpdate.bind(this);
this._scale.onUpdate = this._onScaleUpdate.bind(this);
this._pivot.onUpdate = this._onPivotUpdate.bind(this);
this._rotation.onUpdate = this._onRotationUpdate.bind(this);
this._quaternion.onUpdate = this._onQuaternionUpdate.bind(this);
Object.assign(this, params);
},
/**
* @param {boolean} [isChild=false] 是否子节点,子节点不会处理动画及骨骼Mesh,即如果有动画将共享
* @return {Node} 返回clone的Node
*/
clone(isChild) {
const node = new this.constructor();
node.name = this.name;
node.jointName = this.jointName;
node.animationId = this.animationId;
node.setPosition(this.x, this.y, this.z);
node.setScale(this.scaleX, this.scaleY, this.scaleZ);
node.setRotation(this.rotationX, this.rotationY, this.rotationZ);
this.children.forEach((child) => {
node.addChild(child.clone(true));
});
if (!isChild) {
if (this.anim) {
node.anim = this.anim.clone(node);
}
node.resetSkinedMeshRootNode();
}
return node;
},
/**
* 设置节点的动画,这个需要是模型的根节点
* @param {Animation} anim 动画实例
* @return {Node} this
*/
setAnim(anim) {
this.anim = anim;
anim.rootNode = this;
return this;
},
/**
* 重置子孙元素中 SkinedMesh 的根节点为当前元素
*/
resetSkinedMeshRootNode() {
this.traverse((mesh) => {
if (mesh.isSkinedMesh && mesh.skeleton) {
mesh.skeleton.rootNode = this;
}
}, true);
},
/**
* 将所以子孙元素放到一个对象中,对象key为元素的name,value为该元素
* @return {Object} 返回获取的对象
*/
getChildrenNameMap() {
const map = {};
this.traverse((child) => {
map[child.name] = child;
// fix smd animation bug
const originName = child._originName;
if (originName !== undefined && !map[originName]) {
map[originName] = child;
}
}, true);
return map;
},
/**
* 添加一个子元素
* @param {Node} child 需要添加的子元素
* @return {Node} this
*/
addChild(child) {
if (child.parent) {
child.removeFromParent();
}
child.parent = this;
this.children.push(child);
return this;
},
/**
* 移除指定的子元素
* @param {Node} child 需要移除的元素
* @return {Node} this
*/
removeChild(child) {
const index = this.children.indexOf(child);
if (index > -1) {
this.children.splice(index, 1);
child.parent = null;
}
return this;
},
/**
* 将当前元素添加到某个父元素的子元素中
* @param {Node} parent 需要添加到的父元素
* @return {Node} this
*/
addTo(parent) {
parent.addChild(this);
return this;
},
/**
* 将当前元素从其父元素中移除
* @return {Node} this
*/
removeFromParent() {
if (this.parent) {
this.parent.removeChild(this);
}
return this;
},
/**
* 更新本地矩阵
* @return {Node} this
*/
updateMatrix() {
if (this._matrixDirty) {
this._matrixDirty = false;
this.matrixVersion++;
this._matrix.fromRotationTranslationScaleOrigin(this.quaternion, this._position, this._scale, this._pivot, true);
}
return this;
},
/**
* 更新四元数
* @return {Node} this
*/
updateQuaternion() {
if (this._quatDirty) {
this._quatDirty = false;
this._quaternion.fromEuler(this._rotation, true);
}
return this;
},
/**
* 更新transform属性
* @return {Node} this
*/
updateTransform() {
this._matrix.decompose(this._quaternion, this._position, this._scale, this._pivot);
this._onQuaternionUpdate();
this._matrixDirty = false;
return this;
},
/**
* 更新世界矩阵
* @param {Boolean} [force=true] 是否强制更新
* @return {Node} this
*/
updateMatrixWorld(force) {
this.traverse((node) => {
if (node.autoUpdateWorldMatrix || force) {
if (node.parent) {
if (!node.__forceUseParentWorldMatrix) {
node.worldMatrix.multiply(node.parent.worldMatrix, node.matrix);
} else {
node.worldMatrix.copy(node.parent.worldMatrix);
}
} else {
node.worldMatrix.copy(node.matrix);
}
node.worldMatrixVersion++;
}
if (!node.autoUpdateChildWorldMatrix && !force) {
return TRAVERSE_STOP_CHILDREN;
}
return TRAVERSE_STOP_NONE;
});
return this;
},
/**
* 获取当前元素相对于指定元素的矩阵
* @param {Node} [ancestor] 相对于的元素,需要是当前元素的祖先元素,不传表示获取世界矩阵
* @return {Matrix4} 返回获取的矩阵
*/
getConcatenatedMatrix(ancestor) {
const mtx = new Matrix4();
for (let o = this; o && o !== ancestor; o = o.parent) {
mtx.multiply(o.matrix, mtx);
}
return mtx;
},
/**
* _traverse
* @private
* @param {NodeTraverseCallback} callback
* @param {Boolean} onlyChild
* @return {Enum} TRAVERSE_STOP_ALL, TRAVERSE_STOP_CHILDREN, TRAVERSE_STOP_NONE
*/
_traverse(callback, onlyChild) {
if (!onlyChild) {
const res = callback(this);
if (res === TRAVERSE_STOP_ALL || res === TRAVERSE_STOP_CHILDREN) {
return res;
}
}
const children = this.children;
for (let i = 0, l = children.length; i < l; i++) {
const res = children[i]._traverse(callback, false);
if (res === TRAVERSE_STOP_ALL) {
return res;
}
}
return TRAVERSE_STOP_NONE;
},
/**
* 遍历当前元素的子孙元素
* @param {NodeTraverseCallback} callback 每个元素都会调用这个函数处理
* @param {Boolean} [onlyChild=false] 是否只遍历子元素
* @return {Node} this
*/
traverse(callback, onlyChild = false) {
this._traverse(callback, onlyChild);
return this;
},
/**
* 遍历当前元素的子孙元素(广度优先)
* @param {NodeTraverseCallback} callback 每个元素都会调用这个函数处理
* @param {Boolean} [onlyChild=false] 是否只遍历子元素
* @return {Node} this
*/
traverseBFS(callback, onlyChild = false) {
let currentQueue;
let nextQueue;
if (!onlyChild) {
nextQueue = [this];
} else {
nextQueue = this.children;
}
while (nextQueue.length) {
currentQueue = nextQueue;
nextQueue = [];
for (let i = 0, l = currentQueue.length; i < l; i++) {
const child = currentQueue[i];
const res = callback(child);
if (res === TRAVERSE_STOP_ALL) {
return this;
}
if (res !== TRAVERSE_STOP_CHILDREN) {
nextQueue = nextQueue.concat(child.children);
}
}
}
return this;
},
/**
* 根据函数来获取一个子孙元素(广度优先)
* @param {NodeGetChildByCallback} fn 判读函数
* @return {Node|null} 返回获取到的子孙元素
*/
getChildByFnBFS(fn) {
let result = null;
this.traverseBFS((child) => {
if (fn(child)) {
result = child;
return TRAVERSE_STOP_ALL;
}
return TRAVERSE_STOP_NONE;
}, true);
return result;
},
/**
* 根据 name path 来获取子孙元素
* @param {String[]} path 名字数组, e.g., getChildByNamePath(['a', 'b', 'c'])
* @return {Node|null} 返回获取到的子孙元素
*/
getChildByNamePath(path) {
let currentNode = this;
for (let i = 0, l = path.length; i < l; i++) {
const name = path[i];
const node = currentNode.getChildByFnBFS(child => child.name === name);
if (node) {
currentNode = node;
} else {
return null;
}
}
return currentNode;
},
/**
* 遍历调用子孙元素onUpdate方法
* @param {Number} dt
* @return {Node} this
*/
traverseUpdate(dt) {
this.traverse((node) => {
if (node.onUpdate) {
node.onUpdate(dt);
}
if (!node.needCallChildUpdate) {
return TRAVERSE_STOP_CHILDREN;
}
return TRAVERSE_STOP_NONE;
});
return this;
},
/**
* 根据函数来获取一个子孙元素
* @param {NodeGetChildByCallback} fn 判读函数
* @return {Node|null} 返回获取到的子孙元素
*/
getChildByFn(fn) {
let result = null;
this.traverse((child) => {
if (fn(child)) {
result = child;
return TRAVERSE_STOP_ALL;
}
return TRAVERSE_STOP_NONE;
}, true);
return result;
},
/**
* 根据函数来获取匹配的所有子孙元素
* @param {NodeGetChildByCallback} fn 判读函数
* @return {Node[]} 返回获取到的子孙元素
*/
getChildrenByFn(fn) {
let result = [];
this.traverse((child) => {
if (fn(child)) {
result.push(child);
}
}, true);
return result;
},
/**
* 获取指定name的首个子孙元素
* @param {string} name 元素name
* @return {Node|null} 获取的元素
*/
getChildByName(name) {
return this.getChildByFn(child => child.name === name);
},
/**
* 获取指定name的所有子孙元素
* @param {string} name 元素name
* @return {Node[]} 获取的元素数组
*/
getChildrenByName(name) {
return this.getChildrenByFn(child => child.name === name);
},
/**
* 获取指定id的子孙元素
* @param {string} id 元素id
* @return {Node|null} 获取的元素
*/
getChildById(id) {
return this.getChildByFn(child => child.id === id);
},
/**
* 获取指定类名的所有子孙元素
* @param {string} className 类名
* @return {Node[]} 获取的元素数组
*/
getChildrenByClassName(className) {
return this.getChildrenByFn(child => child.className === className);
},
/**
* 获取指定基类名的所有子孙元素
* @param {string} className 类名
* @return {Node[]} 获取的元素数组
*/
getChildrenByBaseClassName(className) {
return this.getChildrenByFn(child => child['is' + className]);
},
/**
* 设置元素的缩放比例,如果只有一个参数三个轴等比缩放
* @param {number} x X缩放比例
* @param {number} [y] Y缩放比例
* @param {number} [z] Z缩放比例
* @return {Node} this
*/
setScale(x, y = x, z = y) {
this._scale.set(x, y, z);
return this;
},
/**
* 设置元素的位置
* @param {number} x X方向位置
* @param {number} y Y方向位置
* @param {number} z Z方向位置
* @return {Node} this
*/
setPosition(x, y, z) {
this._position.set(x, y, z);
return this;
},
/**
* 设置元素的旋转
* @param {number} x X轴旋转角度, 角度制
* @param {number} y Y轴旋转角度, 角度制
* @param {number} z Z轴旋转角度, 角度制
* @return {Node} this
*/
setRotation(x, y, z) {
this._rotation.setDegree(x, y, z);
return this;
},
/**
* 设置中心点
* @param {Number} x 中心点x
* @param {Number} y 中心点y
* @param {Number} z 中心点z
* @return {Node} this
*/
setPivot(x, y, z) {
this._pivot.set(x, y, z);
return this;
},
/**
* 改变元素的朝向
* @param {Node|Object|Vector3} node 需要朝向的元素,或者坐标
* @return {Node} this
*/
lookAt(node) {
if (this.isCamera) {
tempMatrix4.targetTo(this, node, this.up);
} else {
tempMatrix4.targetTo(node, this, this.up);
}
this._quaternion.fromMat4(tempMatrix4);
return this;
},
/**
* raycast
* @param {Ray} ray
* @param {Boolean} [sort=false] 是否按距离排序
* @param {Boolean} [eventMode=false] 是否事件模式
* @return {raycastInfo[]|null}
*/
raycast(ray, sort = false, eventMode = false) {
if (!this.visible) {
return null;
}
let resArray = [];
this.traverse((child) => {
if (eventMode && !child.pointerEnabled) {
return TRAVERSE_STOP_CHILDREN;
}
if (child.isMesh) {
const res = child.raycast(ray, false);
if (res) {
resArray = resArray.concat(res.map((point) => {
return {
mesh: child,
point
};
}));
}
}
if (eventMode && !child.pointerChildren) {
return TRAVERSE_STOP_CHILDREN;
}
return TRAVERSE_STOP_NONE;
});
if (resArray.length) {
if (sort) {
ray.sortPoints(resArray, 'point');
}
return resArray;
}
return null;
},
/**
* 元素的矩阵
* @type {Matrix4Notifier}
* @readOnly
*/
matrix: {
get() {
this.updateMatrix();
return this._matrix;
},
set(value) {
log.warnOnce('Node.matrix.set', 'node.matrix is readOnly.Use node.matrix.copy instead.');
this._matrix.copy(value);
}
},
/**
* 位置
* @type {Vector3Notifier}
* @readOnly
*/
position: {
get() {
return this._position;
},
set(value) {
log.warnOnce('Node.position.set', 'node.position is readOnly.Use node.position.copy instead.');
this._position.copy(value);
}
},
/**
* x轴坐标
* @type {number}
*/
x: {
get() {
return this._position.elements[0];
},
set(value) {
this._position.elements[0] = value;
this._matrixDirty = true;
}
},
/**
* y轴坐标
* @type {number}
*/
y: {
get() {
return this._position.elements[1];
},
set(value) {
this._position.elements[1] = value;
this._matrixDirty = true;
}
},
/**
* z轴坐标
* @type {number}
*/
z: {
get() {
return this._position.elements[2];
},
set(value) {
this._position.elements[2] = value;
this._matrixDirty = true;
}
},
/**
* 缩放
* @type {Vector3Notifier}
* @readOnly
*/
scale: {
get() {
return this._scale;
},
set(value) {
log.warnOnce('Node.scale.set', 'node.scale is readOnly.Use node.scale.copy instead.');
this._scale.copy(value);
}
},
/**
* 缩放比例x
* @type {number}
*/
scaleX: {
get() {
return this._scale.elements[0];
},
set(value) {
this._scale.elements[0] = value;
this._matrixDirty = true;
}
},
/**
* 缩放比例y
* @type {number}
*/
scaleY: {
get() {
return this._scale.elements[1];
},
set(value) {
this._scale.elements[1] = value;
this._matrixDirty = true;
}
},
/**
* 缩放比例z
* @type {number}
*/
scaleZ: {
get() {
return this._scale.elements[2];
},
set(value) {
this._scale.elements[2] = value;
this._matrixDirty = true;
}
},
/**
* 中心点
* @type {Vector3Notifier}
* @readOnly
*/
pivot: {
get() {
return this._pivot;
},
set(value) {
log.warnOnce('Node.pivot.set', 'node.pivot is readOnly.Use node.pivot.copy instead.');
this._pivot.copy(value);
}
},
/**
* 中心点x
* @type {Number}
*/
pivotX: {
get() {
return this._pivot.elements[0];
},
set(value) {
this._pivot.elements[0] = value;
this._matrixDirty = true;
}
},
/**
* 中心点y
* @type {Number}
*/
pivotY: {
get() {
return this._pivot.elements[1];
},
set(value) {
this._pivot.elements[1] = value;
this._matrixDirty = true;
}
},
/**
* 中心点z
* @type {Number}
*/
pivotZ: {
get() {
return this._pivot.elements[2];
},
set(value) {
this._pivot.elements[2] = value;
this._matrixDirty = true;
}
},
/**
* 欧拉角
* @type {EulerNotifier}
* @readOnly
*/
rotation: {
get() {
return this._rotation;
},
set(value) {
log.warnOnce('Node.rotation.set', 'node.rotation is readOnly.Use node.rotation.copy instead.');
this._rotation.copy(value);
}
},
/**
* 旋转角度 x, 角度制
* @type {number}
*/
rotationX: {
get() {
return this._rotation.degX;
},
set(value) {
this._rotation.degX = value;
}
},
/**
* 旋转角度 y, 角度制
* @type {number}
*/
rotationY: {
get() {
return this._rotation.degY;
},
set(value) {
this._rotation.degY = value;
}
},
/**
* 旋转角度 z, 角度制
* @type {number}
*/
rotationZ: {
get() {
return this._rotation.degZ;
},
set(value) {
this._rotation.degZ = value;
}
},
/**
* 四元数角度
* @type {Quaternion}
*/
quaternion: {
get() {
this.updateQuaternion();
return this._quaternion;
},
set(value) {
log.warnOnce('Node.quaternion.set', 'node.quaternion is readOnly.Use node.quaternion.copy instead.');
this._quaternion.copy(value);
}
},
/**
* 矩阵 version,每次改变会加一
* @readonly
* @type {Number}
* @default 0
*/
matrixVersion: 0,
/**
* 世界矩阵 version,每次改变会加一
* @type {Number}
* @default 0
*/
worldMatrixVersion: 0,
/**
* 获取元素的包围盒信息
*
* @param {Node} [parent] 元素相对于哪个祖先元素的包围盒,不传表示世界
* @param {Matrix4} [currentMatrix] 当前计算的矩阵
* @param {Bounds} [bounds] 当前计算的包围盒信息
* @return {Bounds} 返回计算的包围盒信息
*/
getBounds(parent, currentMatrix, bounds) {
if (!currentMatrix) {
currentMatrix = this.getConcatenatedMatrix(parent);
} else {
currentMatrix.multiply(this.matrix);
}
this.children.forEach((child) => {
bounds = child.getBounds(null, currentMatrix.clone(), bounds);
});
if (this.isMesh) {
bounds = this.geometry.getBounds(currentMatrix, bounds);
}
return bounds;
},
/**
* 冒泡鼠标事件
* @param {Object} event
* @private
*/
_fireMouseEvent(event) {
event.eventCurrentTarget = this;
this.fire(event);
// 处理mouseover事件 mouseover不需要阻止冒泡
// handle mouseover event, mouseover needn't stop propagation.
if (event.type === 'mousemove') {
if (!this.__mouseOver) {
this.__mouseOver = true;
const overEvent = Object.assign({}, event);
overEvent.type = 'mouseover';
this.fire(overEvent);
}
} else if (event.type === 'mouseout') {
this.__mouseOver = false;
}
// 向上冒泡
// handle event propagation
const parent = this.parent;
if (!event._stopped && !event._stopPropagationed && parent) {
parent._fireMouseEvent(event);
}
},
/**
* 销毁 Node 资源
* @param {WebGLRenderer} [renderer] stage时可以不传
* @param {Boolean} [destroyTextures=false] 是否销毁材质的贴图,默认不销毁
* @return {Node} this
*/
destroy(renderer, destroyTextures = false) {
const nodes = this.getChildrenByBaseClassName('Node');
this.off();
nodes.forEach((node) => {
if (node.isMesh) {
node.destroy(renderer, destroyTextures);
} else {
node.off();
node.removeFromParent();
}
});
this.removeFromParent();
return this;
},
_onMatrixUpdate() {
this.matrixVersion++;
this.updateTransform();
},
_onPositionUpdate() {
this._matrixDirty = true;
},
_onScaleUpdate() {
this._matrixDirty = true;
},
_onPivotUpdate() {
this._matrixDirty = true;
},
_onRotationUpdate() {
this._quatDirty = true;
this._matrixDirty = true;
},
_onQuaternionUpdate() {
if (!this.onlySyncQuaternion) {
this._rotation.fromQuat(this._quaternion);
}
this._quatDirty = false;
this._matrixDirty = true;
}
});
export default Node;
/**
* 包围盒信息
* @typedef {object} Bounds
* @property {number} x 包围盒中心的X坐标
* @property {number} y 包围盒中心的Y坐标
* @property {number} z 包围盒中心的Z坐标
* @property {number} width 包围盒的宽度
* @property {number} height 包围盒的高度
* @property {number} depth 包围盒的深度
* @property {number} xMin X轴的最小值
* @property {number} xMax X轴的最大值
* @property {number} yMin Y轴的最小值
* @property {number} yMax Y轴的最大值
* @property {number} zMin Z轴的最小值
* @property {number} zMax Z轴的最大值
*/
/**
* 碰撞信息
* @typedef {object} raycastInfo
* @property {Mesh} mesh 碰撞的 mesh
* @property {Vector3} point 碰撞得点
*/
/**
* Node traverse 回调
* @callback NodeTraverseCallback
* @param {Node} node
* @return {any} Node.TRAVERSE_STOP_NONE | Node.TRAVERSE_STOP_CHILDREN | Node.TRAVERSE_STOP_ALL
*/
/**
* Node getChildByCallback 回调
* @callback NodeGetChildByCallback
* @param {Node} node
* @return {boolean}
*/