This is a sample project to demonstrate how to use web-ifc (https://github.com/ThatOpen/engine_web-ifc) in a native (non WASM) C++ project.
It demonstrates how to load an IFC file, traverse geometry & meta data, with super high performance & super low memory footprint
This repository contains project files for VS, to compile the application on Windows.
Compiling on Linux/Mac OS is also possible, but currently not part of the repository.
All external dependencies are in src/external, and already referenced in the VS project. No need to build those as separate libraries.
Check out a full IFC 3D viewer using the same technology: https://github.com/ifcquery/ifcsplitandmerge

Web-ifc has a so called tape reader as internal data structure, i.e. not an object oriented model. Web-ifc parses the STEP file content by loading the file content in chunks, then replacing the entity IDs with a uint32_t, the data type string with a CRC type code (uint32_t), and then the arguments according to their type.
So the text file content turns into a mix of binary 1-byte tokens, binary values like uint32_t for IDs, double values for floating point numbers, text remains text.
This approach results in a much smaller memory footprint compared to an object oriented model, and loading is much faster.
With the entity ID as offset, the loader jumps to any entity. With an offset [0, n] (n=number of arguments), the argument content can be read.
Here is a basic example how to open an IFC file and read directly from the tape (token stream):
struct LoaderSettings {
bool COORDINATE_TO_ORIGIN = false;
uint16_t CIRCLE_SEGMENTS = 12;
uint32_t TAPE_SIZE = 67108864; // 64 MByte, probably no need for anyone other than web-ifc devs to change this
uint32_t MEMORY_LIMIT = UINT64_MAX * 0.5;
uint16_t LINEWRITER_BUFFER = 10000;
};
webifc::schema::IfcSchemaManager schemaManager;
LoaderSettings fileLoadingSettings;
webifc::parsing::IfcLoader loader (fileLoadingSettings.TAPE_SIZE, fileLoadingSettings.MEMORY_LIMIT, fileLoadingSettings.LINEWRITER_BUFFER, schemaManager);
std::string fileName = "IfcOpenHouse_IFC4.ifc";
std::filesystem::path pathToFile = std::filesystem::absolute(fileName);
if (!std::filesystem::exists(pathToFile)) {
std::cout << "File does not exist: " + pathToFile.string();
return 0;
}
size_t fileSize = std::filesystem::file_size(pathToFile);
loader.LoadFile([&](char* dest, size_t sourceOffset, size_t destSize) {
// this lambda is called for each chunk, so the content can be read from any source, stream etc.
size_t length = std::min(static_cast<size_t>(fileSize - sourceOffset), destSize);
std::ifstream file(pathToFile, std::ios::binary);
if (file) {
file.seekg(sourceOffset);
file.read(dest, length);
return static_cast<size_t>(file.gcount());
}
return size_t(0);
});
std::vector<uint32_t> IfcProjectEntities = loader.GetExpressIDsWithType(webifc::schema::IFCPROJECT);
if (IfcProjectEntities.size() == 0) {
std::cout << "No IfcProject entity found in the file.";
return -1;
}
// IfcProject ----------------------------------------------------------------------------
// IfcGloballyUniqueId GlobalId;
// IfcOwnerHistory OwnerHistory; //optional
// IfcLabel Name; //optional
// IfcText Description; //optional
// IfcLabel ObjectType; //optional
// IfcLabel LongName; //optional
// IfcLabel Phase; //optional
// std::vector<IfcRepresentationContext> RepresentationContexts; //optional
// IfcUnitAssignment UnitsInContext; //optional
uint32_t ifcProjectID = IfcProjectEntities[0];
std::string projectGUID = "";
std::string projectName = "DefaultProject";
// #12=IFCPROJECT('2Xw1_iVsn7OudAGdIJr3pp',#5,'IfcOpenHouse',$,$,$,$,(#41,#47),#11);
loader.MoveToArgumentOffset(ifcProjectID, 0); // GlobalId is argument 0
auto tokenTypeGUID = loader.GetTokenType();
loader.StepBack();
if (tokenTypeGUID == webifc::parsing::STRING) {
projectGUID = loader.GetStringArgument(); // read string "2Xw1_iVsn7OudAGdIJr3pp"
}
loader.MoveToArgumentOffset(ifcProjectID, 2); // Name is argument 2
auto tokenTypeName = loader.GetTokenType();
loader.StepBack();
if (tokenTypeName == webifc::parsing::STRING) {
projectName = loader.GetStringArgument(); // read string "IfcOpenHouse"
}
// load project hierarchy: child-parent relations
std::unordered_map<uint32_t, std::unordered_map<uint32_t, uint32_t> > mapElement2Children; // [parentID] = { childID, relationID };
std::unordered_map<uint32_t, std::pair<uint32_t, uint32_t> > mapElement2ParentRelationObject; // [elementEntityID] = { parentID, relationID };
std::vector<uint32_t> relAggregates = loader.GetExpressIDsWithType(webifc::schema::IFCRELAGGREGATES);
// IfcRelAggregates -----------------------------------------------------------
// IfcGloballyUniqueId GlobalId;
// IfcOwnerHistory OwnerHistory; //optional
// IfcLabel Name; //optional
// IfcText Description; //optional
// IfcObjectDefinition RelatingObject; 4
// std::vector<IfcObjectDefinition> RelatedObjects; 5
for (auto relationID : relAggregates) {
uint32_t parentAttributeNumber = 4;
loader.MoveToArgumentOffset(relationID, parentAttributeNumber);
auto tokenTypeParentEntity = loader.GetTokenType();
loader.StepBack();
if (tokenTypeParentEntity != webifc::parsing::REF) {
// The argument is non-optional, but sometimes it is missing in IFC files anyway. Can be ignored safely
continue;
}
uint32_t parentTag = loader.GetRefArgument();
uint32_t setOfChildrenAttributeNumber = 5; // IfcRelContainedInSpatialStructure: 4
loader.MoveToArgumentOffset(relationID, setOfChildrenAttributeNumber);
auto tokenTypeChildren = loader.GetTokenType();
loader.StepBack();
if (tokenTypeChildren != webifc::parsing::SET_BEGIN) {
// could be SET_END in case of an empty set
continue;
}
const std::vector<uint32_t> RelatedElementsTapeOffsets = loader.GetSetArgument();
for (const uint32_t& tapeOffset : RelatedElementsTapeOffsets) {
uint32_t elementExpressID = loader.GetRefArgument(tapeOffset);
uint32_t elementType = loader.GetLineType(elementExpressID);
mapElement2ParentRelationObject[elementExpressID] = { parentTag, relationID };
mapElement2Children[parentTag].emplace(elementExpressID, relationID);
}
}Download, open the sln file, compile & run:
Support and implementation services for web-ifc in desktop applications, as well as 3D graphics and Qt UI are available on www.IfcSplitAndMerge.com
There is also the existing web based application ecosystem and community for web-ifc: https://github.com/ThatOpen/engine_web-ifc