three.js - intersectObjects with GeometryUtils.merge - three.js

I'm would like to create multiple cubes, add a click-event on each one of them and combine them in a "parent" object with GeometryUtils.merge.
var cubes=[];
var mainGeom = new THREE.Geometry();
var cubegeometry = new THREE.CubeGeometry(50, 50, 50, 1, 1, 1 );
cubes[1] = new THREE.Mesh( cubegeometry );
cubes[1].position.y = -50;
THREE.GeometryUtils.merge( mainGeom, cubes[1] );
cubes[2] = new THREE.Mesh( cubegeometry );
cubes[2].position.y = 50;
THREE.GeometryUtils.merge( mainGeom, cubes[2] );
cubes[3] = new THREE.Mesh( cubegeometry );
cubes[3].position.z = -50;
THREE.GeometryUtils.merge( mainGeom, cubes[3] );
cube = new THREE.Mesh( mainGeom,new THREE.MeshBasicMaterial( { color: 0xff0000 } ));
Combining is working fine.
But how can i add a click event on each one of them? I am using this:
function onDocumentMouseDown( event ) {
event.preventDefault();
mouse = {x : ( event.clientX / window.innerWidth ) * 2 - 1, y :- ( event.clientY / window.innerHeight ) * 2 + 1 };
var vector = new THREE.Vector3( mouse.x, mouse.y, 0.5 );
projector.unprojectVector( vector, camera );
var raycaster = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
var intersects = raycaster.intersectObjects( cube );
if ( intersects.length > 0 ) {
console.log(intersects);
}
}
But the intersects.length is always 0!
What am I doing wrong?
Thanks a lot!
Regards - Kosha

GeorgeProfenza has given me a good hint. The solution was to add these Subobject to a parent object, then - this was the solution! Thanks

Related

How to make camera update on zoom

I finally found how to zoom with the mousescroll, but the camera only updates the zoom level when I move the camera by clicking and dragging.
I tried copy pasting code from three.js examples, I tried different browers, I tried adding controls.update(); to both the render and the gameloop function and I tried Googling my issue.
var renderer = new THREE.WebGLRenderer( );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
// check when the browser size has changed and adjust the camera accordingly
window.addEventListener( 'resize', function( )
{
var WIDTH = window.innerWidth;
var HEIGHT = window.innerHeight;
renderer.setSize( WIDTH, HEIGHT );
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix( );
} );
scene.add( new THREE.AmbientLight( 0x00020 ) );
var controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.addEventListener( 'change', render ); // use only if there is no animation loop
controls.minDistance = 10;
controls.maxDistance = 200;
controls.enableZoom = true;
camera.position.z = 7;
// CUBE
var geometry = new THREE.CubeGeometry( 2, 2, 2 );
var cubeMaterials =
[
new THREE.MeshLambertMaterial( { map: new THREE.TextureLoader( ).load( 'img/1.png' ), side: THREE.DoubleSide } ), // Right side
new THREE.MeshPhongMaterial( { map: new THREE.TextureLoader( ).load( 'img/2.png' ), side: THREE.DoubleSide } ), // Left side
new THREE.MeshLambertMaterial( { map: new THREE.TextureLoader( ).load( 'img/3.png' ), side: THREE.DoubleSide } ), // Top side
new THREE.MeshPhongMaterial( { map: new THREE.TextureLoader( ).load( 'img/4.png' ), side: THREE.DoubleSide } ), // Bottom side
new THREE.MeshLambertMaterial( { map: new THREE.TextureLoader( ).load( 'img/5.png' ), side: THREE.DoubleSide } ), // Front side
new THREE.MeshPhongMaterial( { map: new THREE.TextureLoader( ).load( 'img/6.png' ), side: THREE.DoubleSide } ) // Back side
];
// Create a MeshFaceMaterial, which allows the cube to have different materials on each face
var cubeMaterial = new THREE.MeshFaceMaterial( cubeMaterials );
var cube = new THREE.Mesh( geometry, cubeMaterial );
scene.add( cube );
// Floor
var floorGeometry = new THREE.CubeGeometry( 496, 1, 350 );
var floorMaterial = new THREE.MeshLambertMaterial( { map: new THREE.TextureLoader( ).load( 'images/ctr.png' ), side: THREE.DoubleSide } );
var floorCube = new THREE.Mesh( floorGeometry, floorMaterial );
floorCube.position.y = -5;
scene.add( floorCube );
var ambientLight = new THREE.AmbientLight( 0xFFFFFF, 5.0 );
scene.add(ambientLight);
var light1 = new THREE.PointLight( 0xff0040, 4, 50 );
//scene.add( light1 );
var light2 = new THREE.PointLight( 0x0040ff, 3, 50 );
//scene.add( light2 );
var light3 = new THREE.PointLight( 0x80ff80, 4, 50 );
//scene.add( light3 );
var directionalLight = new THREE.DirectionalLight( 0xFFFFFF, 1 );
directionalLight.position.set( 0, 1, 0 );
//scene.add( directionalLight );
var spotLight = new THREE.SpotLight( 0xFF45F6, 25 );
spotLight.position.set( 0, 3, 0 );
scene.add( spotLight );
// game logic
var update = function( )
{
//cube.rotation.x += 0.01;
//cube.rotation.y += 0.005;
var time = Date.now( ) * 0.0005;
light1.position.x = Math.sin( time * 0.7 ) * 30;
light1.position.y = Math.cos( time * 0.5 ) * 40;
light1.position.z = Math.cos( time * 0.3 ) * 30;
light2.position.x = Math.cos( time * 0.3 ) * 30;
light2.position.y = Math.sin( time * 0.5 ) * 40;
light2.position.z = Math.sin( time * 0.7 ) * 30;
light3.position.x = Math.sin( time * 0.7 ) * 30;
light3.position.y = Math.cos( time * 0.3 ) * 40;
light3.position.z = Math.sin( time * 0.5 ) * 30;
};
// draw scene
var render = function( )
{
renderer.render( scene, camera );
};
// run game loop (update, render, repeat)
var GameLoop = function( )
{
requestAnimationFrame( GameLoop );
update( );
render( );
};
GameLoop( );
</script>
I would really love to be able to zoom in instantly when I scroll and not have it update only when I perform a pan.

Applying postprocessing steps for specific objects

Based on this example i try to create a scene where several objects get the bloom, and other objects dont.
The white cube in the middle is supposed to be just white (without the bloom)
I'm confused on how to get the result that i want. I tried for example adding a 2nd scene with the white cube but it seems i cant get the order right. Maybe my approch with different scenes is wrong?
Whats the "best" way to achieve this behaviour? I always end up just seeing one scene, just the white cube or the 4 colored ones. (example below shows everything atm)
myFiddle: https://jsfiddle.net/qwertasyx/8qw3ys4z/16/
var scene,scene2,camera, controls, pointLight, stats;
var composer, renderer, mixer;
var params = {
exposure: 1,
bloomStrength: 1.5,
bloomThreshold: 0,
bloomRadius: 0
};
var objects = [];
var clock = new THREE.Clock();
var container = document.getElementById( 'container' );
stats = new Stats();
//container.appendChild( stats.dom );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.toneMapping = THREE.ReinhardToneMapping;
container.appendChild( renderer.domElement );
scene = new THREE.Scene();
//scene2 = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 100 );
camera.position.set( 2.5,2.5, 10 );
scene.add( camera );
// scene2.add( camera );
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.maxPolarAngle = Math.PI * 0.5;
controls.minDistance = 1;
controls.maxDistance = 10;
controls.target.set(2.5,2.5,0)
controls.update()
// scene.add( new THREE.AmbientLight( 0x404040 ) );
pointLight = new THREE.PointLight( 0xffffff, 1 );
// camera.add( pointLight );
var renderScene = new THREE.RenderPass( scene, camera );
//var renderScene2 = new THREE.RenderPass( scene2, camera );
var bloomPass = new THREE.UnrealBloomPass( new THREE.Vector2( window.innerWidth, window.innerHeight ), 1.5, 0.4, 0.85 );
bloomPass.renderToScreen = true;
bloomPass.threshold = params.bloomThreshold;
bloomPass.strength = params.bloomStrength;
bloomPass.radius = params.bloomRadius;
composer = new THREE.EffectComposer( renderer );
composer.setSize( window.innerWidth, window.innerHeight );
composer.addPass( renderScene );
composer.addPass( bloomPass );
//composer.addPass( renderScene2 );
//objects
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
var material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
var cube = new THREE.Mesh( geometry, material );
cube.vrz = 0.01;
cube.position.x += 5
scene.add( cube );
objects.push(cube)
var material = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
var cube = new THREE.Mesh( geometry, material );
cube.vrz = 0.01;
cube.position.x += 5
cube.position.y += 5
scene.add( cube );
objects.push(cube)
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
var cube = new THREE.Mesh( geometry, material );
cube.vrz = 0.01;
cube.position.y += 5
scene.add( cube );
objects.push(cube)
var material = new THREE.MeshBasicMaterial( { color: 0x0000ff } );
var cube = new THREE.Mesh( geometry, material );
cube.vrz = 0.01;
scene.add( cube );
objects.push(cube)
// cube thats supposed to be not bloomy
var material = new THREE.MeshBasicMaterial( { color: 0xffffff } );
var cube = new THREE.Mesh( geometry, material );
cube.vrz = 0.01;
cube.position.y += 2.5
cube.position.x += 2.5
scene.add( cube );
objects.push(cube)
var gui = new dat.GUI();
gui.add( params, 'exposure', 0.1, 2 ).onChange( function ( value ) {
renderer.toneMappingExposure = Math.pow( value, 4.0 );
} );
gui.add( params, 'bloomThreshold', 0.0, 1.0 ).onChange( function ( value ) {
bloomPass.threshold = Number( value );
} );
gui.add( params, 'bloomStrength', 0.0, 3.0 ).onChange( function ( value ) {
bloomPass.strength = Number( value );
} );
gui.add( params, 'bloomRadius', 0.0, 1.0 ).step( 0.01 ).onChange( function ( value ) {
bloomPass.radius = Number( value );
} );
window.onresize = function () {
var width = window.innerWidth;
var height = window.innerHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize( width, height );
composer.setSize( width, height );
};
function animate() {
requestAnimationFrame( animate );
objects.forEach(function(obj){
obj.rotation.z += obj.vrz;
});
stats.update();
composer.render();
}
animate();
I had a similar problem once. An example from this comment helped me.
Note that in that example there are 2 scenes and 2 composers (the final composer gets output of the previous composer as its input)
ppoFinal.blendPass.uniforms.tAdd.value = ppoRGB.composer.renderTarget2.texture;
and render() is called on both composers.
ppoRGB.composer.render();
ppoFinal.composer.render();
This pattern allows you to apply postprocessing effects selectively and it works well. The problem is the scalability of the method and probably performance. Because when you want to apply another object with yet different effect, you need to introduce 3rd scene and 3rd composer. For my little project in the past I ended up with 4 scenes and 4 composers...

Raycasting vertices of a mesh with three.js

with threejs i can raycast some meshes that i created like that
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 500 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
//create a triangular geometry
var material = new THREE.MeshBasicMaterial( { color : 0x00cc00 } );
var geometry = new THREE.Geometry();
geometry.vertices.push( new THREE.Vector3( -0.5, -0.5, 0 ) );
geometry.vertices.push( new THREE.Vector3( 0.5, -0.5, 0 ) );
geometry.vertices.push( new THREE.Vector3( 0.5, 0.5, 0 ) );
//create a new face using vertices 0, 1, 2
var face = new THREE.Face3( 0, 1, 2 );
//add the face to the geometry's faces array
geometry.faces.push( face );
var object1 = new THREE.Mesh( geometry, material );
scene.add( object1 );
camera.position.z = 10;
//get mouse position
function onDocumentMouseMove( event ) {
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
//render funciton
function animate() {
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObject( object1 );
if ( intersects.length > 0 ) console.log( intersects );
renderer.render( scene, camera );
}
//run
window.addEventListener( 'mousemove', onDocumentMouseMove, false);
window.addEventListener( 'mousemove', animate, false);
animate();
this line of codes work very well for me. but i want to raycast of indices of this triangular shape instead of all mesh. What should i do?
i tried tihs but it does not work? why?
raycaster.insertObject( object1.geometry.vertices );
Raycaster.intersectObjects documentation
intersects should provide the intersected face, as well as the point of intersection. You can check the distance from the point to a face vertex against a minimum threshold (or simply the closest vertex to point), and return the vertex if it passes.
var iFace = intersects[0].face;
var iPoint = intersects[0].point;
var ab = iFace.a.distanceTo(iFace.b);
var ac = iFace.a.distanceTo(iFace.c);
var bc = iFace.b.distanceTo(iFace.c);
var lambda = Math.min(ab, ac, bc) - 0.1;
if(iFace.a.distanceTo(iPoint) <= lambda){
return iFace.a;
}
if(iFace.b.distanceTo(iPoint) <= lambda){
return iFace.b;
}
if(iFace.c.distanceTo(iPoint) <= lambda){
return iFace.c;
}

How can I make three.js update a scene after adding an object or group of objects?

I am fairly new to three.js and have a problem I can't readily find an answer for.
Here is a codepen that should sum up the situation: http://codepen.io/anon/pen/PPYPzO
var container, stats;
var camera, controls, scene, renderer, raycaster, mouse;
init();
animate();
add_world();
var indie_render = true;
for(var j = 0; j < 20; j++){
add_objects(20);
indie_render = !indie_render;
console.log("adding more objects...");
if(!indie_render){render();}
}
function add_world(){
var geometry = new THREE.BoxGeometry( 1000, 1000, 1000);
var mesh = new THREE.MeshBasicMaterial( {color: 0xf5f5dc, wireframe: false, opacity: 0.2, transparent:true } );
var world = new THREE.Mesh( geometry, mesh );
scene.add( world );
render();
}
function add_objects(num, indiv){
var geometry = new THREE.SphereGeometry( 5, 32,32 );
var material = new THREE.MeshBasicMaterial( { shading: THREE.FlatShading } );
material.color.setRGB( Math.random(), Math.random(), Math.random() );
for ( var i = 0; i < num; i ++ ) {
var mesh = new THREE.Mesh( geometry, material );
mesh.position.x = ( Math.random() - 0.5 ) * 1000;
mesh.position.y = ( Math.random() - 0.5 ) * 1000;
mesh.position.z = ( Math.random() - 0.5 ) * 1000;
mesh.updateMatrix();
mesh.matrixAutoUpdate = false;
scene.add( mesh );
if(indie_render){
console.log("individual render");
render();
}
}
}
function init() {
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.set(500, 500, -1000);
camera.up.set( 0, 1, 0 );
camera.lookAt(500,500,500);
controls = new THREE.OrbitControls( camera );
controls.addEventListener( 'change', render );
//world
scene = new THREE.Scene();
// lights
light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 1, 1, 1 );
scene.add( light );
light = new THREE.DirectionalLight( 0x002288 );
light.position.set( -1, -1, -1 );
scene.add( light );
light = new THREE.AmbientLight( 0x222222 );
scene.add( light );
renderer = new THREE.WebGLRenderer( { antialias: false } );
renderer.setClearColor( 0x000000, 1 );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.sortObjects = false;
container = document.getElementById( 'container' );
container.appendChild( renderer.domElement );
mouse = new THREE.Vector2();
raycaster = new THREE.Raycaster();
container.addEventListener( 'mousemove', onMouseMove, false );
container.addEventListener( 'mousedown', onMouseDown, false );
window.addEventListener( 'resize', onWindowResize, false );
}
function animate() {
requestAnimationFrame( animate );
controls.update();
}
function render() {
renderer.render( scene, camera );
}
function onMouseMove( e ) {
mouse.x = ( e.clientX / renderer.domElement.width ) * 2 - 1;
mouse.y = - ( e.clientY / renderer.domElement.height ) * 2 + 1;
}
function onMouseDown( e ) {
mouse.x = ( e.clientX / renderer.domElement.width ) * 2 - 1;
mouse.y = - ( e.clientY / renderer.domElement.height ) * 2 + 1;
if(e.button == 2){ //right button
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( scene.children, true );
if ( intersects.length > 0 ) {
var geometry = new THREE.SphereGeometry( 5, 32,32 );
var material = new THREE.MeshBasicMaterial( { color:0xff0000, shading: THREE.FlatShading } );
var mesh = new THREE.Mesh( geometry, material );
mesh.position.set(intersects[0].point.x, intersects[0].point.y, intersects[0].point.z);
scene.add(mesh);
render();
}
}
}
In this demo, I init() and animate() a blank scene, and then add a translucent cube, following what seems to be convention. Then I add groups of spheres to the scene in a nested for loop, randomly placing the spheres inside the cube and making a render() call after every scene.add() call.
Currently, the scene adds all the spheres and only then is visible to the user, even though I can add individual objects after the for-loop objects are added (by right-clicking on the cube). I need for the user to be able to watch as spheres are added, rather than waiting for the whole thing to be done.
I realize this may not be the most efficient way to render the scene, but it would be quite helpful if, for example, the info on the objects to be animated is arriving asynchronously from a server. Does anyone have a suggestion?
Thanks
1) First: move call render() to animate:
function animate() {
requestAnimationFrame( animate );
render();
controls.update();
}
2) Call add_objects asynchronously: setTimeout( add_objects, 0, 20 );
http://codepen.io/anon/pen/bVbEEP

detect mouse click / hover event on sides of cube in three js

I have a cube created using three js.(WebGL renderer)
I have applied texture on all six sides of cube.
I want to detect on which side the user has clicked on cube ?
look intersects[0].faceIndex
var geometry = new THREE.BoxGeometry( 200, 200, 200 );
var raycaster = new THREE.Raycaster();
Cube = new THREE.Mesh( geometry,
new THREE.MeshFaceMaterial( materials ) );
scene.add( Cube );
function onDocumentMouseDown( event ) {
var vector = new THREE.Vector3(
( event.clientX / window.innerWidth ) * 2 - 1,
- ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
vector.unproject( camera );
raycaster.set( camera.position, vector.sub( camera.position ).normalize() );
var intersects = raycaster.intersectObject( Cube );
if ( intersects.length > 0 ) {
var index = Math.floor( intersects[0].faceIndex / 2 );
switch (index) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
}
}
}

Resources