GlobalId from IFC

Hello everyone,

I am working with IFC files where the primary identifier for objects is the GlobalId. However, I couldn’t find any reference to the original GlobalId from the file in the Cesium Ion Tiles Viewer.

This is a critical issue because, when triggering events, this ID is essential to correctly identify and handle the corresponding object.

Has anyone encountered this limitation or found a way to map the original GlobalId to the objects in the Cesium viewer? Any insights would be greatly appreciated!

Hi @kharel.valtingojer,

Currently our support for retaining metadata from IFC is somewhat limited to IFC Class, and an expressId which is an internal identifier created during IFC conversion.

To clarify your needs using an example IFC element, can you confirm it is the first element of the below line that you want to retain? (eg. 3e_9ViPQjBmuUesehp3UW0)

#2249=IFCFLOWTERMINAL('3e_9ViPQjBmuUesehp3UW0',#1,'M_Duplex Receptacle:Standard:Standard:956521',$,'Standard',#46939,#44165,'956521');

Hello @ryan_veenstra,

Thank you for your reply!

Yes, the ID I need is indeed similar to the one you’ve shown:

#27381= IFCBUILDINGELEMENTPROXY(‘3$27THPUnFiuPYMhK2izxo’,#41,‘Aluminium Sheet:Aluminium Sheet:281679’,$,‘Aluminium Sheet:Aluminium Sheet’,#27380,#27375,‘281679’,$);
#27715= IFCBEAM(‘3eCXZ58d92xArot77Jzn0o’,#41,‘H-Wide Flange Beams:HE120B:283012’,$,‘H-Wide Flange Beams:HE120B’,#27693,#27713,‘283012’);

I’ve seen examples where this ID is available as ifcGUID, such as in this Sandcastle example:

image

However, when using my own model, the available properties are limited to expressId and ifcClass.

Did you notice if there’s a specific workflow or pre-preprocessing step that could map this globalId to the model in the viewer?
For instance, could an intermediate value be used to establish this relationship?

This particular sample was exported directly from Autodesk Revit using the Cesium ion for Autodesk Revit add-in. When using this workflow, metadata from Revit is retained. It is just our direct IFC to 3D Tiles conversion where we are currently limited with metadata support, so if you have the opportunity to use Autodesk Revit you may be able to make some progress?

1 Like

Hello @ryan_veenstra,

Thank you so much for your detailed reply and for clarifying how the Revit add-in handles metadata retention. Your insights are incredibly helpful!

In our case, preserving the GlobalId is crucial because we are developing a custom platform using the CesiumJS engine. When users interact with objects in the scene, we rely on the GlobalId to fetch corresponding information from our system to enhance the user experience. I believe this is a common use case for many working with IFC files.

I noticed that the Revit add-in retains metadata effectively during conversion. Is there any way we could replicate this behavior with direct IFC uploads? For instance, could the metadata be embedded in the IFC file in a specific way to achieve similar results? I don’t recall seeing any additional parameters in the API documentation that would allow us to provide this information.

Also, does the same metadata limitation apply when using GLB files? We can programmatically replace the “Name” attribute of each object with its GlobalId before converting to GLB, as a potential workaround. Could this approach preserve the object name when converting to tiles?

Your guidance has been incredibly valuable so far, and any additional suggestions would be greatly appreciated!

Thank you again for your time and support!

Hey @ryan_veenstra,

I’ve made some progress! Your tips about working with Revit were pivotal in helping me get to this stage — thank you so much for pointing me in the right direction.

Since preserving the GlobalId is critical in my scenario, I’d like to share the approach I used, focusing specifically on retaining this metadata. I’ll also briefly touch on how to extend this method to include additional properties if needed.

Steps:
#1. Convert IFC to GLB using IfcOpenShell (Installation - IfcOpenShell 0.8.0 documentation)
The --use-element-guid parameter ensures that the GlobalId becomes the name attribute in the resulting GLB file, which is key for subsequent steps:

IfcConvert.exe path/to/ifc.ifc path/to/output.glb --use-element-guid

#2. Convert GLB to GLTF using CesiumGS glTF Pipeline (gltf-pipeline/README.md at main · CesiumGS/gltf-pipeline · GitHub)
Use the following command to transform the GLB file into a GLTF file:

npx gltf-pipeline -i path/to/glb.glb -o path/to/output.gltf

#3. Embed Metadata in the GLTF File
Here, I use a script to loop through the GLTF JSON structure and add the GlobalId (from the name field) as a property. At this stage, you can also pre-extract additional properties from the IFC file, use the GlobalId as key and embed them alongside the GlobalId. This is especially useful if you need other attributes available for interaction in the viewer.

For example, in my case, we use the GlobalId as a key to link viewer objects with data stored in our system. This step also allows the customization of base mesh names if needed.

#4. Upload to Cesium Ion
When uploading to Cesium Ion, ensure the correct data type is specified:

Web Interface: Use the “Architecture, Engineering, or Construction model (BIM/CAD)” option.
API: Include “type”: “3DTILES” and “options”: { “sourceType”: “BIM_CAD” } in your request payload.

Output:
With this process, the GlobalId (or other custom properties) becomes accessible in the click event of the mesh in the Cesium viewer. This workflow can handle GLTF files in both single-file (.gltf) and paired-file (.gltf + .bin) formats — both work seamlessly in Cesium Ion.

A great advantage of this method is its flexibility. You can embed any set of properties in the GLTF file. For example, I tested it with a simple restroom model created in Blender, adding mock metadata, and it worked perfectly.

Below, I’ve included a sample script illustrating step #3 and an interface demonstrating how to embed the GlobalId into the GLTF file from its name provided by --use-element-guid from the step #1.

This script is a proof of concept and can certainly be improved, but I hope it serves as a starting point for others exploring this approach.

import fs from "fs";

const startTime = Date.now();

interface EXT_structural_metadata {
    class: string;
    properties: { ifcGUID: string; [key: string]: any };
}

interface GltfSchema {
    extensionsUsed?: string[];
    extensions?: { [key: string]: any };
    nodes: Array<{
        name?: string;
        extensions?: { EXT_structural_metadata?: EXT_structural_metadata; [key: string]: any };
    }>;
}

function updateGltf(filePath: string): void {
    const gltfData: GltfSchema = JSON.parse(fs.readFileSync(filePath, "utf8"));

    if (!gltfData.extensionsUsed) {
        gltfData.extensionsUsed = [];
    }
    if (!gltfData.extensionsUsed.includes("EXT_structural_metadata")) {
        gltfData.extensionsUsed.push("EXT_structural_metadata");
    }

    if (!gltfData.extensions) {
        gltfData.extensions = {};
    }
    if (!gltfData.extensions.EXT_structural_metadata) {
        gltfData.extensions.EXT_structural_metadata = {
            schema: {
                id: "guidFromNameGenerated",
                classes: {
                    guidFromName: {
                        name: "guidFromName",
                        properties: {
                            ifcGUID: {
                                name: "ifcGUID",
                                type: "STRING",
                                required: false,
                            },
                        },
                    },
                },
            },
        };
    }

    gltfData.nodes.forEach((node) => {
        if (node.name) {
            if (!node.extensions) {
                node.extensions = {};
            }
            if (!node.extensions.EXT_structural_metadata) {
                node.extensions.EXT_structural_metadata = {
                    class: "guidFromName",
                    properties: {
                        ifcGUID: node.name,
                    },
                };
            }
        }
    });

    fs.writeFileSync(filePath, JSON.stringify(gltfData, null, 2), "utf8");
}

const filePath = "./file.gltf";

updateGltf(filePath);

const endTime = Date.now();
const timeExpendInSeconds = (endTime - startTime) / 1000;

console.log("seconds to convert:", filePath, timeExpendInSeconds);

Thank you again!
Please let me know if you have any suggestions on better handle the situation.

Best regards,
Kharel

Given that the insertion here is happening largely manually and on the JSON directly, the path of using gltf-pipeline for the conversion and the custom script for inserting the metadata is probably the easiest and most straightforward approach.

In some other cases, it might be that some of the functionality of the 3d-tiles-tools could come in handy: They contain an implementation of EXT_structural_metadata based on glTF-Transform. The latter offers some convenient functionalities for modifiying glTF in general. And with the extension implementation, one could even add things like property tables relatively easily.

A very basic example for how these functionalities can be used is shown in the ExtStructuralMetadataPropertyTableDemo.

Maybe I’ll add a demo like ConvertNodeNamesToMetadataProperties or so, which could basically offer what is currently happening with the node.nameifcGUID part in the custom script. (I already created similar scripts, for assigning feature IDs to meshes and mesh primitives - maybe this can eventually be combined into some useful core functionality of the tools…)

1 Like