Hi,
Short version, my project aim to have a converter of IFC files to glTF files. This is already done, but now I want to optimize things so that I can load really huge IFC files. So I use InstancedMesh but the rotations are messy.
Longer version:
Context
Let’s say I have a file picker that allow the user to import a 3D model file (.glTF, .fbx, whatever). This file may have several objects which are the same mesh. For instance, we could have a house with the same window object several times.
To optimize the performances, I want to detect the objects that could be instanced and then convert the imported file to a new glTF file, with instancedMesh in it (using the EXT_mesh_gpu_instancing extension ).
Algorithm
So I followed these steps:
Similar objects detection
In my case, I have metadata that allow me to know if it is the same mesh or not. But I guess you could compare the vertices to know if the meshes are identical
I loop over the meshes and store them in a data structure by “type”. So if several objects are similar I group them, if an object have no similarities with other objects it is stored alone.
Loop over every group of object
Now that we have different groups, I loop over them.
If the group contains only one element, there is no need to make an instancedMesh so I generate a mesh of it and add it to the scene node.
If the group have several objects I do the next steps
Get relative positions
I loop over the similar objects and get the position of each object. We have two cases:
- The origin of the object is its center
- The origin of the object is (0;0;0)
First case is easy, we already have the position of the future instances
Second case, we have to get the center of each object. Then we take the first object of the group and determine the position of other objects relatively to this first object.
Now we have the position of every objects
Get relative rotations
Now the tricky part and where I’m stuck.
Same as for the position, we need to get the rotation of every object relatively to the first object of the group.
My method was to loop over the first three vertices of each to get a triangle, compute its normal and get the angle between its normal and the one of the first object. So for the first object, the angle is 0 but others we have a certain angle. Since, we have similar objects, I assume that this triangle is the same for every objects.
const vertices = newGeometry.attributes.position.array;
let index = newGeometry.index.array[0];
const vec1 = new THREE.Vector3(...vertices.slice(3 * index, 3 * index + 3));
index = newGeometry.index.array[1];
const vec2 = new THREE.Vector3(...vertices.slice(3 * index, 3 * index + 3));
index = newGeometry.index.array[2];
const vec3 = new THREE.Vector3(...vertices.slice(3 * index, 3 * index + 3));
const xVector = new THREE.Vector3().subVectors(vec2, vec1);
const yVector = new THREE.Vector3().subVectors(vec3, vec1);
const normal = new THREE.Vector3().crossVectors(xVector, yVector).normalize();
From this angle, I create a rotationMatrix for every object and store it.
const rotationMatrix = new THREE.Matrix4();
rotationMatrix.lookAt(vec1, vec2, normal);
const quaternion = new THREE.Quaternion().setFromRotationMatrix(rotationMatrix);
Generate instances
The final step is to create the instancedMeshes. So here I loop over the groups of similar objects.
For each group, I generate and InstancedMesh with the number of similar objects of this group as length. Then I apply position, rotation and scale:
- Position: I copy the position of the center of each object
- Scale: (1;1;)
And then rotation…
Here I do this steps:
- Get the rotation of the first object (quaternion)
- Invert it (quaternion.invert)
- Multiply it by the rotation of the object that we got at the previous step (quaternion.multiply)
const matrixRotation = new THREE.Matrix4();
matrixRotation.identity();
const quaternion = new THREE.Quaternion();
quaternion
.copy(firstobject.quaternion)
.invert()
.multiply(object.quaternion);
Finaly, I compose a matrix:
matrix.compose(position, quaternion, scale)
Basically, it works for some items but not the entire scene.
Here are the IFC file that I’m testing for my project. It’s an IFC test file used by the library IFC.js. I also made an glTF of it so you can see what it looks like but is not with instancedMesh.
sourceFile.glb (331.2 KB)
sourceFile.ifc (693.5 KB)
Here is the result I get with the algorithm I described (the source file is colorless on the screen but it has colors):
As you can see, the chairs don’t have the right rotation, and on the outside, there are some bars around the window (on the left) that do not have the right scale (I don’t know if it is a scale issue or rotation one).
Here is the file of these screenshots:
scene.glb (321.6 KB)
Do you have any tips on how to have the right rotation?