ThreeJS raycasting is different in R81 than R71 - three.js

I have a plane with a mesh on it. My code draws a ball when the user double clicks on the mesh. This works just fine in R71 but as soon as I switched to R81 raycaster doesn't return an intersect. Here's the code:
In init():
// Plane
plane = new THREE.Mesh(
new THREE.PlaneBufferGeometry( 1000, 1000, 3, 3 ),
new THREE.MeshBasicMaterial( { color: 0xff0000, opacity: .5, transparent: true } )
);
plane.visible = false;
scene.add( plane );
planes.push(plane);
In doubleClickEvent():
event.preventDefault();
var mouse = new THREE.Vector2((event.clientX / window.innerWidth ) * 2 - 1, -(((event.clientY / window.innerHeight ) * 2 - 1)));
var directionVector = new THREE.Vector3();
directionVector.set(mouse.x, mouse.y, 0.1);
directionVector.unproject(camera);
directionVector.sub(camera.position);
directionVector.normalize();
raycaster.set(camera.position, directionVector);
intersects = raycaster.intersectObjects(planes);
if (intersects.length) {
var sphereParent = new THREE.Object3D();
var sphereGeometry = new THREE.SphereGeometry(.1, 16, 8);
var sphereMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff });
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphereParent.add(sphere);
sphereParent.position.set(intersects[0].point.x, intersects[0].point.y, 0.0);
scene.add(sphereParent);
objects.push(sphereParent);
}
If I change
intersects = raycaster.intersectObjects(planes);
to
intersects = raycaster.intersectObjects(scene.children);
the ball gets drawn but it gets drawn on the wrong position.
Any ideas?

I found the answer. The reason why the raycast isn't working is because the plane's visibility is false. The solution is to change the visibility of the material visibility rather the plane.

Related

Get world position of plane

I'm very new to three.js so I'm sure I'm miss-understanding something here.
I've created a plane in the following way:
var planeGeom = new THREE.PlaneGeometry(0.2, 0.2);
planeGeom.rotateX(-Math.PI / 2);
var plane = new THREE.Mesh(planeGeom, new THREE.MeshBasicMaterial({color: 0xffff00, side: THREE.DoubleSide}));
plane.position.set(0, 0.1, 0);
scene.add(plane);
var mathPlane = new THREE.Plane();
planePointA.copy(plane.geometry.vertices[plane.geometry.faces[0].a]);
planePointB.copy(plane.geometry.vertices[plane.geometry.faces[0].b]);
planePointC.copy(plane.geometry.vertices[plane.geometry.faces[0].c]);
plane.localToWorld(planePointA);
plane.localToWorld(planePointB);
plane.localToWorld(planePointC);
mathPlane.setFromCoplanarPoints(planePointA, planePointB, planePointC);
var helper = new THREE.PlaneHelper( mathPlane, 1, 0xffff00 );
scene.add( helper );
Why is my PlaneGeometry object and Plane positioned differently? Why doesn't .localToWorld() get the world position of the plane?
https://jsfiddle.net/sek0yzLp/
Use .updateMatrixWorld() on the plane after setting its position:
plane.position.set(0, 0.1, 0);
plane.updateMatrixWorld();
scene.add(plane);

Three.js self transparency, object should not be see through

Codepen demonstrating the problem
https://codepen.io/anon/pen/GjJpYw?editors=0010
I have 2 meshes, one which contains 2 cubes, and the other which is 1 cube. The mesh with 2 cubes sandwiches the mesh with one cube (so the single cube is in the center). When I set all cubes to transparent but set the opacity of the center cube to 1, I would not expect to be able to see the back cube when looking through the front cube but I can.
I was wondering is there any easy way to fix this? This is a very simplified version of the problem I'm facing so I can't easily split the geometries. I also cannot just set transparent to false since ideally I would like to be able to have the middle cube partially transparent as well. Any suggestions?
var width = window.innerWidth;
var height = window.innerHeight;
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
var scene = new THREE.Scene();
var cubeGeometry = new THREE.CubeGeometry(100, 100, 100);
var cube = new THREE.Mesh(cubeGeometry);
cube.position.set(0, 25, -200);
var cube2 = new THREE.Mesh(cubeGeometry);
cube2.position.set(0, -25, 200);
cube.updateMatrix();
cube2.updateMatrix();
var singleGeometry = new THREE.Geometry();
singleGeometry.merge(cube.geometry, cube.matrix);
singleGeometry.merge(cube2.geometry, cube2.matrix);
var combinedMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000, opacity: 0.5, transparent: true});
var mesh = new THREE.Mesh(singleGeometry, combinedMaterial);
var cubeGeometry = new THREE.CubeGeometry(200, 200, 200);
var cubeMaterial = new THREE.MeshBasicMaterial({ color: 0x0000ff, opacity: 0.8, transparent: true});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
scene.add(cube);
scene.add(mesh);
var camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000);
camera.position.z = 500;
var controls = new THREE.OrbitControls(camera);
controls.addEventListener('change', render);
var pointLight = new THREE.PointLight(0xffffff);
pointLight.position.set(0, 300, 200);
scene.add(pointLight);
render();
animate();
function animate() {
requestAnimationFrame( animate );
controls.update();
}
function render() {
renderer.render( scene, camera );
}

Superimposing of color

I want to paint cubes red color by means of a mouse. But thus the green cube (at the left) becomes not red, but black. The white cube (on the right) is colored normally. What to do?
example here
// init
var material = new THREE.MeshLambertMaterial({
color: 0x00ff00,
side: THREE.DoubleSide,
vertexColors: THREE.FaceColors
});
var geometry = new THREE.BoxGeometry(100, 100, 100, 4, 4, 4);
var Cube = new THREE.Mesh(geometry, material);
Cube.position.x = -100;
scene.add(Cube);
objects.push(Cube);
var material = new THREE.MeshLambertMaterial({
color: 0xffffff,
side: THREE.DoubleSide,
vertexColors: THREE.FaceColors
});
var geometry = new THREE.BoxGeometry(100, 100, 100, 4, 4, 4);
var Cube = new THREE.Mesh(geometry, material);
Cube.position.x = 100;
scene.add(Cube);
objects.push(Cube);
document.addEventListener('mousedown', onDocumentMouseDown, false);
//
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.intersectObjects(objects);
if (intersects.length > 0) {
var index = intersects[0].faceIndex;
// change the color of the closest face.
intersects[0].face.color = color;
intersects[0].object.geometry.colorsNeedUpdate = true;
}
}
In your example, the final color is the component-wise product of the material color ( 0x00ff00 ) and the face color ( 0xff0000 ), which results in black ( 0x000000 ).
For that reason, when you have face colors, it is a good idea to set the material color to white.
three.js r.69
I suspect your lighting model is the cause of this. If you try painting the dark sides of the white cube, you will also see black faces. There is a large difference between white ffffff and green 00ff00. Your white cube even appears blue due to the hemi light.
Try using a point light instead of your hemi light and see if it makes a difference.

Textures and RingGeometry / CylinderGeometry

UPDATE:
I made jsfiddle example - jsfiddle.net/NEXny/1/
[ignore this - just including a code block so stackoverflow will
let me post the above JSFiddle link. Yeah, seriously.]
I'm having trouble with applying texture to RingGeometry and CylinderGeometry, hope this image will explain my issue.
It is possible to apply texture by one of this ways ?
Currently i'm getting very unexpected results...
You have to modify the geometry vertex UVs to your liking.
Instead, why not just use CircleGeometry for your cylinder end-caps. That is, construct the end-caps yourself?
// cylinder
geometry = new THREE.CylinderGeometry( 192, 192, 40, 64, 1, true ); // open-ended
geometry1 = new THREE.CircleGeometry(192, 64);
// end-cap material
material1 = new THREE.MeshBasicMaterial({
map: textures.circle,
overdraw: 0.5 // for canvas renderer only
});
// cylinder material
material = new THREE.MeshBasicMaterial({
map: textures.line,
overdraw: 0.5 // for canvas renderer only
});
object = new THREE.Object3D();
scene.add(object);
// end-caps
var mesh1 = new THREE.Mesh(geometry1, material1);
mesh1.rotation.x = - Math.PI / 2;
mesh1.position.y = 20
object.add(mesh1);
var mesh2 = new THREE.Mesh(geometry1, material1);
mesh2.rotation.x = Math.PI / 2;
mesh2.position.y = -20
object.add(mesh2);
// cylinder
var mesh = new THREE.Mesh(geometry, material);
object.add(mesh);
fiddle: http://jsfiddle.net/NEXny/2/
three.js r.61

Three.js Collision detection of spheres with THREE.Raycaster

For my project I need collision tests in Three.js. In my CollisionDetection class I'm trying to get a Raycaster to work. And I found some weirdness that I can't explain and can't find a way around:
My CollisionDetector works fine for Cubes.. but when I use Spheres instead, it doesn't give me the same results – Am I wrong to expect the same results as for the cubes? Or do I miss something else?
Here is my Code:
var renderer, camera, scene;
init();
animate();
function init() {
var container = document.getElementById("scene");
var width = window.innerWidth;
var height = window.innerHeight;
renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
camera = new THREE.OrthographicCamera( 0, width, 0, height, 1, 10000 );
camera.position.z = 300;
scene = new THREE.Scene();
scene.add(camera);
container.appendChild(renderer.domElement);
var geometry = new THREE.SphereGeometry(10,16, 16);
//var geometry = new THREE.CubeGeometry( 10, 10, 10 );
var material1 = new THREE.MeshBasicMaterial( { color: 0xFF3333} );
var material2 = new THREE.MeshBasicMaterial( { color: 0xFF3333} );
var material3 = new THREE.MeshBasicMaterial( { color: 0xFF3333} );
var material4 = new THREE.MeshBasicMaterial( { color: 0xFF3333} );
var material5 = new THREE.MeshBasicMaterial( { color: 0xFF3333} );
var element1 = new THREE.Mesh( geometry, material1 );
var element2 = new THREE.Mesh( geometry, material2 );
var element3 = new THREE.Mesh( geometry, material3 );
var element4 = new THREE.Mesh( geometry, material4 );
var element5 = new THREE.Mesh( geometry, material5 );
element1.position.set(200,200,0);
element2.position.set(200,100,0);
element3.position.set(200,300,0);
element4.position.set(100,200,0);
element5.position.set(300,200,0);
scene.add(element1);
scene.add(element2);
scene.add(element3);
scene.add(element4);
scene.add(element5);
var CollisionDetector = new CollisionDetection();
CollisionDetector.addRay(new THREE.Vector3(0, -1, 0));
CollisionDetector.addRay(new THREE.Vector3(0, 1, 0));
CollisionDetector.addRay(new THREE.Vector3(1, 0, 0));
CollisionDetector.addRay(new THREE.Vector3(-1, 0, 0));
CollisionDetector.addElement(element1);
CollisionDetector.addElement(element2);
CollisionDetector.addElement(element3);
CollisionDetector.addElement(element4);
CollisionDetector.addElement(element5);
document.onclick = function(){
CollisionDetector.testElement(element1);
};
}
function CollisionDetection(){
var caster = new THREE.Raycaster();
var rays = [];
var elements = [];
this.testElement = function(element){
for(var i=0; i<rays.length; i++) {
caster.set(element.position, rays[i]);
var hits = caster.intersectObjects(elements, true);
for(var k=0; k<hits.length; k++) {
console.log("hit", hits[k]);
hits[k].object.material.color.setHex(0x0000ff);
}
}
}
this.addRay = function(ray) {
rays.push(ray.normalize());
}
this.addElement = function(element){
elements.push(element);
}
}
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
Or best, see for yourself how it behaves: http://jsfiddle.net/mymL5/12/
On Click every element hit by a ray should turn blue and all hits are registered in the console.
Note the (imho) weird console output for spheres.
Also, why is the lower sphere not hit while the upper is?
You can switch between Cubes and Spheres by Commenting/Uncommenting lines 19/20
Can anyone help me? What am I not getting?
PS: I'm new to Three.js, so I'm probably being dumb.
Since this is homework-related, I am only going to provide some tips.
Your scene is rendering upside down because your args to orthographic camera are incorrect.
Your sphere is bigger than your cube.
Your rays are hitting the north and south poles of your spheres exactly. What is different about those points?
The material.side property tells Raycaster which side(s) of a face to consider the "front".
Your fidde example is running an old version (r.54) of three.js.
three.js r.58
Increased spheres size.
Rotated spheres by some non-trivial angle (so they don't get hit right in the N/S pole).
Now it works? :P
var geometry = new THREE.SphereGeometry(20,17, 17);
element1.position.set(0,0,0);
element2.position.set(0,100,0);
element3.position.set(100,0,0);
element4.position.set(0,-100,0);
element5.position.set(-100,0,0);
element1.rotation.set(0,0,10);
element2.rotation.set(0,0,10);
element3.rotation.set(0,0,10);
element4.rotation.set(0,0,10);
element5.rotation.set(0,0,10);
Still, ray test should be aware of the hitting exact vertex or edge of the triangle, so that might be considered as a place-to-improve for Three.js.
I filed an issue about this in the Three.js repository:
https://github.com/mrdoob/three.js/issues/3541

Resources