import { MathUtils } from '../math/MathUtils';
import { Utils } from '../common/Utils'
let _position = MathUtils.createVec3();
let _localPosition = MathUtils.createVec3();
let _tempPosition = MathUtils.createVec3();
let _scale = MathUtils.createVec3();
let _localScale = MathUtils.createVec3();
let _tempScale = MathUtils.createVec3();
let _quat = MathUtils.createQuat();
let _localQuat = MathUtils.createQuat();
let _quatForAngles = MathUtils.createQuat();
let _quatForRotation = MathUtils.createQuat();
let _tempQuat = MathUtils.createQuat();
let _angles = MathUtils.createVec3();
let _mat4_0 = MathUtils.createMat4();
let _mat4_1 = MathUtils.createMat4();
let _mat4_2 = MathUtils.createMat4();
/**
* NodeObject - 节点对象
*
* 从场景中的可渲染对象获取网格的子节点对象。
*
* @class NodeObject
* @memberof THING
* @public
*
*/
class NodeObject {
/**
* 从场景中的可渲染对象获取网格的子节点对象。
* @param {object} param 初始参数。
*/
constructor(param = {}) {
let node = param['node'];
let renderableNode = param['renderableNode'];
this._name = node.name;
this._renderableNode = renderableNode;
this._node = node;
}
// #region Private
_updateMatrix(position, quaternion, scale) {
this.matrix = MathUtils.composeToMat4(position, quaternion, scale, _mat4_0);
}
// #endregion
// #region Coordinates Transform
/**
* 将本地坐标转换为自身坐标。
* @param {Array<number>} position 本地坐标。
* @param {boolean} [ignoreScale=false] 是否忽略缩放因素。
* @returns {Array<number>} 目标的引用。
* @public
* @example
* // 将本地坐标转换为自身坐标,忽略缩放因素
* let localPosition = [1, 2, 3];
* let selfPosition = nodeObject.localToSelf(localPosition, true);
*/
localToSelf(position, ignoreScale = false) {
let worldPosition = this.localToWorld(position, ignoreScale);
return this.worldToSelf(worldPosition, ignoreScale);
}
/**
* 将自身位置转换为本地位置。
* @param {Array<number>} position 自身位置。
* @param {boolean} [ignoreScale=false] 是否忽略缩放因素。
* @returns {Array<number>} 目标的引用。
* @public
* @example
* // 将自身位置转换为本地位置,忽略缩放因素
* let selfPosition = [1, 2, 3];
* let localPosition = nodeObject.selfToLocal(selfPosition, true);
*/
selfToLocal(position, ignoreScale = false) {
let worldPosition = this.selfToWorld(position, ignoreScale);
return this.worldToLocal(worldPosition, ignoreScale);
}
/**
* 将世界位置转换为自身位置。
* @param {Array<number>} position 世界位置。
* @param {boolean} [ignoreScale=false] 是否忽略缩放因素。
* @returns {Array<number>} 目标的引用。
* @public
* @example
* // 将世界位置转换为自身位置,忽略缩放因素
* let worldPosition = [1, 2, 3];
* let selfPosition = nodeObject.worldToSelf(worldPosition, true);
*/
worldToSelf(position, ignoreScale = false) {
return MathUtils.worldToSelf(this.matrixWorld, position, ignoreScale);
}
/**
* 将自身位置转换为世界位置。
* @param {Array<number>} position 自身位置。
* @param {boolean} [ignoreScale=false] 是否忽略缩放因素。
* @returns {Array<number>} 目标的引用。
* @public
* @example
* // 将自身位置转换为世界位置,忽略缩放因素
* let selfPosition = [1, 2, 3];
* let worldPosition = nodeObject.selfToWorld(selfPosition, true);
*/
selfToWorld(position, ignoreScale = false) {
return MathUtils.selfToWorld(this.matrixWorld, position, ignoreScale);
}
/**
* 将相对位置转移给世界位置。
* @param {Array<number>} position 相对位置。
* @param {boolean} [ignoreScale=false] 是否忽略缩放因素。
* @returns {Array<number>} 目标的引用。
* @public
* @example
* // 将自身位置转换为世界位置,忽略缩放因素
* let selfPosition = [1, 2, 3];
* let worldPosition = nodeObject.localToWorld(selfPosition, true);
*/
localToWorld(position, ignoreScale = false) {
let root = this._node.root;
if (root) {
return root.selfToWorld(_position, position, ignoreScale);
}
else {
return position;
}
}
/**
* 将世界位置转换为本地位置。
* @param {Array<number>} position 世界位置。
* @param {boolean} [ignoreScale=false] 是否忽略缩放因素。
* @returns {Array<number>} 目标的引用。
* @public
* @example
* // 将世界位置转换为本地位置,忽略缩放因素
* let worldPosition = [1, 2, 3];
* let localPosition = nodeObject.worldToLocal(worldPosition, true);
*/
worldToLocal(position, ignoreScale = false) {
let root = this._node.root;
if (root) {
return root.worldToSelf(_localPosition, position, ignoreScale);
}
else {
return position;
}
}
// #endregion
// #region Common
get name() {
return this._name;
}
get node() {
return this._node;
}
get isRenderable() {
return !!this._node.isRenderable;
}
// #endregion
// #region Transform
/**
* 绕X轴旋转节点。
* @param {number} angle 旋转角度(以角度为单位)。
* @public
* @example
* // 将节点绕X轴旋转45度
* nodeObject.rotateX(45);
*/
rotateX(angle) {
let quat = MathUtils.getQuatFromAngles([angle, 0, 0]);
MathUtils.quat.multiply(quat, this.localQuaternion, quat);
this.localQuaternion = quat;
}
/**
* 绕Y轴旋转节点。
* @param {number} angle 旋转角度(以角度为单位)。
* @public
* @example
* // 将节点绕Y轴旋转45度
* nodeObject.rotateY(45);
*/
rotateY(angle) {
let quat = MathUtils.getQuatFromAngles([0, angle, 0]);
MathUtils.quat.multiply(quat, this.localQuaternion, quat);
this.localQuaternion = quat;
}
/**
* 绕Z轴旋转节点。
* @param {number} angle 旋转角度(以角度为单位)。
* @public
* @example
* // 将节点绕Z轴旋转45度
* nodeObject.rotateZ(45);
*/
rotateZ(angle) {
let quat = MathUtils.getQuatFromAngles([0, 0, angle]);
MathUtils.quat.multiply(quat, this.localQuaternion, quat);
this.localQuaternion = quat;
}
/**
* 获取世界空间中的上方向。
* @type {Array<number>}
* @public
* @example
* // 获取节点在世界空间中的上方向
* let upDirection = nodeObject.up;
*/
get up() {
const matrixWorld = this.matrixWorld;
return MathUtils.normalizeVector([matrixWorld[4], matrixWorld[5], matrixWorld[6]]);
}
/**
* 获取世界空间中的前方向。
* @type {Array<number>}
* @public
* @example
* // 获取节点在世界空间中的前方向
* let forwardDirection = nodeObject.forward;
*/
get forward() {
const matrixWorld = this.matrixWorld;
return MathUtils.normalizeVector([matrixWorld[8], matrixWorld[9], matrixWorld[10]]);
}
/**
* 获取世界空间中的右方向。
* @type {Array<number>}
* @public
* @example
* // 获取节点在世界空间中的右方向
* let rightDirection = nodeObject.cross;
*/
get cross() {
let up = this.up;
let forward = this.forward;
let target = [0, 0, 0];
MathUtils.vec3.cross(target, up, forward);
MathUtils.vec3.normalize(target, target);
return target;
}
/**
* 获取/设置本地位置。
* @type {Array<number>}
* @example
* object.body.nodes[0].localPosition = [10, 10, 10];
* @public
*/
get localPosition() {
MathUtils.decomposeFromMat4(this.matrix, _localPosition, _tempQuat, _tempScale);
return _localPosition;
}
set localPosition(value) {
let position = value.slice(0);
this._updateMatrix(position, this.localQuaternion, this.localScale);
}
/**
* 获取/设置本地四元数。
* @type {Array<number>}
* @example
* object.body.nodes[0].localQuaternion = THING.Math.getQuatFromAngles([45, 45, 45]);
* @public
*/
get localQuaternion() {
MathUtils.decomposeFromMat4(this.matrix, _tempPosition, _localQuat, _tempScale);
return _localQuat
}
set localQuaternion(value) {
let quaternion = value.slice(0);
this._updateMatrix(this.localPosition, quaternion, this.localScale);
}
/**
* 获取/设置本地角度。
* 欧拉角('XYZ'),单位为 degree(度)。
* @type {Array<number>}
* @example
* object.body.nodes[0].localAngles = [45, 45, 45];
* @public
*/
get localAngles() {
return MathUtils.getAnglesFromQuat(this.localQuaternion, _angles);
}
set localAngles(value) {
this.localQuaternion = MathUtils.getQuatFromAngles(value, _localQuat);
}
/**
* 获取/设置本地缩放。
* @type {Array<number>}
* @example
* object.body.nodes[0].localScale = [3, 3, 3];
* @public
*/
get localScale() {
MathUtils.decomposeFromMat4(this.matrix, _tempPosition, _tempQuat, _localScale);
return _localScale
}
set localScale(value) {
let scale = value.slice(0);
this._updateMatrix(this.localPosition, this.localQuaternion, scale);
}
/**
* 获取/设置世界坐标
* @type {Array<number>}
* @example
* object.body.nodes[0].position = [10, 10, 10];
* @public
*/
get position() {
MathUtils.decomposeFromMat4(this.matrixWorld, _position, _tempQuat, _tempScale);
return _position;
}
set position(value) {
MathUtils.decomposeFromMat4(this.matrixWorld, _position, _quat, _scale);
let matrixWorld = MathUtils.composeToMat4(value, _quat, _scale, _mat4_2);
this.matrixWorld = matrixWorld;
}
/**
* 获取/设置世界四元数。
* @type {Array<number>}
* @example
* object.body.nodes[0].quaternion = THING.Math.getQuatFromAngles([45, 45, 45]);
* @public
*/
get quaternion() {
MathUtils.decomposeFromMat4(this.matrixWorld, _position, _quat, _scale);
return _quat;
}
set quaternion(value) {
MathUtils.decomposeFromMat4(this.matrixWorld, _position, _quat, _scale);
let matrixWorld = MathUtils.composeToMat4(_position, value, _scale, _mat4_2);
this.matrixWorld = matrixWorld;
}
/**
* 获取/设置世界角度。
* 欧拉角('XYZ'),单位为 degree(度)。
* @type {Array<number>}
* @example
* object.body.nodes[0].angles = [45, 45, 45];
* @public
*/
get angles() {
return MathUtils.getAnglesFromQuat(this.quaternion, _angles);
}
set angles(value) {
this.quaternion = MathUtils.getQuatFromAngles(value, _quatForAngles);
}
/**
* 获取/设置世界旋转。
* 欧拉角('XYZ'),单位为 degree(度)。
* @type {Array<number>}
* @example
* object.body.nodes[0].rotation = [45, 45, 45];
* @public
*/
get rotation() {
return MathUtils.getAnglesFromQuat(this.quaternion, _angles);
}
set rotation(value) {
this.quaternion = MathUtils.getQuatFromAngles(value, _quatForRotation);
}
/**
* 获取/设置缩放
* @type {Array<number>}
* @example
* object.body.nodes[0].scale = [3, 3, 3];
* @public
*/
get scale() {
MathUtils.decomposeFromMat4(this.matrixWorld, _tempPosition, _tempQuat, _scale);
return _scale;
}
set scale(value) {
MathUtils.decomposeFromMat4(this.matrixWorld, _position, _quat, _scale);
let matrixWorld = MathUtils.composeToMat4(_position, _quat, value, _mat4_2);
this.matrixWorld = matrixWorld;
}
/**
* 获取/设置矩阵。
* @type {Array<number>}
* @example
* object.body.nodes[0].matrix = THING.Math.composeToMat4([10, 10, 10], [0, 0, 0, 1], [2, 2, 2]);
* @public
*/
get matrix() {
let node = this._node;
return node.matrix;
}
set matrix(value) {
let node = this._node;
this._renderableNode.setSubNodeMatrix(node, value);
}
/**
* 获取/设置世界矩阵。
* @type {Array<number>}
* @example
* object.body.nodes[0].matrixWorld = THING.Math.composeToMat4([10, 10, 10], [0, 0, 0, 1], [2, 2, 2]);
* @public
*/
get matrixWorld() {
let node = this._node;
let root = node.root;
root.updateMatrixWorld();
root.getMatrixWorld(_mat4_0);
return MathUtils.mat4.multiply(_mat4_0, _mat4_0, node.matrix).slice(0);
}
set matrixWorld(value) {
let node = this._node;
let root = node.root;
root.updateMatrixWorld();
root.getMatrixWorld(_mat4_0);
MathUtils.mat4.invert(_mat4_1, _mat4_0);
MathUtils.mat4.multiply(_mat4_1, _mat4_1, value);
this._renderableNode.setSubNodeMatrix(node, _mat4_1);
}
getMatrixWorldIgnoreScale() {
let node = this._node;
// scale the rotation part
const matrix = removeScaleFromMatrix(node.matrix, this.localScale);
const root = node.root;
root.updateMatrixWorld();
root.getMatrixWorld(_mat4_0);
return MathUtils.mat4.multiply(_mat4_0, _mat4_0, matrix).slice(0);
function removeScaleFromMatrix(matrix, scale) {
// 计算每个轴的逆缩放因子
const invScaleX = 1 / scale[0];
const invScaleY = 1 / scale[1];
const invScaleZ = 1 / scale[2];
// 创建一个新的矩阵来存储结果
const matrixWithoutScale = [...matrix];
// 应用逆缩放因子到旋转部分
matrixWithoutScale[0] *= invScaleX;
matrixWithoutScale[1] *= invScaleX;
matrixWithoutScale[2] *= invScaleX;
matrixWithoutScale[4] *= invScaleY;
matrixWithoutScale[5] *= invScaleY;
matrixWithoutScale[6] *= invScaleY;
matrixWithoutScale[8] *= invScaleZ;
matrixWithoutScale[9] *= invScaleZ;
matrixWithoutScale[10] *= invScaleZ;
return matrixWithoutScale;
}
}
// #endregion
get isSubNodeObject() {
Utils.warn(`Please use .isNodeObject`);
return true;
}
get isNodeObject() {
return true;
}
/**
* 获取/设置子节点材质的颜色。
* @type {string|Array<number>}
* @public
* @example
* // 设置子节点材质的颜色为红色
* subNodeObject.color = [1, 0, 0]; // RGB
*/
get color() {
let node = this._node;
return this._renderableNode.getSubNodeMaterial(node, 'color');
}
set color(value) {
let color = Utils.parseColor(value, null);
if (this.color != color) {
let node = this._node;
this._renderableNode.setSubNodeMaterial(node, 'color', color);
}
}
/**
* 获取/设置子节点材质的不透明度。
* @type {number}
* @public
* @example
* // 设置子节点材质的不透明度为0.5
* subNodeObject.opacity = 0.5;
*/
get opacity() {
let node = this._node;
return this._renderableNode.getSubNodeMaterial(node, 'opacity');
}
set opacity(value) {
if (this.opacity != value) {
let node = this._node;
this._renderableNode.setSubNodeMaterial(node, 'opacity', value);
}
}
/**
* 获取/设置子节点的可见性。
* @type {boolean}
* @public
* @example
* // 设置子节点的可见性为false
* subNodeObject.visible = false;
*/
get visible() {
let node = this._node;
return this._renderableNode.getSubNodeVisible(node);
}
set visible(value) {
if (this.visible != value) {
let node = this._node;
this._renderableNode.setSubNodeVisible(node, value);
}
}
// 由于在渲染端只有UNode记录了UserData,subNode(Mesh)并未记录UserData
// 因此我们将subNode的UserData数据以map的形式存在UNode上
// subNode的UserData的key为id-name
get userData() {
let data = this._renderableNode.getUserData();
if (data && data.subNodeUserData) {
let key = this.node.id + '-' + this.node.name;
return data.subNodeUserData.get(key)
}
return null;
}
set userData(value) {
if (this.userData != value) {
let data = this._renderableNode.getUserData();
if (!data) {
data = {};
this._renderableNode.setUserData(data);
}
data.subNodeUserData = data.subNodeUserData || new Map();
let key = this.node.id + '-' + this.node.name;
data.subNodeUserData.set(key, value);
}
}
/**
* 获取AABB包围盒。
* @type {THING.AABBResult}
* @example
* let subObject = object.body.getNodeByName('MeshName')
* if(subObject){
* console.log(subObject.AABB)
* }
* @public
*/
get AABB() {
let node = this._node;
return this._renderableNode.getSubNodeAABB(node);
}
/**
* 获取OBB包围盒。
* @type {THING.OBBResult}
* @example
* let subObject = object.body.getNodeByName('MeshName')
* if(subObject){
* console.log(subObject.OBB)
* }
* @public
*/
get OBB() {
let node = this._node;
return this._renderableNode.getSubNodeOBB(node);
}
/**
* 获取/设置子节点的勾边颜色。
* @type {Array<number>}
* @public
* @example
* // 设置子节点的勾边为[0,0,0]
* subNodeObject.outlineColor = [0,0,0];
* // 取消子节点的勾边
* subNodeObject.outlineColor = null;
*/
set outlineColor(value) {
let node = this._node;
let color = Utils.parseColor(value, null);
return this._renderableNode.setSubNodeEffect(node, 'outlineColor', color);
}
get outlineColor() {
let node = this._node;
return this._renderableNode.getSubNodeEffect(node, 'outlineColor');
}
/**
* 获取/设置子节点的发光强度。
* @type {number}
* @public
* @example
* // 设置子节点的发光强度为1
* subNodeObject.glow = 1;
* // 取消子节点的发光
* subNodeObject.glow = null;
* subNodeObject.glow = 0;
*/
set glow(value) {
let node = this._node;
return this._renderableNode.setSubNodeEffect(node, 'glow', value);
}
get glow() {
let node = this._node;
return this._renderableNode.getSubNodeEffect(node, 'glow');
}
/**
* 获取父节点
* @returns {THING.NodeObject|null} 返回父节点,如果不存在则返回null
* @public
*/
get parentNode() {
// 获取所有子节点
const subNodes = [];
this._renderableNode.getSubNodes(subNodes, null)
// 查找匹配父节点名称的子节点
const parentSubNode = subNodes.find(subNode => subNode.name === this.node.parentName)
if (!parentSubNode) {
return null
}
// 创建并返回新的节点对象
return new NodeObject({
node: parentSubNode,
renderableNode: this._renderableNode
});
}
}
export { NodeObject }