mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 01:18:34 +00:00
cover images added to uploaded objects removed some logs, added some types --------- Co-authored-by: Victor <victor@storyteller.ai>
82 lines
2.5 KiB
TypeScript
82 lines
2.5 KiB
TypeScript
import * as THREE from "three";
|
|
|
|
// from here https://wejn.org/2020/12/cracking-the-threejs-object-fitting-nut/
|
|
|
|
export const fitCameraToCenteredObject = function (
|
|
camera,
|
|
object,
|
|
offset,
|
|
orbitControls,
|
|
) {
|
|
const boundingBox = new THREE.Box3();
|
|
boundingBox.setFromObject(object);
|
|
|
|
const size = new THREE.Vector3();
|
|
boundingBox.getSize(size);
|
|
|
|
// figure out how to fit the box in the view:
|
|
// 1. figure out horizontal FOV (on non-1.0 aspects)
|
|
// 2. figure out distance from the object in X and Y planes
|
|
// 3. select the max distance (to fit both sides in)
|
|
//
|
|
// The reason is as follows:
|
|
//
|
|
// Imagine a bounding box (BB) is centered at (0,0,0).
|
|
// Camera has vertical FOV (camera.fov) and horizontal FOV
|
|
// (camera.fov scaled by aspect, see fovh below)
|
|
//
|
|
// Therefore if you want to put the entire object into the field of view,
|
|
// you have to compute the distance as: z/2 (half of Z size of the BB
|
|
// protruding towards us) plus for both X and Y size of BB you have to
|
|
// figure out the distance created by the appropriate FOV.
|
|
//
|
|
// The FOV is always a triangle:
|
|
//
|
|
// (size/2)
|
|
// +--------+
|
|
// | /
|
|
// | /
|
|
// | /
|
|
// | F° /
|
|
// | /
|
|
// | /
|
|
// | /
|
|
// |/
|
|
//
|
|
// F° is half of respective FOV, so to compute the distance (the length
|
|
// of the straight line) one has to: `size/2 / Math.tan(F)`.
|
|
//
|
|
// FTR, from https://threejs.org/docs/#api/en/cameras/PerspectiveCamera
|
|
// the camera.fov is the vertical FOV.
|
|
|
|
const fov = camera.fov * (Math.PI / 180);
|
|
const fovh = 2 * Math.atan(Math.tan(fov / 2) * camera.aspect);
|
|
const dx = size.z / 2 + Math.abs(size.x / 2 / Math.tan(fovh / 2));
|
|
const dy = size.z / 2 + Math.abs(size.y / 2 / Math.tan(fov / 2));
|
|
let cameraZ = Math.max(dx, dy);
|
|
|
|
// offset the camera, if desired (to avoid filling the whole canvas)
|
|
if (offset !== undefined && offset !== 0) cameraZ *= offset;
|
|
|
|
camera.position.set(
|
|
0,
|
|
0.75, // this centers it vertically enough while testing, but probably needs to be dynamic
|
|
cameraZ,
|
|
);
|
|
|
|
// set the far plane of the camera so that it easily encompasses the whole object
|
|
const minZ = boundingBox.min.z;
|
|
const cameraToFarEdge = minZ < 0 ? -minZ + cameraZ : cameraZ - minZ;
|
|
|
|
camera.far = cameraToFarEdge * 3;
|
|
camera.updateProjectionMatrix();
|
|
|
|
if (orbitControls !== undefined) {
|
|
// set camera to rotate around the center
|
|
orbitControls.target = new THREE.Vector3(0, 0, 0);
|
|
|
|
// prevent camera from zooming out far enough to create far plane cutoff
|
|
orbitControls.maxDistance = cameraToFarEdge * 2;
|
|
}
|
|
};
|