From ccd1c14e0975d8ef062a6bfb57e42d94ed66a407 Mon Sep 17 00:00:00 2001 From: Alexandru Popovici Date: Fri, 5 Jul 2024 10:28:56 +0300 Subject: [PATCH] Threads Undead Patch (#2475) * Fixed sandbox lint error * Gave up with trying to enforce a standard basis across camera controller and applications. Now getPosition and getTarget will always be in whatever basis the camera controller is set to be * No more pre-transformation to (0, 1, 0) for canonical and speckle views and when computin nearest point on geometry * Reverted transformations from onboarding * Updated comments * Fixed the issue where if thread was active at stream load time, the camera position and orientation would be off. This specific issue occured only at startup and it was because the min.max radius for the orbit controls were not yet computed, the frontend calling setView before the orbit controls had a chance to update and compute it's min/max. Also fixed an issue where min distance computation was not using the correct camera positon, but rather the goal position --- .../components/tour/Segmentation.vue | 12 +-- .../frontend-2/lib/tour/slideshowItems.ts | 6 +- packages/viewer-sandbox/src/main.ts | 4 +- .../modules/extensions/CameraController.ts | 74 +++++++++++-------- .../controls/SmoothOrbitControls.ts | 47 +++++++----- .../extensions/controls/SpeckleControls.ts | 3 - 6 files changed, 82 insertions(+), 64 deletions(-) diff --git a/packages/frontend-2/components/tour/Segmentation.vue b/packages/frontend-2/components/tour/Segmentation.vue index e1b226b35..70fbb4f47 100644 --- a/packages/frontend-2/components/tour/Segmentation.vue +++ b/packages/frontend-2/components/tour/Segmentation.vue @@ -64,7 +64,7 @@ diff --git a/packages/frontend-2/lib/tour/slideshowItems.ts b/packages/frontend-2/lib/tour/slideshowItems.ts index b677686ae..3d8523427 100644 --- a/packages/frontend-2/lib/tour/slideshowItems.ts +++ b/packages/frontend-2/lib/tour/slideshowItems.ts @@ -11,7 +11,7 @@ export type SlideshowItem = { export const items = [ { - camPos: [-31.86138, 15.93344, -41.14196, -22.0765, 15.93344, -35.10095, 0, 1], + camPos: [-31.86138, 41.14196, 15.93344, -22.0765, 35.10095, 15.93344, 0, 1], style: {} as Partial, viewed: false, showControls: true, @@ -24,7 +24,7 @@ export const items = [ } }, { - camPos: [-3.3795, 23.25852, -40.78977, -20.65056, 21.78906, -40.72203, 0, 1], + camPos: [-3.3795, 40.78977, 23.25852, -20.65056, 40.72203, 21.78906, 0, 1], style: {} as Partial, viewed: false, showControls: true, @@ -36,7 +36,7 @@ export const items = [ } }, { - camPos: [-39.91711, 42.83686, -46.26069, -18.44162, 34.91624, -29.75982, 0, 1], + camPos: [-39.91711, 46.26069, 42.83686, -18.44162, 29.75982, 34.91624, 0, 1], style: {} as Partial, viewed: false, showControls: false, diff --git a/packages/viewer-sandbox/src/main.ts b/packages/viewer-sandbox/src/main.ts index 91e764351..fff2800f6 100644 --- a/packages/viewer-sandbox/src/main.ts +++ b/packages/viewer-sandbox/src/main.ts @@ -105,7 +105,7 @@ const getStream = () => { // prettier-ignore // 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8?c=%5B-7.66134,10.82932,6.41935,-0.07739,-13.88552,1.8697,0,1%5D' // Revit sample house (good for bim-like stuff with many display meshes) - // 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8' + 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8' // 'https://latest.speckle.dev/streams/c1faab5c62/commits/ab1a1ab2b6' // 'https://speckle.xyz/streams/da9e320dad/commits/5388ef24b8' // 'https://latest.speckle.dev/streams/58b5648c4d/commits/60371ecb2d' @@ -387,7 +387,7 @@ const getStream = () => { // Sum fucking pipes // 'https://app.speckle.systems/projects/122448a81e/models/f21aff1f4a' // Thin plane - 'https://app.speckle.systems/projects/20f72acc58/models/2cf8a736f8' + // 'https://app.speckle.systems/projects/20f72acc58/models/2cf8a736f8' ) } diff --git a/packages/viewer/src/modules/extensions/CameraController.ts b/packages/viewer/src/modules/extensions/CameraController.ts index dfb0bda0a..4e09e96cc 100644 --- a/packages/viewer/src/modules/extensions/CameraController.ts +++ b/packages/viewer/src/modules/extensions/CameraController.ts @@ -22,8 +22,8 @@ import { FlyControls, FlyControlsOptions } from './controls/FlyControls' import { SpeckleControls } from './controls/SpeckleControls' import { GeometryType } from '../batching/Batch' -const UP: Vector3 = new Vector3(0, 1, 0) -const quatBuff = new Quaternion() +// const UP: Vector3 = new Vector3(0, 1, 0) +// const quatBuff = new Quaternion() export enum NearPlaneCalculation { EMPIRIC, @@ -405,8 +405,7 @@ export class CameraController extends Extension implements SpeckleCamera { const renderer = this.viewer.getRenderer() if (!renderer.renderingCamera) return - const camera = renderer.renderingCamera as PerspectiveCamera - const minDist = this.getClosestGeometryDistance(camera) + const minDist = this.getClosestGeometryDistance() if (minDist === Number.POSITIVE_INFINITY) { this.updateNearCameraPlaneEmpiric(targetVolume, offsetScale) return @@ -456,12 +455,9 @@ export class CameraController extends Extension implements SpeckleCamera { renderer.renderingCamera.updateProjectionMatrix() } - protected getClosestGeometryDistance(camera: PerspectiveCamera): number { - const cameraPosition = camera.position + protected getClosestGeometryDistance(): number { + const cameraPosition = this._renderingCamera.position const cameraTarget = this.getTarget() - cameraTarget.applyQuaternion( - quatBuff.setFromUnitVectors(UP, this._activeControls.up) - ) const cameraDir = new Vector3().subVectors(cameraTarget, cameraPosition).normalize() const batches = this.viewer @@ -583,13 +579,9 @@ export class CameraController extends Extension implements SpeckleCamera { } protected setViewSpeckle(view: SpeckleView, transition = true) { - /** SpeckleViews assume Z up, so we pre-transform to Z forward */ - const quat = new Quaternion() - .setFromUnitVectors(new Vector3(0, 1, 0), new Vector3(0, 0, 1)) - .invert() this._activeControls.fromPositionAndTarget( - new Vector3(view.origin.x, view.origin.y, view.origin.z).applyQuaternion(quat), - new Vector3(view.target.x, view.target.y, view.target.z).applyQuaternion(quat) + new Vector3(view.origin.x, view.origin.y, view.origin.z), + new Vector3(view.target.x, view.target.y, view.target.z) ) if (!transition) this._activeControls.jumpToGoal() @@ -608,19 +600,23 @@ export class CameraController extends Extension implements SpeckleCamera { this.viewer.World.worldBox.getBoundingSphere(targetSphere) const distance = this.fitToRadius(targetSphere.radius) - const canonicalPosition = new Vector3() - .copy(this.viewer.World.worldBox.getCenter(new Vector3())) - .applyQuaternion( - new Quaternion() - .setFromUnitVectors(new Vector3(0, 1, 0), new Vector3(0, 0, 1)) - .invert() - ) - const canonicalTarget = new Vector3().copy(canonicalPosition) + const canonicalPosition = new Vector3().copy( + this.viewer.World.worldBox.getCenter(new Vector3()) + ) + const canonicalTarget = new Vector3().copy(canonicalPosition) + const controlerBasis = new Quaternion().setFromUnitVectors( + new Vector3(0, 1, 0), + this._activeControls.up + ) switch (side) { case 'front': this._activeControls.fromPositionAndTarget( - canonicalPosition.add(new Vector3(0, 0, 1).multiplyScalar(distance)), + canonicalPosition.add( + new Vector3(0, 0, 1) + .applyQuaternion(controlerBasis) + .multiplyScalar(distance) + ), canonicalTarget ) if (this._renderingCamera === this.orthographicCamera) this.disableRotations() @@ -628,7 +624,11 @@ export class CameraController extends Extension implements SpeckleCamera { case 'back': this._activeControls.fromPositionAndTarget( - canonicalPosition.add(new Vector3(0, 0, -1).multiplyScalar(distance)), + canonicalPosition.add( + new Vector3(0, 0, -1) + .applyQuaternion(controlerBasis) + .multiplyScalar(distance) + ), canonicalTarget ) if (this._renderingCamera === this.orthographicCamera) this.disableRotations() @@ -637,7 +637,11 @@ export class CameraController extends Extension implements SpeckleCamera { case 'up': case 'top': this._activeControls.fromPositionAndTarget( - canonicalPosition.add(new Vector3(0, 1, 0).multiplyScalar(distance)), + canonicalPosition.add( + new Vector3(0, 1, 0) + .applyQuaternion(controlerBasis) + .multiplyScalar(distance) + ), canonicalTarget ) if (this._renderingCamera === this.orthographicCamera) this.disableRotations() @@ -646,7 +650,11 @@ export class CameraController extends Extension implements SpeckleCamera { case 'down': case 'bottom': this._activeControls.fromPositionAndTarget( - canonicalPosition.add(new Vector3(0, -1, 0).multiplyScalar(distance)), + canonicalPosition.add( + new Vector3(0, -1, 0) + .applyQuaternion(controlerBasis) + .multiplyScalar(distance) + ), canonicalTarget ) if (this._renderingCamera === this.orthographicCamera) this.disableRotations() @@ -654,7 +662,11 @@ export class CameraController extends Extension implements SpeckleCamera { case 'right': this._activeControls.fromPositionAndTarget( - canonicalPosition.add(new Vector3(1, 0, 0).multiplyScalar(distance)), + canonicalPosition.add( + new Vector3(1, 0, 0) + .applyQuaternion(controlerBasis) + .multiplyScalar(distance) + ), canonicalTarget ) if (this._renderingCamera === this.orthographicCamera) this.disableRotations() @@ -662,7 +674,11 @@ export class CameraController extends Extension implements SpeckleCamera { case 'left': this._activeControls.fromPositionAndTarget( - canonicalPosition.add(new Vector3(-1, 0, 0).multiplyScalar(distance)), + canonicalPosition.add( + new Vector3(-1, 0, 0) + .applyQuaternion(controlerBasis) + .multiplyScalar(distance) + ), canonicalTarget ) if (this._renderingCamera === this.orthographicCamera) this.disableRotations() diff --git a/packages/viewer/src/modules/extensions/controls/SmoothOrbitControls.ts b/packages/viewer/src/modules/extensions/controls/SmoothOrbitControls.ts index b9c6ac7f3..923135e0a 100644 --- a/packages/viewer/src/modules/extensions/controls/SmoothOrbitControls.ts +++ b/packages/viewer/src/modules/extensions/controls/SmoothOrbitControls.ts @@ -237,10 +237,13 @@ export class SmoothOrbitControls extends SpeckleControls { const v0 = new Vector3().copy(position) const v1 = new Vector3().copy(target) - v0.sub(v1) + /** Three.js Spherical assumes (0, 1, 0) as up... */ + v0.sub(v1).applyMatrix4(this._basisTransformInv) const spherical = new Spherical() spherical.setFromCartesianCoords(v0.x, v0.y, v0.z) this.setOrbit(spherical.theta, spherical.phi, spherical.radius) + /** Three.js Spherical assumes (0, 1, 0) as up... */ + v1.applyMatrix4(this._basisTransformInv) this.setTarget(v1.x, v1.y, v1.z) } @@ -263,21 +266,17 @@ export class SmoothOrbitControls extends SpeckleControls { } /** - * Gets the current goal position. Needs to be in a basis with (0,1,0) as up + * Gets the current goal position */ public getPosition(): Vector3 { - return this.positionFromSpherical(this.goalSpherical, this.origin).applyMatrix4( - this._basisTransformInv - ) + return this.positionFromSpherical(this.goalSpherical, this.goalOrigin) } /** * Gets the point in model coordinates the model should orbit/pivot around. - * Needs to be in a basis with (0,1,0) as up - * We keep goalOrigin untransformed, so there is no need to transform back from the controller's defined basis */ public getTarget(): Vector3 { - return this.goalOrigin.clone() + return this.goalOrigin.clone().applyMatrix4(this._basisTransform) } public isStationary(): boolean { @@ -304,6 +303,19 @@ export class SmoothOrbitControls extends SpeckleControls { this.setFieldOfView(Math.exp(this.goalLogFov)) } + /** Computes min/max radius values based on the current world size */ + protected computeMinMaxRadius() { + if (this.world) { + const maxDistance = this.world.getRelativeOffset(10) + const minDistance = this.world.getRelativeOffset(0.01) + if (!isNaN(maxDistance) && !isNaN(minDistance)) + Object.assign(this._options, { + maximumRadius: maxDistance, + minimumRadius: minDistance + }) + } + } + /** * Set the absolute orbital goal of the camera. The change will be * applied over a number of frames depending on configured acceleration and @@ -326,6 +338,10 @@ export class SmoothOrbitControls extends SpeckleControls { maximumRadius } = this._options + if (isNaN(minimumRadius) || isNaN(maximumRadius)) { + this.computeMinMaxRadius() + } + const { theta, phi, radius } = this.goalSpherical const nextTheta = clamp(goalTheta, minimumAzimuthalAngle, maximumAzimuthalAngle) @@ -512,15 +528,7 @@ export class SmoothOrbitControls extends SpeckleControls { return false } - if (this.world) { - const maxDistance = this.world.getRelativeOffset(10) - const minDistance = this.world.getRelativeOffset(0.01) - this.applyOptions({ - maximumRadius: maxDistance, - minimumRadius: minDistance - }) - // radiusNormalisationRange = this.world.worldBox.getSize(new Vector3()).length() - } + this.computeMinMaxRadius() const { maximumPolarAngle } = this._options @@ -644,6 +652,7 @@ export class SmoothOrbitControls extends SpeckleControls { return height / (Math.tan(MathUtils.DEG2RAD * Math.exp(this.logFov) * 0.5) * 2) }*/ + /** Three.js Spherical assumes (0, 1, 0) as up... */ protected positionFromSpherical(spherical: Spherical, origin?: Vector3) { const position: Vector3 = new Vector3() position.setFromSpherical(spherical) @@ -655,6 +664,7 @@ export class SmoothOrbitControls extends SpeckleControls { return position } + /** Three.js Spherical assumes (0, 1, 0) as up... */ protected quaternionFromSpherical(spherical: Spherical) { const quaternion: Quaternion = new Quaternion() quaternion.setFromEuler( @@ -823,7 +833,8 @@ export class SmoothOrbitControls extends SpeckleControls { this.panPerPixel dxy.multiplyScalar(metersPerPixel) - const target = this.getTarget() + /** This panProjection assumes (0, 1, 0) as up... */ + const target = this.getTarget().applyMatrix4(this._basisTransformInv) target.add(dxy.applyMatrix3(this.panProjection)) this.setTarget(target.x, target.y, target.z) } diff --git a/packages/viewer/src/modules/extensions/controls/SpeckleControls.ts b/packages/viewer/src/modules/extensions/controls/SpeckleControls.ts index d5d94407d..21d7cce86 100644 --- a/packages/viewer/src/modules/extensions/controls/SpeckleControls.ts +++ b/packages/viewer/src/modules/extensions/controls/SpeckleControls.ts @@ -23,10 +23,7 @@ export abstract class SpeckleControls extends EventEmitter { abstract fitToSphere(sphere: Sphere): void abstract dispose(): void - /** The input position and target vectors will always be in basis where (0,1,0) is up */ abstract fromPositionAndTarget(position: Vector3, target: Vector3): void - /** Always in basis where (0,1,0) is up */ abstract getTarget(): Vector3 - /** Always in basis where (0,1,0) is up */ abstract getPosition(): Vector3 }