How to get an object's world position (Three.js) - javascript

I want to place my already placed object at a new location, but it moves from the local position and not global.
this._scene.updateMatrixWorld();
this._scene.add(mesh);
var v1 = new THREE.Vector3();
v1.setFromMatrixPosition( mesh.matrixWorld );
mesh.position.set(v1.x +2, v1.y + 2, v1.z + 2);

What I did to solve my problem:
Every mesh got a geometry attribute. That is why you can call mesh.geometry.
From this point i created a BoundingSphere around my mesh.
mesh.geometry.computeBoundingSphere();
Now it is possible to get the world position of my boundingSphere, which simultaneously is the world position of my mesh.
var vector = mesh.geometry.boundingSphere.center;
Congratulations! 'vector' got the center world position in (x,y,z) of your mesh.
Just to clarify, 'mesh' is a THREE.Mesh object.

Wouldn't you just then move the object itself by the increment? I'm not sure why you need the matrix involved?
mesh.position.set(mesh.position.x +2, mesh.position.y + 2, mesh.position.z + 2);
edit... if you need to use matrix, you need to set the matrixWorld. Look at http://threejs.org/docs/#Reference/Core/Object3D.matrixWorld but using the position setter will do the heavy lifting for you.

Related

Three.js rope / cable effect - animating thick lines

With Three.js I want to create the effect of an object swinging from a cable or rope. It doesn't require real physics as the "swinging" object simply follows a fixed animation. The easiest solution is using the THREE.Line, however the problem is that THREE.Line can only be 1px thick and looks kinda awful.
In the three.js examples there is a "fat lines" example :
https://threejs.org/examples/?q=lines#webgl_lines_fat
however the problem is that once I have created the line using LineGeometry() I cannot figure out how to animate it.
The only solution I have found so far is to delete then create a new line every single frame, which works but seems like a really uneconomical, poorly optimized way to do it.
Does anyone know of a better way to either animate Line Geometry without having to delete and replace each frame? Or is there another method within three.js which would allow me to create thicker animated lines?
Thanks!!
I actually have a small project where I animate a bulb swinging along some rope. You can access it here, the functions I'm talking about below are in helperFns.js.
Actually, what I basically do is create my attached object separately :
let geometry = new THREE.SphereGeometry( 1, 32, 32 );
var material = new THREE.MeshStandardMaterial({color:0x000000,emissive:0xffffff,emissiveIntensity:lightIntensity});
bulb = new THREE.Mesh( geometry, material );
light = new THREE.PointLight(0xF5DCAF,lightIntensity,Infinity,2)
light.power = lightIntensity*20000
light.position.set(0,length*Math.sin(theta),z0-length*Math.cos(theta))
light.add(bulb)
light.castShadow = true;
hemiLight = new THREE.HemisphereLight( 0xddeeff, 0x0f0e0d, 0.1 );
scene.add(hemiLight)
scene.add(light)
I then add a spline connected to it :
// Create the wire linking the bulb to the roof
var curveObject = drawSpline(light.position,{x:0,y:0,z:z0},0xffffff);
scene.add(curveObject)
Where drawSpline is the following function :
// Build a spline representing the wire between the roof and the bulb. The new middle point is computed as the middle point shifted orthogonally from the lign by shiftRatio
function drawSpline(beginning,end,clr){
// Compute y sign to know which way to bend the wire
let ySign = Math.sign((end.y+beginning.y)/2)
// Compute the bending strength and multiply per Math.abs(beginning.y) to ensure it decreases as the bulb gets closer to the theta = 0 position, and also to ensure
// that the shift is null if thete is null (no discontinuity in the wire movement)
let appliedRatio = -shiftRatio*Math.abs(beginning.y)
// Compute middle line position vector and the direction vector from the roof to the bulb
let midVector = new THREE.Vector3( 0, (end.y+beginning.y)/2, (end.z+beginning.z)/2 )
let positionVector = new THREE.Vector3(0,end.y-beginning.y,end.z-beginning.z)
// Compute the orthogonal vector to the direction vector (opposite sense to the bending shift)
let orthogVector = new THREE.Vector3(0,positionVector.z,-positionVector.y).normalize()
// Compute the curve passing by the three points
var curve = new THREE.CatmullRomCurve3( [
new THREE.Vector3( beginning.x, beginning.y, beginning.z ),
midVector.clone().addScaledVector(orthogVector,ySign*appliedRatio),
new THREE.Vector3( end.x, end.y, end.z ),
]);
// Build the curve line object
var points = curve.getPoints( 20 );
var geometry = new THREE.BufferGeometry().setFromPoints( points );
var material = new THREE.LineBasicMaterial( { color : clr } );
// Create the final object to add to the scene
var curveObject = new THREE.Line( geometry, material );
return curveObject;
}
It creates the CatmullRomCurve3 interpolating the 3 points (one fix end at (0, 0, 0), one middle point to apply the bend, and the bulb position. You can actually start with a straight line, and then try to compute some curve.
To do so, you want to get the vector orthogonal to the line and shift the line (on the good side) along this vector.
And finally, at each animate() call, I redraw the spline for the new position of the bulb :
scene.children[2] = drawSpline(light.position,{x:0,y:0,z:z0},0xffffff)
Tell me if there is a point you do not get, but it should help for your problem.
Just wanted to post a more detailed version of West Langleys great reply. To animate a THREE Line2 you need to use the commands :
line.geometry.attributes.instanceStart.setXYZ( index, x, y, z );
line.geometry.attributes.instanceEnd.setXYZ( index, x, y, z );
What confused me was the index value - rather than thinking about a Line2 as being vertex points (the method used for creating the line) you need to think of a Line2 as being made of separate individual lines between 2 sets of points... so each line has a Start point and and an End point.
A "W" is therefore NOT defined as 5 vertices but by 4 lines. So you can "split" a Line2 by setting a different Start point to the previous lines End point. The index is the number of lines that make up your object. In my case I have two lines forming a V shape... so I set my index to 1 to affect the end of line 0 and the start of line 1, as in West's example :
var index = 1;
line.geometry.attributes.instanceEnd.setXYZ( index - 1, x, y, z );
line.geometry.attributes.instanceStart.setXYZ( index, x, y, z );
And then you just need to update the line using :
line.geometry.attributes.instanceStart.data.needsUpdate = true;
Thanks again to West for this really useful answer. I'd never have guessed this as you cannot see these variables when you look at the Line2 object properties. Very useful info. I hope it helps someone else at some point.

Three.js: global rotation

I want to rotate all objects in a scene around the origin 0,0,0 along the x axis. However, setting obj.rotation.x += 0.1; doesn't rotate along the origin, but along the the center of the object instead. How can I achieve the desired rotation of the object around the origin? I feel that there should be an easy way, but could not find any in the official docs or online.
Instead of adding the objects to the scene, add them to a THREE.Group() object:
var group = new THREE.Group();
scene.add(group);
...
var mesh1 = new THREE.Mesh(...);
group.add(mesh1);
var mesh2 = new THREE.Mesh(...);
group.add(mesh2);
//and so on with meshes
and then in the render loop:
group.rotation.x += 0.1;
Threejs.r84
You can use THREE.Object3D()
Add all your meshes to an object with myObject.add(myMesh)
And then use myObject.rotateX(angle)
where the angle is in radians, myObject is an Object3D object and myMesh is the mesh to be added.
This will rotate the object around x-axis in local space.
More can be found in the documentation: Object3D

Three.js - How to get camera position with respect to origin

The title of this question might a bit ambiguous, but I don't know how to phrase it in a line.
Basically I've got this situation: there is a perspective camera in the scene and a mesh. The mesh is NOT centered at the origin of the axis.
The camera points directly to the center of this object, and it's position (I mean literally the "position" property of the Three.js camera object) is the position with respect to the center of the object, not the origin; so it works in another coordinate system.
My question is: how can I get the position of the camera not with respect of the object center but with respect of the origin of the "global" coordinate system?
To clarify, the situation is this. In this image you can see a hand mesh that has a position far away from the origin of the coordinate system. The camera points directly to the center of the hand (so the origin from the point of view of the camera is the center of the hand), and if I print it's position it gives me these values:
x: -53.46980763626004; y: -2.7201492246619283; z: -9.814480359970839
while actually I want the position with respect to the origin of the coordinate stystem (so in this case the values would be different; for example, the y value would be positive).
UPDATE:
I tried #leota's suggestion, so I used the localToWorld method in this way:
var camera = scene.getCamera();
var viewPos = camera.position;
var newView = new THREE.Vector3();
newView.copy(viewPos);
camera.localToWorld(newView);
I did an experiment with this mesh. As you can see this mesh is also not centered on the origin (you can see the axes on the bottom-left corner).
If I print the normal value of the camera's position (so, with respect to the center of the mesh) it gives me these results:
x: 0; y: 0; z: 15
If now I print the resulting values after the code above, the result is:
x: 0; y: 0; z: 30
which is wrong, because as you can see the camera position in the image has x and y values clearly different than 0 (while z = 30 could be true, as far as I can see).
If for example I rotate the camera so that it's very close to the origin, like this (in the image the camera is just behind the origin, so its position in world coordinates should have negative values for x, y, z), the coordinates with respect of the center of the object are:
x: -4.674180744175711; y: -4.8370441591630255; z: -4.877951155147168
while after the code above they become:
x: 3.6176076166961373; y: -4.98753160894295; z: -4.365141278155379
The y and z values might even be accurate at a glance, but the positive value of x tells me that it's totally wrong, and I don't know why.
I'm going to continue looking for a solution, but this might be a step in the right direction. Still, any more suggestions are appreciated!
UPDATE 2:
Found the solution. What #leota said is correct, that is indeed how you would get absolute coordinates for the camera. In my case though, I finally found a single line of code hidden in the project's code that was scaling everything according to some rule (project related). So the solution for me was to take the camera position as it is and then scale it back according to that rule.
Since #leota's answer was indeed the solution to the original question, I'm accepting it as the correct anwser
Not sure I got your question :) if I did then you need to switch between World and Local coordinate systems. The THREE.PerspectiveCamera inherits from THREE.Object3D so you can use the following methods to set your coordinate system:
.localToWorld ( vector )
vector - A local vector.
Updates the vector from local space to world space.
.worldToLocal ( vector )
vector - A world vector.
Updates the vector from world space to local space.
From Three.js Documentation
Update:
First update your camera Matrix:
camera.updateMatrixWorld();
Then:
var vector = camera.position.clone();
vector.applyMatrix( camera.matrixWorld );
The vector should hold the position in world coordinate
I had same question trying to answer I was confused for a while my guess but not sure is
var plot = camera.position.x - mesh.position.x;
var plotb = camera.position.y - mesh.position.y;
var plotc = camera.position.z - mesh.position.z;
mesh.position.x = (camera.position.x + plot) - mesh.position.x;
mesh.position.y = (camera.position.y + plotb) - mesh.position.y;
mesh.position.z = (camera.position.z + plotc) - mesh.position.z;
or
var plot = (camera.position.x * mesh.position.x) / 1000;
var plotb = (camera.position.y * mesh.position.y) / 1000;
var plotc = (camera.position.z * mesh.position.z) / 1000;
mesh.position.x = mesh.position.x + plot;
mesh.position.y = mesh.position.y + plotb;
mesh.position.z = mesh.position.z + plotc;

How do I 'wrap' a plane over a sphere with three.js?

I am relatively new to three.js and am trying to position and manipulate a plane object to have the effect of laying over the surface of a sphere object (or any for that matter), so that the plane takes the form of the object surface. The intention is to be able to move the plane on the surface later on.
I position the plane in front of the sphere and index through the plane's vertices casting a ray towards the sphere to detect the intersection with the sphere. I then try to change the z position of said vertices, but it does not achieve the desired result. Can anyone give me some guidance on how to get this working, or indeed suggest another method?
This is how I attempt to change the vertices (with an offset of 1 to be visible 'on' the sphere surface);
planeMesh.geometry.vertices[vertexIndex].z = collisionResults[0].distance - 1;
Making sure to set the following before rendering;
planeMesh.geometry.verticesNeedUpdate = true;
planeMesh.geometry.normalsNeedUpdate = true;
I have a fiddle that shows where I am, here I cast my rays in z and I do not get intersections (collisions) with the sphere, and cannot change the plane in the manner I wish.
http://jsfiddle.net/stokewoggle/vuezL/
You can rotate the camera around the scene with the left and right arrows (in chrome anyway) to see the shape of the plane. I have made the sphere see through as I find it useful to see the plane better.
EDIT: Updated fiddle and corrected description mistake.
Sorry for the delay, but it took me a couple of days to figure this one out. The reason why the collisions were not working was because (like we had suspected) the planeMesh vertices are in local space, which is essentially the same as starting in the center of the sphere and not what you're expecting. At first, I thought a quick-fix would be to apply the worldMatrix like stemkoski did on his github three.js collision example I linked to, but that didn't end up working either because the plane itself is defined in x and y coordinates, up and down, left and right - but no z information (depth) is made locally when you create a flat 2D planeMesh.
What ended up working is manually setting the z component of each vertex of the plane. You had originaly wanted the plane to be at z = 201, so I just moved that code inside the loop that goes through each vertex and I manually set each vertex to z = 201; Now, all the ray start-positions were correct (globally) and having a ray direction of (0,0,-1) resulted in correct collisions.
var localVertex = planeMesh.geometry.vertices[vertexIndex].clone();
localVertex.z = 201;
One more thing was in order to make the plane-wrap absolutely perfect in shape, instead of using (0,0,-1) as each ray direction, I manually calculated each ray direction by subtracting each vertex from the sphere's center position location and normalizing the resulting vector. Now, the collisionResult intersection point will be even better.
var directionVector = new THREE.Vector3();
directionVector.subVectors(sphereMesh.position, localVertex);
directionVector.normalize();
var ray = new THREE.Raycaster(localVertex, directionVector);
Here is a working example:
http://jsfiddle.net/FLyaY/1/
As you can see, the planeMesh fits snugly on the sphere, kind of like a patch or a band-aid. :)
Hope this helps. Thanks for posting the question on three.js's github page - I wouldn't have seen it here. At first I thought it was a bug in THREE.Raycaster but in the end it was just user (mine) error. I learned a lot about collision code from working on this problem and I will be using it later down the line in my own 3D game projects. You can check out one of my games at: https://github.com/erichlof/SpacePong3D
Best of luck to you!
-Erich
Your ray start position is not good. Probably due to vertex coordinates being local to the plane. You start the raycast from inside the sphere so it never hits anything.
I changed the ray start position like this as a test and get 726 collisions:
var rayStart = new THREE.Vector3(0, 0, 500);
var ray = new THREE.Raycaster(rayStart, new THREE.Vector3(0, 0, -1));
Forked jsfiddle: http://jsfiddle.net/H5YSL/
I think you need to transform the vertex coordinates to world coordinates to get the position correctly. That should be easy to figure out from docs and examples.

Raycasting direction relative to global coordinate system , not local

I have used raycasting method to detect different colored strips on either side of the track and keeping my car object in position by calculating the distance. But the problem is the ray always points in the constant direction in the global coordinate system and doesnt change with the movement(rotation) of car object. It could have if the ray direction were in the reference frame of car which I am not able to figure out how to do. Currently I am doing this
var ray = new THREE.RayCaster(car.position, new THREE.Vector3(-1,0,0),0,50);
The movement of car is in the X-Z plane
Can someone point out a solution ?
Your ray-casting is being done in world-space, so you need the correct world-space vector.
I am assuming the car is a child of the scene, and not some other rotated object.
To construct a unit vector that points in the direction the car is heading in the world coordinate system, first construct a unit vector that points in the direction the car is heading in it's local coordinate system -- whatever that happens to be in your case:
var vector = new THREE.Vector3( -1, 0, 0 );
Then apply the same rotation to that vector as is applied to the car.
vector.applyQuaternion( car.quaternion );
EDIT: Updated to three.js r.66

Categories