Search code examples
javascriptthree.jsintersectplanefrustum

Move Camera to make all objects fit exactly inside the frustum - three.js


EDIT : I have rephrased my question to help users with the same problem.

I have a three.js scene on which I have added some spheres.

I want to move the camera towards a specific direction until all the objects (which are randomly positioned inside the scene) are "fitting exactly" the user's screen.


Solution

  • I have found the answer to my problem!

    1. I move the camera (zooming to the desired direction) inside a loop, and in every repeat I create a new frustum using the camera's matrix

    2. I check if any of my spheres intersects with a plane of the frustum. If it does, that means that part of one of my objects is outside the frustum so I break the loop and move the camera to its last position.

    The above might also works for any object (not only spheres) because every object has a boundingSphere that can be calculated (it might not be very precise the result though).

    It also works when zooming out, you 'd just have to move the camera from the object until none of the has a negative distance from all the planes (negative distance means object is "outside" the plane of the frustum).

    Code (only for zooming out - r72) :

    var finished = false;
    var camLookingAt = /* calc. */ ;
    
    while( finished === false ){
    
    var toDirection= camera.position.clone().sub(camLookingAt.clone());
    toDirection.setLength(vec.length() - 1); // reduce length for zooming out
    camera.position.set(toDirection.x, toDirection.y, toDirection.z);
    
    camera.updateMatrix(); // make sure camera's local matrix is updated
    camera.updateMatrixWorld(); // make sure camera's world matrix is updated
    
    var frustum = new THREE.Frustum();
    frustum.setFromMatrix( new THREE.Matrix4().multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ) );
    
    for (var j = frustum.planes.length - 1; j >= 0; j--) {  
      var p = frustum.planes[j];  
      for (var i = myMeshSpheres.length - 1; i >= 0; i--) { 
        var sphere = new THREE.Sphere(myMeshSpheres[0].position.clone(), myMeshSpheres[0].radius); 
    
        if( p.distanceToSphere(sphere) < 1 ){ // if is negative means part of sphere is outside plane/frustum
          finished = true; 
        } 
    
      } 
    }