I have a geometry object, and I'm trying to add a Torus mesh that goes around that geometry. What I'm trying to do is have the original geometry, and then when the geometry is clicked, it adds a Torus shape on the line around the location that was clicked. However, I'm having trouble getting it to rotate correctly.
I get the torus to show up at the correct place, but I can't orient it around the line. I'm using a raycaster to get the point clicked, so I have the face and the faceindex of the point clicked. On every implementation I try using rotation (using setEulerFromRotationMatrix), it simply moves the location of the torus mesh, not actually rotate it to allow the line to go through the torus.
This seems like it would be trivial, but it's giving me a lot of trouble. What am I doing wrong? Two methods I tried, both unsucessful and exhibiting the behavior above:
var rotationMatrix = new THREE.Matrix4();
rotationMatrix.makeRotationAxis(geometry.faces[fIndex].centroid.normalize(), Math.PI/2);
torusLoop.matrix.multiply(rotationMatrix);
torusLoop.rotation.setEulerFromRotationMatrix(torusLoop.matrix);
//attempt two, similar results to above attempt
tangent = geometry.tangents[segments/radiusSegments].normalize();
axis.crossVectors( up, tangent ).normalize();
var radians = Math.acos( up.dot( tangent ) );
matrix.makeRotationAxis( axis, radians );
torusLoop.rotation.setEulerFromRotationMatrix( matrix );
I need the torus knot to follow the curve of the spline, but it will only stay flat, and rotations simply cause it to move around, not change angles.
Never mind, I figured it out. For those wondering, I translated before rotating, which caused my figure to be rotating around a different axis. My solution was to rotate first, and then translate, and then after creating the mesh, moving that to the position I needed it to be.
Related
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.
I'm trying to add clouds to my scene using the approach in https://codepen.io/teolitto/pen/KwOVvL, which is to make a large number of plane objects at random positions that each rotate continuously around their z axes.
But I would like to be able to move my camera, change its target, etc., and still have the cloud planes look right.
What I need to do is rotate the planes so that as the camera moves and changes target, the cloud planes stay perpendicular to the camera's axis. And I need to do this without changing the planes' rotation around their own z-axis, because otherwise that would break the cloudlike appearance.
I've tried cloudplane.lookAt(camera.position) but of course that doesn't work. It aims all of the planes at the camera, instead of making them all perpendicular to the camera axis. It also sets the object's z-axis, so the clouds don't evolve.
Any advice is appreciated. (I'm new to three.js, so if I have some of the terminology wrong, I apologize for that.)
Edit:
Setting the cloudplane's rotation.x and rotation.y to match the camera's rotation.x and rotation.y seems to get me pretty close, but it doesn't quite get there - its still possible to orbit the camera to a position where the clouds are all invisible because they're all parallel to the camera.
I'm trying to make a Plane to always face the camera or another moving object but I want the Plane to only rotate on 1 axis. How can I use the lookAt function to make it only rotate side ways without tilting to look up or down at the moving object?
thanks, I managed to solve it easily by just keeping the y position of the rotating object constant.
if(planex){
var yaw_control = controls.getYawObject();
pos = new THREE.Vector3( yaw_control.position.x, planex.position.y, yaw_control.position.z );
planex.lookAt(pos);
}
http://www.lighthouse3d.com/opengl/billboarding/index.php?billCyl
maybe this article of any help for you. You are looking for those cylindrical billboards i think but read up from the first page ;) You can modify the specific mesh matrix yourself, although i am not sure if this is the most efficient way. I also did this myself once.
Get the camera look vec:
three.js set and read camera look vector
Then get the camera upVec and afterwards get the cross prodcut of those = rightVec according to the article above.
using those vectors, you can fill in a new Three.Matrix4() like explained in the article and then replace the meshes matrix with the newly created one. As I said, i am not quite into the matrix stuff in three.js but this works but it is probably not that efficient.
For this to work you will have to deactive the meshes auto matrix update with
mesh.matrixAutoUpdate = false;
I'm working on a simple Three.js demo that uses OrbitControls.js.
I'd like to change the behavior of panning in OrbitControls. Currently, when you pan the camera, it moves the camera in a plane that is perpendicular to the viewing direction. I'd like to change it so that the camera stays a constant distance from the ground plane and moves parallel to it. Google Earth uses a similar control setup.
Edit: I should have mentioned this detail in the first place, but I'd also like the point where you click and start dragging to remain directly under the cursor throughout the entire drag. There needs to be that solid connection between the mouse movement and what the user expects to happen on the screen. Otherwise, it feels as though I'm 'slipping' when I try to move around the scene.
Can someone give me a high-level explanation of how this might be done (with or without OrbitControls.js)?
EDIT: OrbitControls now supports panning parallel to the "ground plane", and it is the default.
To pan parallel to screen-space (the legacy behavior), set:
controls.screenSpacePanning = true;
Also available is MapControls, which has an API similar to that of Google Earth.
three.js r.94
Some time ago I was working on exactly this issue, i.e. adaptation of OrbitControls.js to map navigation.
Here's the code of MapControls.js.
Here's the demo of the controls.
I figured it out. Here's the overview:
Store the mousedown event somewhere.
When the mouse moves, get the new mousedown event.
For each of those points, find the points on the plane where those clicks are located (You'll need to put the points into camera space, transform them into world space, then fire a ray from the camera through each point to find their intersections with the plane. This page explains the ray-plane intersection test).
Subtract the world-space start intersection point from the world-space end intersection point to get the offset.
Subtract that offset from the camera's target point and you're done!
In the case of OrbitControl.js, the camera always looks at the target point, and its position is relative to that point. So when you change the target, the camera moves with it. Since the target always lies on the plane, the camera moves parallel to that plane (as long as you're panning).
You should set your camera 'up' to z axe:
camera.up.set(0,0,1)
And then, the main problem with OrbitControl is its panUp() function. It should be fixed.
My pull request : https://github.com/mrdoob/three.js/pull/12727
y axe is relative to camera axes and should be relative to a fixed plan in the world. To define the expected y axe, make a 90° rotation of camera x axe, based on world z axe.
v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix
v.applyAxisAngle( new THREE.Vector3( 0, 0, 1 ), Math.PI / 2 );
v.multiplyScalar( distance );
panOffset.add( v )
Enjoy!
I tried to obtain the normal of the mesh face using these:
ray = new THREE.Raycaster(x, y);
var intersection = ray.intersectObjects(objectsOptical, true);
var vector = intersection[0].face.normal;
Added intersection[0].point and intersection[0].face.normal (multiplied by constant) as one vertex and intersection[0].point as second vertex of a (gray) line. And I got this (red lines are rays and gray should be normals - but they are not):
Illustrative image
Please help me to obtain NORMALS of the mesh FACE.
Thank you.
The normals that you have plotted with red lines look like they might be correct taking into effect perspective projection.
The raycast test hits a single triangular face from your mesh. The normal you are referring to is the normal for the face object the ray hit, ie. from the original mesh.
In the source code for THREE.Raycaster the intersection calculations can be seen returning the face directly.
Elsewhere it is suggested that Ray.intersectObjects() requires face centroids. However I'm not sure about this since the source code doesn't refer to centroids.
Perhaps the normals in the original geometry weren't correct. Try this function first:
geometry.computeFaceNormals();