Generate Mesh from Points Three.js - javascript

I'm trying to create a clickable shape in Three from a bunch of points that are generated by mouse click.
This code is kind of working:
mouse.x = ( ( event.clientX - renderer.domElement.offsetLeft ) / player.width ) * 2 - 1;
mouse.y = - ( ( event.clientY - renderer.domElement.offsetTop ) / player.height ) * 2 + 1
raycaster.setFromCamera( mouse, camera );
var objects = [];
objects.push(selectedHotspot);
var intersects = raycaster.intersectObjects( objects, true );
if ( intersects.length > 0 ) {
var point = new THREE.Mesh( new THREE.SphereGeometry(1, 1, 1), new THREE.MeshBasicMaterial( { color: 0x00ffff } ) );
point.position.copy(intersects[0].point);
scene.add(point);
points.push(intersects[0].point);
}
var geometry = new THREE.Geometry();
points.forEach( function( point ){
geometry.vertices.push( point );
});
geometry.vertices.push( points[0] );
geometry.faces.push( new THREE.Face3(0, 1, 2));
// material
var material = new THREE.MeshBasicMaterial( { color: 0xffffff } );
// line
var line = new THREE.Mesh( geometry, material );
scene.add( line );
hotspots.push( line );
The points get added, I can draw lines between them I just can't fill in the center so the mouse can detect it!

You can create a mesh from points using THREE.ConvexGeometry.
var mesh = new THREE.ConvexGeometry( vertices_array );
See, for example, https://threejs.org/examples/webgl_geometry_convex.html
This is just the convex hull of your points, but it should be sufficient for your use case.
You must include the three.js file examples/jsm/geometries/ConvexGeometry.js explicitly in your source code.
three.js r.147

There are different ways to create a mesh out of a point cloud - it all depends on what your specific needs are. I'll try to give you a high-level overview of a few approaches.
Perhaps a bounding box is enough? Calculate the bounding box of the point cloud and raycast against the BBox.
If the BBox happens to contains large volumes that have no points in them, then you may need a tighter-fitting mesh around these points. Given the ray being cast, project all points onto a plane normal to the ray, then construct the 2D convex hull of the points on this plane using the Gift wrapping algorithm. There are most likely existing libraries implementing this algorithm. Use the polygon constructed by this algorithm for the raycast test.

Related

How to remove an object from the scene

I want to remove some cylinders that are in scene so i can put new ones in another position.
This is how I place the cylinders (this works just showing so you understand what I am trying to do).
for (i = 0; i < aantalLangs; i++) {
var geometry = new THREE.CylinderGeometry( (langsDiameter * scale), (langsDiameter * scale) , langsLengte * scale , 20 );
var material = new THREE.MeshBasicMaterial( {color: 0xffe26f} );
var cylinder = new THREE.Mesh( geometry, material );
scene.add( cylinder );
cylinder.position.set( 0 , 0 ,onderRandRooster);
onderRandRooster -= (langsMaas * scale);
cylinder.rotation.z = Math.PI / 2;
}
And I use this function to remove them.
function ClearMesh(){
scene.remove(scene.getObjectByName(cylinder));
scene.remove(scene.getObjectByName(cylinder2));
}
I want to use this button to remove the cylinders.
<button onclick="ClearMesh();">Clear mesh</button>
If you want to use Object3D.getObjectByName(), it's necessary to apply a string as a parameter. To be more precise, Object3D.name which you currently don't set in your application. Something like this should work:
// in your for loop
var cylinder = new THREE.Mesh( geometry, material );
cylinder.name = 'cylinder' + i;
// in your ClearMesh() function
scene.remove( scene.getObjectByName( 'cylinder1' ) );
Besides, consider to reuse your material and geometry when creating your cylinder meshes if they have the same properties. Just declare them outside of the for loop. Otherwise you should use the respective .dispose() methods in order to free internal resources of the engine when you remove a cylinder. Have a look at https://stackoverflow.com/a/40730686/5250847 for more details.
three.js R103

convert bezier into a plane road in three.js

I am trying to draw a curved road in three.js from some beziers I get with previous calculations, the problem is that I can't find the way to convert the sequence of curved lines (one starting at the end of the previous one) to a curved plane.
I have a 3D scene where there are some cars, a road created with a plane and the path of the coming road is painted. I use that Bezier curves I said to represent the path as a Line with
function createAdasisBezier(initx, inity, cp1x, cp1y, cp2x, cp2y, finalx, finaly) {
bezier = new THREE.CubicBezierCurve3(
new THREE.Vector3(initx, inity, 0),
new THREE.Vector3(cp1x, cp1y, 0),
new THREE.Vector3( cp2x, cp2y, 0),
new THREE.Vector3(finalx, finaly, 0)
);
curvePath = new THREE.CurvePath();
curvePath.add(bezier);
var geoPath = curvePath.createPointsGeometry( 5 );
var lineMat = new THREE.LineBasicMaterial({color: 0xff0000});
curveLine = new THREE.Line(geoPath, lineMat);
curveLine.rotation.set(-Math.PI/2,0,0);
curveLine.position.y = 0.1;
scene.add(curveLine);
}
First, I tried extruding the line, but then I realized that it might not be the solution because I wanted to do a road, and although I could move top vertices on X and Y to place them near the bezier in order to be the external part of the curve, the result was not only unfavourable, it also made impossible to preserve a relation between a left and a right curve.
To move vertices (once identified) I did a loop and move them manually:
for (var i = 0; i < geoPath.vertices.length; ++i) {
geoPath.vertices[i].y += 10;
}
Bevel is not enabled in the extude.
Then I tried to draw a plane over each bezier (as a child of them) and rotate it to face the path, but the result was not as I expected, and it if it were, it would spoil the arcs of the curves.
To do it, I created a copy of every bezier, and place it aside the original ones, then I created the plane.
var plane = new THREE.PlaneBufferGeometry(10,25,1,1);
var planemesh = new THREE.Mesh(plane, material);
planemesh.position.set(copy.geometry.vertices[0].x, copy.geometry.vertices[0].y, 0);
Last thing I was trying to do is creating a clone of the line, separate it some meters and "connect" the first vertex from one, to the first of the other, so I get a closed geometry, and I can create a Face, but I don't find how to "connect" vertices from 2 different geometries. I tried adding the vertex from one to the other, but it did not work.
Does anybody have an idea how could I convert the line into a curved road? Thanks in adcance.
You should try looking at the Geometry > Extrude > Shapes example. As you can see, all extruded shapes maintain their width and direction, despite turning left/right or looping completely.
Instead of using bezier curves, they're using a CatmullRomCurve3 to define the extrusion. If you look at the source code, the essential code to make the red extruded shape begins in line 69:
// Define the curve
var closedSpline = new THREE.CatmullRomCurve3( [
new THREE.Vector3( -60, -100, 60 ),
new THREE.Vector3( -60, 20, 60 ),
new THREE.Vector3( -60, 120, 60 ),
new THREE.Vector3( 60, 20, -60 ),
new THREE.Vector3( 60, -100, -60 )
] );
closedSpline.type = 'catmullrom';
closedSpline.closed = true;
// Set up settings for later extrusion
var extrudeSettings = {
steps : 100,
bevelEnabled : false,
extrudePath : closedSpline
};
// Define a triangle
var pts = [], count = 3;
for ( var i = 0; i < count; i ++ ) {
var l = 20;
var a = 2 * i / count * Math.PI;
pts.push( new THREE.Vector2 ( Math.cos( a ) * l, Math.sin( a ) * l ) );
}
var shape = new THREE.Shape( pts );
// Extrude the triangle along the CatmullRom curve
var geometry = new THREE.ExtrudeGeometry( shape, extrudeSettings );
var material = new THREE.MeshLambertMaterial( { color: 0xb00000, wireframe: false } );
// Create mesh with the resulting geometry
var mesh = new THREE.Mesh( geometry, material );
From here, it should only be a matter of small tweaks to these parameters to get the specific road shape you want.

Align BoxGeometry between two 3D Points

I make a game where you can add objects to a world without using a grid. Now I want to make a footpath. When you click on "Create footpath", then you can add a point to on the world at the raycaster position. After you add a first point you can add a second point to the world. When these 2 objects where placed. A line/footpath is visible from the first point to the second one.
I can do this really simple with THREE.Line. See the code:
var lineGeometry = new THREE.Geometry();
lineGeometry.vertices.push( new THREE.Vector3(x1,0,z1), new THREE.Vector3(x2,0,z2) );
lineGeometry.computeLineDistances();
var lineMaterial = new THREE.LineBasicMaterial( { color: 0xFF0000 } );
var line = new THREE.Line( lineGeometry, lineMaterial );
scene.add(line);
But I can't add a texture on a simple line. Now I want to do something the same with a Mesh. I have the position of the first point and the raycaster position of the second point. I also have the lenght between the two objects for the lenght of the footpath. But I don't know how I can get the rotation what is needed.
Note. I saw something about LookAt, is this maybe a good idea, how can I use this with a mesh?
Can anyone help me to get the correct rotation for the footpath object?
I use this code for the foodpath mesh:
var loader = new THREE.TextureLoader();
loader.load('images/floor.jpg', function ( texture ) {
var geometry = new THREE.BoxGeometry(10, 0, 2);
var material = new THREE.MeshBasicMaterial({ map: texture, overdraw: 0.5 });
var footpath = new THREE.Mesh(geometry, material);
footpath.position.copy(point2);
var direction = // What can I do here?
footpath.rotation.y = direction;
scene.add(footpath);
});
I want to get the correct rotation for direction.
[UPDATE]
The code of WestLangley helps a lot. But it works not in all directions. I used this code for the lenght:
var lenght = footpaths[i].position.z - point2.position.z;
What can I do that the lenght works in all directions?
You want to align a box between two 3D points. You can do that like so:
var geometry = new THREE.BoxGeometry( width, height, length ); // align length with z-axis
geometry.translate( 0, 0, length / 2 ); // so one end is at the origin
...
var footpath = new THREE.Mesh( geometry, material );
footpath.position.copy( point1 );
footpath.lookAt( point2 );
three.js r.84

rotate around pendulum 0.85 cylinder height

im stuck with a work that i need to do, basicly first i needed to create a pendulum that basicly is 2 spheres and 1 cylinder the 2 spheres are in the up and down a bigger 1 and a smaller 1, what i need to do is the pendulum should move not around his own center but about a center that is 0.85 the height of my cylinder, my idea was to create a pivot point, but since i dont know very good how the pivot point work i tryed a lot of things, i tryed first to add the cylinder to the scene and the spheres to the cylinder, then i created the pivot point and add the pivot point to the cylinder, in the animate function i just tried to rotate the pivot point in the x axis but nothing happens :/
Here is my code guys hope someone can give me a hand
var scene, camera, renderer;
var caixaGrande, caixaPequena1, caixaPequena2,cylinder,Cylinder2,esferaGrande;
var pivotPoint1, pivotPoint2;
const RAIOCILINDRO = 2.5;
const ALTURACILINDRO = 100;
const RAIOESFERAGRANDE = 15;
const RAIOESFERAPEQUENA = 5;
var rotacao = Math.PI/180;
window.onload = function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 10000 );
camera.up=new THREE.Vector3(0,1,0);
camera.position.set(150,50,50);
camera.lookAt(new THREE.Vector3(0, 0, 0));
// Add a directional light to show off the object
var light = new THREE.DirectionalLight( 0xffffff, 1.5);
// Position the light out from the scene, pointing at the origin
light.position.set(1.5, 1.5, 1);
scene.add( light );
var textureCylindro = new THREE.TextureLoader().load("CylinderTexture.png");
var textureSphere = new THREE.TextureLoader().load("SphereTexture.png");
geometry = new THREE.CylinderGeometry(RAIOCILINDRO,RAIOCILINDRO,ALTURACILINDRO);
material = new THREE.MeshPhongMaterial( {color: 0xffffff , map:textureCylindro} );
cylinder = new THREE.Mesh(geometry,material);
scene.add(cylinder);
pivotPoint1 = new THREE.Object3D();
pivotPoint1.position.y=ALTURACILINDRO*0.15;
cylinder.add(pivotPoint1);
var geometry = new THREE.SphereGeometry(RAIOESFERAGRANDE);
var material = new THREE.MeshPhongMaterial( {color: 0xffffff,map:textureSphere} );
esferaGrande = new THREE.Mesh( geometry, material );
esferaGrande.position.y = -ALTURACILINDRO/2;
cylinder.add( esferaGrande );
var geometry = new THREE.SphereGeometry(RAIOESFERAPEQUENA);
var material = new THREE.MeshPhongMaterial( {color: 0xffffff,map:textureSphere} );
var esferaPequena = new THREE.Mesh( geometry, material );
esferaPequena.position.y = ALTURACILINDRO/2;
cylinder.add( esferaPequena );
Cylinder2 = cylinder.clone();
Cylinder2.position.z = 3 * RAIOESFERAGRANDE;
scene.add(Cylinder2);
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
animate();
}
function animate() {
pivotPoint1.rotation.x += 10;
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
What you should do is create an Object3D that you add the other 3 elements to, then you add that Object3D to the scene. Then when you want to rotate the whole thing, you rotate your Object3D.
So for example:
var axis = new Object3D();
sphere1.position.y = -.15 * pendulumLength;
sphere2.position.y = .85 * pendulumLength;
cylinder.position.y = .35 * pendulumLength;
//assuming the cylinder is pendulumLength long, this^ puts it right between the two balls
axis.add(sphere1);
axis.add(sphere2);
axis.add(cylinder);
scene.add(axis);
then in your animate() function, simply rotate your Axis:
axis.rotation.z += .01;
EDIT:
So, here is my bad drawing of what happens. If you look here, the circle when positioned at 0,0 when rotated, rotates around the axis. Again when you move it up to 1,1 it rotates around its center point, because rotation is relative to the center point of the circle.
Again the same is true of the cylinder. At 0,0 it rotates around its center. At .5,.5 it also rotates around its center. It doesn't care where it is, it will rotate around its position point.
So if we want to rotate these two as a group relative to some other point, we need to make them the children of another object, because when we move the parent, the children maintain their relationship to the parent, even though their positions are still 1,1 and .5,.5 respectively.
The reason that they get rotated in the manner on the right is because their relationship to the parent object is 1,1 and .5,.5 relative to a parent object with a rotation of 0 radians. When we rotate the parent object by some number of radians, they need to move in order to maintain their original relationship.
Another way to think of it: You have a wooden board with a nail perfectly through the center and you have a shoe. You set the shoe in the upper right hand corner of the board with the toe pointing away from you. If you rotate the wooden board Math.PI/2 radians, the shoe will not stay in the upper right hand corner (relative to you), even though that's where you put it, because the shoe has been added to the board, and you have moved the board. Now, the shoe should be in the bottom right, and will be facing to your right. Object3D.add() is like the process of putting the shoe on the board.

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!

Categories