Flip Normals Three.JS after flipping geometry - three.js

I followed this stackoverflow example:
ThreeJS geometry flipping
I successfully mirrored my geometry. However now my geometry is black.
Can I flip my normals at the same time as the geometry to correct this? Or should I have used a different approach to mirror the geometry from the beginning?
EDIT:
Tried adding the updates to this code and still have inverted geometry.
#transformation
mS.elements[5] = -1;
mesh.applyMatrix(mS);
#updates
mesh.geometry.verticesNeedUpdate = true;
mesh.geometry.normalsNeedUpdate = true;
mesh.geometry.computeBoundingSphere();
mesh.geometry.computeFaceNormals();
mesh.geometry.computeVertexNormals();

This is an old question, but for those still lost: the face normal is calculated by looking at the counter clockwise order of the vertices.
So if you need to flip the normals you have to reorder the vertices that are assigned to a face. For example:
var tmp;
for(var f = 0; f < geometry.faces.length; f++) {
tmp = geometry.faces[f].clone();
geometry.faces[f].a = tmp.c;
geometry.faces[f].c = tmp.a;
}

mesh.geometry.verticesNeedUpdate = true;
mesh.geometry.normalsNeedUpdate = true;
mesh.geometry.computeBoundingSphere();
mesh.geometry.computeFaceNormals();
mesh.geometry.computeVertexNormals();
https://github.com/mrdoob/three.js/wiki/Updates

You could try:
mesh.geometry.reverse(computeFaceNormals());
mesh.geometry.reverse(computeVertexNormals());

Related

3D three.js Create the ground surface of a 3D building

Following my post last week three.js How to programatically produce a plane from dataset I come back to the community to solve a problem of definition of surface occupied on the ground by a 3D building.
The solution proposed in comments in this post works for this building but is not universal.
To make it universal I chose the following method: when the walls are built I create their clone in another group (see this previous post for walls creation)
// prepare the clones
var clones = new THREE.Group();
scene.add(clones);
var num=0;
// drawing the real walls
var wallGeometry = new THREE.PlaneGeometry(size,(hstair*batims[i][1]));
val = 0xFFFFFF;
opa = 0.5;
if(deltaX > deltaY){val = 0x000000; opa = 0.05;} // shaded wall
var wallMaterial = new THREE.MeshBasicMaterial({color:val,transparent:true, opacity:opa, side:THREE.DoubleSide});
var walls = new THREE.Mesh(wallGeometry, wallMaterial);
walls.position.set((startleft+endleft)/2,(hstair*batims[i][1])/2,(startop+endtop)/2);
walls.rotation.y = -rads;
scene.add(walls);
// add the pseudo-walls to scene
var cloneGeometry=new THREE.PlaneGeometry(long,3);
var cloneMaterial=new THREE.MeshBasicMaterial({color:0xff0000,transparent:true,opacity:0.5,side:THREE.DoubleSide});
var clone=new THREE.Mesh(pseudomursGeometry,pseudomursMaterial);
clone.position.set((startleft+endleft)/2,3,(startop+endtop)/2);
clone.rotation.y=-rads;
clones.add(clone);
num++;
The idea is now to rotate this pseudo-building so that the longest wall is vertical, which allows me to determine the exact floor area occupied with its boundingBox:
var angle=turn=0;
for(i=0; i<dists.length; i++) { // dists is the array of wall lengths
if(dists[i]==longs[0]){ // longs is the reordered lengths array
angle=angles[i][1]; // angle of the longest wall
}
}
// we can now rotate the whole group to put the longest wall vertical
if(angle>0){
turn = angle*-1+(Math.PI/2);
}
else {
turn = angle+(Math.PI/2);
}
clones.rotation.y=turn;
It works perfectly as long as the building has a right angle, whatever its shape: triangle, rectangle, bevel, right angle polygons,
var boundingBox = new THREE.Box3().setFromObject(clones);
var thisarea = boundingBox.getSize();
// area size gives the expected result
console.log('AREA SIZE = '+thisarea.x+' '+thisarea.y+' '+thisarea.z);
...but not when there are no more right angles, for example a trapezoid
The reason is that we rotate the group, and not the cloned walls. I can access and rotate each wall by
for(n=0;n<num;n++){
thisangle = clones.children[n].rotation.y;
clones.children[n].rotation.y = turn-thisangle;
}
But the result is wrong for the others pseudo-walls:
So the question is: how to turn each red pseudo-wall so that the longest one is vertical and the others remain correctly positioned in relation to it? In this way, any building with any shape can be reproduced in 3D with its internal equipment. Any idea on how to achieve this result?
A weird & ugly but well-working solution:
// 1. determines which is the longest side
for(i=0; i<dists.length; i++) {
if(dists[i]==longs[0]){
longest=i;
break; // avoid 2 values if rectangle
}
}
// 2. the group is rotated until the longest side has an angle in degrees
// close to 0 or 180
var letsturn = setInterval(function() {
clones.rotation.y += 0.01;
var group_rotation = THREE.Math.radToDeg(clones.rotation.y); // degrees
var stop = Math.round(angles[longest][0] - group_rotation);
// 3. stop when longest wall is vertical
if( (stop>=179 && stop<=181) || (stop>=-1 && stop<=1) ) {
clearInterval(letsturn);
createPlane() // we can now use boundingBox in reliability
}
}, 1);
et voilĂ .

Is it possible to use a texture material on objects with various sizes?

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 fill a loaded STL mesh ( NOT SIMPLE SHAPES LIKE CUBE ETC) with random particles and animate with this geometry bound in 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

Three.js - fixing distorted lighting when twisting the geometry

I am working on a geometry which is basically a rod twisted around the z-axis by a specific angle. This is done by modifying the vertices of an ExtrudeGeometry based on their position along the z-direction:
function twistMesh(mesh, helixAngle){
var vertices = mesh.geometry.vertices;
for (var i = 0; i < vertices.length; i++) {
var angle = helixAngle*vertices[i].z/100;
var updateX = vertices[i].x * Math.cos(angle)
- vertices[i].y * Math.sin(angle);
var updateY = vertices[i].y * Math.cos(angle)
+ vertices[i].x * Math.sin(angle);
vertices[i].x = updateX;
vertices[i].y = updateY;
}
return mesh;
}
A working example can be found here.
I am running into the problem that the lighting is not update along with the modified vertices, i.e. it seems as if the light incident on the surface is equally modified.
I have read that when modifying a update call is required by setting:
mesh.geometry.verticesNeedUpdate = true;
mesh.geometry.normalsNeedUpdate = true;
however, this doesn't seem to fix the lighting issue i am seeing.
How do I update or otherwise 'fix' the distorted lighting when twisting my geometry this way?
After you update the vertices, you need to update the vertex normals, too.
You can do that manually, where you will have the most control, or you can use the built-in method:
mesh.geometry.computeVertexNormals();
updated fiddle: http://jsfiddle.net/pamv8krb/144/
three.js r.95

Flip normals in three.js on sphere

I have been searching around and haven't found any really good answer to my question yet..
The thing is that I have this sphere.. just a basic sphere, and I want to flip the normals so
the sphere gets the sort of "hollow/carved effect" and then plater on apply my textures to the "inside" of the sphere. any ideas of how to flip the normals?
Also.. if its not possible to do this in three.js.. would it be possible to import a model where the normals are already flipped and get the effect I'm looking for?
This answer only applies to versions of three.js prior to r.125.
The Legacy Geometry class has been removed.
You can flip the normals in your geometry by reversing the winding order of your faces. You then have to fix UVs.
for ( var i = 0; i < geometry.faces.length; i ++ ) {
var face = geometry.faces[ i ];
var temp = face.a;
face.a = face.c;
face.c = temp;
}
geometry.computeFaceNormals();
geometry.computeVertexNormals();
var faceVertexUvs = geometry.faceVertexUvs[ 0 ];
for ( var i = 0; i < faceVertexUvs.length; i ++ ) {
var temp = faceVertexUvs[ i ][ 0 ];
faceVertexUvs[ i ][ 0 ] = faceVertexUvs[ i ][ 2 ];
faceVertexUvs[ i ][ 2 ] = temp;
}
However, you can get the same effect by simply setting Material.side = THREE.BackSide, or Material.side = THREE.DoubleSide.
In either case, your texture will be rendered flipped. You can either flip your texture before-hand, or build a model outside of three.js and import it.
three.js r.124
When you are creating material for your sphere, specify {side:THREE.DoubleSide}. This will make faces visible from both sides.
You can also change it anytime after your material is created.
It is fixed !!
The flip of an object with a negative scale object.scale.x = -1 also reverse the normals since three.js r89 (see: Support reflection matrices. #12787).
(But I have to upgrade to r91 to solve my normal issue.)
Another way is to simply flip the normals manually by making your object's geometry dynamic.
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();

Resources