Orbit Camera | Can I smoothly transition from one focus entity to another?

https://playcanvas.com/editor/scene/663539

Hi - I want to create a scene that lerps from one camera position to the next but utilizes the orbit camera controls so the user can still pan around on mobile and with the mouse.

When I use the playcanvas orbit-controls.js script that I found it wants to lock the camera back on the focus entity instead of locking in the new position.

I have tried to update the focus entity but what I really want to do is smoothly transition from one position to the next while maintaining orbit control around the new position.

Any help would be greatly appreciated. :pray:t5:

1 Like

The orbit camera script has an ‘API’ of sorts. Changing the attributes directly may cause unusual behaviour.

Look at line 31 to 154, it defines properties and functions that you can use to change the focus entity, position, distance from the object etc.

If you want a smooth transition from one focus point to another, resetAndLookAtPoint where you lerp between the first focus position to your new one.

Thanks so much!

Ok when I removed everything from resetAndLookAtPoint it did not really change anything but when I removed everything out of ._updatePosition it stopped returning to the initial camera position.

I assume my issue was in these lines:

Line 275
https://playcanvas.com/editor/code/585975?tabs=15413027

   var position = this.entity.getPosition();
    position.copy(this.entity.forward);
  position.scale(-this._distance);
    position.add(this.pivotPoint);
    this.entity.setPosition(position);

However I have another issue now - I would like to update the rotation of the camera via script - I can do it via tween but it always resets to the previous on-screen rotation.https://playcanvas.com/editor/code/585975?tabs=15413027

I assume it is resetting to the previous pitch and yaw - but anything that I’ve done to make that (pitch/yaw/rotation) sticky has not worked without breaking orbitcontrols.

Any help would be great

Erm… I’m a little confused. You should be able to get the functionality you are asking for without having to modify orbit-camera.js at all.

Edit: I was suggesting to use resetAndLookAtPoint to help with the lerp of position of the target position.

The mouse and touch input scripts are examples of how you should be using the orbit-camera script to change the camera position and orientation using the ‘API’. Anything with an ‘_’ before the function, property or variable name shouldn’t be used.

I felt the same way - I just wanted to lerp the camera position from one location to the next while maintaining the ability to pan.

All I added was a keydown function:

OrbitCamera.prototype.onKeyDown = function (evt) {
    

     var c = this.entity.tween(this.entity.getLocalPosition()).to({x:3.369925535554434,y:1.7481692197074716,z:1.641842059443477},1,pc.QuarticInOut).start();
     var d = this.entity.tween(this.entity.getLocalRotation()).to({x:-0.2765800434860104,y:0.28890630692846697,z:0.08758318746652911,w:0.9123408412624274},1,pc.QuarticInOut).start();
     
   
};

I wanted to move the camera from one position to the next but it kept reverting to the original camera position.

Hi,

I’m experimenting with resetAndLookAtPoint. I get it to switch to a new position and target, but - am I supposed to get help with lerping there too? I would like to lerp there smoothly rather than cutting sharp. Possible out of the box with that script?

regards Björn

Hi @bjorn.syse,

You can do so, with some minimal code changes, check my answer here:

2 Likes

Thanks, I’ll look into that as soon as I’ve figured out this wicked thing:

I try to use resetAndLookAtPoint by passing a new camera position which I’ve defined as an Entity attribute to my script. Like this:

Hotspot.attributes.add('cameraPosition', {
    type: 'entity',
    title: 'Hotspot camera position',
    description: ''
});

However, depending on if that entity reference that I pull in is situated in the document root or if it is down the hierarchy with some parents - I get different world positions. So if it’s down a hierarchy every time I try to move the camera to it, it moves a litte, some sort of incrementation. It’s really strange and I’ve been trying to isolate it for that past hour.

I use this code to move the camera, using the world position on the entity:

console.log("Moving camera to Hotspot world position: " + this.cameraPosition.getPosition());
            this.cameraEntity.script.orbitCamera.resetAndLookAtPoint(this.cameraPosition.getPosition(), this.entity.getPosition());

Result if that new cameraPosition entity is in a hierarchy below the hotspot:

Result if it’s in the root of the graph node:

Is there an example project you can share with this odd behaviour please?

1 Like

Sure, have a look here:

https://playcanvas.com/editor/project/715879

1 Like

Did you have a chance to look at this btw?

Hmm. I’m struggling a little bit with getting my head around this one. My goal is to have a camera position and target (do I even need both? is target = pivot, where the camera orbits around?) associated with every hotspot - so when I click a hotspot, I lerp the camera position there over let’s say the course of half a second. Much like in the hotspots over at sketchfab:

Inertia does this somewhat. What and where would I edit orbit-camera.js in order to add that functionality. The method I’m calling from the shapepicker is the resetAndLookAtPoint. Can I add it in there?

I think I may have some code somewhere that does this, I will get back to you later in the day.

2 Likes

Thanks, that would be much appreciated!

I’m slightly confused. I removed these two lines at the bottom of this function, and somehow that does a little lerp using the inertia code. It’s not perfect, but anyway.

// Set the camera position to a world position and look at a world position
// Useful if you have multiple viewing angles to swap between in a scene
OrbitCamera.prototype.resetAndLookAtPoint = function (resetPoint, lookAtPoint) {
    
    this.pivotPoint.copy(lookAtPoint);
    
    // Setting the world pos of this camera entity, and directing it to lookAtPoint.
    this.entity.setPosition(resetPoint);
    this.entity.lookAt(lookAtPoint);

    var distance = OrbitCamera.distanceBetween;
    distance.sub2(lookAtPoint, resetPoint);
    this.distance = distance.length();

    // Setting the pivot point the second time?
    this.pivotPoint.copy(lookAtPoint);
    
    // Setting yaw and pitch after values calculatred from the camera being moved. 
    var cameraQuat = this.entity.getRotation();
    this.yaw = this._calcYaw(cameraQuat);
    this.pitch = this._calcPitch(cameraQuat, this.yaw);

    //this._removeInertia();
    //this._updatePosition();
};

I’d love to get a nice lerp between camera positions (and targets) in place. I’m also curious to see if the general navigation experience can be improved by automatically updating the pivot point with whatever the user clicks on while orbiting, perhaps using a raycast or similar. Do you have any experience/thoughts on that? Orbiting can be a little weird if one is constantly changing between a zoomed in an zoomed out vantage point, if the pivot point stays in the middle of the entire world if you know what I mean. Compare the Sketchfab example above - when double click.

Sorry, I was on holiday for a week and haven’t had a chance to come back to this.

Here you go, only change I did in the default orbit-camera.js script is introduce this:

this._targetPivotPoint = new pc.Vec3();

Added a single line in the update method:

OrbitCamera.prototype.update = function(dt) {
    // Add inertia, if any
    var t = this.inertiaFactor === 0 ? 1 : Math.min(dt / this.inertiaFactor, 1);
    
    this._targetPivotPoint.lerp(this._targetPivotPoint, this._pivotPoint, 0.1 * t);
    
    this._distance = pc.math.lerp(this._distance, this._targetDistance, t);
    this._yaw = pc.math.lerp(this._yaw, this._targetYaw, t);
    this._pitch = pc.math.lerp(this._pitch, this._targetPitch, t);

    this._updatePosition();
};

And last change the used pivot property in the _updatePosition method:

OrbitCamera.prototype._updatePosition = function () {
    // Work out the camera position based on the pivot point, pitch, yaw and distance
    this.entity.setLocalPosition(0,0,0);
    this.entity.setLocalEulerAngles(this._pitch, this._yaw, 0);

    var position = this.entity.getPosition();
    position.copy(this.entity.forward);
    position.scale(-this._distance);
    position.add(this._targetPivotPoint);
    this.entity.setPosition(position);
};

https://playcanvas.com/editor/scene/984904

Of course this won’t lerp rotations / camera angles. For that you will have to create a more elaborate system. I did something similar on this project:

2 Likes

ah, hmm. Yes, that would do something with the pivot point but the Aphrodite stuff you linked to looks closer to what I’m after. I’ll give that a try and see.

1 Like

Looking at the orbit-camera script, you could lerp between the current and target pivot point and camera position by calling resetAndLookAtPoint each frame via an external script.

Here’s a basic example with some missing functionality (press 1 to move between points).

I would still need to disable the mouse and touch controls during transition and also not use that lerp function. The tween library from PlayCanvas is probably better for this and I would base the duration of the transition on the distance between the starting point and the target point.

https://playcanvas.com/editor/scene/984964

Edit: alternatively, I would probably change it so that I transition the distance, yaw and pitch as individual components rather than moving the camera position for a smoother orbit camera like transition :thinking:

3 Likes

Thanks both for taking a look. Wasn’t aware about the Tween.js at first, thanks for that too. I ended up licening a copy of the Aphrodite starter kit @Leonidas linked to. It’s very well written and easy to extend if needed!

1 Like