Threejs Get raycaster intersection near objects - javascript

I'm trying to get the objects that are 'near' the mouse, i'm using the following:
raycaster.near = 10;
raycaster.far = 100;
var intersects = raycaster.intersectObjects( scene.children );
if ( intersects.length > 0 ) {
if ( INTERSECTED != intersects[ 0 ].object ) {
console.log(intersects);
}
} else {
//Manage mouse out
}
It ignores the near and far properties and just get the objects right under the mouse.

the near and far properties indicate the distance along the ray, not the distance from it. So in your case the raycaster will match objects between 10 and 100 units away from the camera, not a radius around the ray.
As far as I know, a tolerance-value like you are looking for is not available in the raycaster. What you can do though, is to create an separate scene only for the raycasting. For every object that needs raycasting you would then create another object (scaled up by some factor) and add that to the raycasting-scene.

Related

Three.js draw dynamic line between nearest vertices of two models

I have two models loaded in a scene using OBJloader and the Three.JS library. Some of the models are billboarded by use of model.setRotationFromQuaternion( camera.quaternion );
My goal is to draw lines from a vertex on a billboard to a vertex on the corresponding model - it should be drawn between the nearest points on the two models when the scene is first loaded. The models rotate freely so the lines will need to change as it rotates, staying connected to the same initial verticies.
Think of it like the billboard is a label and the line is connected between the label and somewhere on the rotating model.
How can I achieve this?
Below is a snippet of my code - the issue is that the position of all the models is 0,0,0 so I need to know how to get the location of a vertex on both the label and model and connect the two.
addLabelLines(){
var geometry = new THREE.Geometry();
for ( var i = 0; i < this.labels.length; i ++ ) {
var currentLabel = this.labels[i];
var modelMatchingLabel;
//find matching model
for(var j = 0; j < this.models.length; j++){
if(currentLabel.name.toLowerCase().indexOf(this.models[j].name) >= 0){
modelMatchingLabel = this.models[j];
}
}
if(!modelMatchingLabel){
console.warn("no model matching label "+currentLabel.name);
return;
}
else{
console.log('found model '+modelMatchingLabel.name +" matches label "+currentLabel.name);
geometry.vertices.push( currentLabel.position );
geometry.vertices.push( modelMatchingLabel.position );
}
}
var material = new THREE.LineBasicMaterial( { color: 0x800080 } );
line = new THREE.Line( geometry, material, THREE.LineSegments );
scene.add( line );
}
To get the closest point to be able to draw a line requires your typical "nearest neighbor" algorithms. Take a look at this example, as it'll provide you a very efficient way for achieving what you're looking for
https://threejs.org/examples/webgl_nearestneighbour.html

Object shrinks when rotated javascript

I have been developing a basic game engine just to learn the process and I have hit a issue with my rotation function.
It works fine except that the object shrinks and appears to invert.
Here is a jsfiddle that illustrates my point.
I think the problem would be in the rotation code its self but i'm not positive.
function Rotation(vec, rot){
if(Math.acos((vec.x + vec.y + vec.z -1)/2) === 0) { return vec; }
var qVec = new Quaternion(vec.x, vec.y, vec.z, 0);
qVec = Quaternions.multiply(qVec, rot);
qVec = Quaternions.multiply(qVec, rot.conjugate());
return new Vector3(qVec.x, qVec.y, qVec.z);
}
Couple things:
First, the rotation quaternion is not normalized, so it's inverse is not the same as its conjugate. Rotation by a quaternion is defined by:
Where q is the vector you're rotating around, p is the vector you're rotating, and p' is the final rotated vector.
So this is defined using the inverse of q, which is defined as conjugate(q) / magnitude(q)^2. In the case where q is normalized, magnitude(q)^2 == 1, so it's the same as just multiplying by the conjugate.
Also note the order of operations here. Quat multiplications are non-commutative so their order matters.
You can fix this by normalizing your rotation quaternion, and fixing the order of operations:
var qVec = new Quaternion(vec.x, vec.y, vec.z, 0);
qVec = Quaternions.multiply(rot.normalize(), qVec);
qVec = Quaternions.multiply(qVec, rot.conjugate());
return new Vector3(qVec.x, qVec.y, qVec.z);
Second, you want to define your rotation quat as normal to the plane you want to rotate around. In this case, you want to rotate around the x-y plane. The z-axis is normal to this plane, so we want to define the rotation vector along the z-axis:
function update(){
for(var i = 0; i < gameObjects.length; i++){
gameObjects[i].rotation = euler(new Vector3(0, 0, frames/100));
}
}
With these changes, I was able to get the box rotating correctly.
(In terms of why it was scaling up/down, I'm not 100% sure. Still trying to figure that out.)

Three.js collision detection with Raycaster: IntersectObjects method is returning intersection distances that don't make sense

I am making a simple three.js game where the user controls a spaceship called playerModel (a Mesh obj with a simple BoxGeometry), and must avoid asteroids (SphereGeometry) which are generated far off in the -Z direction and move in the +Z direction towards the ship.
This is my detectCollisions function:
// Returns true if playerModel is intersecting with
// any of obstacles, false otherwise.
function detectCollisions(obstacles) {
var origin = playerModel.position.clone();
for (var v = 0; v < playerModel.geometry.vertices.length; v++) {
var localVertex = playerModel.geometry.vertices[v].clone();
var globalVertex = localVertex.applyMatrix4(playerModel.matrix);
var directionVector = globalVertex.sub(playerModel.position);
var ray = new THREE.Raycaster(origin, directionVector.clone().normalize());
var intersections = ray.intersectObjects(obstacles);
if (intersections.length > 0 &&
intersections[0].distance < directionVector.length()) {
console.log("Fatal collision!"); // definitely a collision
return true;
}
}
return false;
}
I call this method in the game loop as follows, where "spheres" is the array of asteroids:
function draw() {
// loop draw function call
requestAnimationFrame(draw);
// draw THREE.JS scene
renderer.render(scene, camera);
if (!isGameOver) {
generateSpheres();
handleKey(); // updates player direction
movePlayer(); // moves player in direction
moveSpheres();
if (detectCollisions(spheres))
gameOver();
}
}
This is 'working' in the sense that it detects intersections and calls the gameOver function when it does so, but it is detecting collisions that, visually, are not even close to being collisions.
The ray.intersectObjects method seems to return intersections whose distance is calculated as quite close (something like 10), even though the position of the asteroid object it is supposedly intersecting with is very far (position is something like: x:-872 y:-331 z:-5940) from the playerModel which is at (0, 0, 50).
It also seems like the intersecting asteroid object causing the fatal collision always has faceIndex 37, not sure why. The asteroids are created with rings and segments == 7 and varying radii between 20 and 150. The playerModel is fairly small (w:15, h:15, d:30). Thanks so much in advance for any help!
Maybe it's the BoundingSphere but this can only be a problem if the Object change the size (without scale).
Object.Model.geometry.computeBoundingSphere();
But I don't think this is the solution for your Problem. Can you upload an jsfiddle please?

Get position in parent mesh

I have this sort of meshes (each is the child of above)
Scene
-scene.add(SpaceMesh)
-SpaceMesh.add(ShipMesh)
SpaceMesh is moving in scene.
ShipMesh is not moving.
if i request ShipMesh.position.x it returns 0 (logically)
How can i get coordinates of my ShipMesh in SpaceMesh?
--
Example:
SpaceMesh.position.x = 100
ShipMesh.position.x = 0
Logical result will have to be ShipMesh.PositionInSpaceMesh.x = -100
I don't know if is the best way, but from:
how to: get the global/world position of a child object
spaceMesh.updateMatrixWorld();
var vector = new THREE.Vector3();
vector.setFromMatrixPosition( spaceMesh.matrixWorld );
vector.multiplyScalar( -1 );
console.log(vector); // my coords in SpaceMesh
If is there better solution, or some "best practices", please correct me, thank you.

Intersection within Object3D

I have some objects added to an Object3D (for grouping elements) and I'm trying to detect clicks on it.
My scene has a size of 600x400, my camera is within a three-object and my event handler code looks like below:
function onDocumentMouseDown( event ) {
event.preventDefault();
var mouse = {};
mouse.x = ( event.clientX / 600 ) * 2 - 1;
mouse.y = - ( event.clientY / 400 ) * 2 + 1;
var vector = new THREE.Vector3( mouse.x, mouse.y, 1 );
projector.unprojectVector( vector, three.camera );
var ray = new THREE.Ray( three.camera.position, vector.subSelf( three.camera.position ).normalize() );
var intersects = ray.intersectObjects( group.children );
alert(intersects.length);
[...]
}
Actually I'm alerting the count of intersected objects. But it stays zero. It couldn't find any intersected objects. I've already played a bit aroud with the x, y and z values of my projection vector - without success.
I've added a stripped down sample for demonstrating this issue on jsfiddle. Maybe someone has a short hint for me what goes wrong with it?
In your fiddle, because you are calling THREE.SceneUtils.createMultiMaterialObject( ), which creates a hierarchical structure, you need to add the recursive flag to ray.intersectObjects().
var intersects = ray.intersectObjects( group.children, true );
EDiT: ray is now an instance of THREE.Raycaster -- not THREE.Ray.
three.js r.58
I had the same problem and WestLangley's answer provides the answer. Great job! For anybody struggling with mouse selection of objects grouped in Object3D wrapper too, I am posting my own solution.
First, I created an array of objects that are selectable - I hope this also saves some performance, as RayCaster doesnt need to search all objects in the scene, but only those you wish to respond to selection. I also attached this array to scene object directly (solely for the fact that it is already accessible from most parts of my app)
scene.selectable = [];
Next step is to push all objects that you wish to make selectable into this array. You will insert only meshes/sprites/etc from your group, not the whole group. Only last line is important here:
var myWrapper = new THREE.Object3D();
var myObject = new THREE.Mesh( something );
myWrapper.add( myObject );
scene.add ( myWrapper );
scene.selectable.push( myObject );
And lastly in your mouse selection routine you will call raycaster like this:
var intersects = ray.intersectObjects( scene.selectable );

Categories

Resources