diff --git a/examples/src/utils/CustomCameraManager.ts b/examples/src/utils/CustomCameraManager.ts index 35971f08fbe..fec36dd75f7 100644 --- a/examples/src/utils/CustomCameraManager.ts +++ b/examples/src/utils/CustomCameraManager.ts @@ -1,3 +1,4 @@ +import { Plane, Vector3 } from 'three'; import * as THREE from 'three'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; import pull from 'lodash/pull'; @@ -19,6 +20,7 @@ export class CustomCameraManager implements CameraManager { private _controls: OrbitControls; private readonly _cameraChangedListener: Array = []; private _stopEventHandler: DebouncedCameraStopEventTrigger; + private readonly cameraManagerHelper = new CameraManagerHelper(); constructor(domElement: HTMLElement, camera: THREE.PerspectiveCamera) { this._domElement = domElement; @@ -119,7 +121,7 @@ export class CustomCameraManager implements CameraManager { update(deltaTime: number, boundingBox: THREE.Box3): void { this._controls.update(); - CameraManagerHelper.updateCameraNearAndFar(this._camera, boundingBox); + this.cameraManagerHelper.updateCameraNearAndFar(this._camera, boundingBox); } dispose(): void { diff --git a/viewer/packages/camera-manager/src/CameraManagerHelper.ts b/viewer/packages/camera-manager/src/CameraManagerHelper.ts index c3c86b48402..15f7fa9b84f 100644 --- a/viewer/packages/camera-manager/src/CameraManagerHelper.ts +++ b/viewer/packages/camera-manager/src/CameraManagerHelper.ts @@ -2,18 +2,54 @@ * Copyright 2022 Cognite AS */ -import { Box3, PerspectiveCamera, Plane, Quaternion, Vector3 } from 'three'; -import range from 'lodash/range'; import { fitCameraToBoundingBox } from '@reveal/utilities'; +import range from 'lodash/range'; +import { Box3, PerspectiveCamera, Plane, Quaternion, Vector3 } from 'three'; + +type NearAndFarPlaneBuffers = { + corners: Vector3[]; + cameraPosition: Vector3; + cameraDirection: Vector3; +}; + +type CameraFarBuffers = { + nearPlane: Plane; + nearPlaneCoplanarPoint: Vector3; +}; /** * Helper methods for implementing a camera manager. */ export class CameraManagerHelper { /** + * @deprecated usage of mutable static variables is unsafe * Reusable buffers used by updateNearAndFarPlane function to avoid allocations. */ - private static readonly _updateNearAndFarPlaneBuffers = { + private static readonly _updateNearAndFarPlaneBuffers: NearAndFarPlaneBuffers = { + cameraPosition: new Vector3(), + cameraDirection: new Vector3(), + corners: new Array( + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3(), + new Vector3() + ) + }; + + /** + * @deprecated usage of mutable static variables is unsafe + * @private + */ + private static readonly _calculateCameraFarBuffers: CameraFarBuffers = { + nearPlaneCoplanarPoint: new Vector3(), + nearPlane: new Plane() + }; + + private readonly _instanceUpdateNearAndFarPlaneBuffers: NearAndFarPlaneBuffers = { cameraPosition: new Vector3(), cameraDirection: new Vector3(), corners: new Array( @@ -28,10 +64,11 @@ export class CameraManagerHelper { ) }; - private static readonly _calculateCameraFarBuffers = { + private readonly _instanceCalculateCameraFarBuffers: CameraFarBuffers = { nearPlaneCoplanarPoint: new Vector3(), nearPlane: new Plane() }; + /** * Calculates camera target based on new camera rotation. * @param camera Used camera instance. @@ -76,9 +113,53 @@ export class CameraManagerHelper { * Updates near and far plane of the camera based on the bounding box. * @param camera Used camera instance. * @param boundingBox Bounding box of all objects on the scene. + * @deprecated use instance method instead */ static updateCameraNearAndFar(camera: PerspectiveCamera, boundingBox: Box3): void { - const { cameraPosition, cameraDirection, corners } = this._updateNearAndFarPlaneBuffers; + CameraManagerHelper.updateCameraNearAndFarInternal( + camera, + boundingBox, + CameraManagerHelper._updateNearAndFarPlaneBuffers, + CameraManagerHelper._calculateCameraFarBuffers + ); + } + + /** + * Updates near and far plane of the camera based on the bounding box. + * @param camera Used camera instance. + * @param boundingBox Bounding box of all objects on the scene. + */ + public updateCameraNearAndFar(camera: PerspectiveCamera, boundingBox: Box3): void { + CameraManagerHelper.updateCameraNearAndFarInternal( + camera, + boundingBox, + this._instanceUpdateNearAndFarPlaneBuffers, + this._instanceCalculateCameraFarBuffers + ); + } + + /** + * Calculates camera position and target that allows to see the content of provided bounding box. + * @param camera Used camera instance. + * @param boundingBox Bounding box to be fitted. + * @param radiusFactor The ratio of the distance from camera to center of box and radius of the box. + * @returns + */ + static calculateCameraStateToFitBoundingBox( + camera: PerspectiveCamera, + boundingBox: Box3, + radiusFactor: number = 2 + ): { position: Vector3; target: Vector3 } { + return fitCameraToBoundingBox(camera, boundingBox, radiusFactor); + } + + private static updateCameraNearAndFarInternal( + camera: PerspectiveCamera, + boundingBox: Box3, + nearAndFarPlaneBuffers: NearAndFarPlaneBuffers, + cameraFarBuffers: CameraFarBuffers + ): void { + const { cameraPosition, cameraDirection, corners } = nearAndFarPlaneBuffers; this.getBoundingBoxCorners(boundingBox, corners); camera.getWorldPosition(cameraPosition); camera.getWorldDirection(cameraDirection); @@ -89,7 +170,7 @@ export class CameraManagerHelper { // 2. Compute the far distance to the distance from camera to furthest // corner of the bounding box that is "in front" of the near plane - const far = this.calculateCameraFar(near, cameraPosition, cameraDirection, corners); + const far = this.calculateCameraFar(near, cameraPosition, cameraDirection, corners, cameraFarBuffers); // 3. Handle when camera is inside the model by adjusting the near value if (boundingBox.containsPoint(cameraPosition)) { @@ -101,28 +182,14 @@ export class CameraManagerHelper { camera.updateProjectionMatrix(); } - /** - * Calculates camera position and target that allows to see the content of provided bounding box. - * @param camera Used camera instance. - * @param boundingBox Bounding box to be fitted. - * @param radiusFactor The ratio of the distance from camera to center of box and radius of the box. - * @returns - */ - static calculateCameraStateToFitBoundingBox( - camera: PerspectiveCamera, - boundingBox: Box3, - radiusFactor: number = 2 - ): { position: Vector3; target: Vector3 } { - return fitCameraToBoundingBox(camera, boundingBox, radiusFactor); - } - private static calculateCameraFar( near: number, cameraPosition: Vector3, cameraDirection: Vector3, - corners: Array + corners: Array, + cameraFarBuffers: CameraFarBuffers ): number { - const { nearPlane, nearPlaneCoplanarPoint } = this._calculateCameraFarBuffers; + const { nearPlane, nearPlaneCoplanarPoint } = cameraFarBuffers; nearPlaneCoplanarPoint.copy(cameraPosition).addScaledVector(cameraDirection, near); nearPlane.setFromNormalAndCoplanarPoint(cameraDirection, nearPlaneCoplanarPoint); diff --git a/viewer/packages/camera-manager/src/DefaultCameraManager.ts b/viewer/packages/camera-manager/src/DefaultCameraManager.ts index b308bb83a9b..f42f882a250 100644 --- a/viewer/packages/camera-manager/src/DefaultCameraManager.ts +++ b/viewer/packages/camera-manager/src/DefaultCameraManager.ts @@ -56,6 +56,8 @@ export class DefaultCameraManager implements CameraManager { private _isDisposed = false; private _nearAndFarNeedsUpdate = false; + private readonly cameraManagerHelper = new CameraManagerHelper(); + // The active/inactive state of this manager. Does not always match up with the controls // as these are temporarily disabled to block onWheel input during `zoomToCursor`-mode private _isEnabled = true; @@ -427,7 +429,7 @@ export class DefaultCameraManager implements CameraManager { return; } if (this.automaticNearFarPlane) { - CameraManagerHelper.updateCameraNearAndFar(camera, boundingBox); + this.cameraManagerHelper.updateCameraNearAndFar(camera, boundingBox); } if (this.automaticControlsSensitivity) { const diagonal = boundingBox.min.distanceTo(boundingBox.max); diff --git a/viewer/packages/camera-manager/src/Flexible/FlexibleCameraManager.ts b/viewer/packages/camera-manager/src/Flexible/FlexibleCameraManager.ts index b1849f06786..1d1e72ec7e0 100644 --- a/viewer/packages/camera-manager/src/Flexible/FlexibleCameraManager.ts +++ b/viewer/packages/camera-manager/src/Flexible/FlexibleCameraManager.ts @@ -50,6 +50,8 @@ export class FlexibleCameraManager extends PointerEvents implements IFlexibleCam private readonly _raycastCallback: RaycastCallback; private readonly _haveEventListeners: boolean; + private readonly cameraManagerHelper = new CameraManagerHelper(); + //================================================ // CONSTRUCTOR //================================================ @@ -470,7 +472,7 @@ export class FlexibleCameraManager extends PointerEvents implements IFlexibleCam if (!this.options.automaticNearFarPlane) { return; } - CameraManagerHelper.updateCameraNearAndFar(this.camera, boundingBox); + this.cameraManagerHelper.updateCameraNearAndFar(this.camera, boundingBox); } private updateControlsSensitivity(boundingBox: Box3): void { diff --git a/viewer/packages/camera-manager/src/StationaryCameraManager.ts b/viewer/packages/camera-manager/src/StationaryCameraManager.ts index 0b4434186a4..28cbbebb859 100644 --- a/viewer/packages/camera-manager/src/StationaryCameraManager.ts +++ b/viewer/packages/camera-manager/src/StationaryCameraManager.ts @@ -29,6 +29,7 @@ export class StationaryCameraManager implements CameraManager { private readonly _stopEventTrigger: DebouncedCameraStopEventTrigger; private _isDragging = false; private _pointerEventCache: Array = []; + private readonly cameraManagerHelper = new CameraManagerHelper(); constructor(domElement: HTMLElement, camera: PerspectiveCamera) { this._domElement = domElement; @@ -156,7 +157,7 @@ export class StationaryCameraManager implements CameraManager { } update(_: number, boundingBox: Box3): void { - CameraManagerHelper.updateCameraNearAndFar(this._camera, boundingBox); + this.cameraManagerHelper.updateCameraNearAndFar(this._camera, boundingBox); } dispose(): void { diff --git a/viewer/reveal.api.md b/viewer/reveal.api.md index 9f661ab9e1b..b8df6ec12bc 100644 --- a/viewer/reveal.api.md +++ b/viewer/reveal.api.md @@ -333,7 +333,9 @@ export class CameraManagerHelper { }; static calculateNewRotationFromTarget(camera: PerspectiveCamera, newTarget: Vector3): Quaternion; static calculateNewTargetFromRotation(camera: PerspectiveCamera, rotation: Quaternion, currentTarget: Vector3, position: Vector3): Vector3; + // @deprecated static updateCameraNearAndFar(camera: PerspectiveCamera, boundingBox: Box3): void; + updateCameraNearAndFar(camera: PerspectiveCamera, boundingBox: Box3): void; } // @public (undocumented)