PerspectiveCamera: fit mesh to viewport, with orbital camera offset

I’m using this threeJS library, which is very similar to OrbitControls, but has more support for transitions and some other features. Principally, it works the same way, using spherical coords and a distance-from-target scalar.

yomotsu/camera-controls

I’m using it for a model viewer application, in which I need to start at a specified polar angle, somewhere in the range 0 to 90 degrees.

After adjusting the model’s position to be central within a container (accounting for any non-zero origin within the model), I want the camera’s distance to be such that the loaded model touches the edges of the threeJS viewport, acting like an aspect-fit rather than an aspect-fill. The camera will not always be directed straight along the X, Y or Z axis; the aim of this camera calculation is to arrive at an opportune starting camera to suit the variable runtime-loaded mesh, with the camera starting at a customised polar angle, looking toward or down upon the model.

I’ve seen examples of this with axis-aligned objects, but not with rotated objects or rotated cameras. In this application, I don’t rotate my model - it’s only the camera that becomes offset.

If my camera is at a polar angle of 0, it’s top-down, and framing the object appears to be much easier. I’d be looking to use the bounds of the top side of the model, and this is more like fitting a 2D rectangle into another 2D rectangle.

But at 45 degrees, with a non-uniform mesh, there’s no obvious front plane.

Can this be done?

Do I need to determine the screen-space extents of the model at an arbitrary camera distance, then adjust this?

Some code snippets or guidance towards an existing solution would be useful!

Thanks

I have a makeshift solution for this:

I’m starting the orbital camera at an arbitrary distance known to be far enough to fit the whole mesh inside the view frustrum. (This is easier because I normalise the scale of the loaded meshes).

I then iteratively move the camera distance closer and check for overlap of the mesh with the view frustrum, projecting all 8 extents of the mesh’s bounds. Once an overlap exists, I stop, and use the previous, most recent safe distance value.

It feels like this absolutely should be able to be repurposed into a non-iterative calculation, to determine the exact distance value without trial and error, but I haven’t yet worked out what that will be.