Hi, Babylon team!
A few weeks ago I reported an issue with the camera related to inertia:
Thank you all who helped implement a temporary solution!
Since then, I’ve continued to work on the camera experience for my company and I’ve found a few more issues that I’d like to report here. It was mentioned in the other forum post that the Babylon team has a camera overhaul planned. I want to make sure that these issues are included in that, and are hopefully fixed.
1. The camera applies inertia without respect to delta time.
Take a look at the checkInputs method of the arc rotate camera (used for reference; issue exists for all camera types).
This code is meant to apply inertia over time. The issue is that “over time” is how fast the checkInputs method is called. At 240 FPS, the inertia will dissipate four times faster than 60 FPS.
The solution is to include delta time:
const deltaTimeScale = this.getEngine().getDeltaTime() / Frequency;
const inertia = this.inertia ** deltaTimeScale;
this.inertialAlphaOffset *= inertia;
this.inertialBetaOffset *= inertia;
this.inertialRadiusOffset *= inertia;
(the double star ** is intentional)
2. The camera inputs don’t respect delta time
As far as I can tell, this affects all the camera input classes. I’ll use ArcRotateCameraPointersInput to illustrate though. The onTouch method in particular.
The onTouch method adds to the camera inertia every time it’s called. This is then applied in the camera checkInputs method. The problem here is that onTouch is called by the browser (per event), while checkInputs is called by Babylon (per frame). These operations can easily happen out of sync with one another. Say, the browser is measuring pointer events at 60 FPS and the render engine is running at 30 FPS. Ultimately, this causes the camera to move at different speeds depending on frame rate when inertia is on.
The solution is to include delta time:
public override onTouch(point: Nullable<PointerTouch>, offsetX: number, offsetY: number): void {
const deltaTimeScale = this.camera.getEngine().getDeltaTime() / Frequency;
if (this.panningSensibility !== 0 && ((this._ctrlKey && this.camera._useCtrlForPanning) || this._isPanClick)) {
this.camera.inertialPanningX += -offsetX / this.panningSensibility * deltaTimeScale;
this.camera.inertialPanningY += offsetY / this.panningSensibility * deltaTimeScale;
} else {
this.camera.inertialAlphaOffset -= offsetX / this.angularSensibilityX * deltaTimeScale;
this.camera.inertialBetaOffset -= offsetY / this.angularSensibilityY * deltaTimeScale;
}
}
3. The camera inertia cutoff doesn’t scale with delta time
This is the issue that I originally reported in this post. I’m putting it here so that it is included with the others since they all, in part, relate to each other.
You may have noticed the Frequency constant used in the above solutions. This is defined as follows:
const Frequency = 1000 / 60;
While not strictly necessary to fix the above issues (delta time is the only necessary part), without it all the numbers would be scaled. This would cause everyone’s existing projects to behave very different. By including this frequency, inertia will behave exactly like it does at 60 FPS regardless of the actual FPS.
I can put together some playgrounds with the above solutions tomorrow. I’ve already implemented these solutions for my company’s project though. You can see it in action here: