Jump to content

Autodesk Forge like camera rotation


BlackMojito
 Share

Recommended Posts

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

Link to comment
Share on other sites

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!

Link to comment
Share on other sites

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...

Link to comment
Share on other sites

  • 2 weeks later...
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

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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...

41516-demo.jpg

 

 

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;
    }
}

 

Link to comment
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...