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
Related
Working with Three.js r113, I'm creating walls from coordinates of a blueprint dynamically as custom geometries. I've set up the vertices, faces and faceVertexUvs already successfully. Now I'd like to wrap these geometries with a textured material, that repeats the texture and keeps the original aspect ratio.
Since the walls have different lengths, I was wondering which is the best approach to do this?
What I've tried so far is loading the texture once and then using different texture.repeat values, depending on the wall length:
let textures = function() {
let wall_brick = new THREE.TextureLoader().load('../textures/light_brick.jpg');
return {wall_brick};
}();
function makeTextureMaterial(texture, length, height) {
const scale = 2;
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( length * scale, height * scale );
return new THREE.MeshStandardMaterial({map: texture});
}
I then call the above function, after creating the geometry and assign the returned materials to the material array to apply it to faces of front and back of each wall. Note: material.wall is an untextured MeshStandardMaterial for the other faces.
let scaledMaterial = [
makeTextureMaterial(textures.wall_brick, this.length.back, this.height),
makeTextureMaterial(textures.wall_brick, this.length.front, this.height),
material.wall
];
this.geometry.faces[0].materialIndex = 0; // back
this.geometry.faces[1].materialIndex = 0; // back
this.geometry.faces[2].materialIndex = 1; // front
this.geometry.faces[3].materialIndex = 1; // front
this.geometry.faces[4].materialIndex = 2;
this.geometry.faces[5].materialIndex = 2;
this.geometry.faces[6].materialIndex = 2;
this.geometry.faces[7].materialIndex = 2;
this.geometry.faces[8].materialIndex = 2;
this.geometry.faces[9].materialIndex = 2;
this.geometry.faces[10].materialIndex = 2;
this.geometry.faces[11].materialIndex = 2; // will do those with a loop later on :)
this.mesh = new THREE.Mesh(this.geometry, scaledMaterial);
What happens is that the texture is displayed on the desired faces, but it's not scaled individually by this.length.back and this.length.front
Any ideas how to do this? Thank you.
I have just found the proper approach to this. The individual scaling is done via faceVertexUvs, as West Langley answered here: https://stackoverflow.com/a/27098476/4355114
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
I write an algorithm that can explode (fold and unfold) mechanical set by double clicking on then.
But i want to move the camera backward or forward after that to see all my objects in the fov.
I'm trying to use frustum to calculate intersection between frustum and objects, but i don't undestand how to use planes.
I'm working with OrthographicCamera.
What i do :
At every frame i recalculate the new frustum (when camera move):
projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
frustum.setFromMatrix(projScreenMatrix);
Then i do a loop over the 6 planes and bounding box of all objects in the scene :
for (var i = 0; i < planes.length; i++) {
var plane = planes[i];
for (var j = 0; j < boxs.length; j++) {
var box = boxs[j];
var line = new THREE.Line3(box.min, nox.max);
//console.log({'plane': plane, 'line': line});
if (plane.isIntersectionLine(line))
// move camera
};
};
but plane.isIntersectionLine(line) is always false.
Do you have any ideas ?
Thanks
I'm attempting to create a large terrain in three.js, i'm using physi.js as the physics engine.
generating the geometry from the heightmap is no problem so far. however, when i try to add it as a THREE.Mesh it works beautifully, when i try adding it as a Physijs.HeightfieldMesh i get the following error:
TypeError: geometry.vertices[(a + (((this._physijs.ypts - b) - 1) * this._physijs.ypts))] is undefined
The geometry is generated as a plane, then the Z position of each vertex gets modified according to the heightmap.
var geometry = new THREE.PlaneGeometry( img.naturalWidth, img.naturalHeight,img.naturalWidth -1, img.naturalHeight -1 );
var material = new THREE.MeshLambertMaterial( { color : 0x0F0F0F } );
//set height of vertices
for ( var i = 0; i<plane.geometry.vertices.length; i++ ) {
plane.geometry.vertices[i].z = data[i];//let's just assume the data is correct since it works as a THREE.Mesh
}
var terrain = new THREE.Mesh(geometry, material); // works
//does not work
var terrain = new Physijs.heightfieldMesh(
geometry,
material,
0
);
I think your problem is you are using "plane.geometry" instead of just "geometry" in the loop to set the vertex height.
Maybe it should be:
//set height of vertices
for ( var i = 0; i < geometry.vertices.length; i++ ) {
geometry.vertices[i].z = data[i];//let's just assume the data is correct since it works as a THREE.Mesh
}
This fiddle that I created seems to work ok.
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;
}