Flip normals in three.js on sphere - three.js

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();

Related

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

three.js: Get updated vertices with skeletal animations?

Similar to the question in this stack overflow question Three.js: Get updated vertices with morph targets I am interested in how to get the "actual" position of the vertices of a mesh with a skeletal animation.
I have tried printing out the position values, but they are never actually updated (as I am to understand, this is because they are calculated on the GPU, not CPU). The answer to the question above mentioned doing the same computations on the CPU as on the GPU to get the up to date vertex positions for morph target animations, but is there a way to do this same approach for skeletal animations? If so, how??
Also, for the morph targets, someone pointed out that this code is already present in the Mesh.raycast function (https://github.com/mrdoob/three.js/blob/master/src/objects/Mesh.js#L115 ). However, I don't see HOW the raycast works with skeletal animation meshes-- how does it know the updated position of the faces?
Thank you!
A similar topic was discussed in the three.js forum some time ago. I've presented there a fiddle which computes the AABB for a skinned mesh per frame. The code actually performs the same vertex displacement via JavaScript like in the vertex shader. The routine looks like so:
function updateAABB( skinnedMesh, aabb ) {
var skeleton = skinnedMesh.skeleton;
var boneMatrices = skeleton.boneMatrices;
var geometry = skinnedMesh.geometry;
var index = geometry.index;
var position = geometry.attributes.position;
var skinIndex = geometry.attributes.skinIndex;
var skinWeigth = geometry.attributes.skinWeight;
var bindMatrix = skinnedMesh.bindMatrix;
var bindMatrixInverse = skinnedMesh.bindMatrixInverse;
var i, j, si, sw;
aabb.makeEmpty();
//
if ( index !== null ) {
// indexed geometry
for ( i = 0; i < index.count; i ++ ) {
vertex.fromBufferAttribute( position, index[ i ] );
skinIndices.fromBufferAttribute( skinIndex, index[ i ] );
skinWeights.fromBufferAttribute( skinWeigth, index[ i ] );
// the following code section is normally implemented in the vertex shader
vertex.applyMatrix4( bindMatrix ); // transform to bind space
skinned.set( 0, 0, 0 );
for ( j = 0; j < 4; j ++ ) {
si = skinIndices.getComponent( j );
sw = skinWeights.getComponent( j );
boneMatrix.fromArray( boneMatrices, si * 16 );
// weighted vertex transformation
temp.copy( vertex ).applyMatrix4( boneMatrix ).multiplyScalar( sw );
skinned.add( temp );
}
skinned.applyMatrix4( bindMatrixInverse ); // back to local space
// expand aabb
aabb.expandByPoint( skinned );
}
} else {
// non-indexed geometry
for ( i = 0; i < position.count; i ++ ) {
vertex.fromBufferAttribute( position, i );
skinIndices.fromBufferAttribute( skinIndex, i );
skinWeights.fromBufferAttribute( skinWeigth, i );
// the following code section is normally implemented in the vertex shader
vertex.applyMatrix4( bindMatrix ); // transform to bind space
skinned.set( 0, 0, 0 );
for ( j = 0; j < 4; j ++ ) {
si = skinIndices.getComponent( j );
sw = skinWeights.getComponent( j );
boneMatrix.fromArray( boneMatrices, si * 16 );
// weighted vertex transformation
temp.copy( vertex ).applyMatrix4( boneMatrix ).multiplyScalar( sw );
skinned.add( temp );
}
skinned.applyMatrix4( bindMatrixInverse ); // back to local space
// expand aabb
aabb.expandByPoint( skinned );
}
}
aabb.applyMatrix4( skinnedMesh.matrixWorld );
}
Also, for the morph targets, someone pointed out that this code is already present in the Mesh.raycast function
Yes, you can raycast against morphed meshes. Raycasting against skinned meshes is not supported yet. The code in Mesh.raycast() is already very complex. I think it needs some serious refactoring before it is further enhanced. In the meantime, you can use the presented code snippet to build a solution by yourself. The vertex displacement logic is actually the most complicated part.
Live demo: https://jsfiddle.net/fnjkeg9x/1/
three.js R107

Three.js raycasting collision not working

I am working on an arcade style Everest Flight Simulator.
In my debugger where I am building this, I have a terrain and helicopter class which generate the BufferGeometry terrain mesh, the Groups for the helipad Geometries, and the group for the helicopter Camera and Geometry.
My issue is that currently I can't seem to get any collision to detect. I imagine it may not support BufferGeometries so that is an issue for me because I need the terrain to be a Buffer since it's far too expansive... as a standard geometry it causes a memory crash in the browser.
However, testing the helipad geometries alone it still does not trigger. They are in a group so I add the groups to a global window array and set the collision check to be recursive but to no avail.
Ultimately, I am open to other forms of collision detection and may need two types as I have to use buffer geometries. Any ideas on how to fix this or a better solution?
The Helicopter Object Itself
// Rect to Simulate Helicopter
const geometry = new THREE.BoxGeometry( 2, 1, 4 ),
material = new THREE.MeshBasicMaterial(),
rect = new THREE.Mesh( geometry, material );
rect.position.x = 0;
rect.position.y = terrain.returnCameraStartPosY();
rect.position.z = 0;
rect.rotation.order = "YXZ";
rect.name = "heli";
// Link Camera and Helicopter
const heliCam = new THREE.Group(),
player = new Helicopter(heliCam, "OH-58 Kiowa", 14000);
heliCam.add(camera);
heliCam.add(rect);
heliCam.position.set( 0, 2040, -2000 );
heliCam.name = "heliCam";
scene.add(heliCam);
Adding Objects to Global Collision Array
// Add Terrain
const terrain = new Terrain.ProceduralTerrain(),
terrainObj = terrain.returnTerrainObj(),
helipadEnd = new Terrain.Helipad( 0, 1200, -3600, "Finish", true ),
helipadStart = new Terrain.Helipad( 0, 2000, -2000, "Start", false ),
helipadObjStart = helipadStart.returnHelipadObj(),
helipadObjEnd = helipadEnd.returnHelipadObj();
window.collidableMeshList.push(terrainObj);
window.collidableMeshList.push(helipadObjStart);
window.collidableMeshList.push(helipadObjEnd);
Collision Detection Function Run Every Frame
collisionDetection(){
const playerOrigin = this.heli.children[1].clone(); // Get Box Mesh from Player Group
for (let i = playerOrigin.geometry.vertices.length - 1; i >= 0; i--) {
const localVertex = playerOrigin.geometry.vertices[i].clone(),
globalVertex = localVertex.applyMatrix4( playerOrigin.matrix ),
directionVector = globalVertex.sub( playerOrigin.position ),
ray = new THREE.Raycaster( playerOrigin, directionVector.clone().normalize() ),
collisionResults = ray.intersectObjects( window.collidableMeshList, true ); // Recursive Boolean for children
if ( collisionResults.length > 0 ){
this.landed = true;
console.log("Collision");
}
// if ( collisionResults.length > 0 && collisionResults[0].distance < directionVector.length() ){
// this.landed = true;
// console.log("Collision with vectorLength")
// }
}
}
It's hard to tell what's going on inside your custom classes, but it looks like you're using an Object3D as the first argument of the raycaster, instead of a Vector3 when you use this.heli.children[1].clone(). Why don't you try something like:
var raycaster = new THREE.Raycaster();
var origin = this.heli.children[1].position;
raycaster.set(origin, direction);
Also, are you sure you're using a BufferGeometry? Because when you access a vertex value like this: playerOrigin.geometry.vertices[i], it should give you an error. There is no vertices attribute in a BufferGeometry so I don't know how you're determining the direction vector.

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();

Flip Normals Three.JS after flipping geometry

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());

Resources