BimRv SDK: Export to IFC

Ilya Kovaldov

December 03, 2021

Introduction

This article describes how to export from BimRv to IFC using the BmIfcExportEx application and TB_IfcExport library.

You can export the BimRv element parameters in the form of IfcPropertySets, and you can obtain, calculate and create IfcCommonProperties and IfcQuantities. You can also export a set of elements instead of the whole model and filter those elements by visibility in a view. The examples below show how to set up the export process in TB_IfcExport by modifying the BmIfcExportEx code.

Export only Visible Elements

Sometimes the document may contain views and plans with elements that you don't want to export. For example, the following file has unwanted elements:

 

export revit to ifc

 

In this case, you can modify the BmIfcExportEx code to filter the unwanted elements and to only export the 3D elements that are visible in the view.

First, add the necessary headers.

#include "Database/Managers/BmViewTable.h"
#include "Database/Entities/BmDBView.h"
#include "ExBimCommandContext.h"
#include "ExStringIO.h"
#include "Ed/EdCommandStack.h"

Next, implement a function that finds a BmDBView element or creates it if necessary using the BmDBView3dCreate command.

OdBmObjectId get3DViewId(OdBmDatabase* pDb)
{
  const OdChar* strDefaultViewName = L"{3D}";

  OdBmViewTablePtr pViewTable = pDb->getAppInfo(OdBm::ManagerType::ViewTable);
  OdBmObjectId idView = pViewTable->findViewIdByName("{3D}");
  
  // in case when DB doesn't contain {3D} view creating own view to collect visible elements
  if (idView.isNull())
  {
    ::odrxDynamicLinker()->loadModule(OdBmCommands);

    OdSmartPtr<ExStringIO> pStringIO = ExStringIO::create(L"{3D} n");
    OdBmCommandContextPtr pDbCmdCtx = ExBimCommandContext::createObject(pStringIO, pDb);
    ::odedRegCmds()->executeCommand(L"BmDBView3dCreate", pDbCmdCtx);
    idView = pViewTable->findViewIdByName("{3D}");
  }
  return idView;
}

In the main function, call the function implemented above, turn on the VisibleOnly option, and set the view ID of the FilterViewId option in the export properties. Finally, initiate the export.

// Only export elements visible in the chosen view
    
OdBmObjectId viewId = get3DViewId(pDb);
exporter->properties()->putAt("VisibleOnly", OdRxVariantValue(true));
OdUInt64 filterViewId = (OdUInt64)viewId.getHandle();
exporter->properties()->putAt("FilterViewId", OdRxVariantValue(filterViewId));

// Export BIM file

TB_IFCEXPORT::OdIfcExport::ExportResult expRes = exporter->exportIfc();
if (TB_IFCEXPORT::OdIfcExport::success == expRes)
  odPrintConsoleString(L"\nExport success...\n");

After the modified export, the unwanted elements are filtered out.

 

revit ifc export

Export a Set of Elements

You can export a set of elements instead of the whole model, for example, you might want to export only one element, export a set of elements located on the same level, or export all elements except walls. The following code sample shows how to modify the BmIfcExportEx application to export only the elements located on a chosen level.

#include "Database/Managers/BmLevelPlanViewTracking.h"
#include "HostObj/Entities/BmRoof.h"
#include "Family/Entities/BmFamilyInstance.h"
#include "StairsRamp/Entities/BmStairsElement.h"
#include "Structural/Entities/BmTrussElement.h"
#include "MEP/Entities/BmRbsCurve.h"

Next, implement a class with filter rules to filter elements by the associated level ID.

struct c_evalElem : std::unary_function<OdBmElementPtr, bool> {
  c_evalElem(const OdBmObjectId& assocLevelId) : oAssocLevelId(assocLevelId) {}
  virtual bool operator()(const OdBmElementPtr& pElem) const {
    OdBmObjectId levelId = pElem->getAssocLevelId();
    if (!levelId.isNull())
      return (levelId == oAssocLevelId);

    OdBmElementPtr pElemToCheck = pElem;
    OdArray<OdBm::BuiltInParameter::Enum> priortizedParameterList;
    if (pElem->isA() == OdBmFamilyInstance::desc())
    {
      const OdBmFamilyInstance* pFam = static_cast(pElem.get());
      // If this is a nested family, check the top-level instance for the level parameter information.
      OdBmObjectId elemToCheckId = pFam->getSuperInstanceId();
      if (!elemToCheckId.isNull())
        pElemToCheck = elemToCheckId.safeOpenObject();

      priortizedParameterList.append(OdBm::BuiltInParameter::FAMILY_BASE_LEVEL_PARAM);
      priortizedParameterList.append(OdBm::BuiltInParameter::INSTANCE_SCHEDULE_ONLY_LEVEL_PARAM);
      priortizedParameterList.append(OdBm::BuiltInParameter::INSTANCE_REFERENCE_LEVEL_PARAM);
    }
    else if (pElem->isA() == OdBmRoof::desc())
      priortizedParameterList.append(OdBm::BuiltInParameter::ROOF_CONSTRAINT_LEVEL_PARAM);
    else if (pElem->getHeaderCategoryId() == OdBm::BuiltInCategory::OST_Stairs)
      priortizedParameterList.append(OdBm::BuiltInParameter::STAIRS_BASE_LEVEL_PARAM);
    else if (pElem->isA() == OdBmTrussElement::desc())
      priortizedParameterList.append(OdBm::BuiltInParameter::TRUSS_ELEMENT_REFERENCE_LEVEL_PARAM);

    for (OdBm::BuiltInParameter::Enum levelParameterVal : priortizedParameterList)
    {
      if (eOk == pElemToCheck->getParam(levelParameterVal, levelId))
      {
        if (!levelId.isNull())
          return (levelId == oAssocLevelId);
      }
    }

    if (pElemToCheck->isKindOf(OdBmRbsCurve::desc()))
    {
      const OdBmRbsCurve* pRbsCurve = static_cast(pElemToCheck.get());
      OdBmObjectId levelId = pRbsCurve->getStartLevelId();
      if (!levelId.isNull())
        return levelId == oAssocLevelId;
    }

    return false;
  }
protected:
  OdBmObjectId oAssocLevelId;
};

Next, implement a function to get all elements with the associated level ID from the database.

OdUInt64Array getLevelElementIds(OdBmDatabase* pDb, unsigned int nLevelIndex)
{
  OdUInt64Array elemIds;
  OdBmMap<OdBmObjectId, OdBmSet<OdBmObjectId> > mapLevelIds;
  const OdBmLevelPlanViewTrackingPtr pLevelTable = pDb->getAppInfo(OdBm::ManagerType::LevelPlanViewTracking);
  pLevelTable->getLevelIdToPlanViewIds(mapLevelIds);

  unsigned int nIndex = 0;
  if (mapLevelIds.size() < nLevelIndex)
    nLevelIndex = mapLevelIds.size() - 1;
  
  for (const auto& pair : mapLevelIds)
  {
    if (nIndex != nLevelIndex)
    {
      nIndex++;
      continue;
    }
    auto pElementsIt = pDb->newElemTableIterator()->filter(c_evalElem(pair.first));
    for (auto pElem : pElementsIt)
      elemIds.append((OdUInt64)pElem->objectId().getHandle());

    break;
  }
  return elemIds;
}

Finally, in the main function, call the implemented function with the level index, set the received IDs to the ElementsToExport option, and then initiate the export.

// Chosen elements to export

OdUInt64Array ids = getLevelElementIds(pDb, 3);
exporter->properties()->putAt(L"ElementsToExport", OdRxVariantValue(ids));

// Export BIM file

TB_IFCEXPORT::OdIfcExport::ExportResult expRes = exporter->exportIfc();
if (TB_IFCEXPORT::OdIfcExport::success == expRes)
  odPrintConsoleString(L"\nExport success...\n");

After the export, you have only one level of elements in the IFC model.

export ifc

IfcPropertySets and IfcQuantities

There are three options used for exporting to IFC:

  • BimRvPropSets &mdash Exports BimRv element parameters as IfcProperties.
  • IfcCommonPropSets &mdash Calculates and exports IfcCommonPropertySets.
  • BaseQuantities &mdash Calculates and exports IfcQuantities.

The following example shows how to modify the BmIfcExportEx application to turn on all export properties.

// Property sets and quantities export
    
exporter->properties()->putAt(L"IfcCommonPropSets", OdRxVariantValue(true));
exporter->properties()->putAt(L"BimRvPropSets", OdRxVariantValue(true));
exporter->properties()->putAt(L"BaseQuantities", OdRxVariantValue(true));

// Export BIM file

TB_IFCEXPORT::OdIfcExport::ExportResult expRes = exporter->exportIfc();
if (TB_IFCEXPORT::OdIfcExport::success == expRes)
  odPrintConsoleString(L"\nExport success...\n");

After the export you can check the properties in the right panel of the OpenIfcViewer application.