Applying a matrix in Three.js does not what I expect - matrix

For a project I am working on I am trying to get get a 3D-model of a building visible in a browser. Of all the elements of the building I have vertices, indices and a matrix3d. This information comes from an application that uses OpenGL to show the elements in a offline program.
Now I am trying to add these elements to my Three.js scene.
I am at the point that I can add elements to the scene defined by the vertices and indices an I can see them by using materials and lights, but I can not rotate and translate them into the right place. For example I add an element like this:
var m242242255255 = new THREE.MeshPhongMaterial({color:0xf2f2ff, transparent:true, opacity:1, side:THREE.DoubleSide});
var geometry = new THREE.BufferGeometry();
geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array([821,-15,2825.1,-821,-15,2825.1,-821,-39,2825.1,821,-39,2825.1,-821,-39,54,-821,-15,54,821,-15,54,821,-39,54,-875,-54,0,-821,-54,54,-821,-54,2825.1,821,-54,54,-875,-54,2879.1,821,-54,2825.1,875,-54,0,875,-54,2879.1,875,0,0,821,0,54,821,0,2825.1,-821,0,54,875,0,2879.1,-821,0,2825.1,-875,0,0,-875,0,2879.1]), 3));
geometry.setIndex(new THREE.BufferAttribute(new Uint16Array([8,9,10,9,8,11,12,10,13,10,12,8,11,14,13,14,11,8,13,15,12,15,13,14,16,17,18,17,16,19,20,18,21,18,20,16,19,22,21,22,19,16,21,23,20,23,21,22,8,22,16,16,14,8,14,16,20,20,15,14,15,20,23,23,12,15,12,23,22,22,8,12,13,18,17,17,11,13,11,17,19,19,9,11,9,19,21,21,10,9,10,21,18,18,13,10]), 1));
var mesh = new THREE.Mesh(geometry, m242242255255);
mesh.matrixAutoUpdate = false;
mesh.applyMatrix(new THREE.Matrix4().set(0,0,-1,0, -0.42262,-0.90631,0,0, -0.90631,0.42262,0,0, 64754.68,15569.13,-4647.5,1));
mesh.updateMatrix();
scene.add(mesh);
The element shows up in my scene and it looks like is rotated but it is not translated to its correct position.
I can add the translation before the adding of the mesh to the scene, but it feels like it should not be necessary.
mesh.applyMatrix(new THREE.Matrix4().makeTranslation(-64754.68, -15569.13, -4647.5));
mesh.updateMatrix();
It also looks like the element is rotated along the wrong axis. It is rotated along the x-axis instead of the z-axis. Can someone tell me what it is I am doing wrong? Should I changed the matrix first to be able to use it in Three.js?
Edit:
I just found out that I had to invert my matrix to correct the rotation-problem. So I now have:
var geometry = new THREE.BufferGeometry();
geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array([821,-15,2825.1,-821,-15,2825.1,-821,-39,2825.1,821,-39,2825.1,-821,-39,54,-821,-15,54,821,-15,54,821,-39,54,-875,-54,0,-821,-54,54,-821,-54,2825.1,821,-54,54,-875,-54,2879.1,821,-54,2825.1,875,-54,0,875,-54,2879.1,875,0,0,821,0,54,821,0,2825.1,-821,0,54,875,0,2879.1,-821,0,2825.1,-875,0,0,-875,0,2879.1]), 3));
geometry.setIndex(new THREE.BufferAttribute(new Uint16Array([8,9,10,9,8,11,12,10,13,10,12,8,11,14,13,14,11,8,13,15,12,15,13,14,16,17,18,17,16,19,20,18,21,18,20,16,19,22,21,22,19,16,21,23,20,23,21,22,8,22,16,16,14,8,14,16,20,20,15,14,15,20,23,23,12,15,12,23,22,22,8,12,13,18,17,17,11,13,11,17,19,19,9,11,9,19,21,21,10,9,10,21,18,18,13,10]), 1));
var mesh = new THREE.Mesh(geometry, m242242255255);
mesh.matrixAutoUpdate = false;
var matrix = new THREE.Matrix4();
matrix.set(0,0,-1,0,-0.42262,-0.90631,0,0,-0.90631,0.42262,0,0,64754.68,15569.13,-4647.5,1);
matrix.getInverse(matrix);
mesh.applyMatrix( matrix );
mesh.updateMatrix();
mesh.applyMatrix( new THREE.Matrix4().makeTranslation( 64754.68, 15569.13, -4647.5 ) );
mesh.updateMatrix();
scene.add(mesh);
But I still have a problem with translating using the matrix. How can I avoid updating the mesh twice?

You need to specify your matrix elements by rows, like so:
matrix.set( n11, n12, n13, n14,
n21, n22, n23, n24,
n31, n32, n33, n34,
n41, n42, n43, n44 );
It is done this way so it is human-readable.
three.js r.76

suppost you ratate in the x, y, z sequence.
rotationMatrix = new THREE.Matrix4().multiplyMatrices(new THREE.Matrix4().makeRotationY(rV.y), new THREE.Matrix4().makeRotationX(rV.x));
rotationMatrix.premultiply(new THREE.Matrix4().makeRotationZ(rV.z));
matrix.copy(rM).setPosition(vector3);

From the documentation for modifying the object's matrix directly
Note that matrixAutoUpdate must be set to false in this case, and you should make sure not to call updateMatrix. Calling updateMatrix will clobber the manual changes made to the matrix, recalculating the matrix from position, scale, and so on.
You'll find that after you call mesh.updateMatrix(), the mesh transformation matrix will be different than the one you set. You verify this by comparing matrix.elements to mesh.matrixWorld.elements, which will be the same after you remove updateMatrix.

Related

three js LoadObject pivot [duplicate]

What I'm trying to achieve is a rotation of the geometry around pivot point and make that the new definition of the geometry. I do not want te keep editing the rotationZ but I want to have the current rotationZ to be the new rotationZ 0.
This way when I create a new rotation task, it will start from the new given pivot point and the newly given rad.
What I've tried, but then the rotation point moves:
// Add cube to do calculations
var box = new THREE.Box3().setFromObject( o );
var size = box.getSize();
var offsetZ = size.z / 2;
o.geometry.translate(0, -offsetZ, 0)
// Do ratation
o.rotateZ(CalcUtils.degreeToRad(degree));
o.geometry.translate(0, offsetZ, 0)
I also tried to add a Group and rotate that group and then remove the group. But I need to keep the rotation without all the extra objects. The code I created
var box = new THREE.Box3().setFromObject( o );
var size = box.size();
var geometry = new THREE.BoxGeometry( 20, 20, 20 );
var material = new THREE.MeshBasicMaterial( { color: 0xcc0000 } );
var cube = new THREE.Mesh( geometry, material );
cube.position.x = o.position.x;
cube.position.y = 0; // Height / 2
cube.position.z = -size.z / 2;
o.position.x = 0;
o.position.y = 0;
o.position.z = size.z / 2;
cube.add(o);
scene.add(cube);
// Do ratation
cube.rotateY(CalcUtils.degreeToRad(degree));
// Remove cube, and go back to single object
var position = o.getWorldPosition();
scene.add(o)
scene.remove(cube);
console.log(o);
o.position.x = position.x;
o.position.y = position.y;
o.position.z = position.z;
So my question, how do I save the current rotation as the new 0 rotation point. Make the rotation final
EDIT
I added an image of what I want to do. The object is green. I have a 0 point of the world (black). I have a 0 point of the object (red). And I have rotation point (blue).
How can I rotate the object around the blue point?
I wouldn't recommend updating the vertices, because you'll run into trouble with the normals (unless you keep them up-to-date, too). Basically, it's a lot of hassle to perform an action for which the transformation matrices were intended.
You came pretty close by translating, rotating, and un-translating, so you were on the right track. There are some built-in methods which can help make this super easy.
// obj - your object (THREE.Object3D or derived)
// point - the point of rotation (THREE.Vector3)
// axis - the axis of rotation (normalized THREE.Vector3)
// theta - radian value of rotation
// pointIsWorld - boolean indicating the point is in world coordinates (default = false)
function rotateAboutPoint(obj, point, axis, theta, pointIsWorld){
pointIsWorld = (pointIsWorld === undefined)? false : pointIsWorld;
if(pointIsWorld){
obj.parent.localToWorld(obj.position); // compensate for world coordinate
}
obj.position.sub(point); // remove the offset
obj.position.applyAxisAngle(axis, theta); // rotate the POSITION
obj.position.add(point); // re-add the offset
if(pointIsWorld){
obj.parent.worldToLocal(obj.position); // undo world coordinates compensation
}
obj.rotateOnAxis(axis, theta); // rotate the OBJECT
}
After this method completes, the rotation/position IS persisted. The next time you call the method, it will transform the object from its current state to wherever your inputs define next.
Also note the compensation for using world coordinates. This allows you to use a point in either world coordinates or local space by converting the object's position vector into the correct coordinate system. It's probably best to use it this way any time your point and object are in different coordinate systems, though your observations may differ.
As a simple solution for anyone trying to quickly change the pivot point of an object, I would recommend creating a group and adding the mesh to the group, and rotating around that.
Full example
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube)
Right now, this will just rotate around its center
cube.rotation.z = Math.PI / 4
Create a new group and add the cube
const group = new THREE.Group();
group.add(cube)
scene.add(group)
At this point we are back where we started. Now move the mesh:
cube.position.set(0.5,0.5,0)
Then move the group
group.position.set(-0.5, -0.5, 0)
Now use your group to rotate the object:
group.rotation.z = Math.PI / 4

Three.js How to find vertices of the face by face index?

In my Three.js project i have a mesh using a buffergeometry.
Using raycaster i find the intersection of a ray with this mesh in a specific face of which it says me the index.
How can i find vertices position of this face?
Instead of using the faceIndex, it's easier to use the face property of an intersection object. You can use it like so:
var vA = new THREE.Vector3();
var vB = new THREE.Vector3();
var vC = new THREE.Vector3();
var face = intersection.face;
var geometry = intersection.object.geometry;
var position = geometry.attributes.position;
vA.fromBufferAttribute( position, face.a );
vB.fromBufferAttribute( position, face.b );
vC.fromBufferAttribute( position, face.c );
If you need the vertices in world space, then multiple these three vectors with the world matrix of your object.
three.js R102

Inconsistent surface behaviour in Three.js

when I set a point light at a THREE.BoxGeometry object it looks like this:
THREE.BoxGeometry with point light
var light = new THREE.PointLight (0xffffff, 1, 100);
light.position.set (10, 10, 10);
scene.add (light);
var geometry = new THREE.BoxGeometry (1, 1, 1);
var material = new THREE.MeshPhongMaterial ();
var cube = new THREE.Mesh (geometry, material);
scene.add (cube);
When I now set a point light at a THREE.PolyhedronGeometry object it looks like this:
THREE.PolyhedronGeometry with point light
var light = new THREE.PointLight (0xffffff, 1, 100);
light.position.set (10, 10, 10);
scene.add (light);
var geometry = new THREE.PolyhedronGeometry (vertices, faces, 1, 0);
var vertices = [-1,-1,-1,1,-1,-1,1,1,-1,-1,1,-1,-1,-1,1,1,-1,1,1,1,1,-1,1,1];
var faces = [2,1,0,0,3,2,0,4,7,7,3,0,0,1,5,5,4,0,1,2,6,6,5,1,2,3,7,7,6,2,4,5,6,6,7,4];
var material = new THREE.MeshPhongMaterial ();
var cube = new THREE.Mesh (geometry, material);
scene.add (cube);
I want to know, where this behaviour comes from and how I can manage to make polyhedrons' faces behave as nice as boxs?
I read that it might be related to geometry.computeFaceNormals().
So I tried it out, but it doesn't make any difference.
when something is different with how light behaves on a surface, first candidates to look at are normals
this is true for the box face
boxGeometry.faces[i].normal.equals(boxGeometry.faces[i].vertexNormals[j]);//true
so box has only simple normal for each face
the polyhedron has different face normal from the vertex normals
polyhedronGeo.faces[i].normal.equals(polyhedronGeo.faces[i].vertexNormals[j]);//not true
and some of the vertex normals are not equal among each other
polyhedronGeo.faces[i].vertexNormals[j].equals(polyhedronGeo.faces[i].vertexNormals[k]);
//not true for some j,k
that is why the light looks ~shadowy - normal is interpolated for the shader from vertexNormals
to modify the polyhedron to look like box just change the vertex notmals to match the face normal
as for
geometry.computeFaceNormals();
it will only compute the face normals, not the vertexNormals
there is another function
geometry.computeVertexNormals();
but that would create vertex normals as are in polyhedron
Thanks Derte. Your reflection got me closer to the point. So with advanced keywords I found this: https://github.com/mrdoob/three.js/issues/1982
The answer to my question is this line, flattening shading for "free forms":
material.shading = THREE.FlatShading;

Unexpected mesh results from ThreeCSG boolean operation

I am creating a scene & have used a boolean function to cut out holes in my wall. However the lighting reveals that the resultant shapes have messed up faces. I want the surface to look like one solid piece, rather than fragmented and displaying lighting backwards. Does anyone know what could be going wrong with my geometry?
The code that booleans objects is as follows:
//boolean subtract two shapes, convert meshes to bsps, subtract, then convert back to mesh
var booleanSubtract = function (Mesh1, Mesh2, material) {
//Mesh1 conversion
var mesh1BSP = new ThreeBSP( Mesh1 );
//Mesh2 conversion
var mesh2BSP = new ThreeBSP( Mesh2 );
var subtract_bsp = mesh1BSP.subtract( mesh2BSP );
var result = subtract_bsp.toMesh( material );
result.geometry.computeVertexNormals();
return result;
};
I have two lights in the scene:
var light = new THREE.DirectionalLight( 0xffffff, 0.75 );
light.position.set( 0, 0, 1 );
scene.add( light );
//create a point light
var pointLight = new THREE.PointLight(0xFFFFFF);
// set its position
pointLight.position.x = 10;
pointLight.position.y = 50;
pointLight.position.z = 130;
// add to the scene
scene.add(pointLight);
EDIT: Using WestLangley's suggestion, I was able to partially fix the wall rendering. And by using material.wireframe=true; I can see that after the boolean operation my wall faces are not merged. Is there a way to merge them?
Your problems are due to two issues.
First, you should be using FlatShading.
Second, as explained in this stackoverflow post, MeshLambert material only calculates the lighting at each vertex, and interpolates the color across each face. MeshPhongMaterial calculates the color at each texel.
You need to use MeshPhongMaterial to avoid the lighting artifacts you are seeing.
three.js r.68

Changing material color on a merged mesh with three js

Is that possible to interact with the buffer used when merging multiple mesh for changing color on the selected individual mesh ?
It's easy to do such thing with a collection of mesh but what about a merged mesh with multiple different material ?
#hgates, your last comment was very helpful to me, I was looking for the same thing for days !
Ok i set on each face a color, and set to true vertexColor on the
material, that solve the problem ! :)
I write here the whole concept that I used in order to add a proper answer for those who are in the same situation :
// Define a main Geometry used for the final mesh
var mainGeometry = new THREE.Geometry();
// Create a Geometry, a Material and a Mesh shared by all the shapes you want to merge together (here I did 1000 cubes)
var cubeGeometry = new THREE.CubeGeometry( 1, 1, 1 );
var cubeMaterial = new THREE.MeshBasicMaterial({vertexColors: true});
var cubeMesh = new THREE.Mesh( cubeGeometry );
var i = 0;
for ( i; i<1000; i++ ) {
// I set the color to the material for each of my cubes individually, which is just random here
cubeMaterial.color.setHex(Math.random() * 0xffffff);
// For each face of the cube, I assign the color
for ( var j = 0; j < cubeGeometry.faces.length; j ++ ) {
cubeGeometry.faces[ j ].color = cubeMaterial.color;
}
// Each cube is merged to the mainGeometry
THREE.GeometryUtils.merge(mainGeometry, cubeMesh);
}
// Then I create my final mesh, composed of the mainGeometry and the cubeMaterial
var finalMesh = new THREE.Mesh( mainGeometry, cubeMaterial );
scene.add( finalMesh );
Hope it will help as it helped me ! :)
Depends on what you mean with "changing colors". Note that after merging, the mesh is like any other non-merged mesh.
If you mean vertex colors, it would be possibly to iterate over the faces and determine the vertices which color to change based on the material index.
If you mean setting a color to the material itself, sure it's possible. Merged meshes can still have multiple materials the same way ordinary meshes do - in MeshFaceMaterial, though if you are merging yourself, you need to pass in a material index offset parameter for each geometry.
this.meshMaterials.push(new THREE.MeshBasicMaterial(
{color:0x00ff00 * Math.random(), side:THREE.DoubleSide}));
for ( var face in geometry.faces ) {
geometry.faces[face].materialIndex = this.meshMaterials.length-1;
}
var mesh = new THREE.Mesh(geometry);
THREE.GeometryUtils.merge(this.globalMesh, mesh);
var mesh = new THREE.Mesh(this.globalMesh, new THREE.MeshFaceMaterial(this.meshMaterials));
Works like a charm, for those who need example but ! This creates mutliple additional buffers (indices and vertex data) , and multiple drawElements call too :(, i inspect the draw call with webgl inpector, before adding the MeshFaceMaterial : 75 call opengl api running at 60fps easily, after : 3490 call opengl api fps drop about 20 % 45-50 fps, this means that drawElements is called for every mesh, we loose the context of merging meshes, did i miss something here ? i want to share different materials on the same buffer

Resources