ThatOpen / engine_web-ifc

Reading and writing IFC files with Javascript, at native speeds.
https://thatopen.github.io/engine_web-ifc/demo
Mozilla Public License 2.0
617 stars 190 forks source link

Some model meshes not loading #123

Closed yermolim closed 2 years ago

yermolim commented 2 years ago

Trying to import the model using API (web-ifc@0.0.30). As a result, some meshes are not imported.

That is how it looks in three.js:

image

That is how it should look (screenshot from Xbim Xplorer):

image

Console shows a lot of "unexpected mesh type" messages.

For example, some of the IFC IDs of the problematic elements: 1o$JvD39L20h4minvXgKp7 0hSp94qDP3yRG$KaEKS1U1 1U18W3H2D2tvqQskXszS3A 0IHD$GsvfFBOXH$Z_IbB4x

Link to the file: https://drive.google.com/file/d/1kzYINzc0vkRcSYxvvzAf6eN5QKLhXN54/view?usp=sharing

Thanks!

dlabz commented 2 years ago

Seems they are all IfcElementProxy ->IfcClosedShell -> IfcFacetedBrep -> IfcSweptSolid

dlabz commented 2 years ago

Seems the missing elements load just fine in the web-ifc-three helloworld viewer...

https://ifcjs.github.io/hello-world/examples/web-ifc-three/helloworld/

yermolim commented 2 years ago

Seems the missing elements load just fine in the web-ifc-three helloworld viewer...

https://ifcjs.github.io/hello-world/examples/web-ifc-three/helloworld/

hmm. you're right. then I will look for the source of the problem on my end. maybe something wrong with my API calls. thanks for spending your time. and sorry for the disturbance. 😞 I'll close this, at least for now.

yermolim commented 2 years ago

Tried to find out if something is wrong on my end but failed. I found out that FlatMesh.geometries.size() is equal to 0 for the problematic elements. So I can't get the geometry I need. What is the reason for such behavior?

That's my IFC import code using web-ifc API:

import * as IFC from "web-ifc";

export class IFCLoader extends THREE.Loader {
  private readonly _ifcAPI: IFC.IfcAPI;

  constructor(wasmPath: string)
  {
    super();

    this._ifcAPI = new IFC.IfcAPI();
    this._ifcAPI.SetWasmPath(wasmPath);
    this._ifcAPI.Init();
  }

  load(url: string,
    onLoad: (gltf: THREE.Object3D) => void,
    onProgress?: (event: ProgressEvent) => void,
    onError?: (event: ErrorEvent) => void) {

    const loader = new THREE.FileLoader(this.manager);
    loader.setPath(this.path);
    loader.setResponseType("arraybuffer");
    loader.setRequestHeader(this.requestHeader);
    loader.setWithCredentials(this.withCredentials);
    loader.load(url,
      async (buffer) => {
        try {
          const data = new Uint8Array(<any>buffer);
          const result = await this.loadModelAsync(data);
          onLoad(result);
        } catch (e) {
          console.log(e);
          if (onError) {
            onError(e);
          } else {
            console.error(e);
          }
          this.manager.itemError(url);
        }
      },
      onProgress,
      onError,
    );
  }

  async loadModelAsync(data: Uint8Array): Promise<THREE.Object3D> {
    const root = new THREE.Group();

    const modelId = this._ifcAPI.OpenModel(data, { COORDINATE_TO_ORIGIN: false, USE_FAST_BOOLS: false });
    const ifcMeshes = this._ifcAPI.LoadAllGeometry(modelId);

    for (let i = 0; i < ifcMeshes.size(); i++) {      
      const ifcMesh = ifcMeshes.get(i);  

      const lineAttrs = this._ifcAPI.GetLine(modelId, ifcMesh.expressID, false);
      const globalId = lineAttrs.GlobalId?.value || `unknown_${i}`;

      if (globalId === "1o$JvD39L20h4minvXgKp7") {
        console.log("FOUND");
        console.log(ifcMesh.geometries.size()); // 0 !!!
      }

      const ifcMeshGeometries = ifcMesh.geometries;
      for (let j = 0; j < ifcMeshGeometries.size(); j++) {      
        if (globalId === "1o$JvD39L20h4minvXgKp7") {
          console.log("Obviously, never get here =(");
        }

        const ifcMeshGeometry = ifcMeshGeometries.get(j);
        const threeMesh = this.convertIfcGeometryToThreeMesh(modelId, ifcMeshGeometry);
        threeMesh.name = globalId;
        root.add(threeMesh);
      }
    }

    this._ifcAPI.CloseModel(modelId);

    return root;
  }

  private convertIfcGeometryToThreeMesh(modelId: number, ifcMeshGeometry: IFC.PlacedGeometry) {
    const geometry = this._ifcAPI.GetGeometry(modelId, ifcMeshGeometry.geometryExpressID);
    const vertices = this._ifcAPI.GetVertexArray(geometry.GetVertexData(), geometry.GetVertexDataSize());
    const indices = this._ifcAPI.GetIndexArray(geometry.GetIndexData(), geometry.GetIndexDataSize());
    const bufferGeometry = this.buildThreeGeometry(vertices, indices);

    const material = this.buildMeshMaterial(ifcMeshGeometry.color);

    const mesh = new THREE.Mesh(bufferGeometry, material);
    const matrix = new THREE.Matrix4().fromArray(ifcMeshGeometry.flatTransformation.map(x => +x.toFixed(5)));
    mesh.matrix = matrix;
    mesh.matrixAutoUpdate = false;

    return mesh;
  }

  private buildMeshMaterial(ifcColor: IFC.Color): THREE.Material {
    const threeColor = new THREE.Color(ifcColor.x, ifcColor.y, ifcColor.z);
    const material = new THREE.MeshPhongMaterial({ color: threeColor, side: THREE.DoubleSide });
    material.transparent = ifcColor.w !== 1;
    if (material.transparent) {
      material.opacity = ifcColor.w;
    }
    return material;
  }

  private buildThreeGeometry(vertices: Float32Array, indices: Uint32Array): THREE.BufferGeometry {
    const geometry = new THREE.BufferGeometry();    
    const positionNormalBuffer = new THREE.InterleavedBuffer(vertices, 6);    
    geometry.setAttribute("position", new THREE.InterleavedBufferAttribute(positionNormalBuffer, 3, 0));
    geometry.setAttribute("normal", new THREE.InterleavedBufferAttribute(positionNormalBuffer, 3, 3));
    geometry.setIndex(new THREE.BufferAttribute(indices, 1));
    return geometry;
  }
}

That's a simplified version of what I've seen in IFCLoader from three.js, and it matches my project requirements (I need only meshes with their colors and globalIDs). It works fine with most of the models I work with but fails on the one I posted above.

tomvandig commented 2 years ago

This is a bug in the library, will update when I know more.

tomvandig commented 2 years ago

Looks like I made a bad assumption about string lengths, should be easy to fix.

tomvandig commented 2 years ago

Will be fixed in 0.0.32 image

tomvandig commented 2 years ago

0.0.32 released

jonas32 commented 2 years ago

I have a similar (or the same maybe) problem with the "unexpected mesh type" error messages and 0.0.32 did not solve it for me. Sadly, the ifc file belongs to a customer and is under NDA so i cant publish it here. Any Idea how i could start debugging this? Im randomly missing elements all over the object. This should be consistent but as you see, there are many elements missing.

image

tomvandig commented 2 years ago

@jonas32 please load the model in the viewer in this repo and open an issue with the dev console results. Those will tell me which element is not supported