How to add event for a mesh in threejs? - three.js

I am new to three.js, I am try to create a click event for a mesh I have created :
const geometry = new THREE.BoxBufferGeometry(size,size*0.5,size);
const material = new THREE.MeshStandardMaterial({color:0x00aa00 });
var option1 = new THREE.Mesh(geometry, material);
option1.position.set( 6, 5, 1)
// option1.callback = objectClickHandler;
// option1.on('click', function(ev) {
// console.log(ev)
// });
console.log(option1)
scene.add(option1)
But this is not working. onclick I will hide and show the object I have imported. But not able to do so as click event is not triggering.Help would be much appreciated.

everything happens in three.js is only in this one DOM that is the canvas.
to detect a click on a mesh, what you do is:
add a click event listener on the canvas.
check if mouse is on the mesh when clicked.
and to check if mouse is on mesh, you use RayCaster.
it should be something like this:
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var targetMesh
function onMouseClick( event ) {
raycaster.setFromCamera( mouse, camera );
var isIntersected = raycaster.intersectObject( targetMesh );
if (isIntersected) {
console.log('Mesh clicked!')
}
}
function onMouseMove( event ) {
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
window.addEventListener( 'mouseclick', onMouseClick, false );
window.addEventListener( 'mousemove', onMouseMove, false );
documents: https://threejs.org/docs/#api/en/core/Raycaster

Related

Auto rotate animation on Three.js

I'm using Three.js in order to render a 3D element.
I work with mousemove in order to rotate the scene with the movement of the mouse.
I'm looking to add an animation that slightly rotates the scene automatically.
It essentially would emulate the rotation done via mouse movement but automatically so the object doesn't appear static when loading.
Does someone know a trivial way to achieve that?
Thanks in advance,
$(function() {
var logoSrc = './assets/vector_gltf/scene.gltf';
var renderer,
scene,
camera,
holder = document.getElementById('holder');
canvas = document.getElementById('canvasLogo');
renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true,
alpha: true,
});
renderer.setSize($(holder).width(), $(holder).height());
holder.appendChild(renderer.domElement);
renderer.setClearColor( 0x000000, 0 );
renderer.setPixelRatio(window.devicePixelRatio);
// renderer.setSize(window.innerWidth, window.innerHeight);
//on resize
window.addEventListener('resize', () => {
// Update camera
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
// Update renderer
// renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setSize($(holder).width(), $(holder).height());
holder.appendChild(renderer.domElement);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
});
camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 1000 );
scene = new THREE.Scene();
var light = new THREE.AmbientLight(0x0000ff, 1);
scene.add(light);
var light2 = new THREE.PointLight(0xff0000, 1);
scene.add(light2);
var loader = new THREE.GLTFLoader();
loader.load(logoSrc, handle_load);
var s;var mesh;
function handle_load(gltf) {
s = gltf.scene;
mesh = s.children[0].children[0].children[0];
// s.children[0].material = new THREE.MeshStandardMaterial({
// normalMap : logoSrc + 'textures/StingrayPBS1SG_normal.png',
// emissiveMap : logoSrc + 'textures/StingrayPBS1SG_emissive.png',
// metalnessMap : logoSrc + 'textures/StingrayPBS1SG_metallicRoughness.png',
// map : logoSrc + 'textures/StingrayPBS1SG_baseColor.png'
// });
scene.add(s);
s.position.y = -0.2;
s.position.z = -2;//15
}
$(".intro").on("mousemove", function(e){
mesh.rotation.set(-0.003 * (e.pageY - window.innerHeight / 20),0, -0.02 * (e.pageX - (window.innerWidth / 2) - 3.14));
//.set(0.0018 * (e.pageY - 291.45) - 1.45,0, 0.02 * (e.pageX - (window.innerWidth / 2) - 3.14))
});
//anim
const clock = new THREE.Clock();
const loop = () =>
{
const elapsedTime = clock.getElapsedTime();//delta time
// Render
renderer.render(scene, camera);
// Call tick again on the next frame
window.requestAnimationFrame(loop);
}
loop();
});
This is what you want: http://threejs.org/examples/misc_controls_orbit.html
Include the orbit controls (after you have downloaded them):
<script src="js/controls/OrbitControls.js"></script>
Setup the variable:
var controls;
Attach the controls to the camera and add a listener:
controls = new THREE.OrbitControls( camera );
controls.addEventListener( 'change', render );
and in your animate function update the controls:
controls.update();
controls.autoRotate();
That last line (autoRotate) is really what you want for the rotation, but everything else is setting up your controls.
You can use a function called animate() that is making your 3D element move automatically and combining it to a render() method.
function animate() {
requestAnimationFrame( animate );
group.rotation += 0.005;
render();
}
function render() {
renderer.render( scene, camera );
}

Three.js FirstPersonControls camera rotation jumps back after GSAP tween / restart controls

Trying to rotate camera after mousedown on raycaster with slerp. But after the animation when i turn the control back on, the camera jumps back to its previous rotation. Updating controls seems not to solve this. What i'm missing here?
function mousedown( event ) {
mouse.x = ( event.clientX / windowWidth ) * 2 - 1;
mouse.y = - ( event.clientY / windowHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
intersects = raycaster.intersectObjects( targets );
if ( intersects.length > 0 ) {
// stop controls
controls.enabled = false;
var targetOrientation = intersects[0].object.quaternion.normalize();
gsap.to({}, 1, {
onUpdate: function() {
controls.update();
//camera.lookAt( intersects[0].object.position );
camera.quaternion.slerp( targetOrientation , this.progress() );
},
onComplete: function() {
controls.update();
//camera.lookAt(new THREE.Vector3( 0, 0, 0 ) );
// restart controls
controls.enabled = true;
}
});
}
}
https://jsfiddle.net/pixeldino/s30pbzj7/152/
FirstPersonControls maintains an internal state of the camera's orientation. By using camera.quaternion.slerp() this state gets out of sync.
You can solve this by using FirstPersonControls.lookAt() at the end of your animation. In this way, the internal state will be updated by forcing the controls to look at the destination point.
https://jsfiddle.net/fnw8643a/

Threejs object Translate and restore

I am new to Three.js. I have loaded a collada (.dae) file having multiple objects.
Now I want to translate each object on its z axis while on mouse over and restore its position when mouse left that object.
I am using "Raycaster" for this purpose.
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
raycaster = new THREE.Raycaster(camera.position, vector.sub( camera.position ).normalize());
mouse = new THREE.Vector2();
and the function is
function onDocumentMouseMove ( event ) {
event.preventDefault();
mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
// console.log(scene);
var intersect = raycaster.intersectObjects( scene.children[2].children);
// console.log(intersect);
if ( intersect.length > 0 )
{
}
else
{
}
}
I have the object where my mouse is intersecting but I'm not sure how to translate the object and restore it on mouse out so it looks smooth.
Edited
You need to move your intersect check out of your onDocumentMouseMove and check on your loop. onDocumentMouseMove will be called only when your moves moves, but the check will be called every frame.
Your loop should look contain the following:
raycaster.setFromCamera( mouse, camera );
var intersect = raycaster.intersectObjects(scene.children);
if ( intersect.length > 0 ) {
intersect[0].object.translateZ(1);
} else {
// For all of your objects:
cube.position.z = 0;
}
Here's a CodePen.

GUI and Raycaster dont work together (three.js)

I have the following code to add a gui and the ability to click on objects.
When I set the controls as controls = new THREE.TrackballControls( camera, renderer.domElement); the GUI works but the Raycaster doesnt seem to work.
If I define the constrols as controls = new THREE.TrackballControls( camera); the raycaster works, however once I click the gui on the corner, then wherever I move the mouse the values of the gui change, while if I close it, the GUI resize with response to mouse movements
Can Anyone give me any hint how to fix this?
At the moment works but I'm able to unclick from the control by simultaneously left and right clicking
This link shows how far I have gone and give you an idea of the problem
http://subsurface.gr/joomla/threejs/StreamFnc_ws.html
Here is the full code:
if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
// global variables
var camera, controls, scene, renderer;
var container, stats;
var raycaster, intersects;
var threshold = 0.05;
var mouse = new THREE.Vector2();
var cube;
// Parameters for GUI
var params = {
AAmin: 0.0,
AAmax: 1000.0
};
// main functions
init();
animate();
function init(){
container = document.createElement( 'div' );
document.body.appendChild( container );
//Setup Camera
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 1000 );
camera.name = 'camera';
camera.position.z = 20;
// Setup world
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2( 0xcccccc, 0.002 );
// Setup lights
var directionalLight = new THREE.DirectionalLight( 0xffffff );
directionalLight.position.set( 1, 1, 1 );
scene.add( directionalLight );
directionalLight.name = 'directionalLight';
var directionalLight1 = new THREE.DirectionalLight( 0x002288 );
directionalLight1.position.set( -1, -1, -1 );
directionalLight1.name = 'directionalLight1';
scene.add( directionalLight1 );
var ambientLight = new THREE.AmbientLight( 0x222222 );
ambientLight.name = 'ambientLight';
scene.add( ambientLight );
raycaster = new THREE.Raycaster();
raycaster.params.Points.threshold = threshold;
// Main Scene
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
cube = new THREE.Mesh( geometry, material );
cube.name = 'mycube';
scene.add( cube );
// GUI parameter
var gui = new dat.GUI();
gui.add( params, 'AAmin', -1000, 500 );
gui.add( params, 'AAmax', 500, 2000 );
gui.open();
//renderer
renderer = new THREE.WebGLRenderer( { antialias: false } );
renderer.setClearColor( scene.fog.color );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
//controls
controls = new THREE.TrackballControls( camera);
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
controls.keys = [ 65, 83, 68 ];
controls.addEventListener( 'change', render);
stats = new Stats();
container.appendChild( stats.dom );
// events
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
render();
}
function animate() {
requestAnimationFrame( animate );
render();
controls.update();
}
function render() {
renderer.render( scene, camera );
stats.update();
}
function onDocumentMouseDown( event ){
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
intersects = raycaster.intersectObject( cube );
if ( intersects.length > 0 ){
console.log("You click a cube!");
}
}
If I understand correctly, the TrackballControls is stopping propagation of the mouseDown event (bound to the renderer.domElement) -- meaning your onDocumentMouseDown handler isn't being invoked. Try moving your document.addEventListener(...) to come before new THREE.TrackballControls(...).
Responding to your comment below:
I see the problem now. There appears to be an incompatibility between TrackballControls and Dat.GUI in that the TrackballControls mouseup events stops propagation of the event, which causes Dat.GUI to bug out and get stuck resizing the UI for some reason.
I believe you can solve this by disabling TrackballControls when clicking the GUI, and re-enabling TrackballControls on mouseup:
gui.domElement.addEventListener( 'mousedown', function(){ controls.enabled = false; }, false );
document.addEventListener( 'mouseup', function(){ controls.enabled = true; }, false );

Determine the object that was clicked in three.js

I wish to detect when an object has been clicked. I am generating a page from the three.js editor. It is a brilliant environment.
Following the advice from this post (https://mandemeskel.wordpress.com/2013/08/19/mouse-events-raycasting-with-three-js/) I ended up with the following code. I have placed a comment where I am having a problem.
function mousedown( event ) {
var scene = this.parent;
var projector = new THREE.Projector();
var mouse_vector = new THREE.Vector3(),
mouse = { x: 0, y: 0, z: 1 },
ray = new THREE.Raycaster( new THREE.Vector3(0,0,0), new THREE.Vector3(0,0,0) ),
intersects = [];
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
mouse_vector.set( mouse.x, mouse.y, mouse.z );
// How do I get the camera?
projector.unprojectVector( mouse_vector, camera );
var direction = mouse_vector.sub( camera.position ).normalize();
ray.set( camera.position, direction );
var object = scene.getObjectByProperty( 'uuid', this.uuid, true );
intersects = ray.intersectObject( object );
if( intersects.length ) {
alert( "hit" );
event.preventDefault();
}
}
I have temporarily fixed the problem by modifying app.js in the published page. By moving the camera variable to be global I got around it, but it is obviously a fudge. Is there any way to access the camera generated by the editor without modifying app.js?
var camera;
var APP = {
Player: function () {
....
The awesome mrdoob has modified the editor to include a getCamera() call
https://github.com/mrdoob/three.js/issues/7510#issuecomment-153059247
The default camera can now be accessed by
var camera = player.getCamera();

Resources