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 );
Related
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.
I have a bug where I copy the Quaternion of one object and pass it to a function where it gets applied to another object to keep their rotations in sync. I cannot get the Quaternion to apply to the second object.
Given object 1 is msh and object 2 is msh2, this code will not apply the rotation of msh to msh2
var rot = new THREE.Quaternion().copy(msh.rotation);
msh2.rotation.copy(rot);
You can see this further in this Stack Snippet which contains the problem in the smallest reproducable fashion (but isn't the exact code I'm working with in my real project)
var renderer = new THREE.WebGLRenderer({canvas : $("#can")[0]});
renderer.setSize($("#can").width(), $("#can").height());
var cam = new THREE.PerspectiveCamera(45, $("#can").width() / $("#can").height(), 0.1, 100);
cam.position.set(0,2,6);
cam.quaternion.multiply(new THREE.Quaternion().setFromAxisAngle( new THREE.Vector3( 1, 0, 0 ), -Math.PI/8 ));
var scene = new THREE.Scene();
var geo = new THREE.BoxGeometry(1,1,1);
var mat = new THREE.MeshBasicMaterial({color : 0xff0000});
var msh = new THREE.Mesh(geo,mat);
scene.add(msh);
geo = new THREE.BoxGeometry(1,2,1);
mat = new THREE.MeshBasicMaterial({color : 0xff00ff});
var msh2 = new THREE.Mesh(geo,mat);
msh2.position.set(2,0,0);
scene.add(msh2);
function render() {
requestAnimationFrame( render );
msh.rotateX(0.001);
msh.rotateY(0.002);
//For some reason, this doesn't work??
var rot = new THREE.Quaternion().copy(msh.rotation);
msh2.rotation.copy(rot);
//Yet this does (but it doesn't fit the flow of my
//original project because I don't want to pass
//objects around.
//msh2.rotation.copy(msh.rotation);
renderer.render( scene, cam );
}
render();
<script src="https://cdn.rawgit.com/mrdoob/three.js/dev/build/three.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="can" width="400" height="300"></canvas>
Also see the JSFiddle here
Am I missing something here? I do this with Vector3 all the time so I don't see why I can't do it here...
msh.rotation is an instance of THREE.Euler, but you're trying to copy to/from THREE.Quaternion. msh2.rotation.copy(msh.rotation) should work fine, as would msh2.quaternion.copy(msh.quaternion)
If I understand you correctly you want both objects to rotate at the same speed and angle. You just need to update the render(); function with these two lines:
msh2.rotateX(0.001);
msh2.rotateY(0.002);
Updated JSFiddle
I have a object that I'm adding into my scene. I also have various other cubes in the scene. I'm using the following code to fire a Ray and see if I can detect a collision:
var ray = new THREE.Raycaster(camera.position, vec);
var intersects = ray.intersectObjects( scene.children );
For some reason, the regular shapes (cubes) are detected, but the objects that loaded from the obj files are not.
var loader = new THREE.OBJMTLLoader();
loader.load( 'models/technicalTable1.obj', 'models/technicalTable1.mtl', function ( obj ) {
obj.scale.set(0.4, 0.4, 0.4);
obj.position.x = - roomWidth/2 + 100;
obj.position.y = 36;
obj.position.z = - roomLength/2 + 25;
scene.add( obj );
}, onProgress, onError );
Thanks for any help!
You need to pass the recursive flag to Raycaster.intersectObjects().
var intersects = ray.intersectObjects( scene.children, true );
three.js r.70
It doesnt work because the OBJMTLLoader creates a Three.Group
The Group does not have a raycaster.intersectObjects
(I just finished tracing it down and it went to a non implemented function
This REALLY limits the the use of OBJ objects.
I have tried JSON but since you can not load materials and have to create UVmap I find that I need a person that can 3D model or not be able to use the intersectObjects.
Not sure if this is a bug
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.
In three.js, I want to add a mesh to a position in the scene
I've tried:
// mesh is an instance of THREE.Mesh
// scene is an instance of THREE.Scene
scene.add(mesh)
scene.updateMatrixWorld(true)
mesh.matrixWorld.setPosition(new THREE.Vector3(100, 100, 100))
scene.updateMatrix()
BUT it didn't affect anything.
What should I do ?
I would recommend you to check the documentation over here:
http://threejs.org/docs/#Reference/Objects/Mesh
As you can see on the top of the docu-page, Mesh inherits from "Object3D". That means that you can use all methods or properties that are provided by Object3D. So click on the "Object3D" link on the docu-page and check the properties list. You will find the property ".position". Click on ".position" to see what data-type it is. Paha..its Vector3.
So try to do the following:
// scene is an instance of THREE.Scene
scene.add(mesh);
mesh.position.set(100, 100, 100);
i saw it on a github earlier. (three.js r71 )
mesh.position.set(100, 100, 100);
and can be done for individuals
mesh.position.setX(200);
mesh.position.setZ(200);
reference: https://threejs.org/docs/#api/math/Vector3
detailed explanation is below:
since mesh.position is "Vector3". Vector3() has setX() setY() and setZ() methods. we can use it like this.
mesh.position = new THREE.Vector3() ; //see position is Vector3()
vector1 = new THREE.Vector3();
mesh.position.setX(100); //or this
vector1.setX(100) // because all of them is Vector3()
camera1.position.setZ(100); // or this
light1.position.setY(100) // applicable to any object.position
I prefer to use Vector3 to set position.
let group = new THREE.Group();
// position of box
let vector = new THREE.Vector3(10, 10, 10);
// add wooden Box
let woodenBox = new THREE.Mesh(boxGeometry, woodMaterial);
//update postion
woodenBox.position.copy(vector);
// add to scene
group.add(woodenBox)
this.scene.add(group);
If some one looking for way to update position from Vector3
const V3 = new THREE.Vector3(0,0,0) // Create variable in zero position
const box = new THREE.Mesh(geometry, material) // Create an object
Object.assign(box.position, V3) // Put the object in zero position
OR
const V3 = new THREE.Vector3(0,0,0) // Create variable in zero position
const box = new THREE.Mesh(geometry, material) // Create an object
box.position.copy(V3)