I`m using three.js. To move around in my scene I am using orbit control and to select my objects I use the raycaster. The Raycaster is sent on click, as well as the orbit control. So if an object is selected and i move the camera around on mouse release, another object will be selected. Is there a way to check for camera movement in orbit control.
Or what is the common way to prevent unwanted selection?
Here is my selection handler:
function onMouseClick( event ) {
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
mouse.x = ( event.clientX / canvasWidth) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
// update the picking ray with the camera and mouse position
raycaster.setFromCamera( mouse, camera );
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects( CentroLite.children, true );
if (intersects.length === 0){
intersects = raycaster.intersectObjects( millingTable.children, true );
}
SELECTED = intersects[0].object;
// DO SOMETHING
}
thanks
Flags might come handy here. You could prevent the selection from happening if the mouse is moved. Something like:
var doClickOnRelease = false;
document.onmousedown = function() {
// Get ready to see if the user wants to select something
doClickOnRelease = true
};
document.onmouseup = function() {
if (doClickOnRelease) {
// Your select function
};
document.onmousemove = function() {
// Since you're dragging, that must be because you
// didn't intend to select something in the first place
doClickOnRelease = false;
};
Related
Three.js : rev 73
I'm using the following construct to find intersection of mouse click with objects in the 3d world (orthographic setup):
function onDocumentMouseDown(event) {
event.preventDefault();
console.log("Click");
var mouse = new THREE.Vector2();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
var raycaster = new THREE.Raycaster();
// update the picking ray with the camera and mouse position
raycaster.setFromCamera(mouse, camera);
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects(scene.children);
console.log("intersects: " + intersects.length);
for (var i = 0; i < intersects.length; i++) {
console.log(intersects[i].point);
}
}
However, the intersection is very inaccurate. Intersection is captured when I click near the top left of the box only.
jsFiddle: Can someone please help me understand why is this misbehaving ?
Also, if no object is being selected, I want to find out where is the click in 3d world relative to the box - left, right, below the box ? Can I use the ray itself to compute this ?
you have to get accurate mouse position for ray-casting :-
mouse.x = ( (event.clientX -renderer.domElement.offsetLeft) / renderer.domElement.width ) * 2 - 1;
mouse.y = -( (event.clientY - renderer.domElement.offsetTop) / renderer.domElement.height ) * 2 + 1;
you have to fire event listener when window resize
i update the fiddle again check it now .
add new function for window resize...
code is self explaintory ...hope you got it .
Check Update Fiddle now
updated fiddle :-
http://jsfiddle.net/gc1v0rza/5/
Trying to pick some objects on click, and I'm trying to use the standard code used in every example:
function onMouseDown(evt) {
evt.preventDefault();
canvasAbsoluteHeight = $('canvas').height();
canvasAbsoluteWidth = $('canvas').width();
mouseDown = true;
mouseX = evt.offsetX == undefined ? evt.layerX : evt.offsetX;
mouseY = evt.offsetY == undefined ? evt.layerY : evt.offsetY;
var mouse = new THREE.Vector3();
mouse.x = ( evt.clientX / canvasAbsoluteWidth ) * 2 - 1;
mouse.y = 1 - ( evt.clientY / canvasAbsoluteHeight ) * 2;
mouse.z = 0;
ray = new THREE.Raycaster( mouse, camera );
var intersects = ray.intersectObjects( objects);
console.log('intersects', intersects);
if ( intersects.length > 0 ) {
console.log('intersects', intersects);
}
}
objects is an array of THREE.Object3D which should be able to be picked.
I think it may be something connected with the camera. My camera is a child of THREE.Object3D for easier manipulation, and the parent object is not set at origin.
Other thing is that canvas is not fullscreen, which may have something with mouse position? (it is inside a div with some offset from the edges of the page).
Check this fiddle. There is Picker class that you can use. First of all you have to init this class with camera and domelement
Picker.init(camera,domelement)
and then you have to attach mesh
Picker.attach(mesh)
and after that you have to specify what do you want to do after mosedown/mouseup in Picker methods.
I have a group of circle geometry elements which are positioned on the vertices of a icosahedron element. I want to do a click event on each individual circle element using jQuery. So how to implement jQuery click events on these elements..
Suggest me some good references for doing it..
Use the raycasting method. The basic mechanism is something like this:
(three revision 70 and javascript vanila)
1) Setup targets
var targets = [],
// When defining your 3d objects, use the push method
// to select the meshes you want to intersect.
// targets.push(objectMesh);
2) Setup raycaster
var raycaster = new THREE.Raycaster(),
mouse = new THREE.Vector2(),
intersects;
3) Intersect target
searchTarget(event){
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
intersects = raycaster.intersectObjects(targets);
if (intersects.length > 0){
// Use the intersected objects:
// intersects[0] represents the foremost object that was hovered
}
}
4) Setup listener
renderer.domElement.addEventListener('mousemove', searchTarget, false);
You could also use jQuery: $( "#canvas" ).mousemove( searchTarget );
I've got an exported animation from blender loaded into my scene. What I’m trying to achieve is to click the object, the object animated 50% the way through it's keyframes and stops, then if I click the object again the remaining 50% of keyframes are iterated over.
So far I have the object animating to 50%. The issue is that no intersection is found in the click eventhandler.
function mousedown( event )
{
event.preventDefault();
// update mouse object
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
// find intersections
// create a reay with origin at the mouse position
// and direction into the scene (camera direction)
var vector = new THREE.Vector3( mouse.x, mouse.y, 1 );
var ray = projector.pickingRay( vector.clone(), camera );
//Check for intersections
var intersects = ray.intersectObjects( targetList , true);
// if there is one (or more) intersections
if ( intersects.length > 0 )
{
console.log(intersects);
//Start animating - animation is a global var in this scene triggering
//the animation.
animation = true;
}
else
{
console.log("No intersections");
}
}
This works when the object is first instantiated. It doesn't work after the object has 'been animated' though. After some debugging I can see that even after the object has moved (the first 50% of the animation has played through) the object's geometry's position = (0, 0, 0).
Which seems to make sense as to why the mousedown event isn't picking up an intersection, as in the 'world' the object isn't actually positioned where it seems to be on the screen.
Here's the code for loading the object:
jsonLoader.load("assets/models/cheese01.js", function(geometry){
geometry.computeMorphNormals();
geometry.computeVertexNormals();
// geometry.computeBoundingSphere();
// geometry.normalsNeedUpdate = true;
// verticesNeedUpdate = true;
var material = new THREE.MeshLambertMaterial({
color: 0xaa1100,
morphTargets: true,
morphNormals: true,
wrapAround: true
});
var mesh = new THREE.MorphAnimMesh(geometry, material);
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.duration = 3000;
mesh.time = 0;
mesh.scale.set( 1, 1, 1 );
mesh.rotation.x = degToRad(175);
mesh.matrixAutoUpdate = false;
mesh.updateMatrix();
geometry.normalsNeedUpdate = true;
mesh.parseAnimations();
scene.add(mesh);
//Objects subject to intersection checks (from mouse click)
targetList.push(mesh);
//Objects to animate
morphs.push(mesh);
});
Cheers in advance for any help!
I'm trying to use THREE.js and been looking at some examples, Voxel Painter exmaple
I'm trying to get it so that every time you click to create a new cube the roll over mesh will always move on top of the cube just pasted rather than being at the point of intersecting of the current mouse position...
All of the source code can be viewed from the link but I believe what I'm trying to do has something to do with this...
You click the mouse to add a Voxel, when onMouseDown() function is active it will check if current mouse position is intersecting with the plane and if CTRL button has been pressed for either a new cube or delete a cube.
function onDocumentMouseDown( event ) {
event.preventDefault();
var intersects = raycaster.intersectObjects( scene.children );
if ( intersects.length > 0 ) {
intersector = getRealIntersector( intersects );
// delete cube
if ( isCtrlDown ) {
if ( intersector.object != plane ) {
scene.remove( intersector.object );
}
}
// create cube
else {
intersector = getRealIntersector( intersects);
setVoxelPosition( intersector );
var voxel = new THREE.Mesh( cubeGeo, cubeMaterial );
voxel.position.copy( voxelPosition );
voxel.matrixAutoUpdate = false;
voxel.updateMatrix();
scene.add( voxel );
}
}
}
When creating a new cube I believe THREE.js grabs the current point where the mouse intersects intersector = getRealIntersector( intersects); and then sets the new Voxel position with the function setVoxelPosition( intersector ); with the intersect point being passed in.
This is the setVoxelPosition function
function setVoxelPosition( intersector ) {
normalMatrix.getNormalMatrix( intersector.object.matrixWorld );
tmpVec.copy( intersector.face.normal );
tmpVec.applyMatrix3( normalMatrix ).normalize();
voxelPosition.addVectors( intersector.point, tmpVec );
voxelPosition.x = Math.floor( voxelPosition.x / 50 ) * 50 + 25;
voxelPosition.y = Math.floor( voxelPosition.y / 50 ) * 50 + 25;
voxelPosition.z = Math.floor( voxelPosition.z / 50 ) * 50 + 25;
}
and the render loop
function render() {
if ( isShiftDown )
theta += mouse2D.x * 1.5;
raycaster = projector.pickingRay( mouse2D.clone(), camera )
var intersects = raycaster.intersectObjects( scene.children );
if ( intersects.length > 0 ) {
intersector = getRealIntersector( intersects );
if ( intersector ) {
setVoxelPosition( intersector );
rollOverMesh.position = voxelPosition;
}
}
camera.position.x = 1400 * Math.sin( THREE.Math.degToRad( theta ) );
camera.position.z = 1400 * Math.cos( THREE.Math.degToRad( theta ) );
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
I have tried to pass in different values into setVoxelPosition( intersector ) but I can't seem to get it right..
Could someone please point me in the right direction?
Thanks.
There are several ways of doing this. I'm not going to sugar coat this answer because frankly, reverse engineering this code will do you some good. I will say, having worked with this voxel code myself, it's important you understand what's happening when you click the mouse button and create a new voxel box.
You're correct in understanding that, this function is in fact taking the current mouse position and creating the box. When the click is complete and the box has been made, the process starts over so the program again looks to where the mouse is and places the Ghost box. In this case the Ghost box is not on top of the previously made box, so you'd have to move the mouse up manually a few pixels to get it there.
Rather than fool around with the setVoxelPosition function directly, I'd recommend you 'temporarly change the x,y,z position of the ghost in relation to the matrix intersect mouse position of your computer. Upon a successful click, increase the matrixIntersect.x .y .z properties of this matrixIntersects object, increasing these values only a little so you get the 'box-on-top' effect you want directly after a click. Remember to change them back when the users mouse moves off the object otherwise the Ghost box will no longer be directly under the mouse and things can get messy fast if these properties grow after every click.