David
David

Reputation: 61

Determine if a rotation angle is in view of a camera inside a sphere

I'm hoping someone can help me on this. In three.js, I have created a camera inside a SphereGeometry located at position (0,0,0). I am projecting a panoramic image on the wall of the sphere.

I'm trying to create some interactive JS elements outside of the threejs framework which triggers based on the direction in which the camera is facing. I am given two parameters, theta & phi, which are the 360 degree rotation angles of x & z and y & z, respectively, along the image. The concept is that if that point (or vector) is in the camera's view, it would trigger some JS event. Note that the point is not an object, but rather an arbitrary set of rotation angles. Additionally, the camera is moved using the mouse or device orientation. In other words, I'm not interested in moving the camera to those points programmatically, but rather the JS is executed only if the camera is rotated manually and the points come into view.

Originally, I started using the Frustums.containsPoint method, but realized that perhaps I'm not really looking for a specific point in space, but rather the camera angles. I thought maybe instead using the camera.getWorldDirection() or camera.rotation would provide the right angels, but I seem to be stuck here.

Any help would be greatly appreciated.

Upvotes: 2

Views: 534

Answers (3)

David
David

Reputation: 61

Ahh, seems I figured out the trigonometry. By adding an event listener to the device orientation, I can now update the lat/lon based on the world direction.

window.addEventListener( 'deviceorientation', function(e) {
    e.preventDefault();

    var angle = window.orientation;
    if ( angle == null ) return;

    var dir = camera.getWorldDirection();
    var d_phi = Math.acos( dir.y );
    var d_theta = Math.atan2( dir.x, dir.z );
    d_theta = Math.atan2(Math.sin(d_theta), Math.cos(d_theta));

    // update lat / lon
    lon = 90 - THREE.Math.radToDeg(d_phi);
    lat = THREE.Math.radToDeg(d_theta);
});

Now, the only kicker is compensating for the rotation of the camera, with which I have been having trouble. In threejs, it seems on iOS the world direction vector is aligned to the rotation of the camera when you first load the page, then only the window.orientation angle changes. So it seems the original orientation affects the position/rotation. If I want the center of the image to be (0,0), the following seems to work on iOS, but I'm not sure if this is right way to go.

    var d_theta = Math.atan2( dir.x, dir.z );
    if ( init_orient === 0 ) d_theta = Math.PI - d_theta;
    else if ( init_orient == -90 ) d_theta = -Math.PI/2 - d_theta;
    else if ( init_orient == 90 ) d_theta = Math.PI/2 - d_theta;
    d_theta = Math.atan2(Math.sin(d_theta), Math.cos(d_theta));

Unfortunately, this doesn't seem to be consistent on Android devices. Perhaps someone can comment on how best to approach this problem.

Upvotes: 0

David
David

Reputation: 61

Apparently, the latitude / longitude angles are already captured and stored with each mousemove event as follows:

function onDocumentMouseMove( event )
{
    if ( isUserInteracting === true && !isControlInteraction )
    {
        lon = (( onPointerDownPointerX - event.clientX ) * 0.1 + onPointerDownLon) % 360;
        lat = (( event.clientY - onPointerDownPointerY ) * 0.1 + onPointerDownLat) % 360;
    }
}

I can then use the lat/lon variables to calculate a theta/phi offset from some point p. If the offset is less than some designated radius, say 30 degrees, then it can be be considered in range.

    var v_theta = THREE.Math.degToRad(lat);
    var v_phi = THREE.Math.degToRad(lon);
    var p_theta = THREE.Math.degToRad(p[0]);
    var p_phi = THREE.Math.degToRad(p[1]);

    v_theta = Math.atan2(Math.sin(v_theta), Math.cos(v_theta));
    v_phi = Math.atan2(Math.sin(v_phi), Math.cos(v_phi));
    p_theta = Math.atan2(Math.sin(p_theta), Math.cos(p_theta));
    p_phi = Math.atan2(Math.sin(p_phi), Math.cos(p_phi));

    var theta_offset = Math.abs(v_theta - p_theta) * 180 / Math.PI;
    var phi_offset = Math.abs(v_phi - p_phi) * 180 / Math.PI;
    if ( theta_offset <= radius && phi_offset <= radius ) 
    {
       // execute some code here
    }

However, this does not cover device orientation control events which work independently. I guess where I'm stuck now is how to calculate the lat/lon from device orientation. I'm able to get the camera rotation and position but not sure how to get the lat/lon from the camera rotation vectors.

Upvotes: 0

WacławJasper
WacławJasper

Reputation: 3384

Define regions you want interactivity as a cone (a direction vec3 + angle) and whenever the camera rotates, check if the camera forward dir is inside the cone.

Upvotes: 0

Related Questions