BlackMojito Posted October 23, 2017 Share Posted October 23, 2017 Hi Folks, I would like to implement a camera which behaves similarly like the ArcRotateCamera but instead of rotating around the target, it needs to behave like that the object/scene itself rotates around its center. How can I achieve that? It seems that ArcRotateCamera does not have an optional pivot which I can set. Here is what I really want, https://viewer-rocks.autodesk.io/ Thanks a lot Quote Link to comment Share on other sites More sharing options...
RaananW Posted October 23, 2017 Share Posted October 23, 2017 Howdy @xuchen_shadow, I guess you can achieve this with setTarget. Check this (vry simple) example - https://playground.babylonjs.com/#88WH5C . Click on one of the meshes, and you will rotate around it. You can also pan (using the control button and your mouse at the same time) to change the pivot point. Hope this helps! Quote Link to comment Share on other sites More sharing options...
BlackMojito Posted October 23, 2017 Author Share Posted October 23, 2017 2 minutes ago, RaananW said: Howdy @xuchen_shadow, I guess you can achieve this with setTarget. Check this (vry simple) example - https://playground.babylonjs.com/#88WH5C . Click on one of the meshes, and you will rotate around it. You can also pan (using the control button and your mouse at the same time) to change the pivot point. Hope this helps! Yeah but in your sample, the picked objects is always at the center of the scene because it is set as the target of the Camera. But in Forge Viewer, even the object is not at the center of the viewport, the rotation still works as if it was at the center... Quote Link to comment Share on other sites More sharing options...
RaananW Posted October 23, 2017 Share Posted October 23, 2017 You are right, we always rotate around the center. I couldn't quite rotate around an object that is not currently centered using the autodesk viewer. Want to show me how? Quote Link to comment Share on other sites More sharing options...
BlackMojito Posted October 23, 2017 Author Share Posted October 23, 2017 I think it does first a translation, something like T = objCenter - target, then does the rotation normally. Finally translate back by TBack = - (rotation * T) ? Quote Link to comment Share on other sites More sharing options...
RaananW Posted October 23, 2017 Share Posted October 23, 2017 No, what I mean is that I can't see it happening in the Autodesk viewer as well Quote Link to comment Share on other sites More sharing options...
BlackMojito Posted November 1, 2017 Author Share Posted November 1, 2017 On 10/23/2017 at 8:35 PM, RaananW said: No, what I mean is that I can't see it happening in the Autodesk viewer as well Hi RaananW, If you click this link https://viewer-rocks.autodesk.io/, you can see that there are models that we can play with (rotate, pan, zoom). Obviously the camera rotates around a pivot which I guess is the center of the model's bounding sphere. I tried to customize ArcRotateCamera but it still cannot work like theirs. Could you please provide me some ideas? Thanks Quote Link to comment Share on other sites More sharing options...
BlackMojito Posted November 1, 2017 Author Share Posted November 1, 2017 Basically, the problem of the existing ArcRotateCamera is that when the object is not at the center of the viewport, the rotation is not correct because the camera always rotates around its target. Quote Link to comment Share on other sites More sharing options...
aWeirdo Posted November 1, 2017 Share Posted November 1, 2017 Hi @xuchen_shadow I see what you mean, and to me, It looks like they are just rotating/moving the mesh(es) and the camera is stationary, i didn't look in their code, but it would be the easiest way to achieve this effect.Edit; i had a few minutes, and this is basicly it. https://playground.babylonjs.com/#X43N4U#2 Box is parented to ground, Manipulates; ground position instead of panning, ground rotation y instead of camera alpha for 360* rotation, and last, camera beta for the up and down rotation. Quote Link to comment Share on other sites More sharing options...
BlackMojito Posted November 1, 2017 Author Share Posted November 1, 2017 54 minutes ago, aWeirdo said: Hi @xuchen_shadow I see what you mean, and to me, It looks like they are just rotating/moving the mesh(es) and the camera is stationary, i didn't look in their code, but it would be the easiest way to achieve this effect.Edit; i had a few minutes, and this is basicly it. https://playground.babylonjs.com/#X43N4U#2 Box is parented to ground, Manipulates; ground position instead of panning, ground rotation y instead of camera alpha for 360* rotation, and last, camera beta for the up and down rotation. No, I am pretty sure that they are modifying the view matrix instead of using your approach. In Autodesk Fusion 360, they have the same camera stuff. I know that it is a camera with a pivot, but I just don't know how to calculate the view matrix. Quote Link to comment Share on other sites More sharing options...
BlackMojito Posted November 1, 2017 Author Share Posted November 1, 2017 Below is my small class. I still don't know what is missing. The basic idea is that the somehow like what the image below illustrates. a is the pivot and b is the target... import * as BABYLON from 'babylonjs' import * as MathUtils from '../../Utils/MathUtils' export class ArcRotatePivotCamera extends BABYLON.ArcRotateCamera { private _pivot: BABYLON.Vector3 | null; private _oldAlpha: number; private _oldBeta: number; private _pivotToEyeDistance: number = 0; constructor(name: string, alpha: number, beta: number, radius: number, target: BABYLON.Vector3, scene: BABYLON.Scene) { super(name, alpha, beta, radius, target, scene); this._oldAlpha = alpha; this._oldBeta = beta; this._pivotToEyeDistance = radius; } get pivot(): BABYLON.Vector3 | null { return this._pivot; } set pivot(pivot: BABYLON.Vector3 | null) { this._pivot = pivot; } private updatePivotToEyeDistance(): void { let pivotToTargetLengthSquared = this._getTargetPosition().subtract(this._pivot).lengthSquared(); this._pivotToEyeDistance = Math.sqrt(pivotToTargetLengthSquared + this.radius * this.radius); } private updateAlphaBeta(): void { let diff: BABYLON.Vector3 = this._newPosition.subtract(this._pivot); this.beta = Math.acos(diff.z / this._pivotToEyeDistance); this.alpha = Math.atan2(diff.y, diff.x); } _checkInputs(): void { this._oldAlpha = this.alpha; this._oldBeta = this.beta; //if (async) collision inspection was triggered, don't update the camera's position - until the collision callback was called. if (this._collisionTriggered) { return; } this.inputs.checkInputs(); // Zoom Inertia if (this.inertialRadiusOffset !== 0) { this.radius -= this.inertialRadiusOffset; let viewDir = this._target.subtract(this._newPosition).normalize(); this._newPosition.addInPlace(viewDir.scaleInPlace(this.inertialRadiusOffset)); this.updatePivotToEyeDistance(); this.updateAlphaBeta(); this.inertialRadiusOffset *= this.inertia; if (Math.abs(this.inertialRadiusOffset) < this.speed * BABYLON.Epsilon) this.inertialRadiusOffset = 0; } // Rotation Inertia if (this.inertialAlphaOffset !== 0 || this.inertialBetaOffset !== 0) { if (this.getScene().useRightHandedSystem) { this.alpha -= this.beta <= 0 ? -this.inertialAlphaOffset : this.inertialAlphaOffset; } else { this.alpha += this.beta <= 0 ? -this.inertialAlphaOffset : this.inertialAlphaOffset; } this.beta += this.inertialBetaOffset; this.inertialAlphaOffset *= this.inertia; this.inertialBetaOffset *= this.inertia; if (Math.abs(this.inertialAlphaOffset) < BABYLON.Epsilon) this.inertialAlphaOffset = 0; if (Math.abs(this.inertialBetaOffset) < BABYLON.Epsilon) this.inertialBetaOffset = 0; } // Panning inertia if (this.inertialPanningX !== 0 || this.inertialPanningY !== 0) { if (!this._localDirection) { this._localDirection = BABYLON.Vector3.Zero(); this._transformedDirection = BABYLON.Vector3.Zero(); } this._localDirection.copyFromFloats(this.inertialPanningX, this.inertialPanningY, this.inertialPanningY); this._localDirection.multiplyInPlace(this.panningAxis); this._viewMatrix.invertToRef(this._cameraTransformMatrix); BABYLON.Vector3.TransformNormalToRef(this._localDirection, this._cameraTransformMatrix, this._transformedDirection); //Eliminate y if map panning is enabled (panningAxis == 1,0,1) if (!this.panningAxis.y) { this._transformedDirection.y = 0; } if (!this._targetHost) { if (this.panningDistanceLimit) { this._transformedDirection.addInPlace(this._target); var distanceSquared = BABYLON.Vector3.DistanceSquared(this._transformedDirection, this.panningOriginTarget); if (distanceSquared <= (this.panningDistanceLimit * this.panningDistanceLimit)) { let oldTarget = this.target.clone(); this._target.copyFrom(this._transformedDirection); let diff = this._target.subtract(oldTarget); this._newPosition.addInPlace(diff); } } else { this._target.addInPlace(this._transformedDirection); this._newPosition.addInPlace(this._transformedDirection); } } this.updatePivotToEyeDistance(); this.updateAlphaBeta(); this.inertialPanningX *= this.panningInertia; this.inertialPanningY *= this.panningInertia; if (Math.abs(this.inertialPanningX) < this.speed * BABYLON.Epsilon) this.inertialPanningX = 0; if (Math.abs(this.inertialPanningY) < this.speed * BABYLON.Epsilon) this.inertialPanningY = 0; } // Limits this._checkLimits(); this.onAfterCheckInputsObservable.notifyObservers(this); } _getViewMatrix(): BABYLON.Matrix { // Compute let cosa = Math.cos(this.alpha); let sina = Math.sin(this.alpha); let cosb = Math.cos(this.beta); let sinb = Math.sin(this.beta); if (sinb === 0) { sinb = 0.0001; } let target = this._getTargetPosition(); let rotateEnd = new BABYLON.Vector3(cosa * sinb, cosb, sina * sinb); let trackballVector = rotateEnd.scale(this.radius); let rotationMatrix: BABYLON.Matrix = null; if (this._pivot) { let cosaOld = Math.cos(this._oldAlpha); let sinaOld = Math.sin(this._oldAlpha); let cosbOld = Math.cos(this._oldBeta); let sinbOld = Math.sin(this._oldBeta); if (sinbOld === 0) { sinbOld = 0.0001; } let pivotToEye = this._newPosition.subtract(this._pivot); let targetToEye = this._newPosition.subtract(this._pivot); let targetDist = targetToEye.length(); targetToEye.normalize(); let rotateStart = new BABYLON.Vector3(cosaOld * sinbOld, cosbOld, sinaOld * sinbOld); let transform = MathUtils.rotationMatrixBetweenVectors(rotateStart, rotateEnd); pivotToEye = BABYLON.Vector3.TransformNormal(pivotToEye, transform); targetToEye = BABYLON.Vector3.TransformNormal(targetToEye, transform); this._newPosition = this._pivot.add(pivotToEye); target = this._newPosition.subtract(targetToEye.scale(targetDist)); } if (this.getScene().collisionsEnabled && this.checkCollisions) { if (!this._collider) { this._collider = new BABYLON.Collider(); } this._collider.radius = this.collisionRadius; this._newPosition.subtractToRef(this.position, this._collisionVelocity); this._collisionTriggered = true; this.getScene().collisionCoordinator.getNewPosition(this.position, this._collisionVelocity, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId); } else { this.position.copyFrom(this._newPosition); let up = this.upVector; if (this.allowUpsideDown && sinb < 0) { up = up.clone(); up = up.negate(); } if (this.getScene().useRightHandedSystem) { BABYLON.Matrix.LookAtRHToRef(this.position, target, up, this._viewMatrix); } else { BABYLON.Matrix.LookAtLHToRef(this.position, target, up, this._viewMatrix); } this._viewMatrix.m[12] += this.targetScreenOffset.x; this._viewMatrix.m[13] += this.targetScreenOffset.y; } this._currentTarget = target; return this._viewMatrix; } } Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.