I am defining a cone that I need to be able to rotate around its top point (the point where the cone thickness is the smallest one). I couldn't find yet the way to set the point around which the rotation to happen.
var coneGeometry = new THREE.CylinderGeometry(1000, 0, width, 50, 50, false);
var cone = new THREE.Mesh(coneGeometry, material);
cone.position.x = x;
cone.position.y = y + width / 2;
cone.position.z = z;
// I want this rotation to happen around the point given by the (x, y, z) location
cone.rotation.x = dip;
The cone geometry is centered at the origin. What you need to do is translate the cone geometry right after it is created so the tip of the cone is at the origin.
coneGeometry.applyMatrix( new THREE.Matrix4().makeTranslation( 0, coneHeight/2, 0 ) );
(The sign of the translation changes depending on which end of the cone is the small end.)
Now, when you rotate the cone, it will rotate around the tip. The tip will also be located at the position you set.
EDIT: You can now do this, instead:
coneGeometry.translate( 0, coneHeight/2, 0 ); // three.js r.72
Related
Like this site http://www.gsmlondon.ac.uk/global-oil-map/
I would like to click on this marker, let this marker turn to the center of the screen.
Now know the mark latitude and longitude, how to turn after the click? I do not understand.
Interesting question, I tried Matrix to get right orientation, but the result is a little bit strange. I choose another way using spherical coordinates system, and it works now.
we need to get two points coordinates, one is the point on the sphere surface which is closest to the camera we note it as P(the line from camera to center of sphere intersect with the sphere). another point is where we click on the sphere surface, we note it as Q .
we use raycaster to get P and Q Cartesian Coordinates. and convert the Cartesian Coordinates to Spherical Coordinates(always described like (r,θ,φ)).
then, we calculate the angular displacement from Q to P. and make the displacement as an addition to sphere rotation.
Here is my snippet:
//get the point coordinates which a line from camera to sphere center intersect with the sphere
var vector = new THREE.Vector3().copy(sphere.position);
vector = vector.unproject(camera);
var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
var intersects = raycaster.intersectObjects([sphere],true);
var intersected_point = new THREE.Vector3().copy(intersects[0].point);
//calculate the intersected point spherical coordinates
var radius = sphere.children[0].geometry.parameters.radius;
var heading = Math.atan2(intersects[0].point.x,intersects[0].point.z);
var pitch = Math.asin(-(intersects[0].point.y)/radius);
document.addEventListener("click",OnDocumentClick,false);
function OnDocumentClick(event)
{
//get the point coordinates which you click on the sphere surface
var vector = new THREE.Vector3(( event.clientX / window.innerWidth ) * 2 - 1, -( event.clientY / window.innerHeight ) * 2 + 1, 0.5);
vector = vector.unproject(camera);
var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
var intersects = raycaster.intersectObjects([sphere],true);
if(intersects.length > 0)
{
//get click point spherical coordinates
var heading1 = Math.atan2(intersects[0].point.x,intersects[0].point.z);
var pitch1 = Math.asin(-(intersects[0].point.y)/radius);
//calculate displacement of click point to intersected point
var delta_heading = heading - heading1;
var delta_pitch = pitch - pitch1;
var target_pitch = parseFloat(sphere.rotation.x) +delta_pitch;
var target_heading = parseFloat(sphere.rotation.y) + delta_heading;
//using an animation to rotate the sphere
anime({
targets:sphere.rotation,
x:target_pitch,
y:target_heading,
elasticity: 0
});
}
}
At the end, I use an animation lib to make the rotation smooth.
Here is my demo:rotate the earth.
I made a little progress, the previous version has a little bit off. when I turn up and down the earth, I got a bad result. I think the code sphere.rotation.x += delta_pitch make sphere rotate on object axises. but what we need is making the sphere rotate on the world space axises. we know world axises coordinates are always x_axis = (1,0,0) ; y_axis = (0,1,0) ; z_axis = (0,0,1); then, I convert the world coordinates to object coordinates, Sphere matrix interpret sphere rotate from indentity rotation to current rotation. and the inverse matrix interpret the backword. so we can apply the inverse matrix to basic axises to get object space coordinates. make sphere rotate on new axises. I just make a little change in OnDcoumentClick function:
var heading1 = Math.atan2(intersects[0].point.x,intersects[0].point.z);
var pitch1 = Math.asin(-(intersects[0].point.y)/radius);
//get the sphere inverse matrix;
var sphere_matrix = new THREE.Matrix4().copy(sphere.matrix);
var inverse_sphere_matrix = new THREE.Matrix4();
inverse_sphere_matrix.getInverse(sphere_matrix);
//convert world space x and y axises to sphere object space coordinates.
var x_axis = new THREE.Vector3(1,0,0);
var y_axis = new THREE.Vector3(0,1,0);
x_axis.applyMatrix4(inverse_sphere_matrix);
y_axis.applyMatrix4(inverse_sphere_matrix);
//calculate displacement of click point to intersected point
var delta_heading = heading - heading1;
var delta_pitch = pitch - pitch1;
//make sphere rotate around whith world x and y axises.
sphere.rotateOnAxis(x_axis,delta_pitch);
sphere.rotateOnAxis(y_axis,delta_heading);
Here is my new demo: rotate earth new version.
I would like to take the world space coordinates of a sphere to screenspace coordinates in order to get the screen space bounds, which I can then use to overlay a div.
Ideally I would like to extend this function to return the height and width of the object, as well as the x & y :
toScreenPosition : function (obj, camera)
{
var vector = new THREE.Vector3();
var widthHalf = 0.5*world.renderer.context.canvas.width;
var heightHalf = 0.5*world.renderer.context.canvas.height;
obj.updateMatrixWorld();
vector.setFromMatrixPosition(obj.matrixWorld);
vector.project(camera);
vector.x = ( vector.x * widthHalf ) + widthHalf;
vector.y = - ( vector.y * heightHalf ) + heightHalf;
return {
x: vector.x,
y: vector.y
};
},
You can create few THREE.Object3D and locate them in the scene in position of the border of the main object you want to project to the screen.
then you can use the method you used on the main object on the other empty objects and get the pixels position on the screen of the border of the main object.
If for example you want to know the screen coordinates of the border of a sphere that has a radius of 5:
var first = new THREE.Object3D();
var second = new THREE.Object3D();
var third = new THREE.Object3D();
var fourth = new THREE.Object3D();
first.position.set(sphere.x,sphere.y+5,sphere.z);
second.position.set(sphere.x,sphere.y-5,sphere.z);
then you can apply the function you wrote, but instead of:
obj.updateMatrixWorld();
etc...
you will do:
first.updateMatrixWorld();
second.updateMatrixWorld();
etc...
then you will have the x,y coordinates of those two objects (that are on border of the main object) on screen and you can check the height by subtracting.
I would write a function that takes as input a 3D point and returns the 2D screenpoint, like the one presented there. Then if you deal with a sphere, that would be easy: get the center of the sphere in 3D, compute the 2D point, that will be the center of the overlay div, get any 3D point on the surface of the sphere, compute the 2D point, you will know the required radius of the overlay div, from there you can easily compute a rectangular area.
I've been spending most of the day trying to rotate a mesh on it's Y axis to face a specific Mesh location.
Found out some Methods but none helped me.
This is what I have.
var vector = new THREE.Vector3(0,0,5);
var axis = new THREE.Vector3(0, 1, 0);
turret.quaternion.setFromUnitVectors(axis, vector.clone().normalize());
So I'm not sure I understeand why the turret wont rotate to face the specific vector position. I also tried this with Euler rotation by using vec1.angleTo(vec2) and rotate the turret to the specified direction but witouth success.
I would be really pleased if somebody can explain how this should work and what's the logic behind it if i'm wrong
This is the THREE.Vector3 constructor (Docs):
Vector3( x, y, z )
x -- Float the vector's x value
y -- Float the vector's y value
z -- Float the vector's z value
You want to rotate a mesh on its Y axis, but you're actually rotating on its Z axis, so instead of:
var vector = new THREE.Vector3( 0, 0, 5);
try:
var vector = new THREE.Vector3( 0, 5, 0);
Here I want to rotate 'endpoint' vector around 'startpoint' vector.
This rotates endpoint vector around world's z-axis:
endpoint.copy(new THREE.Vector3(startpoint.x,startpoint.y,startpoint.z));
endpoint.add(new THREE.Vector3(0, scale, 0 ));
var matrix = new THREE.Matrix4().makeRotationAxis( axis_z, angle );
endpoint.applyMatrix4( matrix );
I have tried to save vector's translation to temporary matrix, and after applying rotation, restore translation back to endpoint vector:
endpoint.copy(new THREE.Vector3(startpoint.x,startpoint.y,startpoint.z));
endpoint.add(new THREE.Vector3(0, scale, 0 ));
var translateMatrix = new THREE.Matrix4().makeTranslation(endpoint.x, endpoint.y, endpoint.z);
var translation = new THREE.Matrix4().copyPosition(translateMatrix);
translateMatrix.makeRotationAxis(axis_z, angle);
translateMatrix.copyPosition(translation);
endpoint.applyMatrix4(translateMatrix);
This code works for rotations, but all the translations are wrong.
Is there an easy way for vector rotation relative to another vector?
UPD. Resolved this. I was blind. No need for translations. What I had to do: apply rotation to vectors delta (endpoint - startpoint), and in the end, add the rotated delta to startpoint (startpoint + delta):
var vector_delta = new THREE.Vector3().subVectors(endpoint, startpoint);
vector_delta.applyAxisAngle( axis_z, rota );
endpoint.addVectors(startpoint, vector_delta);
Ive been having the linewidth problem (something to do with ANGLE on window). I have resorted to using cylinders between 2 points (in 3D space). I have already solved the issue on getting the length of the cylinder based on the 2 points-3D distance formula.
I have been having trouble however getting the angle. I want the cylinder to rotate so that the angle found will make it so that the cylinder connects the two points.
Essensially I am trying to find a way to find the angle between (x1,y1,z1) and (x2,y2,z2). And having it modify a cylinder (cylinder.rotation.x, cylinder.rotation.y, and cylinder.rotation.z).
You can use a transformation matrix to do that. Here's some example code:
function createCylinderFromEnds( material, radiusTop, radiusBottom, top, bottom, segmentsWidth, openEnded)
{
// defaults
segmentsWidth = (segmentsWidth === undefined) ? 32 : segmentsWidth;
openEnded = (openEnded === undefined) ? false : openEnded;
// Dummy settings, replace with proper code:
var length = 100;
var cylAxis = new THREE.Vector3(100,100,-100);
var center = new THREE.Vector3(-100,100,100);
////////////////////
var cylGeom = new THREE.CylinderGeometry( radiusTop, radiusBottom, length, segmentsWidth, 1, openEnded );
var cyl = new THREE.Mesh( cylGeom, material );
// pass in the cylinder itself, its desired axis, and the place to move the center.
makeLengthAngleAxisTransform( cyl, cylAxis, center );
return cyl;
}
// Transform cylinder to align with given axis and then move to center
function makeLengthAngleAxisTransform( cyl, cylAxis, center )
{
cyl.matrixAutoUpdate = false;
// From left to right using frames: translate, then rotate; TR.
// So translate is first.
cyl.matrix.makeTranslation( center.x, center.y, center.z );
// take cross product of cylAxis and up vector to get axis of rotation
var yAxis = new THREE.Vector3(0,1,0);
// Needed later for dot product, just do it now;
// a little lazy, should really copy it to a local Vector3.
cylAxis.normalize();
var rotationAxis = new THREE.Vector3();
rotationAxis.crossVectors( cylAxis, yAxis );
if ( rotationAxis.length() < 0.000001 )
{
// Special case: if rotationAxis is just about zero, set to X axis,
// so that the angle can be given as 0 or PI. This works ONLY
// because we know one of the two axes is +Y.
rotationAxis.set( 1, 0, 0 );
}
rotationAxis.normalize();
// take dot product of cylAxis and up vector to get cosine of angle of rotation
var theta = -Math.acos( cylAxis.dot( yAxis ) );
//cyl.matrix.makeRotationAxis( rotationAxis, theta );
var rotMatrix = new THREE.Matrix4();
rotMatrix.makeRotationAxis( rotationAxis, theta );
cyl.matrix.multiply( rotMatrix );
}
I didn't write this. Find the full working sample here.
It's part of Chapter 5: Matrices from this awesome free Interactive 3D Graphics course taught using three.js.
I warmly recommend it. If you didn't have a chance to play with transformations you might want to start with Chapter 4.
As a side note. You can also cheat a bit and use Matrix4's lookAt() to solve the rotation, offset the translation so the pivot is at the tip of the cylinder, then apply the matrix to the cylinder.