three.js: finding nearest point of a Mesh to mouse cursor - javascript

Imagine this three.js scene, set up with an OrthographicCamera and OrbitControls:
When the user drags the yellow disc (meant to represent the Sun), the disc needs to move along its yellow circle in response to this action. Here's the scene from another angle, so you can see the full yellow circle:
So, my event handler must determine which point on this circle is closest to the current cursor position. This yellow circle is a THREE.Mesh, by the way.
I'm using THREE.Raycaster to determine some mouseover events, using its intersectObjects() function, but it's not clear to me how to find the nearest point of a single object with this Raycaster. I'm guessing there is some simple math I can do after translating the mouse's position to world co-ordinates. Can someone help me with this? Is Three.js's Raycaster useful here? If not, how do I determine the nearest point of this mesh?
The full source code is here, if it's helpful: https://github.com/ccnmtl/astro-interactives/blob/master/sun-motion-simulator/src/HorizonView.jsx Search for this.sunDeclination, which corresponds to the yellow circle's Mesh object.
For a working demo, go here: https://ccnmtl.github.io/astro-interactives/sun-motion-simulator/
For reference, the sun should behave like this: https://cse.unl.edu/~astrodev/flashdev2/sunMotions/sunMotions068.html (requires Flash)

The simplest version:
get a point on disk
make a projection in the plane of the circle
knowing the radius of the circle, calculate the multiplier for multiplying the vector by the scalar
var point = res.point.clone();
point.z = 0; // Project on circle plane
var scale = circleRadius / point.length();
point.multiplyScalar(circleRadius / point.length())
[ https://jsfiddle.net/c4m3o7ht/ ]

The raycaster returns all objects hit by the ray.. all of the hit points in worldspace.. (which you can convert to/from model space via object3d.worldToLocal and localToWorld)
It returns the hit distances.. which you can sort by whatever heuristic you need...
What I usually do is cast on mouseDown.. record the object and point.. then on mouseMove get the same objects hit point, and apply my edit operation using the difference between those 2 points.
Is this what you're talking about?

Related

THREE.js: Can you force a different order of operations for three.js?

See this question: Threejs Transform Matrix ordering
I'm drawing the orbits of the planets and having problems with changing inertial reference frames (I chose to align the three.js world coordinates with the viewed planet's inertial reference frame). But I need to force three.js to apply operations in the order shown below so the orbit is properly displayed (presently the orbit stays at world (0,0,0) and spins crazily).
The normal (physics) way to do this is to do the following in order:
zero position and rotation of the ellipse.
ellipse.position.set(0,0,0);
ellipse.rotation.set(0,0,0);
Apply Direction Cosine Matrix (matrix which changes reference frame).
ellipse.applyMatrix(DCM);
Translate orbit to be centered on orbiting body, where r is the radius vector of the planet.
ellipse.translateX(r.x);
ellipse.translateY(r.y);
ellipse.translateZ(r.z);
Rotate the ellipse.
ellipse.rotateZ(RAAN); // RAAN : right angle of ascending node
ellipse.rotateX(inc); // inc : orbit inclination
ellipse.rotateZ(ArgP); // ArgP : Argument of Periapsis.
Adjust for difference between center and focal point.
ellipse.translateX(-c);
Profit.
So I'm not sure if there's some kind of command flow in three.js that I'm not familiar with, or if node.js is causing asynchronous problems.
Okay. I just posted this, but found the solution for anyone who has the same problem in the future. If you want to apply things in a different order of operations than three.js wants you to (as seen in this post: Threejs Transform Matrix ordering).
Then you must use the applyMatrix function between each operation that you wish to do in a specific order.
Using the above as an example:
ellipse.position.set(0,0,0);
ellipse.rotation.set(0,0,0);
ellipse.updateMatrix();
ellipse.applyMatrix(DCM);
ellipse.updateMatrix();
ellipse.translateX(r.x);
ellipse.translateY(r.y);
ellipse.translateZ(r.z);
ellipse.updateMatrix();
ellipse.rotateZ(RAAN); // RAAN : right angle of ascending node
ellipse.rotateX(inc); // inc : orbit inclination
ellipse.rotateZ(ArgP); // ArgP : Argument of Periapsis.
ellipse.updateMatrix();
ellipse.translateX(-c);
ellipse.updateMatrix();

Cubic Bezier Curve between two points on a sphere in three.js

I'm letting the user click on two points on a sphere and I would then like to draw a line between the two points along the surface of the sphere (basically on the great circle). I've been able to get the coordinates of the two selected points and draw a QuadraticBezierCurve3 between the points, but I need to be using CubicBezierCurve3. The problem is is that I have no clue how to find the two control points.
Part of the issue is everything I find is for circular arcs and only deals with [x,y] coordinates (whereas I'm working with [x,y,z]). I found this other question which I used to get a somewhat-working solution using QuadraticBezierCurve3. I've found numerous other pages with math/code like this, this, and this, but I really just don't know what to apply. Something else I came across mentioned the tangents (to the selected points), their intersection, and their midpoints. But again, I'm unsure of how to do that in 3D space (since the tangent can go in more than one direction, i.e. a plane).
An example of my code: http://jsfiddle.net/GhB82/
To draw the line, I'm using:
function drawLine(point) {
var middle = [(pointA['x'] + pointB['x']) / 2, (pointA['y'] + pointB['y']) / 2, (pointA['z'] + pointB['z']) / 2];
var curve = new THREE.QuadraticBezierCurve3(new THREE.Vector3(pointA['x'], pointA['y'], pointA['z']), new THREE.Vector3(middle[0], middle[1], middle[2]), new THREE.Vector3(pointB['x'], pointB['y'], pointB['z']));
var path = new THREE.CurvePath();
path.add(curve);
var curveMaterial = new THREE.LineBasicMaterial({
color: 0xFF0000
});
curvedLine = new THREE.Line(path.createPointsGeometry(20), curveMaterial);
scene.add(curvedLine);
}
Where pointA and pointB are arrays containing the [x,y,z] coordinates of the selected points on the sphere. I need to change the QuadraticBezierCurve3 to CubicBezierCurve3, but again, I'm really at a loss on finding those control points.
I have a description on how to fit cubic curves to circular arcs over at http://pomax.github.io/bezierinfo/#circles_cubic, the 3D case is essentially the same in that you need to find out the (great) circular cross-section your two points form on the sphere, and then build the cubic Bezier section along that circle.
Downside: Unless your arc is less than or equal to roughly a quarter circle, one curve is not going to be enough, you'll need two or more. You can't actually model true circular curves with Bezier curves, so using cubic instead of quadratic just means you can approximate a longer arc segment before it starts to look horribly off.
So on a completely different solution note: if you have an arc command available, much better to use that than to roll your own (and if three.js doesn't support them, definitely worth filing a feature request for, I'd think)

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.

combine rotation and translation with three.js

i'm using Three.js (without shaders, only with existing objects methods) in order to realize animations, but my question is very simple : i'm sure it's possible, but can you tell me (or help me) how should i combine several animations on a shape ? For example, rotating and translating a sphere.
When i'm doing :
three.sphere.rotation.y += 0.1;
three.sphere.translateZ += 1;
the sphere rotates but the translation vector is also rotating, so the translation has no effect.
I know a bit openGL and i already have used glPushMatrix and glPopMatrix functions, so do them exist in this framework ?
Cheers
Each three.js object3D has a position, rotation and scale; the rotation (always relative to its origin or "center") defines its own local axis coordinates (say, what the object sees as its own "front,up, right" directions) and when you call translateZ, the object is moved according to those local directions (not along the world -or parent- Z axis). If you want the later, do three.sphere.position.z += 1 instead.
The order of transformation is important. You get a different result if you translate first and then rotate than if you rotate first and then translate. Of course with a sphere it will be hard to see the rotation.

Three.js lookat seems to be flipped

I have a demo of what I mean here: Test Site or (Backup)
For some reason, even though the mouse vector is correct my object is rotated by 90 degrees always in favor of the positive Y axis. The only call that this could be going wrong, as far as I can tell, in is the call: ship.mesh.lookAt(mouse);, I call this every time the screen is animated.
Can anyone tell me what to do to fix this and why it is doing it?
object.lookAt( position ) orients the object so that the object's local positive z-axis points toward the desired position.
Your "ship's" front points in the direction of the local positive y-axis.
EDIT:
To re-orient your geometry, apply a matrix right after the geometry is created, like so:
geometry.applyMatrix( new THREE.Matrix4().makeRotationX( Math.PI / 2 ) );

Categories