threejs raycasting does not work - three.js

I'm having a problem trying to write a unit test to check collision detection. I simplify code as only this possible - I have a plane in (0, 0, 0) and I do raycasting from above this plane (from (0, 100, 0)) to bottom (0, -1, 0) and I suppose to find intersections with that plane but no luck.
console.clear();
var intersections,
from = new THREE.Vector3(0, 100, 0);
direction = new THREE.Vector3(0, -1, 0),
raycaster = new THREE.Raycaster();
var geometry = new THREE.PlaneGeometry(10, 10, 1, 1);
var ground = new THREE.Mesh(geometry);
ground.position.set(0, 0, 0);
ground.rotation.x = THREE.Math.degToRad(-90);
raycaster.set(from, direction);
intersections = raycaster.intersectObjects([ground]);
console.log(intersections);
What is wrong here? Why this simple code does not show any intersections? (r73).
jsfiddle example

You need to update the world transform of the ground mesh prior to raycasting. (Normally, the renderer does this for your in the render() call.)
console.clear();
var intersections,
from = new THREE.Vector3(0, 100, 0);
direction = new THREE.Vector3(0, -1, 0),
raycaster = new THREE.Raycaster();
var geometry = new THREE.PlaneGeometry(10, 10, 1, 1);
var ground = new THREE.Mesh(geometry);
ground.position.set(0, 0, 0);
ground.rotation.x = THREE.Math.degToRad(-90);
ground.updateMatrixWorld(); // add this
raycaster.set(from, direction);
intersections = raycaster.intersectObjects([ground]);
console.log(intersections);
three.js r.73

Related

How to linke objects and move them relatively in three js?

I created kind of mannequin with three.js.
I want to make it possible to:
link all objects with connection child-parent
move objects relatively (if hand moved, than elbow whould be moved, but if wrist moved, than other parts should not be moved)
I wrote code below and made jsfiddle example. Not sure whether it is correct way.
var scene, camera, renderer, pivot;
//main
init();
animate();
//init
function init() {
//scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0xf0f0f0);
//camera
camera = new THREE.PerspectiveCamera(140, window.innerWidth / window.innerHeight, 0.1, 10000);
//neck
var geometry = new THREE.BoxBufferGeometry(2, 3, 1);
var material = new THREE.MeshNormalMaterial({
transparent: true,
opacity: 0.5
});
var figure = new THREE.Mesh(geometry, material);
figure.position.set(0, 1.5, 0);
//body
var geometry2 = new THREE.BoxBufferGeometry(0.5, 0.5, 0.5);
/* geometry2.rotateZ(THREE.Math.degToRad(90)); // ROTATE GEOMETRY SO IT'S IN THE CORRECT ORIENTATION */
var figure2 = new THREE.Mesh(geometry2, material);
figure2.position.set(0, 1.5, 0); // MOVE THE GEOMOETRY UP BY HALF SO PIVOT IS AT THE BOTTOM OF THE GEO
/* figure2.rotation.set(0, 0, Math.PI / 2); */
figure.add(figure2);
//head
var geometry3 = new THREE.SphereGeometry(0.75, 32, 32);
var material3 = new THREE.MeshBasicMaterial({
color: 0xffff00
});
var sphere3 = new THREE.Mesh(geometry3, material3);
sphere3.position.set(0, 2.5, 0);
figure.add(sphere3)
//right shoulder
var geometry4 = new THREE.SphereGeometry(0.25, 32, 32);
var material4 = new THREE.MeshBasicMaterial({
color: 0xffff00
});
var sphere4 = new THREE.Mesh(geometry4, material4);
sphere4.position.set(1.25, 1.25, 0);
figure.add(sphere4)
//right hand top
var geometry5 = new THREE.BoxBufferGeometry(0.5, 1.5, 0.5);
var figure5 = new THREE.Mesh(geometry5, material);
figure5.position.set(1.25, 0.25, 0);
figure.add(figure5);
//right elbow
var geometry6 = new THREE.SphereGeometry(0.25, 32, 32);
var sphere6 = new THREE.Mesh(geometry6, material3);
sphere6.position.set(1.25, -0.75, 0);
figure.add(sphere6)
//right hand bottom
var geometry7 = new THREE.BoxBufferGeometry(0.5, 1.5, 0.5);
var figure7 = new THREE.Mesh(geometry7, material);
figure7.position.set(1.25, -1.75, 0);
figure.add(figure7);
//right hand bottom sphere
var geometry8 = new THREE.SphereGeometry(0.25, 32, 32);
var sphere8 = new THREE.Mesh(geometry8, material3);
sphere8.position.set(1.25, -2.75, 0);
figure.add(sphere8);
//right wrist
var geometry9 = new THREE.BoxBufferGeometry(0.5, 0.5, 0.5);
var figure9 = new THREE.Mesh(geometry9, material);
figure9.position.set(1.25, -3.25, 0);
figure.add(figure9);
//left shoulder
var geometry10 = new THREE.SphereGeometry(0.25, 32, 32);
var sphere10 = new THREE.Mesh(geometry10, material4);
sphere10.position.set(-1.25, 1.25, 0);
figure.add(sphere10)
//left hand top
var geometry11 = new THREE.BoxBufferGeometry(0.5, 1.5, 0.5);
var figure11 = new THREE.Mesh(geometry11, material);
figure11.position.set(-1.25, 0.25, 0);
figure.add(figure11);
//left elbow
var geometry12 = new THREE.SphereGeometry(0.25, 32, 32);
var sphere12 = new THREE.Mesh(geometry12, material3);
sphere12.position.set(-1.25, -0.75, 0);
figure.add(sphere12)
//left hand bottom
var geometry13 = new THREE.BoxBufferGeometry(0.5, 1.5, 0.5);
var figure13 = new THREE.Mesh(geometry13, material);
figure13.position.set(-1.25, -1.75, 0);
figure.add(figure13);
//left hand bottom sphere
var geometry14 = new THREE.SphereGeometry(0.25, 32, 32);
var sphere14 = new THREE.Mesh(geometry14, material3);
sphere14.position.set(-1.25, -2.75, 0);
figure.add(sphere14);
//left wrist
var geometry15 = new THREE.BoxBufferGeometry(0.5, 0.5, 0.5);
var figure15 = new THREE.Mesh(geometry15, material);
figure15.position.set(-1.25, -3.25, 0);
figure.add(figure15);
//left hip
var geometry16 = new THREE.SphereGeometry(0.5, 32, 32);
var sphere16 = new THREE.Mesh(geometry16, material3);
sphere16.position.set(-0.5, -2.25, 0);
figure.add(sphere16);
//left haunch
var geometry17 = new THREE.BoxBufferGeometry(1, 1.75, 1);
var figure17 = new THREE.Mesh(geometry17, material);
figure17.position.set(-0.5, -3.65, 0);
figure.add(figure17);
//left knee
var geometry18 = new THREE.SphereGeometry(0.5, 32, 32);
var sphere18 = new THREE.Mesh(geometry18, material3);
sphere18.position.set(-0.5, -5, 0);
figure.add(sphere18);
//left shin
var geometry19 = new THREE.BoxBufferGeometry(1, 1.75, 1);
var figure19 = new THREE.Mesh(geometry19, material);
figure19.position.set(-0.5, -6.35, 0);
figure.add(figure19);
//left ankle
var geometry20 = new THREE.SphereGeometry(0.25, 32, 32);
var sphere20 = new THREE.Mesh(geometry20, material3);
sphere20.position.set(-0.5, -7.5, 0);
figure.add(sphere20);
//left foot
var geometry21 = new THREE.BoxBufferGeometry(1, 0.25, 1.5);
var figure21 = new THREE.Mesh(geometry21, material);
figure21.position.set(-0.5, -7.85, 0.25);
figure.add(figure21);
//right hip
var geometry22 = new THREE.SphereGeometry(0.5, 32, 32);
var sphere22 = new THREE.Mesh(geometry22, material3);
sphere22.position.set(0.5, -2.25, 0);
figure.add(sphere22);
//right haunch
var geometry23 = new THREE.BoxBufferGeometry(1, 1.75, 1);
var figure23 = new THREE.Mesh(geometry23, material);
figure23.position.set(0.5, -3.65, 0);
figure.add(figure23);
//right knee
var geometry24 = new THREE.SphereGeometry(0.5, 32, 32);
var sphere24 = new THREE.Mesh(geometry24, material3);
sphere24.position.set(0.5, -5, 0);
figure.add(sphere24);
//right shin
var geometry25 = new THREE.BoxBufferGeometry(1, 1.75, 1);
var figure25 = new THREE.Mesh(geometry25, material);
figure25.position.set(0.5, -6.35, 0);
figure.add(figure25);
//right ankle
var geometry26 = new THREE.SphereGeometry(0.25, 32, 32);
var sphere26 = new THREE.Mesh(geometry26, material3);
sphere26.position.set(0.5, -7.5, 0);
figure.add(sphere26);
//right foot
var geometry27 = new THREE.BoxBufferGeometry(1, 0.25, 1.5);
var figure27 = new THREE.Mesh(geometry27, material);
figure27.position.set(0.5, -7.85, 0.25);
figure.add(figure27);
//add figure
scene.add(figure);
scene.add(new THREE.AxesHelper());
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
//add grid
/* scene.add(new THREE.GridHelper(10, 20)) */
camera.position.z = 5;
//top view on object
/* camera.position.set(0, 10, 0);
camera.up.set(0, 0, -1);
camera.lookAt(0, 0, 0); */
//controls to rotate camera
controls2 = new THREE.OrbitControls(camera, renderer.domElement);
controls2.minPolarAngle = Math.PI/2;
controls2.maxPolarAngle = Math.PI/2;
//controls to drag object in 3d
/* var cubePoints = [];
cubePoints.push(figure2);
var controls = new THREE.DragControls(cubePoints, camera, renderer.domElement); */
}
//animate
function animate() {
requestAnimationFrame(animate);
render();
};
//render
function render() {
renderer.render(scene, camera);
};
To link meshes in Three.js I usually use groups (see https://threejs.org/docs/#api/en/objects/Group), I also use them to change the pivot point of a mesh by placing the mesh in a group with the wanted pivot point in the center coordinates of the group.
Of course you can nest groups for your case, like a group with the hand mesh which can be inside another group with the forearm mesh and so on.
In order to move the elbow position according to the hand position (if I get what you say right), you should use Inverse Kinematics, there are implementations for Three.js like https://github.com/jsantell/THREE.IK but I never used it so I can't tell you more about it.
Hope you solve your problem.

Video texture on THREE.Geometry

The only examples i can find are of video textures being used on PlaneGeometry's or BoxGeometry's. I have a custom plane that i have a video applied to. The video plays (colors change) and shows however the scale is off (looks like its zoomed in).
Video playing on top right, green plane is a custom Geometry which the video is on.
THREE.Geometry
this.geometry = new THREE.Geometry();
this.geometry.vertices.push(
new THREE.Vector3(900, 0, 840)
new THREE.Vector3(-900, 0, 1200),
new THREE.Vector3(900, 900, 840),
new THREE.Vector3(-900, 900, 1200),
);
this.geometry.faces.push(
new THREE.Face3(0, 1, 2),
new THREE.Face3(1, 3, 2),
);
this.geometry.computeFaceNormals();
this.object = new THREE.Mesh(this.geometry, this._videoMaterial());
videoMaterial function
_videoMaterial() {
let videoImage = document.createElement('canvas');
videoImage.width = 480;
videoImage.height = 204;
this.videoImageContext = videoImage.getContext('2d');
this.videoImageContext.fillStyle = '#211e79';
this.videoImageContext.fillRect(0, 0, videoImage.width, videoImage.height);
this.videoTexture = new THREE.Texture(videoImage);
this.videoTexture.minFilter = THREE.LinearFilter;
this.videoTexture.magFilter = THREE.LinearFilter;
this.videoTexture.format = THREE.RGBFormat;
return new THREE.MeshBasicMaterial({
map: this.videoTexture,
overdraw: true,
side: THREE.DoubleSide
});
}
As prisoner849 said, you need to add UVs to your geometry so it knows how to map the texture on its surface
// Add UVs of face 1
this.geometry.faceVertexUvs[0].push([
new THREE.Vector2(0, 0)
new THREE.Vector2(0, 1),
new THREE.Vector2(1, 0)
]);
// Add UVs of face 2
this.geometry.faceVertexUvs[0].push([
new THREE.Vector2(0, 1)
new THREE.Vector2(1, 0),
new THREE.Vector2(1, 1)
]);
this.geometry.uvsNeedUpdate();
You might need to tweak the order of the UVs to match the order of your vertices on the faces, but you want an arrangement like this:
https://threejs.org/docs/index.html#api/en/core/Geometry.faceVertexUvs

how to draw a flat shape in 3-space in three.js?

I'm trying to construct a collection of flat shapes in three.js. Each one is defined as a series of coplanar Vector3 points, but the shapes are not all coplanar. Imagine two flat rectangles as the roof of a house, but with much more complex shapes.
I can make flat Shape objects and then rotate and position them, but since my shapes are conceived in 3d coordinates, it would be much simpler to keep it all in 3-space, which the Shape object doesn't like.
Is there some much more direct way to simply specify an array of coplanar Vector3's, and let three.js do the rest of the work?
I thought about this problem and came up with the idea, when you have a set of co-planar points and you know the normal of the plane (let's name it normal), which your points belong to.
We need to rotate our set of points to make it parallel to the xy-plane, thus the normal of that plane is [0, 0, 1] (let's name it normalZ). To do it, we find quaternions with .setFromUnitVectors() of THREE.Quaternion():
var quaternion = new THREE.Quaternion().setFromUnitVectors(normal, normalZ);
var quaternionBack = new THREE.Quaternion().setFromUnitVectors(normalZ, normal);
Apply quaternion to our set of points
As it's parallel to xy-plane now, z-coordinates of points don't matter, so we can now create a THREE.Shape() object of them. And then create THREE.ShapeGeometry() (name it shapeGeom) from given shape, which will triangulate our shape.
We need to put our points back to their original positions, so we'll apply quaternionBack to them.
After all, we'll assign our set of points to the .vertices property of the shapeGeom.
That's it. If it'll work for you, let me know ;)
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(0, 20, 40);
camera.lookAt(scene.position);
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.target = new THREE.Vector3(10, 0, 10);
controls.update();
var grid = new THREE.GridHelper(50, 50, 0x808080, 0x202020); // xy-grid
grid.geometry.rotateX(Math.PI * 0.5);
scene.add(grid);
var points = [ // all of them are on the xz-plane
new THREE.Vector3(5, 0, 5),
new THREE.Vector3(25, 0, 5),
new THREE.Vector3(25, 0, 15),
new THREE.Vector3(15, 0, 15),
new THREE.Vector3(15, 0, 25),
new THREE.Vector3(5, 0, 25),
new THREE.Vector3(5, 0, 5)
]
var geom = new THREE.BufferGeometry().setFromPoints(points);
var pointsObj = new THREE.Points(geom, new THREE.PointsMaterial({
color: "red"
}));
scene.add(pointsObj);
var line = new THREE.LineLoop(geom, new THREE.LineBasicMaterial({
color: "aqua"
}));
scene.add(line);
// normals
var normal = new THREE.Vector3(0, 1, 0); // I already know the normal of xz-plane ;)
scene.add(new THREE.ArrowHelper(normal, new THREE.Vector3(10, 0, 10), 5, 0xffff00)); //yellow
var normalZ = new THREE.Vector3(0, 0, 1); // base normal of xy-plane
scene.add(new THREE.ArrowHelper(normalZ, scene.position, 5, 0x00ffff)); // aqua
// 1 quaternions
var quaternion = new THREE.Quaternion().setFromUnitVectors(normal, normalZ);
var quaternionBack = new THREE.Quaternion().setFromUnitVectors(normalZ, normal);
// 2 make it parallel to xy-plane
points.forEach(p => {
p.applyQuaternion(quaternion)
});
// 3 create shape and shapeGeometry
var shape = new THREE.Shape(points);
var shapeGeom = new THREE.ShapeGeometry(shape);
// 4 put our points back to their origins
points.forEach(p => {
p.applyQuaternion(quaternionBack)
});
// 5 assign points to .vertices
shapeGeom.vertices = points;
var shapeMesh = new THREE.Mesh(shapeGeom, new THREE.MeshBasicMaterial({
color: 0x404040
}));
scene.add(shapeMesh);
render();
function render() {
requestAnimationFrame(render);
renderer.render(scene, camera);
}
body {
overflow: hidden;
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.90.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.90.0/examples/js/controls/OrbitControls.js"></script>

Drawing custom shapes in Babylon.js

I have been searching all over the web for a way to draw custom 3d shapes in babylon.js. I would be grateful if somebody could provide a working example. For instance, a 3d irregular pentagon, triangle fan, or a wedge.
you can find a lot of info about parametric shapes in Babylon.js here:
http://doc.babylonjs.com/page.php?p=24847
And mainly for the ribbon here:
http://doc.babylonjs.com/page.php?p=25088
Here is a wedge using the ribbon object:
var createScene = function() {
var scene = new BABYLON.Scene(engine);
scene.clearColor = new BABYLON.Color3(0.8, 0.8, 0.8);
var camera = new BABYLON.ArcRotateCamera("Camera", 3 *Math.PI / 2, Math.PI / 2, 20, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, false);
// lights
var light = new BABYLON.HemisphericLight("hemi", new BABYLON.Vector3(0, 1, 0), scene);
light.groundColor = new BABYLON.Color3(0.2, 0.2, 0.5);
light.intensity = 0.6;
var light2 = new BABYLON.PointLight("light2", new BABYLON.Vector3(-20, 0, -20), scene);
light2.diffuse = BABYLON.Color3.White();
light2.specular = BABYLON.Color3.Green();
light2.intensity = 0.6;
// material
var mat = new BABYLON.StandardMaterial("mat1", scene);
mat.alpha = 1.0;
mat.diffuseColor = new BABYLON.Color3(0.5, 0.5, 1.0);
//mat.backFaceCulling = false;
mat.wireframe = true;
// tubular ribbon
path1 = [];
path2 = [];
path1.push( new BABYLON.Vector3(0, 0, 0) );
path2.push( new BABYLON.Vector3(0, 2, 0) );
path1.push( new BABYLON.Vector3(1, 0, 0) );
path2.push( new BABYLON.Vector3(1, 2, 0) );
path1.push( new BABYLON.Vector3(0, 0, 1) );
path2.push( new BABYLON.Vector3(0, 2, 1) );
var ribbon = BABYLON.Mesh.CreateRibbon("ribbon", [path1, path2], false, true, 0, scene);
ribbon.material = mat;
scene.registerBeforeRender(function(){
light2.position = camera.position;
});
return scene;};

Smooth Ring 3D in three.js

I want to draw a ring with the help of ExtrudeGeometry.
Ring3D = function(innerRadius, outerRadius, heigth, Segments) {
var extrudeSettings = {
amount: heigth,
bevelEnabled: false,
curveSegments: Segments
};
var arcShape = new THREE.Shape();
arcShape.moveTo(outerRadius, 0);
arcShape.absarc(0, 0, outerRadius, 0, Math.PI * 2, false);
var holePath = new THREE.Path();
holePath.moveTo(innerRadius, 0);
holePath.absarc(0, 0, innerRadius, 0, Math.PI * 2, true);
arcShape.holes.push(holePath);
var geometry = new THREE.ExtrudeGeometry(arcShape, extrudeSettings);
var material = new THREE.MeshPhongMaterial({
color: 0x00ffff
});
var mesh = new THREE.Mesh(geometry, material);
mesh.rotation.x = Math.PI / 2;
mesh.position.y = heigth / 2;
var object = new THREE.Object3D;
object.add(mesh);
return object;
}
The resulting figure has visible scars. And the cylinder and torus such scars not. How to get rid of them? Example here.
with geometry.computeVertexNormals();
var shape = new THREE.Shape();
shape.moveTo(0, 0);
shape.lineTo(0, 10);
shape.quadraticCurveTo(35, 30, 0, 50);
shape.lineTo(0, 60);
shape.quadraticCurveTo(48, 30, 0, 0);
var extrudeSettings = {
amount : 20,
steps : 10,
bevelEnabled: false,
curveSegments: 8
};
var geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
var mesh = new THREE.Mesh( geometry, new THREE.MeshPhongMaterial( { color: '0x804000' ,transparent: true,opacity: 0.2} ) );
scene.add( mesh );
You need to .computeVertexNormals() from your geometry. But it seems there is some issue (with a solution) explained here: https://github.com/mrdoob/three.js/issues/323. I am not sure if it will work in your case.
I have found a comment in the code of ExtrudeGeometry:
this.computeFaceNormals();
// can't really use automatic vertex normals
// as then front and back sides get smoothed too
// should do separate smoothing just for sides
//this.computeVertexNormals();
So it seems it is not supported now :(

Resources