Introduction
Over the past few years, the TBC Team has been building the BIM/IFC features out to be a robust and multifaceted toolset. We started with a simple workflow in mind, Import > Georeference > Export > Stakeout. As BIM features have rolled out to other Trimble products, we've had requests to expand what you can do with BIM data in and out of TBC.
As we remain agile in our development, we also plan ahead as best we can, and prefer that we never require you (the users) to remake projects to use the latest BIM features. This has lead to some fundamental changes being required to continue with these goals in mind. Given that Macro developers are also working hard to create amazing features with TBC, we strive to better inform you all of major changes that will be coming to future releases that will affect existing BIM Macros.
In late 2023, we released TBC 2023.1, which had some major design changes in how BIM data should be created and stored. We'd love for future Macros to function as-is in perpetuity, but when that isn't the case we hope that we can help prepare you all for any big changes like this in the future.
TBC 5.90 to 2023.12
With the release of Trimble Business Center 2023.1, we introduced the ShellMeshInstance as a replacement for the IFCMesh snapin. This change was made with two main goals:
- Have the same number of selectable objects in TBC as you get in Trimble Connect, Trimble Access, and other programs that import BIM data.
- Stop storing duplicate mesh data for exact copies of the same mesh.
To achieve the first goal, we decided to move from having individually user-selectable mesh objects (IFCMeshes) to instead having you select the BIMEntitys themselves. Because previous versions of TBC allowed you to move each mesh individually; this gave us a challenge when trying to load existing VCEs from previous versions (TBC 5.90 and before.) There will be some cases where you will have moved imported IFCMesh objects in workflows we did not intend. The way we remedy this, for imported IFCMesh objects, is explained more in the next section.
The second goal is achieved by the way this change is architected and will be simple to implement if you already create IFCMeshes.
See the section ahead: "From IFCMesh to ShellMeshInstance: The 'how?'"
From IFCMesh to ShellMeshInstance: The 'why?'
Our IfcImporter and TrbImporter aggregate their BIM data in the IfcDataBlock class. When we want to make changes based on loading a previous version's data, we make those changes while loading that IfcDataBlock class. We want to (for the most part) assume that you have imported complete BIM data. Therefore each piece of BIM data should be positioned correctly relative to each other before it is imported into TBC. This is specific to the workflows we are developing for Import > Georeference > Export based workflows.
While there are still issues with some data sets, the goal was to have BIM data contained in IfcDataBlock instances to update seamlessly if you have moved your data together in a single transformation using the Move Objects command. Since we didn't want to ruin projects by users who have not kept to our ideal workflow, we still needed to support the existing mode of interaction, and we still do to this day. This is one of the reasons why IFCMeshes still exist and still function in TBC today.
We want to encourage any Macro developer whose macros create or work with IFCMeshes to include support for, and instead create BIMEntitys with ShellMeshInstances moving forward. We would like to eventually deprecate and sunset the IFCMesh, though there is NO planned timeline for that change.
From IFCMesh to ShellMeshInstance: The 'what?'
To facilitate the change between the old and new mesh classes, several interfaces and supporting classes were added. Some method signatures were also changed to consolidate behavior.
Classes (namespace: Trimble.Sdk.Data)
- ShellMeshInstance (base class: SnapInTransact)
Individual instance of a mesh within a BIMEntity. Has a location (relative to its containing BIMEntity) and references a ShellMeshData instance for its mesh data.
A BIMEntity may contain none, one, or many ShellMeshInstances.
Example: A single mechanical assembly, represented by a BIMEntity, may contain many ShellMeshInstances which reference the same ShellMeshData instantiation.
The mesh data for a bolt, for example, can be stored in one ShellMeshData, then reused by many ShellMeshInstances in one (or many) BIMEntitys.
- ShellMeshData (base class: SnapInTransact)
A unique mesh definition.
It is best to define its vertices as close to the origin (X = 0.0, Y = 0.0, Z = 0.0) as possible.
- ShellMeshDataCollection (base class: EntitycontainerBase)
All ShellMeshData are stored here.
Automatically checks and prevents duplication of ShellMeshData
Interfaces (namespace: Trimble.Vce.Interfaces)
- IShell3DData
Implemented by Shell3D/IFCMesh, ShellMeshInstance, ShellMeshData
For setting and retrieving raw mesh data
- IShell3DDisplay
Implemented by Shell3D/IFCMesh, BIMEntity, ShellMeshInstance
Settings for rendering individual meshes
- IShell3DDescription
Useful properties that describe a mesh.
Inherited by the other interfaces in this collection.
- IShell3D - (IShell3DDisplay + IShell3DDescription)
Implemented by Shell3D/IFCMesh, BIMEntity, ShellMeshInstance
Convenient interface for updating some function parameters.
From IFCMesh to ShellMeshInstance: The 'how?'
Creating the new "ShellMesh" schema representations starts from the ShellMeshDataCollection. ShellMeshDataCollection.ProvideShellMeshDataCollection(...) retrieve's the Project's instance. From there, TryCreateFromShell3D(...) is used to make or fetch a ShellMeshData. TryCreateFromShell3D(...) takes a IShell3DData as a parameter. This IShell3DData can be implemented as a simple argument to create from raw data. It can also be an existing IFCMesh, which will have its data copied.
Create a ShellMeshInstance by calling Add<ShellMeshInstance>() on IBIMEntity (which themselves are createable in each other or at the "root level" in BIMEntityCollection.) Then, call ShellMeshInstance.CreateShell(...) passing in the ShellMeshData's SerialNumber as well as a Color and transform (a Matrix4D).
From IFCMesh to ShellMeshInstance: Example code (in C#)
internal ShellMeshInstance CopyFromIfcMesh(IProject project, IEntityRecord entityRecord, IGeometryInstanceRecord instRec, IFCMesh original)
{
ShellMeshData meshData;
var bimEntity = original.GetSite() as IBIMEntity;
if (bimEntity == null)
throw new InvalidOperationException();
if (bimEntity.Layer == 0) // If the BIMEntity's Layer is unset
bimEntity.Layer = original.Layer; // Take the original mesh's Layer
// Create (or fetch) the ShellMeshData
var dataCollection = ShellMeshDataCollection.ProvideShellMeshDataCollection(project, true);
if (dataCollection.TryCreateFromShell3D(original, 0, out meshData))
{
// A new ShellMeshData was created.
// Nothing to do, though.
// "meshData" is set either way.
}
// Assemble the ShellMeshInstance
Matrix4D meshTransformation = original.Transformation;
ShellMeshInstance meshInstance = bimEntity.Add<ShellMeshInstance>();
meshInstance.CreateShell(0, meshData.SerialNumber, original.Color, meshTransformation);
// Delete the "original" and replace it with the new instance
project.ReplaceEntity(original.SerialNumber, new uint[] { meshInstance.SerialNumber });
return meshInstance;
}
TBC 5.90 to 2023.12 continued
BIMEntity
In addition to the changes to mesh representation, BIMEntitys also stores linework internally. BIMEntity.AddLineSegment(...) and BIMEntity.AddGridLineSegment(...) are used to add straight line segments.
BIMEntity.LineSegments is a List<ILineSegmentData>.
About ILineSegmentData: If the segment is given a "Name" it's considered a grid line. When we import grids, they'll have a one-character label (e.g. "A") which we do not display in the views in TBC, but we do store for later export. All of these segments have exactly 2 Vertices for now. When Arcs get added to BIMEntity's, we'll be seeing changes to this class.
Along with the update from the IFCMesh standard to ShellMeshInstances, Trimble.Vce.ForeignCad.Lines that would be added to a BIMEntity should be, instead, added by the BIMEntity.AddLineSegment(...) or .AddGridLineSegment(...). As of TBC 2024.10, there is no equivalent for Trimble.Vce.ForeignCad.Arcs. If you need to make an Arc, you can continue to do so with BIMEntity.Add<Arc>().
BIMEntitys now have both a LineColor and MeshColor field.
BIMEntity.MeshColor, by default, is set to TrimbleColorCodeValues.AsImported (namespace: Trimble.Vce.Graphics).
This allows each contained ShellMeshInstance to define its own color.
IfcDataBlock
Imported IFC or TRB data from older (<= 5.90) VCEs will try to update to the latest schema. If a consistent transformation cannot be determined, the user will be given the option to reset to the original transformation (or try the most-likely correct transformation) with this popup.
You can type in the hidden command in the command pane (F12) to get this popup to re-appear if appropriate: UpdateBIMDataCmd
Additional info
Each ShellMeshInstance has a randomly assigned (but persistent) Guid. This is used to distinguish between ShellMeshInstances inside of a BIMEntity. This is used by the graphics engine to differentiate the ShellMeshInstances from the parent (BIMEntity) selection.
Used in the following methods:
SubselectionTriangleList (namespace: Trimble.Vce.Interfaces.SubSelection)
Used in InternalSelect.
Access via: GlobalSelect.Items(...) : ISubSelectionOfEntitiesAccess
BIMEntity.GetTrianglesForInspection(...)
TBC 2023.12 to 2024.10
Namespace: Trimble.Vce.Interfaces
BIMEntity:
Added to LineSegments:
`bool HasName {get;}`
`public GetPolyseg(Matrix4D transform)`
GetVisualSnaps(...) now has support for End points, Mid points, and Intersections.
GetIntersectionPolysegs() is properly implemented.
To examine the topology/shape of mesh data selected by the user, the following methods/interfaces were added. More information is available via the built-in XML documentation.
Many IShell3D classes got a GetVerticesOfFaces(...) method.
Added: IShell3DDataExtended with the GetFaceCountIndicies(...) method.
Added to IShell3DData: GetShellHashCode(), used to optimize comparisons between meshes.