Fitting object to window by moving object - three.js

I want to fit my object to my output window. There are some answers that describe how to do this by moving the camera or changing the fov but for my use case, I want to do it by moving the object and leaving the camera at the origin.
Distance from camera to object is I think:
var dist = size / 2 / Math.tan(Math.PI * camera.fov / 360);
I think this is the distance from closest point on object to the camera so I mix in the radius of the sphere (in this example) to account for that.
I have tried to adapt the code in other answers but it is not working. Can anyone spot my mistake?
https://jsfiddle.net/m1rLzm12/

Related

Three.js: How can I find the camera's current plane

In three.js, when we define the camera we use something like camera = new THREE.PerspectiveCamera(fov, window.innerWidth / window.innerHeight, near, far); If the object is moved outside the bounds set by the two planes, far and near then it is clipped.
Suppose now I rotate the object and I zoom in/out.
How can I find the current plane my camera is on please? I am learning three.js, hence I dont know If I am explaining this clear enough.
I thought that it was camera.position.z that would give me this info. In fact, I think it gives the correct value when my camera looks down the z-axis. However when I rotate by 90 degrees, (effectively moving my camera on the x-axis) the value of camera.position.z changes by a lot.
I have added a graph in case it helps. The plane defined by the blue outline cuts through the data and is parallel to the far and near planes. As I zoom in and out, I am moving the plane forward and backward, right? Do I understand this correct or I have totally missed it? I would like to know the value (a float between far and near) indicating how far that blue plane is from the camera. If you rotate that distance shouldn't change
My end goal is to be able to find-out how close the scene is to the viewer. If it gets too close then I will be adding some more elements to the scene. If it is too far these elements will be removed.

Raycasting to intersect objects that have been displaced by vertex shader

Let's say I have a vertical list of meshes created from PlaneBufferGeometry with ShaderMaterial. The meshes are distributed vertically and evenly spaced.
The list will have two states:
Displaying the meshes as they are
Displaying meshes with each object's vertices transformed by the vertex shader to the same arbitrary value, let's say z = -50. This gives a zoomed out effect and the user can scroll through this list (in the code we do this by moving the camera y position)
In my app I'm trying to make my mouseover events work for the second state but it's tricky since the GPU transforms the vertices so the updated vertices are not reflected in the attributes on the JS side.
*Note I've looked into GPU picking and do not want to use it because I believe there should be a simpler way to do this without render targets
Attempted Solution
My current approach is to manually change the boundingBox of each plane when we are in the second state like so:
var box = new THREE.Box3().setFromObject(plane);
box.min.z = -50;
box.max.z = -50;
plane.geometry.boundingBox = box;
And then to change the boundingSphere's center to have the same z position of -50 after computing it.
I did this approach because I looked into the Raycaster and Mesh code for THREE.js and it seems like they check both boundingSphere and boundingBox for object intersections. So I thought if I modified both of them to reflect the transforms done by the GPU, the raycaster would work fine but it doesn't seem to be working for me.
The relevant raycaster code is here:
// mouse being vec2 of normalized coordinates and camera being a perspective camera
raycaster.setFromCamera( mouse, camera );
const intersects = raycaster.intersectObjects( planes );
Possible Theories
The only thing I can think of that's wrong about this approach is maybe I'm not projecting the mouse coords right? Since all the objects now lie on the plane z = -50 would I need to project those mouse coordinates to that plane?
Inspired by the link posted by #prisoner849 I found a working solution to just create additional transparent planes equal to the number of planes in the scene. In these planes, I set the z position to -50 and just intersect with these when in state #2.
A bit hacky, but works for now.

How do I place an element in front of the camera, facing it regardless of where it is?

I have this simple scene with a "tooltip" entity composed of some data, I'd like to know how to position it in front of the camera. The tooltip will have to face certain points a few meters away so the user can see it. It must obey camera direction (it can be gathered by calculating it from previousPoint to nextPoint where the camera will move), but only y axis (can't be tilted or anything like that).I tried digging through math but couldn't understand good enough to employ a solution for this little project; I appreciate all the help!
setTimeout(function(){
var camera = document.getElementById("cameraS");
var tt = document.getElementById("ttS");
var cameraPos = camera.getAttribute('position');
var ttPos = tt.getAttribute('position');
tt.setAttribute('position', cameraPos);
tt.setAttribute('rotation', {'y': -90});
}, 5000);
JSFiddle
EDIT
I made an image showing what I'm after: http://imgur.com/a/eDhqE
I have point A and point B; the camera will play an animation moving from previous point to the next, and upon reaching there the tooltip will be displayed a few meters away from the point (box) so we can see it. It must take camera orientation into consideration but it must be perpendicular to the ground (can't be tilted).
There is a command THREE.Object.lookAt(THREE.Vector3); that will rotate an object (assuming (0.0,1.0,0.0) is up) to face a vector. You can use this to have it face your camera.
If you only want Y rotation, you can copy the current rotation, then do look at, then copy the rotation.x and rotation.z from the previous frame rotation copy - so that way it'll only correct the y with .lookAt because you reset x and z.

Converting a Vector3 point to screen space (Vector2) using Three.js [73]

I have initialised a perspective camera at a position looking at the origin (0,0,0). Reading around the most common solution to this I've found is the one described here https://stackoverflow.com/a/27412386/1330719.
From my understanding of the project method is, I should get a vector where the x,y coordinates are between -1 and 1. This doesn't seem to be the case at all and I end up getting coordinates that are completely out of bounds.
Furthermore, if the original vector point is at (0,0,0) I seem to get (NaN, NaN) back. If my camera is looking at position (0,0,0) I expect the Vector3 (0,0,0) to return (width/2, height/2).
In case it is needed, this is how I'm initialising my camera:
this.camera = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight, 1, 10E5);
this.camera.position.set(0, 500, -500);
this.camera.lookAt(new THREE.Vector3(0,0,0));
this.camera.updateProjectionMatrix();
Does anyone have a reason why this might not be working? Or alternatively a recommended way of mapping a Vector3 point to the screen space given a camera?
A jsfiddle of what I mean:
https://jsfiddle.net/m78wjLyc/
You should also use camera.updateMatrixWorld(true) before projecting.
Usually this is done automatically by renderer, but you don't use any, so the camera.matrixWorld stays untouched after you change the position, and makes the camera project things as if it was at the world origin.

WebGL/OpenGL: Rotate camera according to device orientation

I have a web application I am trying to show a plane of map image tiles in a 3D space.
I want the plane to be always horizontal however the device rotate, the final effect is similar to this marine compass demo.
I can now capture device orientation through the W3C device orientation API for mobile devices, and I successfully rendered the map image tiles.
My problem is me lacking of essential math knowledge of how to rotate the camera correctly according to the device orientation.
I am using the Three.js library. I tried to set rotation of the camera object directly by the alpha/beta/gamma (converted to radian). But it's not working since the camera seems to always rotate according to the world axis used by openGL/webGL not according to its local axis.
I came across the idea of rotate a point 100 unit in front the camera and rotate the point relatively to the camera position by angles supplied by the device orientation API. But I don't know how to implement this either.
Can anyone help me with what I want to achieve?
EDIT:
MISTAKE CORRECTION:
For anyone interested in implementing similar things, I found out that Three.js objects uses local space axis by default, not world space axis, I was wrong. Though the official document stated that by setting "object.matrixAutoUpdate = false" and then modify "object.matrixWorld" and call "object.updateWorldMatrix()" you can manually move/rotate/scale the object in world axis. However it does not work when the object has a parent, the local axis matrix will always be used when the object has a parent.
According to the W3C Device Orientation Event Specification, the angles alpha, beta and gamma form a set of intrinsic Tait-Bryan angles of type Z-X'-Y''.
The Three.js camera also rotates according to intrinsic angles. However the default order in which the rotations are applied is:
camera.rotation.order = 'XYZ'.
What you need to do, then, is to set:
camera.rotation.order = 'ZXY'; // or whatever order is appropriate for your device
You then set the camera rotation like so:
camera.rotation.x = beta * Math.PI / 180;
camera.rotation.y = gamma * Math.PI / 180;
camera.rotation.z = alpha * Math.PI / 180;
Disclaimer: I do not have your device type. This is an educated guess based on my knowledge of three.js.
EDIT: Updated for three.js r.65

Resources