How to access the BufferGeometry of IFC items in web-ifc-three

1.2k views Asked by At

I'm trying to get the geometry of an element i.e. a BufferGeometry object corresponding to an expressId I have (not through picking).

Basically I'm asking how to traverse the IFC model and export each object as a separate OBJ.

I'll note I have reverse engineered code to achieve that for some version of the package, but it uses undocumented functionality, so naturally it broke in later versions (the code also colors the geometry according to the material's color so I don't need an mtl):

Don't copy this code it won't work

Object.values(bimModel.ifcManager.state.models[bimModel.modelID].items).forEach(type => {
  Object.entries(type.geometries).forEach(([id, geometry]) => {
    const properties = bimModel.getItemProperties(Number(id))
    const numVertices = geometry.getAttribute('position').count
    const color = type.material.color.toArray().map(x => x * 255)
    const vertexColors = new Uint8Array(Array.from({ length: numVertices }, () => color).flat())
    geometry.setAttribute('color', new BufferAttribute(vertexColors, 3, true))
  })
})
1

There are 1 answers

0
Antonio González Viegas On

This is exactly what we do to export models to glTF. The basic workflow is:

  • Decide what IFC categories you would like to export.
  • Get all the items of each category.
  • Reconstruct the mesh for each item.
  • Export the mesh using the Three.js exporter of your choice.

Let's see a basic example to get all the meshes from the walls. The process is not as straightforward as having each IFC item as a separate mesh, but that's the price for having the draw calls at minimum (otherwise, a browser wouldn't stand even medium-sized IFC files):

import { IFCWALLSTANDARDCASE } from 'web-ifc';

async function getAllWallMeshes() {

    // Get all the IDs of the walls

    const wallsIDs = manager.getAllItemsOfType(0, IFCWALL, false);
    const meshes = [];
    const customID = 'temp-gltf-subset';

    for (const wallID of wallsIDs) {
        const coordinates = [];
        const expressIDs = [];
        const newIndices = [];

        const alreadySaved = new Map();

        // Get the subset for the wall

        const subset = viewer.IFC.loader.ifcManager.createSubset({
            ids: [wallID],
            modelID,
            removePrevious: true,
            customID
        });

        // Subsets have their own index, but share the BufferAttributes 
        // with the original geometry, so we need to rebuild a new 
        // geometry with this index

        const positionAttr = subset.geometry.attributes.position;
        const expressIDAttr = subset.geometry.attributes.expressID;

        const newGroups = subset.geometry.groups
            .filter((group) => group.count !== 0);

        const newMaterials = [];
        const prevMaterials = subset.material;
        let newMaterialIndex = 0;

        newGroups.forEach((group) => {
            newMaterials.push(prevMaterials[group.materialIndex]);
            group.materialIndex = newMaterialIndex++;
        });

        let newIndex = 0;
        for (let i = 0; i < subset.geometry.index.count; i++) {
            const index = subset.geometry.index.array[i];

            if (!alreadySaved.has(index)) {
                coordinates.push(positionAttr.array[3 * index]);
                coordinates.push(positionAttr.array[3 * index + 1]);
                coordinates.push(positionAttr.array[3 * index + 2]);

                expressIDs.push(expressIDAttr.getX(index));
                alreadySaved.set(index, newIndex++);
            }

            const saved = alreadySaved.get(index);
            newIndices.push(saved);
        }

        const geometryToExport = new BufferGeometry();
        const newVerticesAttr = new BufferAttribute(Float32Array.from(coordinates), 3);
        const newExpressIDAttr = new BufferAttribute(Uint32Array.from(expressIDs), 1);

        geometryToExport.setAttribute('position', newVerticesAttr);
        geometryToExport.setAttribute('expressID', newExpressIDAttr);
        geometryToExport.setIndex(newIndices);
        geometryToExport.groups = newGroups;
        geometryToExport.computeVertexNormals();

        const mesh = new Mesh(geometryToExport, newMaterials);
        meshes.push(mesh);
    }

    viewer.IFC.loader.ifcManager.removeSubset(modelID, undefined, customID);
    return meshes;
}