diff --git a/source/CameraController.ts b/source/CameraController.ts index 8255262..26219f6 100644 --- a/source/CameraController.ts +++ b/source/CameraController.ts @@ -10,6 +10,9 @@ import { Vector3, Matrix4, Box3, + Spherical, + Euler, + Quaternion, } from "three"; import math from "@ff/core/math"; @@ -30,6 +33,9 @@ const _mat4 = new Matrix4(); const _box3 = new Box3(); const _vec3a = new Vector3(); const _vec3b = new Vector3(); +const _vec3c = new Vector3(); +const _eua = new Euler(); +const _quat = new Quaternion(); enum EControllerMode { Orbit, FirstPerson } enum EManipMode { Off, Pan, Orbit, Dolly, Zoom, PanDolly, Roll } @@ -42,6 +48,7 @@ export default class CameraController implements IManip orbit = new Vector3(0, 0, 0); offset = new Vector3(0, 0, 50); + pivot = new Vector3(0, 0, 0); minOrbit = new Vector3(-90, -Infinity, -Infinity); maxOrbit = new Vector3(90, Infinity, Infinity); @@ -146,15 +153,23 @@ export default class CameraController implements IManip this.viewportHeight = height; } + /** + * Copy the object's matrix into the controller's properties + * effectively the inverse operation of updateCamera + */ updateController(object?: Object3D, adaptLimits?: boolean) { const camera = this.camera; object = object || camera; - const orbit = this.orbit; const offset = this.offset; - threeMath.decomposeOrbitMatrix(object.matrix, orbit, offset); - this.orbit.multiplyScalar(threeMath.RAD2DEG); + object.matrix.decompose(_vec3b, _quat, _vec3c); + //Rotation + _eua.setFromQuaternion(_quat, "YXZ"); + _vec3a.setFromEuler(_eua).multiplyScalar(threeMath.RAD2DEG); + this.orbit.copy(_vec3a); + this.offset.copy(_vec3b.sub(this.pivot).applyQuaternion(_quat.invert())); + if (adaptLimits) { this.minOffset.min(offset); @@ -179,12 +194,16 @@ export default class CameraController implements IManip // rotate box to camera space _vec3a.copy(this.orbit).multiplyScalar(math.DEG2RAD); + _quat.setFromEuler(_eua.setFromVector3(_vec3a)); _vec3b.setScalar(0); - threeMath.composeOrbitMatrix(_vec3a, _vec3b, _mat4); + _vec3c.setScalar(1); + //Ignore the pivot point for now + _mat4.compose(_vec3b, _quat, _vec3c); _box3.copy(box).applyMatrix4(_mat4.transpose()); _box3.getSize(_vec3a); _box3.getCenter(_vec3b); + _vec3b.sub(this.pivot); offset.x = _vec3b.x; offset.y = _vec3b.y; @@ -220,19 +239,26 @@ export default class CameraController implements IManip return false; } + //Rotation _vec3a.copy(this.orbit).multiplyScalar(math.DEG2RAD); - _vec3b.copy(this.offset); + _eua.setFromVector3(_vec3a, "YXZ"); + _quat.setFromEuler(_eua); + //Position, relative to pivot point + _vec3b.copy(this.offset).applyEuler(_eua).add(this.pivot); + //Keep scale + _vec3c.setFromMatrixScale(object.matrix); + //Compose everything + object.matrix.compose(_vec3b, _quat, _vec3c); + + + object.matrixWorldNeedsUpdate = true; if (camera.isOrthographicCamera) { - _vec3b.z = this.maxOffset.z; // fixed distance = maxOffset.z camera.size = this.offset.z; // use size to visualize distance camera.far = 2 * this.maxOffset.z; // adjust far clipping camera.updateProjectionMatrix(); } - threeMath.composeOrbitMatrix(_vec3a, _vec3b, object.matrix); - object.matrixWorldNeedsUpdate = true; - return true; } diff --git a/source/math.ts b/source/math.ts index 0545a3e..5b5e1a3 100644 --- a/source/math.ts +++ b/source/math.ts @@ -36,7 +36,9 @@ const math = { QUARTER_PI: 0.78539816339744830961566084581988, DEG2RAD: 0.01745329251994329576923690768489, RAD2DEG: 57.295779513082320876798154814105, - + /** + * one-step orbit matrix composition when the pivot point is (0, 0, 0). + */ composeOrbitMatrix: function(orientation: Vector3, offset: Vector3, result?: Matrix4): Matrix4 { const pitch = orientation.x;