Three.js - calculating relative rotations - javascript

I have a THREE.Scene with two meshes added, meshA and meshB, each of which has been rotated in different ways. My goal is to remove meshB from the scene and re-add it as a child of meshA, while preserving its global position and rotation -- in other words, the position and rotation of meshB should appear the same before and after this code.
My current, almost working attempt is as follows:
var globalOffset = new THREE.Vector3().subVectors( meshB.position, meshA.position );
var localOffset = meshA.worldToLocal( meshB.position );
var rotationOffset = meshA.quaternion.clone().inverse();
var rotation = meshB.quaternion.clone().multiply( rotationOffset );
scene.remove(meshB);
meshA.add(meshB);
meshB.position = localOffset;
meshB.rotation.setFromQuaternion(rotation);
The positioning works fine; the rotation only works if both meshA and meshB have been rotated around the same axis -- if meshA and meshB have been rotated around different axes, meshB appears to change rotation before and after this code.
Any ideas how I can correct the code above (or an idea for a different approach) so that meshB still has the same "global" rotation before and after being removed and re-added to the scene?
Thanks!

You want to remove meshB from the scene and re-add it as a child of meshA, while preserving its global position and rotation.
You can do that like so:
meshA.attach( meshB );
three.js r.109

Related

Three js plane geometry disappears when rotating

I'm trying to create a very simple scene containing a triangular planar face continuously rotating about the x axis.
Here's the code creating the geometry object, as indicated in this previous SO question:
// create triangular plane geometry
var geometry_1 = new THREE.Geometry();
var v1 = new THREE.Vector3(0,0,0);
var v2 = new THREE.Vector3(3,0,0);
var v3 = new THREE.Vector3(0,3,0);
geometry_1.vertices.push(v1);
geometry_1.vertices.push(v2);
geometry_1.vertices.push(v3);
geometry_1.faces.push(new THREE.Face3(0, 1, 2));
The animation function renders the scene and adds a small rotation to the mesh:
function animate() {
requestAnimationFrame( animate );
mesh_1.rotation.x += 0.005;
renderer.render( scene, camera );
}
Everything works fine until the value of mesh.rotation.x goes into the [Math.PI, 2*Math.PI] interval, at which point it disappears for exactly half of the cycle. This JSFiddle replicates the behavior I'm observing.
This is not a light problem, as there are an ambient light and a directional light supposed to illuminate the mesh at all points of it revolution.
This should not be a material problem, as I did set its side property to THREE.DoubleSide and in fact in the interval mesh.rotation.x into [0, Math.PI] I already observe both faces.
I tried adding another face to the same geometry with geometry_1.faces.push(new THREE.Face3(0, 2, 1)); but that still didn't solve the problem.
Adding a second geometry with an opposite face geometry_2.faces.push(new THREE.Face3(0, 2, 1)); and having the mesh rotate negatively mesh_2.rotation.x -= 0.005; allows me to observe the desired result because the two geometries are now disappearing in opposite halves of the [0, 2*Math.PI] interval. This however is a hacky and not ideal solution.
So what is going on? How can I solve it? Thanks!
Documentation says:
OrthographicCamera( left, right, top, bottom, near, far )
so, set your camera like this:
camera = new THREE.OrthographicCamera(-FRUSTUM_SIDE/2, FRUSTUM_SIDE/2,
FRUSTUM_SIDE/2, -FRUSTUM_SIDE/2);
thus you'll have default near and far camera frustrum planes (0.1 and 2000).
Explanation: You set your cam at z-position, which is equal to FRUSTRUM_SIDE/2 and also you set your far camera frustrum plane with the same value. So you see everything between your cam position and the distance from it, which is FRUSTRUM_SIDE/2. In world coordinates, your far plane is at point (0, 0, 0). That's why your triangle disappears when it goes further then the distance of FRUSTRUM_SIDE/2 from your cam.
Extending the answer from #prisoner849, the problem shown in the JSFiddle has nothing to do with the geometry or the material of the mesh, but with the shape and extension of the frustum defined by the OrthographicCamera.
As explained nicely in this video tutorial and in the documentation the frustum of an OrthographicCamera is a rectangular parallelepiped defined by the values left, right, top, bottom, near, far:
The camera should effectively be thought of as being attached to the surface on the near side and facing towards negative values of the z axis.
Once the frustum's shape is defined with:
FRUSTUM_SIDE = 20;
camera = new THREE.OrthographicCamera(
-FRUSTUM_SIDE/2, FRUSTUM_SIDE/2,
FRUSTUM_SIDE/2, -FRUSTUM_SIDE/2,
-FRUSTUM_SIDE/2, FRUSTUM_SIDE/2);
we will be able to see in our image all the objects in the scene which are entirely contained in it.
However, after defining the frustum the position of the camera is changed: camera.position.z = FRUSTUM_SIDE/2; (line 24 of the fiddle). This effectively moves the whole frustum and not just the location of the image, so while previously any object in (0,0,0) was in the center of the frustum, now it will lie on the very far end plane of it.
The animation function:
function animate() {
requestAnimationFrame( animate );
mesh_1.rotation.x += 0.005;
renderer.render( scene, camera );
}
rotates the triangle towards the image plane, but for angles between [Math.PI, 2*Math.PI] the plane is effectively pointing outside of the frustum and thus becomes invisible.
Removing the camera.position.z = FRUSTUM_SIDE/2; line, or defining a different frustum, or moving the mesh position to the middle of the new frustum are all possible solutions. I.e. corrected fiddle.

How do I set the position a camera in three.js without it rotating?

I have an interface I developed with three.js using the CSS3DObject rendering tool.
I have set the orbit to 0 to prevent rotating and limit my movement to panning and zooming.
Please note I'm also using Orbit Control.
I set the position of the camera to x=-2000 with the following code:
camera.position.x=-2000;
camera.position.z=4000;
When I do this, the camera moves positions but is still pointing to (0,0,0) resulting in a skewed look.
So I assume that I need to give it a vector
camera.up = new THREE.Vector3(0,1,0); //keeps the camera horizontal
camera.lookAt(new THREE.Vector3(2000,0,0)); //should point the camera straight forward
Please note that I'm still trying to find a good explanation of how setting up the lookAt works.
After a bit more research, it seems that the orbit control is overriding the camera.lookAt and as a result, doesn't do anything.
To achieve the panning I set the location of the camera x position equal to the value of the target.
I also removed the camera.up line.
var myCameraX = -2000;
var myCameraY = 500;
camera.position.x=myCameraX;
camera.position.y=myCamerYa;
control.target.set(myCameraX,myCameraY,0);
Hope that helps someone.

How can I apply TrackballControls to a moving target?

I would like to apply the three.js script TrackballControls to a moving object in a way that preserves the ability to zoom and rotate the camera while the object moves through the scene. For example, I would like to be able to have the camera "follow" a moving planet, while the user retains the ability to zoom and rotate around the planet. Here is a basic jsfiddle:
http://jsfiddle.net/mareid/8egUM/3/
(The mouse control doesn't actually work on jsfiddle for some reason, but it does on my machine).
I would like to be able to attach the camera to the red sphere so that it moves along with it, but without losing the ability to zoom and rotate. I can get the camera to follow the sphere easily enough by adding lines like these to the render() function:
mouseControls.target = new THREE.Vector3(object.position);
camera.position.set(object.position.x,object.position.y,object.position.z+20);
But if I do that, the fixed camera.position line overrides the ability of TrackballControls to zoom, rotate, etc. Mathematically, I feel like it should be possible to shift the origin of all of the TrackballControls calculations to the centre of the red sphere, but I can't figure out how to do this. I've tried all sorts of vector additions of the position of the red sphere to the _this.target and _eye vectors in TrackballControls.js, to no avail.
Thanks for your help!
I'd recommend OrbitControls rather than TrackballControls. My answer here applies primarily to OrbitControls; the same principle might also work with TrackballControls (I have not looked at its code in a while).
If you look at how OrbitControls works you should notice it uses this.object for the camera, and this.target for where the camera is looking at, and it uses that vector (the difference between them) for some of its calculations. Movement is applied to both positions when panning; only the camera is moved when rotating. this.pan is set to shift the camera, but out of the box it only deals with a panning perpendicular to the this.object to this.target vector because of the way it sets the panning vector.
Anyway, if you subtract the vector from object.position to controls.target and set controls.pan to that vector, that should make it jump to that object:
Fixed example code:
Add a helper to OrbitControls.js which has access to its local pan variable:
function goTo( vector ) {
// Cloning given vector since it would be modified otherwise
pan.add( vector.clone().sub( this.target ) );
}
Your own code:
function animate() {
requestAnimationFrame(animate);
mouseControls.goTo( object.position ); // Call the new helper
mouseControls.update();
render();
}
You'll also probably want to set mouseControls.noPan to true.

OrbitControls : updating position and World/View scale of a 'helper'

I'm trying to manage a 3Dobject that visualize the controls target in order to make rotation and zoom more immediatly understandable ...
I've made a sample here with an AxisHelper which is supposed to indicate the target and to keep the same size. In OrbitControls.js there are comments on each line I've added.
As you can see if you pan and zoom (right click and scroll) it manages cursors too, but the 'helper' has two problems :
the position and scale of the helper is set after the renders, that why it seems to be somewhat elastic ... And if I place the position/scale updates into the scope.update() function that's the same thing.
the function bellow scales the helper to a constant size, it computes a World/View scale at a defined point (the control's target) from a unit vector. But it seems it's not the good solution because when you scroll to the max zoom the helper is growing.
var point = new THREE.Vector3( 1, 0, 0 );
point = point.applyMatrix4( scope.object.matrixWorld );
var scale = point.distanceTo( scope.target );
helper.scale.set(scale, scale, scale);
So if you have an idea to achieve this you are welcome ...
i'm currently developing something similar with threejs and i think that:
I can't see the demo, my proxy not allow it.
I think that is correct...if the object helper is a mesh or a sprite added to the scene when orbit controls zoom the camera became more near to the objects in the scene, the objects dimension remain the same.

I'm doing something wrong with SpotLights and shadows in Three.js

I have a really simple scene which has one .dae mesh in it, and a 7000*7000 plane underneath the mesh. I'd like it to be lit by a high SpotLight, so the mesh throws a shadow on the ground. But, something seems to be broken! No matter how high I put the SpotLight, it never lights up the plane! Also, it lights the mesh up only a little, while it is in a small square (perimeter).
You can see the situation here:
As soon as I move the mesh (a monster) around, it wont be lit anymore.
This is how I instantiate the light:
// create a spotlight
self.spotLight = new THREE.SpotLight();
// set its position
self.spotLight.position.y = 1000; //I recon it needs to be relatively high so it lights up everything
self.spotLight.position.x = 0; //(0, 0) are the coordinates where the mesh is spawned, and are the center of the plane
self.spotLight.position.z = 0;
self.spotLight.castShadow = true;
This is how the plane is made:
//The plane.
self.plane = new THREE.Mesh(new THREE.PlaneGeometry(self.groundSize, self.groundSize), new THREE.MeshLambertMaterial({color: 0x5C8A00}));
self.plane.receiveShadow = true;
self.plane.position.x = 0;
self.plane.position.y = -26;
self.plane.position.z = 0;
Also, here's another picture, this time, I've added a lot of PointLights:
You can see how the shadow still disappears!
Now, what am I doing wrong here? AFAIK, light should disperse equally in all directions! And also, there is another problem, I seem to be unable to add multiple SpotLights on the scene! Everything slows down completely if I do so - is this intended? Maybe it's because I enabled shadows on all of them...
#Neil, the same thing happens in your code as well!
I have created a jsfiddle showing a plane with Lambert material and a rotating cube that is casting a shadow, maybe you can see what is different to yours.
edit
Try playing about with some of the params, I can stop the clipping on my demo with:
spotLight.shadowCameraFov = 70;
update demo and moving demo

Categories