Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create b3dm, attribute lost #164

Open
lvfeijian opened this issue Dec 10, 2024 · 13 comments
Open

Create b3dm, attribute lost #164

lvfeijian opened this issue Dec 10, 2024 · 13 comments

Comments

@lvfeijian
Copy link

lvfeijian commented Dec 10, 2024

Hello developer, my English is not particularly good and I am using a translation tool to communicate with you.
I use the command: npx 3d-tiles-tools glbToB3dm -i ./test2.glb -o ./out/test2.b3dm
After converting my GLB to B3DM, I used Cesium for rendering. Lost these attributes and custom data information
4f09b1eb779fd08812f69f029de2657

@lvfeijian
Copy link
Author

lvfeijian commented Dec 10, 2024

test2.zip
This is my glb file
Let me describe what I need to do. I need to render the ifc file exported from Archicad in cesium.
ifc=>glb=>3dTiles
This is my idea. I am currently using the ifcConvert tool of ifcOpenShell to convert ifc to glb. This is how the test2.glb file came about.
Now use the 3d files tools tool to convert glb to b3dm. The rendered attributes and other information are lost

@javagl
Copy link
Contributor

javagl commented Dec 10, 2024

It is not clear what the actual goal is. When you have attribute information in IFC, and you export this data with ifcOpenShell, then I don't think that the attribute information is exported as part of the GLB. So there is no direct way to transport the attribute information from IFC into B3DM.

But the functionality of Cesium ion has recently been extended to improve the handling of IFC data and the attributes/metadata that it contains. You could try uploading your model to Cesium ion, as described in https://cesium.com/learn/3d-tiling/tiler-data-formats/ . This way, the attribute information should be preserved in the resulting tileset data. (This data will be GLB and not B3DM, but the GLB will contain the necessary attribute information)

@lvfeijian
Copy link
Author

lvfeijian commented Dec 11, 2024

It is not clear what the actual goal is. When you have attribute information in IFC, and you export this data with ifcOpenShell, then I don't think that the attribute information is exported as part of the GLB. So there is no direct way to transport the attribute information from IFC into B3DM.

But the functionality of Cesium ion has recently been extended to improve the handling of IFC data and the attributes/metadata that it contains. You could try uploading your model to Cesium ion, as described in https://cesium.com/learn/3d-tiling/tiler-data-formats/ . This way, the attribute information should be preserved in the resulting tileset data. (This data will be GLB and not B3DM, but the GLB will contain the necessary attribute information)

Hello developer, thank you for your reply.
My requirement is to preview the IFC file exported from ArchiCAD modeling software in Cesium, and to view the corresponding attribute information for each component by clicking on it.
I don't need all the attribute information to be transferred from IFC to B3DM. I have exported an Excel spreadsheet locally, which retains a large amount of component information. My idea is to convert ifc to b3dm, which can keep a unique ID for me. I can use the unique ID to find the corresponding attribute information in the Excel spreadsheet.
Do you have any other better suggestions
I uploaded the ifc file to Cesium ion. The ifc file can be previewed, but the title set data is a bit strange and I don't know how to handle it.
6ddef10a025091836b5053f695be15e
b359629429ceb5d726743de749993c0
As shown in the picture, I want to find the corresponding 2SWv3esUb55gG7borg7vqV or 9C8390E8-D9E9-4516-A407-CF2D6A1F9D1F through 4846 in the title set. It's too troublesome to manually find them one by one. I want to directly obtain them

@javagl
Copy link
Contributor

javagl commented Dec 11, 2024

When you convert the data from IFC to GLB with ifcOpenShell, and you want additional/special metadata to be preserved in the GLB file in one way or another, then this is rather something that has to be addressed in https://github.com/IfcOpenShell/IfcOpenShell itself. When ifcOpenShell does not preserve this attribute information, then there is no "easy" way to "reconstruct" that data to make it available in Cesium.

However, from inspecting the GLB file, it looks like they are preserving this ID as part of the 'name' of the nodes in the glTF file. And in theory, it is possible to access this information in CesiumJS.

I do not recommend this!

It relies on an aspect of ifcOpenShell that is probably not strictly specified. And it uses some private API of CesiumJS.

But you can access the ID, like 9C8390E8-D9E9-4516-A407-CF2D6A1F9D1F, in CesiumJS:

Cesium Picking from GLB

Again: This is not something that you should "rely" on for production use. But here is the sandcastle that shows how this data can be accessed:

const viewer = new Cesium.Viewer("cesiumContainer");

const url = "http://localhost:8003/test2.glb";

const position = Cesium.Cartesian3.fromDegrees(
  -75.152325, 39.94704, 10
);
const entity = viewer.entities.add({
  name: url,
  position: position,
  model: {
    uri: url,
  },
});
viewer.trackedEntity = entity;

const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (movement) {
  const pick = viewer.scene.pick(movement.endPosition);
  const name = pick?.detail?.node?._name;
  console.log("Name: "+name);
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

@lvfeijian
Copy link
Author

When you convert the data from IFC to GLB with ifcOpenShell, and you want additional/special metadata to be preserved in the GLB file in one way or another, then this is rather something that has to be addressed in https://github.com/IfcOpenShell/IfcOpenShell itself. When ifcOpenShell does not preserve this attribute information, then there is no "easy" way to "reconstruct" that data to make it available in Cesium.

However, from inspecting the GLB file, it looks like they are preserving this ID as part of the 'name' of the nodes in the glTF file. And in theory, it is possible to access this information in CesiumJS.

I do not recommend this!

It relies on an aspect of ifcOpenShell that is probably not strictly specified. And it uses some private API of CesiumJS.

But you can access the ID, like 9C8390E8-D9E9-4516-A407-CF2D6A1F9D1F, in CesiumJS:

Cesium Picking from GLB

Again: This is not something that you should "rely" on for production use. But here is the sandcastle that shows how this data can be accessed:

const viewer = new Cesium.Viewer("cesiumContainer");

const url = "http://localhost:8003/test2.glb";

const position = Cesium.Cartesian3.fromDegrees(
  -75.152325, 39.94704, 10
);
const entity = viewer.entities.add({
  name: url,
  position: position,
  model: {
    uri: url,
  },
});
viewer.trackedEntity = entity;

const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (movement) {
  const pick = viewer.scene.pick(movement.endPosition);
  const name = pick?.detail?.node?._name;
  console.log("Name: "+name);
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

It seems like this is possible, but you don't recommend doing it this way.
How can I obtain a unique ID if I directly upload an IFC file to Cesium ion. How should I obtain it.
Or can Cesium render IFC with build information?
This is my IFC file
test2-ifc.zip

@javagl
Copy link
Contributor

javagl commented Dec 11, 2024

When uploading the IFC model to Cesium ion, it will be converted into 3D Tiles. In this 3D Tiles tileset, there are GLB/glTF files that contain only some identifiers. There is no direct way to access the data from the original IFC file based on that.

Similarly, when converting the IFC file to GLB with ifcOpenShell, most of the information from the IFC file is lost. But you can access the identifiers that you requested (like "9C8390E8-D9E9-4516-A407-CF2D6A1F9D1F" for the front wall). So even though this does not rely on the specification and it uses a private API, it might be your only option right now.

(Unless there are other tools that convert IFC to GLB - but given the complexity of IFC, this is unlikely...)

@lvfeijian
Copy link
Author

When uploading the IFC model to Cesium ion, it will be converted into 3D Tiles. In this 3D Tiles tileset, there are GLB/glTF files that contain only some identifiers. There is no direct way to access the data from the original IFC file based on that.

Similarly, when converting the IFC file to GLB with ifcOpenShell, most of the information from the IFC file is lost. But you can access the identifiers that you requested (like "9C8390E8-D9E9-4516-A407-CF2D6A1F9D1F" for the front wall). So even though this does not rely on the specification and it uses a private API, it might be your only option right now.

(Unless there are other tools that convert IFC to GLB - but given the complexity of IFC, this is unlikely...)

Hello developer, I'm glad to receive your reply.
I'm glad that I can now obtain component information using GLB. But I want to try uploading the IFC model directly to Cesium ion and converting it into 3dTiles. How can I obtain a unique ID? I tried to obtain it, but couldn't find it.
There is a unique ID in the ifc file, but when it changes to 3dTiles, I cannot find it. I don't know what measures were taken internally by Cesium ion.
I don't know how to get the ID in 3dTiles, as shown in the demo I wrote
85bea39a0715f242acaab78fba701e8
This is 3dTiles folder
test2-3dTiles.zip

@javagl
Copy link
Contributor

javagl commented Dec 12, 2024

I'm not entirely sure if this is what you referred to. But the following sandcastle shows how you can obtain the expressId and ifcClass in a sandcastle, from the ion asset that was created from the data that you uploaded:

// Grant CesiumJS access to your ion assets
Cesium.Ion.defaultAccessToken = /*========= !!! INSERT YOUR TOKEN HERE !!! */

const viewer = new Cesium.Viewer("cesiumContainer");

const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(2918934, {
  //This tileset doesn't have a location, so we're using a modelMatrix to place it at 0, 0 instead of drawing at the center of the earth
  modelMatrix: Cesium.Transforms.eastNorthUpToFixedFrame(
    Cesium.Cartesian3.fromDegrees(0, 0),
  ),
});
viewer.scene.primitives.add(tileset);
await viewer.zoomTo(tileset);

// Create an HTML element that will serve as the
// tooltip that displays the feature information
function createTooltip() {
  const tooltip = document.createElement("div");
  viewer.container.appendChild(tooltip);
  tooltip.style.backgroundColor = "black";
  tooltip.style.position = "absolute";
  tooltip.style.left = "0";
  tooltip.style.top = "0";
  tooltip.style.padding = "14px";
  tooltip.style["pointer-events"] = "none";
  tooltip.style["block-size"] = "fit-content";
  return tooltip;
}
const tooltip = createTooltip();

// Show the given HTML content in the tooltip
// at the given screen position
function showTooltip(screenX, screenY, htmlContent) {
  tooltip.style.display = "block";
  tooltip.style.left = `${screenX}px`;
  tooltip.style.top = `${screenY}px`;
  tooltip.innerHTML = htmlContent;
}

// Create an HTML string that contains information
// about the given feature, under the given title
function createFeatureHtml(title, feature) {
  if (!Cesium.defined(feature)) {
    return `(No ${title})<br>`;
  }
  const propertyKeys = feature.getPropertyIds();
  if (!Cesium.defined(propertyKeys)) {
    return `(No properties for ${title})<br>`;
  }
  let html = `<b>${title}:</b><br>`;
  for (let i = 0; i < propertyKeys.length; i++) {
    const propertyKey = propertyKeys[i];
    const propertyValue = feature.getProperty(propertyKey);
    html += `&nbsp;&nbsp;${propertyKey} : ${propertyValue}<br>`;
  }
  return html;
}

// Given an object that was obtained via Scene#pick: If it is
// a Cesium3DTileFeature, then it is returned.
// Otherwise, 'undefined' is returned.
function obtainFeature(picked) {
  if (!Cesium.defined(picked)) {
    return undefined;
  }
  const isFeature = picked instanceof Cesium.Cesium3DTileFeature;
  if (!isFeature) {
    return undefined;
  }
  return picked;
}

// Install the handler that will perform picking when the
// mouse is moved, and update the label entity when the
// mouse is over a Cesium3DTileFeature
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (movement) {
  let tooltipText = "";
  const picked = viewer.scene.pick(movement.endPosition);

  const feature = obtainFeature(picked);
  tooltipText += createFeatureHtml("Feature", feature);

  const screenX = movement.endPosition.x;
  const screenY = movement.endPosition.y;
  showTooltip(screenX, screenY, tooltipText);
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

@lvfeijian
Copy link
Author

lvfeijian commented Dec 12, 2024

I'm not entirely sure if this is what you referred to. But the following sandcastle shows how you can obtain the expressId and ifcClass in a sandcastle, from the ion asset that was created from the data that you uploaded

// Grant CesiumJS access to your ion assets
Cesium.Ion.defaultAccessToken = /*========= !!! INSERT YOUR TOKEN HERE !!! */

const viewer = new Cesium.Viewer("cesiumContainer");

const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(2918934, {
  //This tileset doesn't have a location, so we're using a modelMatrix to place it at 0, 0 instead of drawing at the center of the earth
  modelMatrix: Cesium.Transforms.eastNorthUpToFixedFrame(
    Cesium.Cartesian3.fromDegrees(0, 0),
  ),
});
viewer.scene.primitives.add(tileset);
await viewer.zoomTo(tileset);

// Create an HTML element that will serve as the
// tooltip that displays the feature information
function createTooltip() {
  const tooltip = document.createElement("div");
  viewer.container.appendChild(tooltip);
  tooltip.style.backgroundColor = "black";
  tooltip.style.position = "absolute";
  tooltip.style.left = "0";
  tooltip.style.top = "0";
  tooltip.style.padding = "14px";
  tooltip.style["pointer-events"] = "none";
  tooltip.style["block-size"] = "fit-content";
  return tooltip;
}
const tooltip = createTooltip();

// Show the given HTML content in the tooltip
// at the given screen position
function showTooltip(screenX, screenY, htmlContent) {
  tooltip.style.display = "block";
  tooltip.style.left = `${screenX}px`;
  tooltip.style.top = `${screenY}px`;
  tooltip.innerHTML = htmlContent;
}

// Create an HTML string that contains information
// about the given feature, under the given title
function createFeatureHtml(title, feature) {
  if (!Cesium.defined(feature)) {
    return `(No ${title})<br>`;
  }
  const propertyKeys = feature.getPropertyIds();
  if (!Cesium.defined(propertyKeys)) {
    return `(No properties for ${title})<br>`;
  }
  let html = `<b>${title}:</b><br>`;
  for (let i = 0; i < propertyKeys.length; i++) {
    const propertyKey = propertyKeys[i];
    const propertyValue = feature.getProperty(propertyKey);
    html += `&nbsp;&nbsp;${propertyKey} : ${propertyValue}<br>`;
  }
  return html;
}

// Given an object that was obtained via Scene#pick: If it is
// a Cesium3DTileFeature, then it is returned.
// Otherwise, 'undefined' is returned.
function obtainFeature(picked) {
  if (!Cesium.defined(picked)) {
    return undefined;
  }
  const isFeature = picked instanceof Cesium.Cesium3DTileFeature;
  if (!isFeature) {
    return undefined;
  }
  return picked;
}

// Install the handler that will perform picking when the
// mouse is moved, and update the label entity when the
// mouse is over a Cesium3DTileFeature
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(function (movement) {
  let tooltipText = "";
  const picked = viewer.scene.pick(movement.endPosition);

  const feature = obtainFeature(picked);
  tooltipText += createFeatureHtml("Feature", feature);

  const screenX = movement.endPosition.x;
  const screenY = movement.endPosition.y;
  showTooltip(screenX, screenY, tooltipText);
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

Hello, I have reviewed the demo case you provided me with.
You may have misunderstood my meaning. What I want to clarify is that there is no unique ID that can correspond between 3dTiles and the Excel sheet I exported. I want to have a unique ID that can correspond to it, just like when I use GLB.
I don't know how the ifcClass and expresId properties can be associated with my Excel spreadsheet.
I feel like 3dTiles has lost its association with my Excel spreadsheet

@lvfeijian
Copy link
Author

lvfeijian commented Dec 12, 2024

@javagl
I can manually find the corresponding Guid value F0C388DA-A48B-46B5-9256-B6E458A24764 in the ifc file through ExpressId. Through F0C388DA-A48B-46B5-9256-B6E458A24764, I can bind the data I want to obtain in my JSON table. But I don't know how to obtain the Guid through ExpressId in Cesium
1
2
3

@javagl
Copy link
Contributor

javagl commented Dec 12, 2024

The GUID is no longer contained in the data when it is converted with ion. After it is converted with ion, the data only contains the expressId and the ifcClass.

When you export the data with ifcOpenShell, then the nodes contain that GUID as part of the name of the node. It may be that this is the only option for accessing this data right now. If you need this GUID, then you can export the data with ifcOpenShell. But there is no guarantee that this will always work.

@lvfeijian
Copy link
Author

The GUID is no longer contained in the data when it is converted with ion. After it is converted with ion, the data only contains the expressId and the ifcClass.

When you export the data with ifcOpenShell, then the nodes contain that GUID as part of the name of the node. It may be that this is the only option for accessing this data right now. If you need this GUID, then you can export the data with ifcOpenShell. But there is no guarantee that this will always work.

Okay, thank you very much for your help.
It seems that I can only handle it this way at the moment.
I looked at it Cesium ion for Autodesk Revit Add-In I don't know if there will be a conversion plugin for ArchiCAD software in the future. Or can I open the model modeled by ArchiCAD using Autodesk Revit software and export the data through Cesium's Add In for Autodesk Revit plugin. (This is just my crazy idea)
There are no other issues, thank you again for your help

@javagl
Copy link
Contributor

javagl commented Dec 13, 2024

The support for AECO data, including IFC, is constantly improved within ion. You can expect that there will be broader support for different forms of metadata in the future. I cannot give details an exact timeline about that. But users will be informed about new features and improved support, via the community forum, or via blog posts like the one that you already linked to.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants