ThreeJS: Screen position to camera position - javascript

I've seen a lot of posts people want to have the camera position to screen position. My question is how to do the contrary.
What I currently want to achieve is set the "door" position to a % of the screen, this calculation is ready, and I do have the final screen X, Y (px) position. The current Z offset = 250 of the camera.
I've found this code to convert camera position to screen position:
var vector = projector.projectVector(door.position.clone(), camera);
vector.x = (vector.x + 1) / 2 * window.innerWidth;
vector.y = -(vector.y - 1) / 2 * window.innerHeight;
To do the reversie I tried this, but does not give the result I expect:
var mouseX = ((perc.x) / window.innerWidth) * 2 - 1,
mouseY = -((perc.y) / window.innerHeight) * 2 + 1;
var vector = new THREE.Vector3(mouseX, mouseY, 1);
projector.unprojectVector(vector, camera);
return vector.sub(camera.position).normalize()
I have tried several ways and tried to Google, but didn't found an answer.
Q: How to convert screen position to camera position?

Well, as posted by Gero3, what you want to do is to create a plane that will cover all of your visible camera area (something like new THREE.PlaneGeometry(5000,5000); and then use raycaster to locate screen coordinated on that plane.
Here is an example from another similar question:
http://jsfiddle.net/PwWbT/
Hope that helps.

Use a large invisble plane that defines in which direction the door can expand and then use a raycaster on the plane to search for the position to where the door object should extend.

Related

How to load the 3D model (JSON) on current mouse position in Three.JS?

I'm trying to achieve a functionality to load the 3D model on the current mouse coordinates. I'm using jQuery drag and drop functionality to load the 3D model. I can load the model into the canvas, but it is not snapping to the mouse coordinates.
I've used the below code to convert the 2D mouse coordinates to 3D coordinates but it's not providing the exact position.
function 2DTo3DSpace(event) {
var planeZ = new THREE.Plane(new THREE.Vector3(1500, 1500, 1500), 0);
var mv = new THREE.Vector3((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window.innerHeight) * 2 + 1, 0.5);
var raycaster = projector.pickingRay(mv, camera);
var pos = raycaster.ray.intersectPlane(planeZ);
return pos;
}
Make sure to use mouse position relative to the canvas elemente.
See this thread.

Calculating relative item positions based on camera position and rotation

For a 2d game, I have these concepts:
stage: container in which items and cameras are placed.
item: a visible entity, located on a certain point in the world, anchored from center.
camera: an invisible entity, used to generate relative images of world, located on a certain point in the world, anchored from center.
In the illustrations, you can see how they are related, and what the end result should be.
Here is the code I have: (dumbed down to make it easier to read)
Note1: This is not happening on canvas, so I will not use canvas translation or rotation (and even then, I don't think it would make the problem any easier).
Note2: Item and camera positions are center coordinates.
var sin = Math.sin(rotationRad);
var cos = Math.cos(rotationRad);
var difX = item.x - camera.x;
var difY = item.y - camera.y;
var offsetX = camera.width / 2;
var offsetY = camera.height / 2;
var view.x = (cos * difX) - (sin * difY) + _Ax + _Bx;
var view.y = (sin * difX) + (cos * difY) + _Ay + _By;
This is supposed to calculate an items new position by:
calculating new position of item by rotating it around camera center
(_A*) adjusting item position by offsetting camera position
(_B*) adjusting item position by offsetting camera size
I tried several different solutions to use for _A* and _B* here, but none of them work.
What is the correct way to do this, and if possible, what is the explanation?
You first subtract new origin position from object position. You then rotate it by the inverse of the rotation. New origin can be camera position of top left corner of viewport. Of course if you know viewport center its top left corner is computed by subtracting half of its dimensions.
Like this:
var topLeft = Camera.Position - Camera.Size / 2;
var newPosition = Object.Position - topLeft;
newPosition = Rotate(newPosition, -camera.Angle);
Rotation is very simple:
rotatedX = x * cos(angle) - y * sin(angle)
rotatedY = y * cos(angle) + x * sin(angle)

Three.js Click and Drag - Object Lagging Behind Pointer

I've created a near exact implementation of the Three.js Draggable Cubes example, except that my implementation ignores the z axis for movement in 2D. The example can be found here: http://threejs.org/examples/#webgl_interactive_draggablecubes
While I am able to click and move around my object, if I move the mouse too fast, the object will lag behind; the mouse pointer will move to it's location and then the object will follow the mouses path to the location. This issue is also noticeable in the Three.js example. Try dragging one of the cubes at anything beyond a moderate speed.
I've tried a few things to get the object to be directly under the mouse pointer but none have worked. The closest I think I might have come to a solution was by changing the mouse position update in the MouseMove event. However, it appears that the Three.js implementation of the code returns values between 1 and -1 rather than the screen X and Y coordinates, which leads me to wonder if the original implementation is causing the lag.
//Original Implementation - Works With Lag
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
console.log(mouse.x + "," + mouse.y); //Output: -0.9729166666666667,0.8596858638743455
//My Implementation - Does Not Work
mouse.x = event.clientX - (window.innerWidth / 2);
mouse.y = event.clientY - (window.innerHeight / 2);
console.log(mouse.x + "," + mouse.y); //Output: -934,-410.5
//Update Function
void Update()
{
var vector = new THREE.Vector3(mouse.x, mouse.y, 1);
projector.unprojectVector(vector, camera);
var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
var intersects = raycaster.intersectObject(plane);
meshToMove.position.x = intersects[0].point.x;
meshToMove.position.y = intersects[0].point.y;
}
//Main Loop
function Loop()
{
Update();
Render();
requestAnimationFrame(Loop);
}
Q: How can I update the three.js Draggable Cubes example so that objects don't lag behind the mouse during a click and drag?

Three.js - Orthographic camera

I've working on an app which displays some 3D models. We load the models, create the meshes, add them to the scene...standard procedure. After the last mesh is added, we compute the bounding box in order to move the camera and cover all the scene, using the size of the total geometry and the size of the viewport to do the math.
if (bounds.bx / bounds.by < camera.aspect) {
/* Vertical max */
r = bounds.by / (2 * Math.tan(Math.PI / 8));
} else {
/* Horizontal max */
hFOV = 2 * Math.atan(Math.tan(Math.PI / 8) * camera.aspect);
r = bounds.bx / (2 * Math.tan((hFOV / 2)));
}
bounds is an object containing the width and height of the bounding box. After this calculation, we move the camera(plus a little ratio, just aesthetics, we want a little room between the geometry and the screen border :) ) and render
camera.position.z = r * 1.05;
So far this is implemented and runs ok. This has been done with PerspectiveCamera. Now we want to change that and use OrthographicCamera...turns out to be a mess. Models are too small, we lose the mousewheel zoom from the TrackBall Controls and the algorithm to move the camera is not working anymore. Also I don't understand the parameters of the constructor for the camera...these width and height are for the geometry or the viewport?
The pattern for instantiating an orthographic camera in three.js is:
var camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, near, far );
where width and height are the width and height of the camera's cuboid-shaped frustum measured in world-space units.
near and far are the world-space distances to the near and far planes of the frustum. Both near and far should be greater than zero.
To prevent distortion, you will typically want the aspect ratio of the orthographic camera ( width / height ) to match the aspect ratio of the render's canvas. (see *Note below)
It is unfortunate that many of the three.js examples pass window.innerWidth and window.innerHeight as args to this constructor. Doing so only makes sense if the orthographic camera is used for rendering to a texture, or if the world units for your orthographic scene are in pixels.
*Note: Actually, the camera aspect ratio should match the aspect ratio of the renderer's viewport. The viewport can be a sub-region of the canvas. If you do not set the renderer's viewport directly using renderer.setViewport(), the viewport will be the same size as the canvas, and hence have the same aspect ratio as the canvas.
three.js r.73
For future reference:
Updated video
var w = container.clientWidth;
var h = container.clientHeight;
var viewSize = h;
var aspectRatio = w / h;
_viewport = {
viewSize: viewSize,
aspectRatio: aspectRatio,
left: (-aspectRatio * viewSize) / 2,
right: (aspectRatio * viewSize) / 2,
top: viewSize / 2,
bottom: -viewSize / 2,
near: -100,
far: 100
}
_camera = new THREE.OrthographicCamera (
_viewport.left,
_viewport.right,
_viewport.top,
_viewport.bottom,
_viewport.near,
_viewport.far
);
In my specific case, my world units are pixels. Hence, I am using container.clientWidth and container.clientHeight as width and height. You probably don't want to do this.
camera.top = (.95*camera.top);
camera.bottom = (.95*camera.bottom);
camera.left = (.95*camera.left);
camera.right = (.95*camera.right);

Adjusting camera for visible Three.js shape

I have a CubeGeometry which the camera is looking at, and I want the camera to zoom so that the cube is entirely visible, but no larger.
My initial attempt was to convert the cube verticies to the camera coordinate system,
function toScreenXY(position, camera) {
var pos = position.clone();
var projScreenMat = new THREE.Matrix4();
projScreenMat.multiply(camera.projectionMatrix, camera.matrixWorldInverse);
projScreenMat.multiplyVector3( pos );
return pos;
}
function ScaleInView() {
camera.fov = 0.0;
for (var i=0; i<8; i++) {
proj2d = toScreenXY(cube.geometry.vertices[i],camera);
angle = 57.296 * Math.max(Math.atan(proj2d.x/proj2d.z), Math.atan(proj2d.y/proj2d.z));
camera.fov = Math.max(camera.fov,angle);
}
camera.updateProjectionMatrix();
}
I thought this would work, but sometimes it's too small, and other times too large (depending on the position of the camera).
I also need to do this for Orthographic Camera.
Edit:
I know how to do this when the cube is facing the camera, I'm looking for a way to do it when the camera is moved to some arbitrary (r, theta, phi) position (spherical polar coordinates; r is actually constant for my purposes).
Perspective Camera. If the camera is centered and viewing the cube head-on, define
dist = distance from the camera to the front face ( important ! ) of the cube
and
height = height of the cube.
If you set the camera field-of-view as follows
fov = 2 * Math.atan( height / ( 2 * dist ) ) * ( 180 / Math.PI );
then the cube height will match the visible height.
Orthographic Camera. If the camera is centered and viewing the cube head-on, define
aspect = the aspect ratio of your window (i.e., width / height)
and
height = height of the cube.
Then construct your camera this way:
camera = new THREE.OrthographicCamera( -aspect * height/2, aspect * height/2, height/2, -height/2, near, far );
The cube height will match the visible height.
In either case, if the camera is not centered, or is otherwise viewing the cube at an angle, then the problem is more complicated.
Also, if the window is narrower than it is high, then the width is the constraining factor, and the problem is more complicated.
Multiplying by camera.matrixWorldInverse gives a vector in the camera's coordinates, but importantly does not apply perspective.
function toCameraCoords(position) {
return camera.matrixWorldInverse.multiplyVector3(position.clone());
}
We can then find the smallest angle that will fit all the box corners in the scene. arctan(D.x / D.z) gives the angle BCD where B is what the camera is looking at, C is the camera's position, and D the position of an object that you want to be visible in the camera coordinates.
In my case, the following ensures that the the cube boundbox is fully visible.
function ScaleInView() {
var tmp_fov = 0.0;
for (var i=0; i<8; i++) {
proj2d = toCameraCoords(boundbox.geometry.vertices[i]);
angle = 114.59 * Math.max( // 2 * (Pi / 180)
Math.abs(Math.atan(proj2d.x/proj2d.z) / camera.aspect),
Math.abs(Math.atan(proj2d.y/proj2d.z))
);
tmp_fov = Math.max(tmp_fov, angle);
}
camera.fov = tmp_fov + 5; // An extra 5 degrees keeps all lines visible
camera.updateProjectionMatrix();
}

Categories

Resources