import { Utils } from '../common/Utils';
import { MathUtils } from '../math/MathUtils';
import { BaseComponent } from './BaseComponent';
import { AxisType } from '../const';
const __ = {
private: Symbol('private'),
}
const TAN_30 = Math.tan(60 * 0.5 * Math.PI / 180)
const cLookAtTargetEventTag = '__cLookAtTargetEventTag__';
const cKeepSizeEventTag = '__cKeepSizeEventTag__';
const cKeepSizeEventPriority = -10000000; // Make it update at last
let _defaultParams = {};
let _vector3 = MathUtils.createVec3();
let _position = MathUtils.createVec3();
let _mat4 = MathUtils.createMat4();
let _quat = MathUtils.createQuat();
let _originMat4 = MathUtils.createMat4();
let _axis = [0, 0, 0];
let _x_axis = [1, 0, 0];
let _y_axis = [0, 1, 0];
let _z_axis = [0, 0, 1];
// #region Private Functions
// Get the radian for looking at target.
function _getRadian(x, y) {
return Math.atan2(-y, -x) + Math.PI / 2;
}
// Set world position only (keep all children world position).
function _setWorldPositionOnly(node, position) {
// Keep all children world position after set node position
let children = node.getChildren().filter(child => {
if (child.getUserData('isDebugNode')) {
return false;
}
return true;
});
let childPosition = [0, 0, 0];
let positions = children.map(child => {
return child.getWorldPosition(childPosition).slice(0);
});
node.setWorldPosition(position);
// Start to resume children world position
for (let i = 0; i < children.length; i++) {
children[i].setWorldPosition(positions[i]);
}
}
// #endregion
/**
* @class TransformComponent
* 变换组件。
* @memberof THING
* @extends THING.BaseComponent
* @public
*/
class TransformComponent extends BaseComponent {
static mustCopyWithInstance = true;
/**
* 变换组件类,用于管理对象的变换属性,如位置、旋转和缩放。
*/
constructor() {
super();
this[__.private] = {};
let _private = this[__.private];
_private.lookAtTargetInfo = null;
_private.keepSizeAtFov = null;
_private.keepSizeInfo = null;
_private.keepSizeDistance = null;
_private.keepSizeDistanceLimited = [-Infinity, Infinity];
// 控制 keepSize 时是否直接修改 body.localScale(true)而不是 object.localScale(false,默认)
_private.keepSizeUseBodyLocalScale = false;
_private.onChange = null;
}
// #region Private
_lookAtPosition(position, up) {
let _private = this[__.private];
let info = _private.lookAtTargetInfo;
let node = this.object.node;
node.lookAt(position, up);
this.onLookAt(position, up);
// Notify outside
let update = info.update;
if (update) {
update();
}
}
_lookAtTarget() {
let _private = this[__.private];
let info = _private.lookAtTargetInfo;
// Get the target
let target = info.target;
// Get the object
let object = this.object;
let node = object.node;
// Get the look point
let lookPoint;
if (info.lookOnPlane) {
lookPoint = MathUtils.getPositionOnPlane(object.position, target.position, target.forward);
}
else {
lookPoint = target.position;
}
// Get the lock axis
let lockAxis = info.lockAxis;
if (lockAxis) {
lookPoint = object.worldToLocal(lookPoint);
node.getPosition(_position);
if (lockAxis == AxisType.X) {
let y = lookPoint[1] - _position[1];
let z = lookPoint[2] - _position[2];
let radian = _getRadian(y, z);
MathUtils.getQuatFromAxisRadian(_x_axis, -radian, _quat);
node.setQuaternion(_quat);
}
else if (lockAxis == AxisType.Y) {
let x = lookPoint[0] - _position[0];
let z = lookPoint[2] - _position[2];
let radian = _getRadian(x, z);
MathUtils.getQuatFromAxisRadian(_y_axis, -radian, _quat);
node.setQuaternion(_quat);
}
else if (lockAxis == AxisType.Z) {
let x = lookPoint[0] - _position[0];
let y = lookPoint[1] - _position[1];
let radian = _getRadian(x, y);
MathUtils.getQuatFromAxisRadian(_z_axis, -radian, _quat);
node.setQuaternion(_quat);
}
}
else {
let up = info.useTargetUpDirection ? target.up : info.up;
this._lookAtPosition(lookPoint, up);
}
// Notify outside
let update = info.update;
if (update) {
update();
}
}
_updateKeepSize() {
let _private = this[__.private];
let object = this.object;
let keepSizeInfo = _private.keepSizeInfo;
let position = object.position;
const camera = keepSizeInfo.camera
MathUtils.vec3.transformMat4(_position, position, camera.inversedMatrixWorld);
let length = Math.abs(_position[2]);
if (length) {
length = MathUtils.clamp(length, _private.keepSizeDistanceLimited[0], _private.keepSizeDistanceLimited[1]);
let scaleFactor = keepSizeInfo.scaleFactor;
const factor = TAN_30 / Math.tan(camera.fov * 0.5 * Math.PI / 180)
const realScaleFactor = MathUtils.scaleVector(scaleFactor, factor)
let newScale = [length / realScaleFactor[0], length / realScaleFactor[1], length / realScaleFactor[2]];
// 根据开关决定使用 object.body.localScale 还是 object.localScale
if (_private.keepSizeUseBodyLocalScale) {
// 允许对象对子类的 keepSize 缩放做修正(由对象自行保证实现)
const fixedScale = object.onFixKeepSizeScale(newScale);
if (fixedScale && fixedScale.length === 3) {
newScale = fixedScale;
}
object.body.localScale = newScale;
} else {
object.localScale = newScale;
}
}
}
_getDistanceToCameraInKeepSizeMode(camera) {
let _private = this[__.private];
// 先获取用户设置的keepSizedistance,没有就计算物体到相机的距离作为默认的distance
let keepSizeDistance = _private.keepSizeDistance;
if (!keepSizeDistance) {
keepSizeDistance = MathUtils.getDistance(this.object.position, camera.position);
}
// 如果设置了keepSizeDistanceLimited,需要限制一下保持大小的距离
if (_private.keepSizeDistanceLimited) {
keepSizeDistance = MathUtils.clamp(keepSizeDistance, _private.keepSizeDistanceLimited[0], _private.keepSizeDistanceLimited[1])
}
const cameraFov = _private.keepSizeAtFov || camera.fov;
const factor = Math.tan(cameraFov * 0.5 * Math.PI / 180) / TAN_30;
keepSizeDistance *= factor;
return keepSizeDistance
}
// #endregion
// #region BaseComponent Interface
onRemove() {
let _private = this[__.private];
_private.lookAtTargetInfo = null;
_private.keepSizeInfo = null;
_private.onChange = null;
super.onRemove();
}
// #endregion
// #region Overrides
onNotifyChange() {
let _private = this[__.private];
if (_private.onChange) {
_private.onChange();
}
}
onLookAt(position, up) {
}
// #endregion
localToSelf(position, ignoreScale = false, target = []) {
let worldPosition = this.localToWorld(position, ignoreScale, target);
return this.worldToSelf(worldPosition, ignoreScale, target);
}
selfToLocal(position, ignoreScale = false, target = []) {
let worldPosition = this.selfToWorld(position, ignoreScale, target);
return this.worldToLocal(worldPosition, ignoreScale, target);
}
worldToSelf(position, ignoreScale = false, target = []) {
return this.object.node.worldToSelf(target, position, ignoreScale);
}
selfToWorld(position, ignoreScale = false, target = []) {
return this.object.node.selfToWorld(target, position, ignoreScale);
}
localToWorld(position, ignoreScale = false, target = []) {
let parent = this.object.parent;
if (parent) {
return parent.selfToWorld(position, ignoreScale, target);
}
else {
return position;
}
}
worldToLocal(position, ignoreScale = false, target = []) {
let parent = this.object.parent;
if (parent) {
return parent.worldToSelf(position, ignoreScale, target);
}
else {
return position;
}
}
getLocalPosition(target = [0, 0, 0]) {
return this.object.node.getPosition(target);
}
/**
* Set local position.
* @param {Array<number>} position The local position.
* @private
*/
setLocalPosition(position) {
// Update local position
this.object.node.setPosition(position);
// Notify transform changed
this.onNotifyChange();
}
/**
* Get angles of the inertial space.
* Euler angles('XYZ'),unit: degree(度)。
* @returns {Array<number>} Euler angles [x, y, z] in degree(度)。
* @private
*/
getLocalAngles() {
let angles = [];
this.object.node.getEuler(angles);
return [angles[0], angles[1], angles[2]];
}
/**
* Set angles of the inertial space.
* @param {Array<number>} angles Euler angles('XYZ'),unit: degree(度)。
* @private
*/
setLocalAngles(angles) {
// Update local quaternion
this.object.node.setEuler(angles);
// Notify transform changed
this.onNotifyChange();
}
getLocalQuaternion(target = [0, 0, 0, 1]) {
return this.object.node.getQuaternion(target);
}
/**
* Set quaternion of the inertial space.
* @param {Array<number>} quat The angles.
* @private
*/
setLocalQuaternion(quat) {
// Update local quaternion
this.object.node.setQuaternion(quat);
// Notify transform changed
this.onNotifyChange();
}
getLocalScale(target = [0, 0, 0]) {
return this.object.node.getScale(target);
}
/**
* Set scale of the self coordinate system.
* @param {Array<number>} value The scale factor.
* @private
*/
setLocalScale(value) {
// Update local scale
let node = this.object.node;
node.setScale(value);
// Notify transform changed
this.onNotifyChange();
}
/**
* Get local matrix.
* @returns {Array<number>}
* @private
*/
getMatrix() {
return this.object.node.getMatrix(_mat4);
}
/**
* Set local matrix.
* @param {Array<number>} value The matrix value.
* @private
*/
setMatrix(value) {
// Update matrix
let node = this.object.node;
node.setMatrix(value);
// Notify transform changed
this.onNotifyChange();
}
getWorldPosition(target = [0, 0, 0]) {
return this.object.node.getWorldPosition(target);
}
/**
* Set world position.
* @param {Array<number>} position The world position.
* @private
*/
setWorldPosition(position) {
// Update world position
this.object.node.setWorldPosition(position);
// Notify transform changed
this.onNotifyChange();
}
/**
* Get angles of the world space.
* Euler angles('XYZ'),unit: degree(度)。
* @returns {Array<number>} Euler angles [x, y, z] in degree(度)。
* @private
*/
getWorldAngles() {
let angles = [];
this.object.node.getWorldEuler(angles);
return [angles[0], angles[1], angles[2]];
}
/**
* Set angles of the world space.
* @param {Array<number>} angles Euler angles('XYZ'),unit: degree(度)。
* @private
*/
setWorldAngles(angles) {
// Update world angels
this.object.node.setWorldEuler(angles);
// Notify transform changed
this.onNotifyChange();
}
getWorldQuaternion(target = [0, 0, 0, 1]) {
return this.object.node.getWorldQuaternion(target);
}
/**
* Set quaternion of the world space.
* @param {Array<number>} quat The angles.
* @private
*/
setWorldQuaternion(quat) {
// Update world quaternion
this.object.node.setWorldQuaternion(quat);
// Notify transform changed
this.onNotifyChange();
}
getWorldScale(target = [0, 0, 0]) {
return this.object.node.getWorldScale(target);
}
/**
* Set scale of the world coordinate system.
* @param {Array<number>} value The scale factor.
* @private
*/
setWorldScale(value) {
// Update world scale
let node = this.object.node;
node.setWorldScale(value);
// Notify transform changed
this.onNotifyChange();
}
/**
* Get matrix world.
* @param {Array<number>} target The target to store value.
* @param {boolean} [updateMatrix=false] True indicates update all parents matrix world.
* @returns {Array<number>} The reference of target.
* @private
*/
getMatrixWorld(target = [], updateMatrix = false) {
return this.object.node.getMatrixWorld(target, updateMatrix);
}
/**
* Set matrix world.
* @param {Array<number>} value The matrix value.
* @param {boolean} [updateMatrix=true] True indicates update all parents matrix world.
* @private
*/
setMatrixWorld(value, updateMatrix = true) {
// Update matrix world
let node = this.object.node;
node.setMatrixWorld(value, updateMatrix);
// Notify transform changed
this.onNotifyChange();
}
updateWorldMatrix(updateParents, updateChildren) {
this.object.node.updateWorldMatrix(updateParents, updateChildren);
}
updateMatrixWorld() {
this.object.node.updateMatrixWorld();
}
translateOnAxis(axis, distance) {
MathUtils.vec3.normalize(_axis, axis);
MathUtils.vec3.transformQuat(_vector3, _axis, this.getLocalQuaternion());
MathUtils.vec3.scale(_vector3, _vector3, distance);
let position = this.getLocalPosition();
this.setLocalPosition(MathUtils.addVector(position, _vector3));
}
translateX(distance) {
this.translateOnAxis(Utils.xAxis, distance);
}
translateY(distance) {
this.translateOnAxis(Utils.yAxis, distance);
}
translateZ(distance) {
this.translateOnAxis(Utils.zAxis, distance);
}
translate(offset) {
this.translateX(offset[0]);
this.translateY(offset[1]);
this.translateZ(offset[2]);
}
rotateOnAxis(axis, angle) {
axis = MathUtils.normalizeVector(axis, [0, 0, 0]);
MathUtils.quat.setAxisAngle(_quat, axis, MathUtils.degToRad(angle));
let localQuat = this.getLocalQuaternion();
MathUtils.quat.multiply(localQuat, localQuat, _quat);
this.setLocalQuaternion(localQuat);
}
rotateX(angle) {
this.rotateOnAxis(Utils.xAxis, angle);
}
rotateY(angle) {
this.rotateOnAxis(Utils.yAxis, angle);
}
rotateZ(angle) {
this.rotateOnAxis(Utils.zAxis, angle);
}
/**
* Get the up direction of world space.
* @returns {Array<number>}
* @private
*/
getUpDirection() {
this.object.node.getMatrixWorld(_mat4);
return MathUtils.normalizeVector([_mat4[4], _mat4[5], _mat4[6]]);
}
/**
* Get the forward direction in world space.
* @returns {Array<number>}
* @private
*/
getForward(target = [0, 0, 0]) {
return this.object.node.getForward(target);
}
/**
* Get the cross direction in world space.
* @returns {Array<number>}
* @private
*/
getCross() {
let up = this.getUpDirection();
let forward = this.getForward();
let target = [0, 0, 0];
MathUtils.vec3.cross(target, up, forward);
MathUtils.vec3.normalize(target, target);
return target;
}
/**
* Get the direction to target position.
* @param {Array<number>} target The target position.
* @returns {Array<number>}
* @private
*/
getDirectionToTarget(target) {
if (!target) {
return null;
}
let direction = [0, 0, 0];
MathUtils.vec3.sub(direction, target, this.object.position);
MathUtils.vec3.normalize(direction, direction);
return direction;
}
getSelfQuaternionFromTarget(target) {
let targetPosition = this.worldToSelf(target);
if (MathUtils.exactEqualsVector3([0, 0, 0], targetPosition)) {
return [0, 0, 0, 1];
}
let mat = MathUtils.lookAt(targetPosition, [0, 0, 0], [0, 1, 0]);
MathUtils.mat4.multiply(_mat4, this.matrix, mat);
return MathUtils.getQuatFromMat4(_mat4);
}
getSelfAnglesFromTarget(target) {
return MathUtils.getAnglesFromQuat(this.getSelfQuaternionFromTarget(target));
}
getWorldQuaternionFromTarget(target) {
if (!target || !Utils.isArray(target)) {
return [0, 0, 0, 1];
}
let object = this.object;
let position = object.position;
if (MathUtils.equalsVector3(target, position)) {
return object.quaternion;
}
return MathUtils.getQuatFromTarget(position, target, [0, 1, 0]);
}
getWorldAnglesFromTarget(target) {
return MathUtils.getAnglesFromQuat(this.getWorldQuaternionFromTarget(target));
}
getWorldPositionFromSelfAngles(value, distance) {
let quat = MathUtils.getQuatFromAngles(value);
MathUtils.vec3.transformQuat(_vector3, [0, 0, 1], quat);
MathUtils.vec3.scale(_vector3, _vector3, distance);
let position = MathUtils.vec3.add(_vector3, this.position, _vector3);
return position;
}
getLocalMatrix(target, updateMatrix = false) {
if (!target || !target.isObject3D) {
return _originMat4;
}
var inversedMatrixWorld = MathUtils.mat4.invert([], target.getMatrixWorld([], updateMatrix));
var selfMatrixWorld = this.getMatrixWorld([], updateMatrix);
var localMatrix = MathUtils.mat4.multiply([], inversedMatrixWorld, selfMatrixWorld);
return localMatrix;
}
lookAt(target, param = _defaultParams) {
let _private = this[__.private];
// Start to look at target
if (target) {
if (!target.isBaseObject && !Utils.isArray(target)) {
Utils.error(`Look at target failed, due to target is not a BaseObject or position`);
return;
}
// Set the default values
let defaultValues = {
lookOnPlane: false
};
// Parse arguments
let up = Utils.parseValue(param['up'], [0, 1, 0]);
let lockAxis = param['lockAxis'];
let always = Utils.parseValue(param['always'], false);
let useTargetUpDirection = Utils.parseValue(param['useTargetUpDirection'], false);
let lookOnPlane = Utils.parseValue(param['lookOnPlane'], defaultValues['lookOnPlane']);
let update = param['update'];
// Update info
_private.lookAtTargetInfo = {
up: up.slice(0),
target,
lockAxis,
always,
useTargetUpDirection,
lookOnPlane,
update,
owner: this,
object: this.object
};
// Look at target directly
if (target.isBaseObject) {
this._lookAtTarget();
// Continue to look
if (always) {
this.object.on('update', () => {
this._lookAtTarget();
}, cLookAtTargetEventTag);
}
else {
this.object.off('update', cLookAtTargetEventTag);
}
}
// Look at position
else {
this._lookAtPosition(target, up);
// Continue to look
if (always) {
this.object.on('update', () => {
this._lookAtPosition(target, up);
}, cLookAtTargetEventTag);
}
else {
this.object.off('update', cLookAtTargetEventTag);
}
}
}
// Stop to look
else {
_private.lookAtTargetInfo = null;
this.object.off('update', cLookAtTargetEventTag);
}
}
/**
* Keep object's size in screen(auto adjust object's scale).
* @param {boolean} value True indicates keep size.
* @param {THING.Camera} camera The camera.
* @private
*/
keepSize(value, camera, options = {}) {
let _private = this[__.private];
let object = this.object;
if (value) {
if (!_private.keepSizeInfo) {
let distanceToCamera = this._getDistanceToCameraInKeepSizeMode(camera);
// 根据开关决定使用 body.localScale 还是 object.localScale 作为初始缩放值
let initialScale = _private.keepSizeUseBodyLocalScale ? object.body.localScale : object.localScale;
_private.keepSizeInfo = {
camera,
initialScale,
scaleFactor: [distanceToCamera / initialScale[0], distanceToCamera / initialScale[1], distanceToCamera / initialScale[2]],
options: {
autoResetLocalScale: Utils.parseValue(options['autoResetLocalScale'], true)
},
refresh: () => {
let distanceToCamera = this._getDistanceToCameraInKeepSizeMode(camera);
_private.keepSizeInfo.scaleFactor = [distanceToCamera / initialScale[0], distanceToCamera / initialScale[1], distanceToCamera / initialScale[2]];
this._updateKeepSize();
}
};
object.on('update', () => {
this._updateKeepSize();
}, cKeepSizeEventTag, cKeepSizeEventPriority);
this._updateKeepSize();
}
}
else {
// Clear previous keep size
if (_private.keepSizeInfo) {
if (_private.keepSizeInfo.options['autoResetLocalScale']) {
// 根据开关决定恢复 body.localScale 还是 object.localScale
if (_private.keepSizeUseBodyLocalScale) {
object.body.localScale = _private.keepSizeInfo.initialScale;
} else {
object.localScale = _private.keepSizeInfo.initialScale;
}
}
_private.keepSizeInfo = null;
object.off('update', cKeepSizeEventTag);
}
}
}
/**
* Get pivot from oriented box.
* @returns {Array<number>}
* @private
*/
getPivot() {
const object = this.object;
if (object.isCamera && !object.hasComponent('bounding')) {
// We must return new pivot here to prevent user modify it outside
return [0.5, 0.5, 0.5];
}
else {
const localBox = object.body.getLocalBoundingBox();
const size = MathUtils.fixScaleFactor(MathUtils.scaleVector(localBox.halfSize, 2));
const selfPosition = [];
object.bodyNode.worldToSelf(selfPosition, object.position);
const min = MathUtils.subVector(localBox.center, localBox.halfSize);
const offset = MathUtils.subVector(selfPosition, min);
const pivot = MathUtils.divideVector(offset, size);
return pivot;
}
}
/**
* Set pivot from oriented box.
* @param {Array<number>} value The pivot value.
* @private
*/
setPivot(value) {
if (value.length < 3) {
value[0] = Utils.parseValue(value[0], 0.5);
value[1] = Utils.parseValue(value[1], 0.5);
value[2] = Utils.parseValue(value[2], 0.5);
}
const object = this.object;
const localBox = object.body.getLocalBoundingBox();
const size = MathUtils.scaleVector(localBox.halfSize, -2);
value = MathUtils.subVector(value, [0.5, 0.5, 0.5]);
const localPos = MathUtils.scaleVector(value, size);
MathUtils.subVector(localPos, localBox.center, localPos);
if (object.pivot) {
const pivotNode = object.body.getPivotNode()
pivotNode?.setPosition([0, 0, 0])
}
object.bodyNode.selfToWorld(localPos, localPos, false)
this.setPivotWorldPosition(localPos);
}
/**
* Set pivot by world position.
* @param {Array<number>} position The world position.
* @private
*/
setPivotWorldPosition(position) {
// Get object
let object = this.object;
let body = object.body;
// Pivot node must works with root node
body.createRootNode();
// Create pivot node and backup its position
let pivotNode = body.createPivotNode();
// Update object position but without changing children location
_setWorldPositionOnly(pivotNode, position);
// Notify transform changed
this.onNotifyChange();
}
/**
* Clear pivot node.
* @private
*/
clearPivot() {
let object = this.object;
object.body.clearPivotNode();
}
bindSubNode(target, subNodeName, bindBodyNode = true, ignoreScale = true) {
if (!target) {
return false;
}
this.unbindSubNode();
// Get the sub node object
let subNodeObject = target.body.getNodeByName(subNodeName);
if (!subNodeObject) {
return false;
}
// Prepare to bind sub node
let subNode = subNodeObject.node;
if (!subNode) {
return false;
}
// Keep the world matrix when start to bind
MathUtils.mat4.invert(_mat4, subNodeObject.getMatrixWorldIgnoreScale());
// Bind sub node
if (bindBodyNode) {
MathUtils.mat4.multiply(_mat4, _mat4, this.object.body.matrixWorld);
if (!this.object.bodyNode.bindSubNode(subNode, _mat4, ignoreScale)) {
return false;
}
}
else {
MathUtils.mat4.multiply(_mat4, _mat4, this.object.matrixWorld);
if (!this.object.node.bindSubNode(subNode, _mat4, ignoreScale)) {
return false;
}
}
return true;
}
/**
* Unbind sub node.
* @private
*/
unbindSubNode() {
this.object.bodyNode.unbindSubNode();
}
// #region Accessor
get lookAtTargetInfo() {
let _private = this[__.private];
return _private.lookAtTargetInfo;
}
get keepSizeInfo() {
let _private = this[__.private];
return _private.keepSizeInfo;
}
get localPosition() {
return this.getLocalPosition();
}
set localPosition(value) {
this.setLocalPosition(value);
}
get localAngles() {
return this.getLocalAngles();
}
set localAngles(value) {
this.setLocalAngles(value);
}
get localQuaternion() {
return this.getLocalQuaternion();
}
set localQuaternion(value) {
this.setLocalQuaternion(value);
}
get localScale() {
return this.getLocalScale();
}
set localScale(value) {
this.setLocalScale(value);
}
get position() {
return this.getWorldPosition();
}
set position(value) {
this.setWorldPosition(value);
}
get angles() {
return this.getWorldAngles();
}
set angles(value) {
this.setWorldAngles(value);
}
get quaternion() {
return this.getWorldQuaternion();
}
set quaternion(value) {
this.setWorldQuaternion(value);
}
get scale() {
return this.getWorldScale();
}
set scale(value) {
this.setWorldScale(value);
}
get matrix() {
return this.getMatrix();
}
set matrix(value) {
this.setMatrix(value);
}
get matrixWorld() {
return this.getMatrixWorld();
}
set matrixWorld(value) {
this.setMatrixWorld(value);
}
get inversedMatrix() {
return MathUtils.mat4.invert(_mat4, this.matrix);
}
get inversedMatrixWorld() {
return MathUtils.mat4.invert(_mat4, this.getMatrixWorld([], true));
}
get up() {
return this.getUpDirection();
}
get forward() {
return this.getForward();
}
get cross() {
return this.getCross();
}
set keepSizeAtFov(value) {
let _private = this[__.private];
_private.keepSizeAtFov = value;
// Refresh keep size
let keepSizeInfo = _private.keepSizeInfo;
if (keepSizeInfo) {
keepSizeInfo.refresh();
}
}
get keepSizeAtFov() {
let _private = this[__.private];
return _private.keepSizeAtFov;
}
/**
* 获取/设置保持大小的距离(单位米),设置后物体的大小将保持在所设置的距离上,需要开启keepSize。
* @type {number}
* @example
* // 开启keepSize,物体的大小将保持在相机距离物体10米时的大小
* const box = new THING.Box({
* keepSize: true,
* keepSizeDistance: 10,
* });
* @public
*/
get keepSizeDistance() {
let _private = this[__.private];
return _private.keepSizeDistance;
}
set keepSizeDistance(value) {
let _private = this[__.private];
_private.keepSizeDistance = value;
// Refresh keep size
let keepSizeInfo = _private.keepSizeInfo;
if (keepSizeInfo) {
keepSizeInfo.refresh();
}
}
/**
* 获取/设置保持大小的距离限制,设置后保持大小的作用仅在所设置的距离范围内生效,需要开启keepSize。(优先级高于keepSizeDistance)
* @type {Array.<number>}
* @example
* // 开启keepSize,将保持大小的距离限制在 20米 到 100米 之间
* const box = new THING.Box({
* keepSize: true,
* keepSizeDistanceLimited: [20,100],
* });
* @public
*/
get keepSizeDistanceLimited() {
let _private = this[__.private];
return _private.keepSizeDistanceLimited;
}
set keepSizeDistanceLimited(value) {
let _private = this[__.private];
if (value) {
_private.keepSizeDistanceLimited[0] = value[0];
_private.keepSizeDistanceLimited[1] = value[1];
}
else {
_private.keepSizeDistanceLimited[0] = -Infinity;
_private.keepSizeDistanceLimited[1] = Infinity;
}
// Refresh keep size
let keepSizeInfo = _private.keepSizeInfo;
if (keepSizeInfo) {
this._updateKeepSize();
}
}
/**
* 获取/设置 keepSize 时是否使用 body.localScale(true)而不是 object.localScale(false,默认)。
* @type {boolean}
* @private
*/
get keepSizeUseBodyLocalScale() {
let _private = this[__.private];
return _private.keepSizeUseBodyLocalScale;
}
set keepSizeUseBodyLocalScale(value) {
let _private = this[__.private];
_private.keepSizeUseBodyLocalScale = value;
// 如果 keepSize 已启用,需要刷新以应用新的缩放方式
let keepSizeInfo = _private.keepSizeInfo;
if (keepSizeInfo) {
// 重新计算初始缩放值
let object = this.object;
let initialScale = value ? object.body.localScale : object.localScale;
keepSizeInfo.initialScale = initialScale;
// 重新计算 scaleFactor
let distanceToCamera = this._getDistanceToCameraInKeepSizeMode(keepSizeInfo.camera);
keepSizeInfo.scaleFactor = [distanceToCamera / initialScale[0], distanceToCamera / initialScale[1], distanceToCamera / initialScale[2]];
this._updateKeepSize();
}
}
get onChange() {
let _private = this[__.private];
return _private.onChange;
}
set onChange(value) {
let _private = this[__.private];
_private.onChange = value;
}
// #endregion
}
export { TransformComponent }