Drag to rotate an object in local space

Back again ! I was wondering how could we implement inertia to the rotation in this script. Something similar to the orbit-camera. Any ideas?

Hi @contractkiller,

Check this thread, @LeXXik presented some code on how to smooth out movement from one position to the other. You could something similar using lerp() to smooth out your target rotation:

https://forum.playcanvas.com/t/moving-an-entity-from-one-position-to-another-smoothly/14735/6

1 Like

The orbit camera cheats as it’s actually rotating slower than it would be without inertia. All it’s really doing is lerping to the target rotation value :grimacing:

Combining your statement with what Leonidas said, we need to have a target rotation value to lerp to. If thats the case, what would be the target rotation value and should we have a property like below,

Object.defineProperty(OrbitCamera.prototype, "yaw", {
//     get: function() {
//         return this._targetYaw;
//     },

//     set: function(value) {
//         this._targetYaw = value;

//         // Ensure that the yaw takes the shortest route by making sure that 
//         // the difference between the targetYaw and the actual is 180 degrees
//         // in either direction
//         var diff = this._targetYaw - this._yaw;
//         var reminder = diff % 360;
//         if (reminder > 180) {
//             this._targetYaw = this._yaw - (360 - reminder);
//         } else if (reminder < -180) {
//             this._targetYaw = this._yaw + (360 + reminder);
//         } else {
//             this._targetYaw = this._yaw + reminder;
//         }
//     }
// });

You don’t have to. All you really needs it the target rotation and the current rotation and have the latter lerp towards the former in the update. The input just changes the target rotation.

Hi @yaustar I tried the following for the lerp rotation, but it didnt seem to be working as expected. Am I missing something?

Rotate.attributes.add('inertiaFactor', {
    type: 'number',
    default: 0,
    title: 'Inertia Factor',
    description: 'Higher value means that the object will continue moving after the user has stopped dragging. 0 is fully responsive.'
});

Rotate.attributes.add('offsetAngle', {
    type: 'number',
    default: 10,
    title: 'Inertia Angle',
    description: 'Inertia Angle in degree. Object rotates additionally to this degree after user stopped dragging'
});

Rotate.prototype.initialize = function() {
    this.xAngle = 0;
    this.yAngle = 0;
      
    this.app.mouse.on(pc.EVENT_MOUSEMOVE, this.onMouseMove, this);
    
    this.lastTouchPoint = new pc.Vec2();
    if (this.app.touch) {
        this.app.touch.on(pc.EVENT_TOUCHSTART, this.onTouchStart, this);
        this.app.touch.on(pc.EVENT_TOUCHMOVE, this.onTouchMove, this);        
    }
};

Rotate.prototype.update = function(dt){
    
    var xTargetAngle = Rotate.xTargetAngle;
    xTargetAngle =  this.xAngle+this.offsetAngle;
    var yTargetAngle = Rotate.yTargetAngle;
    yTargetAngle = this.yAngle+this.offsetAngle;
  
    var t = this.inertiaFactor === 0 ? 1 : Math.min(dt / this.inertiaFactor, 1);
    this.xAngle = pc.math.lerp(this.xAngle, xTargetAngle, t);
    this.yAngle = pc.math.lerp(this.yAngle, yTargetAngle, t);
};

Rotate.prototype.rotate = function (dx, dy) {
    var horzQuat = Rotate.horizontalQuat;
    var vertQuat = Rotate.verticalQuat;
    var resultQuat = Rotate.resultQuat;
    
    this.xAngle += dy * this.orbitSensitivity;
    this.xAngle = pc.math.clamp(this.xAngle, -90, 90);
    
    this.yAngle += dx * this.orbitSensitivity;

    horzQuat.copy(vertQuat.copy(pc.Quat.IDENTITY));
    
    vertQuat.setFromAxisAngle(this.cameraEntity.right, this.xAngle);    
    this.entity.setRotation(vertQuat);
    
    horzQuat.setFromAxisAngle(this.entity.up, this.yAngle);
    resultQuat.mul2(horzQuat, this.entity.getRotation());    
    this.entity.setRotation(resultQuat);
};

Rotate.horizontalQuat = new pc.Quat();
Rotate.verticalQuat = new pc.Quat();
Rotate.resultQuat = new pc.Quat();
Rotate.xTargetAngle = 0;
Rotate.yTargetAngle = 0;

Rotate.prototype.onMouseMove = function (event) {    
    var mouse = this.app.mouse;
    if (mouse.isPressed(pc.MOUSEBUTTON_LEFT)) {
        this.rotate(event.dx, event.dy);
    }
};

.rotate should be changing the target angles and the code in .rotate where it works out the quats for rotation should be done in update so it is applied every frame.

Not sure what the offsetAngle is for?

So I have to update the resultQuat to get the lerp? Offset angle is the extra rotation given to the object, which will be added to the current rotation angle to get the target angle, so that the lerp happens from current angle to the target angle. Let me know if this is not clear.

@contractkiller Updated the example example to have inertia: https://playcanvas.com/editor/scene/664238

Awesome… working great ! thanks a ton !