threejs raycasting - intersections between camera and a loaded obj model - javascript

I'm moving a camera through a scene that contains an obj I've loaded as a mesh, and I want to detect if the camera has collided with any of the walls of my obj.
I've based my code off this threejs example: http://threejs.org/examples/misc_controls_pointerlock.html, and tried to apply your example here: http://stemkoski.github.io/Three.js/Collision-Detection.html - but I can't seem to get a collision.
My example is here
and the relevant javascript is here:
If anyone can point me in the right direction on how to detect a collision, I'd be grateful. Here's the relevant piece of code:
var objects = [];
var oLoader = new THREE.OBJLoader();
//oLoader.load('models/chair.obj', function(object, materials) {
oLoader.load('models/model-for-threejs.obj', function(object, materials) {
// var material = new THREE.MeshFaceMaterial(materials);
var material = new THREE.MeshLambertMaterial({ color: 0x000000 });
//var material = new THREE.MeshBasicMaterial({wireframe: true});
object.traverse( function(child) {
if (child instanceof THREE.Mesh) {
//objects.push(child); //MH - not sure if the object needs to be added here or not, but if I do, this really bogs down things down
}
});
object.position.x = 0;
object.position.y = 12;
object.position.z = 0;
scene.add(object);
objects.push(object);
});
}
var curPos = controls.getObject().position; //gives the position of my camera
raycaster.ray.origin.copy( curPos );
//raycaster.ray.origin.y -= 10;
raycaster.ray.origin.z +=10; //guessing here, but since I'm moving in 2d space (z / x), maybe I should be moving my ray ahead in z space?
var intersections = raycaster.intersectObjects( objects ); //
var isOnObject = intersections.length > 0;
if (isOnObject){ console.log('collide' }; //MH - nothing happening here

Raycaster's intersect object takes an Object3D with children, and has a flag for recursion.
https://github.com/mrdoob/three.js/blob/master/src/core/Raycaster.js#L33
So it should look like this:
var intersections = raycaster.intersectObjects( yourRootObject3D, true );

Related

How to raycast onto a pointcloud in ThreeJs R71

I am working in autodesk forge which includes Threejs r71 and I want to use a raycaster to detect clicks on different elements within a pointcloud.
Sample code for how to do this with ThreeJs r71 be appreciated.
Right now, I register an extension with the forge api and run the code below within it. It creates creates a pointcloud and positions the points at predetermined locations (saved within the cameraInfo array).
let geometry = new THREE.Geometry();
this.cameraInfo.forEach( function(e) {
geometry.vertices.push(e.position);
}
)
const material = new THREE.PointCloudMaterial( { size: 150, color: 0Xff0000, sizeAttenuation: true } );
this.points = new THREE.PointCloud( geometry, material );
this.scene.add(this.points);
/* Set up event listeners */
document.addEventListener('mousemove', event => {
// console.log('mouse move!');
let mouse = {
x: ( event.clientX / window.innerWidth ) * 2 - 1,
y: - ( event.clientY / window.innerHeight ) * 2 + 1
};
let raycaster = new THREE.Raycaster();
raycaster.params.PointCloud.threshold = 15;
let vector = new THREE.Vector3(mouse.x, mouse.y, 0.5).unproject(this.camera);
raycaster.ray.set(this.camera.position, vector.sub(this.camera.position).normalize());
this.scene.updateMatrixWorld();
let intersects = raycaster.intersectObject(this.points);
if (intersects.length > 0) {
const hitIndex = intersects[0].index;
const hitPoint = this.points.geometry.vertices[ hitIndex ];
console.log(hitIndex);
console.log(hitPoint);
}
}, false);
The output seems to be illogical. At certain camera positions, it will constantly tell me that it is intersecting an item in the pointcloud (regardless of where the mouse is). And at certain camera positions, it won't detect an intersection at all.
TLDR: it doesn't actually detect an intersection b/w my pointcloud and the mouse.
I've simplified the code a bit, using some of the viewer APIs (using a couple of sample points in the point cloud):
const viewer = NOP_VIEWER;
const geometry = new THREE.Geometry();
for (let i = -100; i <= 100; i += 10) {
geometry.vertices.push(new THREE.Vector3(i, i, i));
}
const material = new THREE.PointCloudMaterial({ size: 50, color: 0Xff0000, sizeAttenuation: true });
const points = new THREE.PointCloud(geometry, material);
viewer.impl.scene.add(points);
const raycaster = new THREE.Raycaster();
raycaster.params.PointCloud.threshold = 50;
document.addEventListener('mousemove', function(event) {
const ray = viewer.impl.viewportToRay(viewer.impl.clientToViewport(event.clientX, event.clientY));
raycaster.ray.set(ray.origin, ray.direction);
let intersects = raycaster.intersectObject(viewer.impl.scene, true);
if (intersects.length > 0) {
console.log(intersects[0]);
}
});
I believe you'll need to tweak the raycaster.params.PointCloud.threshold value. The ray casting logic in three.js doesn't actually intersect the point "boxes" that you see rendered on the screen. It only computes distance between the ray and the point (in the world coordinate system), and only outputs an intersection when the distance is under the threshold value. In my example I tried setting the threshold to 50, and the intersection results were somewhat better.
As a side note, if you don't necessarily need point clouds inside the scene, consider overlaying HTML elements over the 3D view instead. We're using the approach in the https://forge-digital-twin.autodesk.io demo (source) to show rich annotations attached to specific positions in the 3D space. With this approach, you don't have to worry about custom intersections - the browser handles everything for you.

How would I create a terrain like this in Three.js

I am trying to create a terrain from a heightmap with a "closed" bottom see the example here:
http://demos.playfuljs.com/terrain/
My terrain generation function is as so:
var img = document.getElementById("landscape-image");
var numSegments = img.width - 1; // We have one less vertex than pixel
var geometry = new THREE.PlaneGeometry(2400, 2400, numSegments, numSegments);
var material = new THREE.MeshLambertMaterial({
color: 0xccccff,
wireframe: false
});
plane = new THREE.Mesh(geometry, material);
plane.name = 'Terrain';
// set height of vertices
for (var i = 0; i < plane.geometry.vertices.length; i++) {
plane.geometry.vertices[i].z = (terrain[i]/255) * height;
}
geometry.computeFaceNormals();
geometry.computeVertexNormals();
plane.position.x = 0;
plane.rotation.x = 0;
plane.position.y = -10;
The problem I am having is how do I create that connected bottom part of the terrain with a THREE.PlaneGeometry. I can't extrude as:
The bottom must be flat if I extrude it will be bumpy like the
terrain.
Extrude takes a Shape object, not a Geometry object.
Really scratching my head here anyone done this before.
Edit: Maybe I could use two planes and merge them but how would I merge the side faces into a single piece of Geometery ?
P.S. The example draws straight to the canvas
create a plane for each side which has your amount of Segments in width and 1 in height. them set the top Vertices according to your heightmap
something like this
var geometry_l = new THREE.PlaneGeometry(2400, 0, numSegments, 1);
plane_l = new THREE.Mesh(geometry_l, material);
for (var i = 0; i < numSegments; i++) {
plane_l.geometry_l.vertices[i].z = (Terrain[0][i]/255) * height;
}
//Offset to the edge of your main plane
you might want to Change your Terrain Array to be two-dimensional for this. so you can always read the row you want.

How do I extract the intersect point (Vector3) of the intersectObjects?

I am trying to find the intersect point between a ray from 'child' and a mesh (child2), using Raycaster:
var raycaster = new THREE.Raycaster();
var meshList = [];
meshList.push(child2);
for (var i = 0; i < child.geometry.vertices.length; i++) {
var diff = new THREE.Vector3();
diff.subVectors (child.geometry.vertices[i],child2.position);
raycaster.set(child.geometry.vertices[i],diff.normalize());
var intersects = raycaster.intersectObjects( meshList );
console.log(intersects[0].point);
}
But the above code is giving me error in the last line (console.log(intersects[0].distance)): "TypeError: undefined is not an object (evaluating 'intersects[0].point')".
How can I extract the intersect point between the ray and the 'child2' mesh?
Test to ensure there actually were results!
var intersects = raycaster.intersectObjects( meshList );
if (intersects.length) {
console.log(intersects[0].point);
} else {
console.log('ha, you missed me');
}

Raycast doesnt hit mesh when casted from the inside

I have set up a simple scene where I have my camera inside a sphere geometry
var mat = new THREE.MeshBasicMaterial({map: THREE.ImageUtils.loadTexture('0.jpg') , overdraw:true, color: 0xffffff, wireframe: false });
var sphereGeo = new THREE.SphereGeometry(1000,50,50);
var sphere = new THREE.Mesh(sphereGeo,mat);
sphere.scale.x = -1;
sphere.doubleSided = false;
scene.add(sphere);
I set up a funcionality where I can look around inside that sphere and my point is to be able to cast a ray on mouse down, hit the sphere and get the coordinates where that hit occured. Im casting a ray but still the intersects are empty.
var vector = new THREE.Vector3();
vector.set( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
vector.unproject( camera );
raycaster.ray.set( camera.position, vector.sub( camera.position ).normalize());
var intersects = raycaster.intersectObjects(scene.children, true);
Everything works with a test cube also put inside my sphere.
My question is, does it matter whether you hit the object from the inside or no ? Because that is the only explanation that comes to my mind.
Thanks in advance.
sphere.doubleSided was changed to sphere.material.side = THREE.DoubleSide some years ago.
It does matter if you hit the object from the inside. Usually a ray will pass through an "inverted" surface due to backface culling which happens on the pipeline level.
Inverted/flipped surfaces are usually ignored in both rendering and raycasting.
In your case, however, i'd go ahead and try setting sphere.doubleSided = false; to sphere.doubleSided = true;. This should make the raycast return the intersection point with your sphere. [shouldn't work with negative scale]
You can also enter the "dirty vertices" mode, and flip the normals manually:
mesh.geometry.dynamic = true
mesh.geometry.__dirtyVertices = true;
mesh.geometry.__dirtyNormals = true;
mesh.flipSided = true;
//flip every vertex normal in mesh by multiplying normal by -1
for(var i = 0; i<mesh.geometry.faces.length; i++) {
mesh.geometry.faces[i].normal.x = -1*mesh.geometry.faces[i].normal.x;
mesh.geometry.faces[i].normal.y = -1*mesh.geometry.faces[i].normal.y;
mesh.geometry.faces[i].normal.z = -1*mesh.geometry.faces[i].normal.z;
}
mesh.geometry.computeVertexNormals();
mesh.geometry.computeFaceNormals();
I also suggest you set scale back to 1.0 instead of -1.0.
Let me know if it worked!

Interpolated surface in Three.js

Hi folks,
I've got a question belongig surfaces in Three.js:
I got a bunch of Vec3 Points and want want to interpolate a surface through them. While searching, I stumbeled across beziers (three.js bezier - only as lines) and what looked more like I was searching : three.js Nurbs. I've tried to reconstruct the code, but the documentation was terrible (pages like this) and I didn't get how everything worked by reconstructing the code...
So here's the question:
Is there any easy way to get a shape out of my calculated points? (I would still be happy, if it's not interpolated).
Thank you guys!
Mat
Edit: What I want to acchieve is a surface plot. I stumbeled across http://acko.net/blog/making-mathbox/ but it's way too big for my needs...
After some try and error I found a solution: add a plane and than transform the single vertices.
// need to setup 'step', 'xStart', 'xEnd', 'yStart', 'yEnd'
// calc the variables
var width = Math.abs(-xStart+xEnd),
height = Math.abs(-yStart+yEnd);
var stepsX = width*step, stepsY = height*step;
var posX = (xStart+xEnd)/2;
var posZ = (yStart+yEnd)/2;
// add a plane and morph it to a function
var geometry = new THREE.PlaneGeometry( width, height, stepsX - 1, stepsY - 1 );
geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
var size = stepsX * (stepsY),
data = new Float32Array( size );
var count = 0, scope = {};
mesh = new THREE.Mesh( geometry, new THREE.MeshNormalMaterial( {
side : THREE.DoubleSide,
transparent: true,
shading: THREE.SmoothShading,
opacity : _opacity }));
mesh.updateMatrixWorld();
// calc y value for every vertice
for ( var i = 0; i < size; i ++ ) {
// calculate the current values
// http://stackoverflow.com/questions/11495089/how-to-get-the-absolute-position-of-a-vertex-in-three-js
var vector = mesh.geometry.vertices[i].clone();
vector.applyMatrix4(
mesh.matrixWorld
);
// set them into the scope
scope.x = vector.x + posX;
scope.y = vector.z + posZ;
// calculate point and write it in a temp array
data[i] = math.eval(term, scope);
}
// push the new vertice data
for ( var i = 0, l = geometry.vertices.length; i < l; i ++ ) {
geometry.vertices[ i ].y = data[ i ];
}
// update the new normals
geometry.computeFaceNormals();
geometry.computeVertexNormals();
// add to scene
scene.add( mesh );
Only issue is that it is not working for non static functions like tan(x). This snippet is using math.js to calc the term.
Greetings Mat

Categories