UNPKG

@xeokit/xeokit-sdk

Version:

3D BIM IFC Viewer SDK for AEC engineering applications. Open Source JavaScript Toolkit based on pure WebGL for top performance, real-world coordinates and full double precision

405 lines (368 loc) 11.5 kB
import {math} from "../math/index.js"; const angleAxis = math.vec4(4); const q1 = math.vec4(); const q2 = math.vec4(); const xAxis = math.vec3([1, 0, 0]); const yAxis = math.vec3([0, 1, 0]); const zAxis = math.vec3([0, 0, 1]); const veca = math.vec3(3); const vecb = math.vec3(3); const identityMat = math.identityMat4(); /** * A dynamically-updatable transform within a {@link SceneModel}. * * * Can be composed into hierarchies * * Shared by multiple {@link SceneModelMesh}es * * Created with {@link SceneModel#createTransform} * * Stored by ID in {@link SceneModel#transforms} * * Referenced by {@link SceneModelMesh#transform} */ export class SceneModelTransform { /** * @private */ constructor(cfg) { this._model = cfg.model; /** * Unique ID of this SceneModelTransform. * * The SceneModelTransform is registered against this ID in {@link SceneModel#transforms}. */ this.id = cfg.id; this._parentTransform = cfg.parent; this._childTransforms = []; this._meshes = []; this._scale = new Float32Array([1,1,1]); this._quaternion = math.identityQuaternion(new Float32Array(4)); this._rotation = new Float32Array(3); this._position = new Float32Array(3); this._localMatrix = math.identityMat4(new Float32Array(16)); this._worldMatrix = math.identityMat4(new Float32Array(16)); this._localMatrixDirty = true; this._worldMatrixDirty = true; if (cfg.matrix) { this.matrix = cfg.matrix; } else { this.scale = cfg.scale; this.position = cfg.position; if (cfg.quaternion) { } else { this.rotation = cfg.rotation; } } if (cfg.parent) { cfg.parent._addChildTransform(this); } } _addChildTransform(childTransform) { this._childTransforms.push(childTransform); childTransform._parentTransform = this; childTransform._transformDirty(); childTransform._setSubtreeAABBsDirty(this); } _addMesh(mesh) { this._meshes.push(mesh); mesh.transform = this; // childTransform._setWorldMatrixDirty(); // childTransform._setAABBDirty(); } /** * The optional parent SceneModelTransform. * * @type {SceneModelTransform} */ get parentTransform() { return this._parentTransform; } /** * The {@link SceneModelMesh}es transformed by this SceneModelTransform. * * @returns {[]} */ get meshes() { return this._meshes; } /** * Sets the SceneModelTransform's local translation. * * Default value is ````[0,0,0]````. * * @type {Number[]} */ set position(value) { this._position.set(value || [0, 0, 0]); this._setLocalMatrixDirty(); this._model.glRedraw(); } /** * Gets the SceneModelTransform's translation. * * Default value is ````[0,0,0]````. * * @type {Number[]} */ get position() { return this._position; } /** * Sets the SceneModelTransform's rotation, as Euler angles given in degrees, for each of the X, Y and Z axis. * * Default value is ````[0,0,0]````. * * @type {Number[]} */ set rotation(value) { this._rotation.set(value || [0, 0, 0]); math.eulerToQuaternion(this._rotation, "XYZ", this._quaternion); this._setLocalMatrixDirty(); this._model.glRedraw(); } /** * Gets the SceneModelTransform's rotation, as Euler angles given in degrees, for each of the X, Y and Z axis. * * Default value is ````[0,0,0]````. * * @type {Number[]} */ get rotation() { return this._rotation; } /** * Sets the SceneModelTransform's rotation quaternion. * * Default value is ````[0,0,0,1]````. * * @type {Number[]} */ set quaternion(value) { this._quaternion.set(value || [0, 0, 0, 1]); math.quaternionToEuler(this._quaternion, "XYZ", this._rotation); this._setLocalMatrixDirty(); this._model.glRedraw(); } /** * Gets the SceneModelTransform's rotation quaternion. * * Default value is ````[0,0,0,1]````. * * @type {Number[]} */ get quaternion() { return this._quaternion; } /** * Sets the SceneModelTransform's scale. * * Default value is ````[1,1,1]````. * * @type {Number[]} */ set scale(value) { this._scale.set(value || [1, 1, 1]); this._setLocalMatrixDirty(); this._model.glRedraw(); } /** * Gets the SceneModelTransform's scale. * * Default value is ````[1,1,1]````. * * @type {Number[]} */ get scale() { return this._scale; } /** * Sets the SceneModelTransform's transform matrix. * * Default value is ````[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]````. * * @type {Number[]} */ set matrix(value) { if (!this._localMatrix) { this._localMatrix = math.identityMat4(); } this._localMatrix.set(value || identityMat); math.decomposeMat4(this._localMatrix, this._position, this._quaternion, this._scale); this._localMatrixDirty = false; this._transformDirty(); this._model.glRedraw(); } /** * Gets the SceneModelTransform's transform matrix. * * Default value is ````[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]````. * * @type {Number[]} */ get matrix() { if (this._localMatrixDirty) { if (!this._localMatrix) { this._localMatrix = math.identityMat4(); } math.composeMat4(this._position, this._quaternion, this._scale, this._localMatrix); this._localMatrixDirty = false; } return this._localMatrix; } /** * Gets the SceneModelTransform's World matrix. * * @property worldMatrix * @type {Number[]} */ get worldMatrix() { if (this._worldMatrixDirty) { this._buildWorldMatrix(); } return this._worldMatrix; } /** * Rotates the SceneModelTransform about the given axis by the given increment. * * @param {Number[]} axis Local axis about which to rotate. * @param {Number} angle Angle increment in degrees. */ rotate(axis, angle) { angleAxis[0] = axis[0]; angleAxis[1] = axis[1]; angleAxis[2] = axis[2]; angleAxis[3] = angle * math.DEGTORAD; math.angleAxisToQuaternion(angleAxis, q1); math.mulQuaternions(this.quaternion, q1, q2); this.quaternion = q2; this._setLocalMatrixDirty(); this._model.glRedraw(); return this; } /** * Rotates the SceneModelTransform about the given World-space axis by the given increment. * * @param {Number[]} axis Local axis about which to rotate. * @param {Number} angle Angle increment in degrees. */ rotateOnWorldAxis(axis, angle) { angleAxis[0] = axis[0]; angleAxis[1] = axis[1]; angleAxis[2] = axis[2]; angleAxis[3] = angle * math.DEGTORAD; math.angleAxisToQuaternion(angleAxis, q1); math.mulQuaternions(q1, this.quaternion, q1); //this.quaternion.premultiply(q1); return this; } /** * Rotates the SceneModelTransform about the local X-axis by the given increment. * * @param {Number} angle Angle increment in degrees. */ rotateX(angle) { return this.rotate(xAxis, angle); } /** * Rotates the SceneModelTransform about the local Y-axis by the given increment. * * @param {Number} angle Angle increment in degrees. */ rotateY(angle) { return this.rotate(yAxis, angle); } /** * Rotates the SceneModelTransform about the local Z-axis by the given increment. * * @param {Number} angle Angle increment in degrees. */ rotateZ(angle) { return this.rotate(zAxis, angle); } /** * Translates the SceneModelTransform along the local axis by the given increment. * * @param {Number[]} axis Normalized local space 3D vector along which to translate. * @param {Number} distance Distance to translate along the vector. */ translate(axis) { this._position[0] += axis[0]; this._position[1] += axis[1]; this._position[2] += axis[2]; this._setLocalMatrixDirty(); this._model.glRedraw(); return this; } /** * Translates the SceneModelTransform along the local X-axis by the given increment. * * @param {Number} distance Distance to translate along the X-axis. */ translateX(distance) { this._position[0] += distance; this._setLocalMatrixDirty(); this._model.glRedraw(); return this; } /** * Translates the SceneModelTransform along the local Y-axis by the given increment. * * @param {Number} distance Distance to translate along the Y-axis. */ translateY(distance) { this._position[1] += distance; this._setLocalMatrixDirty(); this._model.glRedraw(); return this; } /** * Translates the SceneModelTransform along the local Z-axis by the given increment. * * @param {Number} distance Distance to translate along the Z-axis. */ translateZ(distance) { this._position[2] += distance; this._setLocalMatrixDirty(); this._model.glRedraw(); return this; } _setLocalMatrixDirty() { this._localMatrixDirty = true; this._transformDirty(); } _transformDirty() { this._worldMatrixDirty = true; for (let i = 0, len = this._childTransforms.length; i < len; i++) { const childTransform = this._childTransforms[i]; childTransform._transformDirty(); if (childTransform._meshes && childTransform._meshes.length > 0) { const meshes = childTransform._meshes; for (let j =0, lenj = meshes.length; j < lenj; j++) { meshes[j]._transformDirty(); } } } if (this._meshes && this._meshes.length > 0) { const meshes = this._meshes; for (let j =0, lenj = meshes.length; j < lenj; j++) { meshes[j]._transformDirty(); } } } _buildWorldMatrix() { const localMatrix = this.matrix; if (!this._parentTransform) { for (let i = 0, len = localMatrix.length; i < len; i++) { this._worldMatrix[i] = localMatrix[i]; } } else { math.mulMat4(this._parentTransform.worldMatrix, localMatrix, this._worldMatrix); } this._worldMatrixDirty = false; } _setSubtreeAABBsDirty(sceneTransform) { sceneTransform._aabbDirty = true; if (sceneTransform._childTransforms) { for (let i = 0, len = sceneTransform._childTransforms.length; i < len; i++) { this._setSubtreeAABBsDirty(sceneTransform._childTransforms[i]); } } } }