How fill a loaded STL mesh ( NOT SIMPLE SHAPES LIKE CUBE ETC) with random particles and animate with this geometry bound in three.js - three.js

How I can fill a loaded STL mesh ( like suzane NOT SIMPLE SHAPES LIKE CUBE etc) with random particles and animate it inside this geometry bounds with three.js ?
I see many examples but all of it for simple shapes with geometrical bounds like cube or sphere with limit by coordinates around center
https://threejs.org/examples/?q=points#webgl_custom_attributes_points3
TNX

A concept, using a ray, that counts intersections of the ray with faces of a mesh, and if the number is odd, it means that the point is inside of the mesh:
Codepen
function fillWithPoints(geometry, count) {
var ray = new THREE.Ray()
var size = new THREE.Vector3();
geometry.computeBoundingBox();
let bbox = geometry.boundingBox;
let points = [];
var dir = new THREE.Vector3(1, 1, 1).normalize();
for (let i = 0; i < count; i++) {
let p = setRandomVector(bbox.min, bbox.max);
points.push(p);
}
function setRandomVector(min, max){
let v = new THREE.Vector3(
THREE.Math.randFloat(min.x, max.x),
THREE.Math.randFloat(min.y, max.y),
THREE.Math.randFloat(min.z, max.z)
);
if (!isInside(v)){return setRandomVector(min, max);}
return v;
}
function isInside(v){
ray.set(v, dir);
let counter = 0;
let pos = geometry.attributes.position;
let faces = pos.count / 3;
let vA = new THREE.Vector3(), vB = new THREE.Vector3(), vC = new THREE.Vector3();
for(let i = 0; i < faces; i++){
vA.fromBufferAttribute(pos, i * 3 + 0);
vB.fromBufferAttribute(pos, i * 3 + 1);
vC.fromBufferAttribute(pos, i * 3 + 2);
if (ray.intersectTriangle(vA, vB, vC)) counter++;
}
return counter % 2 == 1;
}
return new THREE.BufferGeometry().setFromPoints(points);
}

The concepts from the previous answer is very good, but it has some performance limitations:
the whole geometry is tested with every ray
the recursion on points outside can lead to stack overflow
Moreover, it's incompatible with indexed geometry.
It can be improved by creating a spatial hashmap storing the geometry triangles and limiting the intersection test to only some part of the mesh.
Demonstration

Related

How to calculate the area of merged plane in ThreeJS

image
get the area of merged planes
I can get area of each plane, but planes overlapped with each other, I can get a area of them all, but it's not what I want, the overlapped areas should excluded.
var geom = plane.geometry;
var area = 0;
for (var i = 0; i < geom.faces.length; i++) {
var face = geom.faces[i];
var tri = new THREE.Triangle(geom.vertices[face.a], geom.vertices[face.b], geom.vertices[face.c]);
var area = area + tri.getArea();
}
console.log(area);
There should be a method to calculate the area.
THREE.ShapeUtils.area( contour) gives a negative result.
If you want to highlight the edges of your geometry, you can use EdgesHelper:
var helper = new THREE.EdgesHelper( mesh, 0x00ffff );
helper.material.linewidth = 2;
scene.add( helper )
and get contour from Edges helper if required

Raycasting against a mesh is not found where it 's visible in the scene

I'm having a strange problem with raycasting. My scene consists of a room with a couple of components that you can move around inside that room. When the component is moving i'm measuring the distances to the walls, an invisible roof and floor. The problem is that the roof which is a ShapeGeometry is visible where it should be at the top of the walls but not hit when raycasting.
Here's where i create the mesh for the invisible roof
const roofShape = new THREE.Shape();
roofShape.moveTo(roofPoints[0].x, roofPoints[0].y);
for (let i = 1; i < roofPoints.length; i++) {
roofShape.lineTo(roofPoints[i].x, roofPoints[i].y);
}
roofShape.lineTo(roofPoints[0].x, roofPoints[0].y);
const geometry = new THREE.ShapeGeometry(roofShape);
const material = new THREE.MeshBasicMaterial({color: 0x000000, side: THREE.DoubleSide});
material.opacity = 0;
material.transparent = true;
const mesh = new THREE.Mesh(geometry, material);
mesh.position.x = 0;
mesh.position.y = 0;
mesh.position.z = room._height;
mesh.name = "ROOF";
mesh.userData = <Object3DUserData> {
id: IntersectType.INVISIBLE_ROOF,
intersectType: IntersectType.INVISIBLE_ROOF,
};
The function that's invoking the raycasting. The direction vector is(0, 0, 1) in this case. And the surfaces parameter is an array which only contains the mesh created above.
function getDistanceToSurface(componentPosition: THREE.Vector3, surfaces: THREE.Object3D[], direction: THREE.Vector3): number {
const rayCaster = new THREE.Raycaster(componentPosition, direction.normalize());
const intersections = rayCaster.intersectObjects(surfaces);
if (!intersections || !intersections.length) {
return 0;
}
const val = intersections[0].distance;
return val;
}
By changing the z direction to -1 i found that the raycaster found the roof at z=0. It seems that the geometry is still at position z=0.
I then tried to translate the geometry shape
geometry.translate(0, 0, room._height);
And now the raycaster finds it where i expect it to be. But visually it it's double the z position(mesh opacity=1). Setting the mesh position z to 0 makes it visibly correct and the raycasting still works.
I've been looking at the examples of raycasting but can't find anywhere where a ShapeGeometry needs do this.
Am i doing something wrong? Have i missed something? Do i have to set z position of the geometry, is it not enough with positioning the mesh?
As hinted in the comment by #radio the solution was as described in How to update vertices geometry after rotate or move object
mesh.position.z = room._height;
mesh.updateMatrix();
mesh.geometry.applyMatrix(mesh.matrix);
mesh.matrix.identity();

CircleGeometry to Shape

I would like to convert a THREE.CircleGeometry to a THREE.Shape, but I haven't found a way yet. I want to extrude this circle, but the ExtrudeGeometry() only works with shapes.
I know I can use curves to draw a circle but I want to keep the topology the CircleGeometry() gives.
If that's not possible, is there a work around to draw a circle shape which is made of a number of triangular segments that are oriented around a central point?
This will generate a shape as circle geometry does.
Here is fiddle: http://jsfiddle.net/tcLr7eg3/
var circleTri = new THREE.Shape();
radius = 30;
segments = 32;
var theta_next, x_next, y_next, j;
for (var i = 0; i < segments; i++) {
theta = ((i + 1) / segments) * Math.PI * 2.0;
x = radius * Math.cos(theta);
y = radius * Math.sin(theta);
j = i + 2;
if( (j - 1) === segments ) j = 1;
theta_next = (j / segments) * Math.PI * 2.0;
x_next = radius * Math.cos(theta_next);
y_next = radius * Math.sin(theta_next);
circleTri.moveTo(0, 0);
circleTri.lineTo(x, y);
circleTri.lineTo(x_next, y_next);
circleTri.lineTo(0, 0);
}
For anyone new to three.js looking to do this, you should try the SphereGeometry shape. You can create a perfectly round sphere this way without the hassle of creating your own shape or trying to extrude a CircleGeometry shape. The code example on the docs should give you a good starting point.
const geometry = new THREE.SphereGeometry( 5, 32, 32 );
const material = new THREE.MeshBasicMaterial( {color: 0xffff00} );
const sphere = new THREE.Mesh( geometry, material );
scene.add( sphere );

three.js - Set the rotation of an object in relation to its own axes

I'm trying to make a static 3D prism out of point clouds with specific numbers of particles in each. I've got the the corner coordinates of each side of the prism based on the angle of turn, and tried spawning the particles in the area bound by these coordinates. Instead, the resulting point clouds have kept only the bottom left coordinate.
Screenshot: http://i.stack.imgur.com/uQ7Q8.png
I've tried to set the rotation of each cloud object such that their edges meet, but they will rotate only around the world centre. I gather this is something to do with rotation matrices and Euler angles, but, having been trying to work them out for 3 solid days, I've despaired. (I'm a sociologist, not a dev, and haven't touched graphics before this project.)
Please help? How do I set the rotation on each face of the prism? Or maybe there is a more sensible way to get the particles to spawn in the correct area in the first place?
The code:
// draw the particles
var n = 0;
do {
var geom = new THREE.Geometry();
var material = new THREE.PointCloudMaterial({size: 1, vertexColors: true, color: 0xffffff});
for (i = 0; i < group[n]; i++) {
if (geom.vertices.length < group[n]){
var particle = new THREE.Vector3(
Math.random() * screens[n].bottomrightback.x + screens[n].bottomleftfront.x,
Math.random() * screens[n].toprightback.y + screens[n].bottomleftfront.y,
Math.random() * screens[n].bottomrightfront.z + screens[n].bottomleftfront.z);
geom.vertices.push(particle);
geom.colors.push(new THREE.Color(Math.random() * 0x00ffff));
}
}
var system = new THREE.PointCloud(geom, material);
scene.add(system);
**// something something matrix Euler something?**
n++
}
while (n < numGroups);
I've tried to set the rotation of each cloud object such that their
edges meet, but they will rotate only around the world centre.
It is true they only rotate around 0,0,0. The simple solution then is to move the object to the center, rotate it, and then move it back to its original position.
For example (Code not tested so might take a bit of tweaking):
var m = new THREE.Matrix4();
var movetocenter = new THREE.Matrix4();
movetocenter.makeTranslation(-x, -y, -z);
var rotate = new THREE.Matrix4();
rotate.makeRotationFromEuler(); //Build your rotation here
var moveback = new THREE.Matrix4();
moveback .makeTranslation(x, y, z);
m.multiply(movetocenter);
m.multiply(rotate);
m.multiply(moveback);
//Now you can use geometry.applyMatrix(m)

Three.js set the center of a Object3D based on internal meshes

I have a mesh set that is in a Object3D when i get the vertices they are not centered on the object. so i need to compute the center of the object3D then move the meshes to align them to the center. I have tried computing the boundingboxes of each Mesh then max - min /2; this does not work. Any help here would be fantastic. I have tried the Object3D.setFromObject(); this only return infinity.
To center an Object3D, depending on its children, you have to iterate through them, as far as I know. The code would look like the following:
// myObject3D is your Object3D
var children = myObject3D.children,
completeBoundingBox = new THREE.Box3(); // create a new box which will contain the entire values
for(var i = 0, j = children.length; i < j; i++){ // iterate through the children
children[i].geometry.computeBoundingBox(); // compute the bounding box of the the meshes geometry
var box = children[i].geometry.boundingBox.clone(); // clone the calculated bounding box, because we have to translate it
box.translate(children[i].position); // translate the geometries bounding box by the meshes position
completeBoundingBox.addPoint(box.max).addPoint(box.min); // add the max and min values to your completeBoundingBox
}
var objectCenter = completeBoundingBox.center()
console.log('This is the center of your Object3D:', objectCenter );
// You want the center of you bounding box to be at 0|0|0
myObject3D.position.x -= objectCenter.x;
myObject3D.position.y -= objectCenter.y;
myObject3D.position.z -= objectCenter.z;
Hope I understood your problem right!
center = function(obj) {
var children = obj.children,
completeBoundingBox = new THREE.Box3();
for(var i = 0, j = children.length; i < j; i++) {
children[i].geometry.computeBoundingBox();
var box = children[i].geometry.boundingBox.clone();
box.translate(children[i].position);
completeBoundingBox.set(box.max, box.min);
}
var objectCenter = completeBoundingBox.center()
console.log('This is the center of your Object3D:', objectCenter );
obj.position.x -= objectCenter.x;
obj.position.y -= objectCenter.y;
obj.position.z -= objectCenter.z;
}

Resources