// Apache 2.0 License // C ABI wrapper around engine_web-ifc for P/Invoke from ifc-dotnet. #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #define NOMINMAX #include #include #include #endif #include "../engine_web-ifc/src/cpp/version.h" #include "../engine_web-ifc/src/cpp/web-ifc/modelmanager/ModelManager.h" namespace wgeom = webifc::geometry; namespace wmanager = webifc::manager; namespace wparse = webifc::parsing; namespace wschema = webifc::schema; class Api; struct Model; struct NativeGeometry; struct Mesh; struct Vertex; #ifdef _WIN32 #define WEBIFC_EXPORT __declspec(dllexport) #else #define WEBIFC_EXPORT #endif extern "C" { WEBIFC_EXPORT Api* InitializeApi(); WEBIFC_EXPORT void FinalizeApi(Api* api); WEBIFC_EXPORT Model* LoadModel(Api* api, const wchar_t* fileName); WEBIFC_EXPORT int GetNumGeometries(Model* model); WEBIFC_EXPORT NativeGeometry* GetGeometryFromId(Model* model, uint32_t id); WEBIFC_EXPORT NativeGeometry* GetGeometryFromIndex(Model* model, int32_t index); WEBIFC_EXPORT uint32_t GetMaxId(Model* model); WEBIFC_EXPORT const char* GetLineFromModel(Model* model, uint32_t id); WEBIFC_EXPORT int GetNumMeshes(NativeGeometry* geom); WEBIFC_EXPORT Mesh* GetMesh(NativeGeometry* geom, int index); WEBIFC_EXPORT uint32_t GetGeometryId(NativeGeometry* geom); WEBIFC_EXPORT uint32_t GetGeometryType(NativeGeometry* geom); WEBIFC_EXPORT uint32_t GetLineId(NativeGeometry* geom); WEBIFC_EXPORT uint32_t GetLineType(NativeGeometry* geom); WEBIFC_EXPORT const char* GetLineArguments(NativeGeometry* geom, uint32_t lineId); WEBIFC_EXPORT int GetNumVertices(Mesh* mesh); WEBIFC_EXPORT Vertex* GetVertices(Mesh* mesh); WEBIFC_EXPORT int GetNumIndices(Mesh* mesh); WEBIFC_EXPORT uint32_t* GetIndices(Mesh* mesh); WEBIFC_EXPORT double* GetTransform(Mesh* mesh); WEBIFC_EXPORT double* GetColor(Mesh* mesh); WEBIFC_EXPORT uint32_t GetMeshId(Mesh* mesh); WEBIFC_EXPORT wchar_t* WebIfcGetVersion(); } struct Vertex { double Vx, Vy, Vz; double Nx, Ny, Nz; }; struct Color { double R = 0; double G = 0; double B = 0; double A = 0; Color() = default; Color(double r, double g, double b, double a) : R(r), G(g), B(b), A(a) { } }; struct Mesh { std::vector vertexData; std::vector indexData; Color color; uint32_t id; std::array transform; explicit Mesh(uint32_t id) : id(id), transform({}) { } }; struct NativeGeometry { uint32_t id; std::vector> meshes; explicit NativeGeometry(uint32_t id) : id(id) { } }; #ifdef _WIN32 class seh_exception : public std::exception { unsigned int _code; public: explicit seh_exception(unsigned int code) : _code(code) { } unsigned int code() const { return _code; } }; static void seh_translator(unsigned int code, EXCEPTION_POINTERS*) { throw seh_exception(code); } struct SEHGuard { _se_translator_function _prev; SEHGuard() : _prev(_set_se_translator(seh_translator)) {} ~SEHGuard() { _set_se_translator(_prev); } }; #else struct SEHGuard { }; #endif struct Model { uint32_t id; wparse::IfcLoader* loader; wgeom::IfcGeometryProcessor* geometryProcessor; std::vector> geometryStorage; std::vector geometryList; std::unordered_map geometries; Model(const wschema::IfcSchemaManager& schemas, wparse::IfcLoader* loader, wgeom::IfcGeometryProcessor* processor, uint32_t id) : id(id), loader(loader), geometryProcessor(processor) { SEHGuard sehGuard; int totalElements = 0; int skippedDupes = 0; int skippedErrors = 0; int totalMeshes = 0; int meshErrors = 0; int emptyMeshes = 0; size_t totalVerts = 0; for (auto type : schemas.GetIfcElementList()) { if (type == wschema::IFCOPENINGELEMENT || type == wschema::IFCSPACE || type == wschema::IFCOPENINGSTANDARDCASE) { continue; } for (auto eId : loader->GetExpressIDsWithType(type)) { totalElements++; if (geometries.count(eId) > 0) { skippedDupes++; continue; } try { auto flatMesh = processor->GetFlatMesh(eId); auto geometry = std::make_unique(eId); for (auto& placedGeom : flatMesh.geometries) { totalMeshes++; try { auto mesh = std::make_unique(placedGeom.geometryExpressID); mesh->color = Color( placedGeom.color.r, placedGeom.color.g, placedGeom.color.b, placedGeom.color.a); auto& srcGeom = processor->GetGeometry(placedGeom.geometryExpressID); srcGeom.GetVertexData(); srcGeom.GetIndexData(); mesh->vertexData = srcGeom.vertexData; mesh->indexData = srcGeom.indexData; mesh->transform = placedGeom.flatTransformation; totalVerts += mesh->vertexData.size(); if (mesh->vertexData.empty()) { emptyMeshes++; } geometry->meshes.push_back(std::move(mesh)); } catch (...) { meshErrors++; } } if (!geometry->meshes.empty()) { auto* geometryPtr = geometry.get(); geometries[eId] = geometryPtr; geometryList.push_back(geometryPtr); geometryStorage.push_back(std::move(geometry)); } } catch (...) { skippedErrors++; } } } std::fprintf( stderr, "GEOM_DEBUG_078: elements=%d dupes=%d errors=%d geometries=%d meshes=%d meshErrors=%d emptyMeshes=%d totalVerts=%zu\n", totalElements, skippedDupes, skippedErrors, static_cast(geometryList.size()), totalMeshes, meshErrors, emptyMeshes, totalVerts); std::fflush(stderr); } NativeGeometry* GetGeometry(uint32_t id) { auto it = geometries.find(id); if (it == geometries.end()) { return nullptr; } return it->second; } }; struct Api { std::unique_ptr manager; wmanager::LoaderSettings settings; std::vector> loadedModels; Api() : manager(std::make_unique(false)) { manager->SetLogLevel(2); settings.COORDINATE_TO_ORIGIN = false; settings.CIRCLE_SEGMENTS = 12; } Model* LoadModel(const wchar_t* fileName) { auto modelId = manager->CreateModel(settings); auto* loader = manager->GetIfcLoader(modelId); std::ifstream ifs(std::filesystem::path(fileName), std::ifstream::in | std::ios::binary); if (!ifs.is_open()) { return nullptr; } loader->LoadFile(ifs); auto model = std::make_unique( manager->GetSchemaManager(), loader, manager->GetGeometryProcessor(modelId), modelId); auto* result = model.get(); loadedModels.push_back(std::move(model)); return result; } }; Api* InitializeApi() { std::fprintf(stderr, "NATIVE_TRACE_078: InitializeApi called\n"); std::fflush(stderr); SEHGuard g; try { return new Api(); } catch (...) { return nullptr; } } void FinalizeApi(Api* api) { if (!api) { return; } SEHGuard g; try { delete api; } catch (...) { } } Model* LoadModel(Api* api, const wchar_t* fileName) { if (!api || !fileName) { return nullptr; } SEHGuard g; try { auto* model = api->LoadModel(fileName); if (model) { std::fprintf(stderr, "NATIVE_DEBUG_078: LoadModel OK, geometries=%d\n", static_cast(model->geometries.size())); } else { std::fprintf(stderr, "NATIVE_DEBUG_078: LoadModel returned NULL\n"); } std::fflush(stderr); return model; } catch (...) { std::fprintf(stderr, "NATIVE_DEBUG_078: LoadModel CRASHED\n"); std::fflush(stderr); return nullptr; } } int GetNumGeometries(Model* model) { if (!model) { return 0; } SEHGuard g; try { return static_cast(model->geometryList.size()); } catch (...) { return 0; } } NativeGeometry* GetGeometryFromId(Model* model, uint32_t id) { if (!model) { return nullptr; } SEHGuard g; try { return model->GetGeometry(id); } catch (...) { return nullptr; } } NativeGeometry* GetGeometryFromIndex(Model* model, int32_t index) { if (!model) { return nullptr; } SEHGuard g; try { if (index < 0 || index >= static_cast(model->geometryList.size())) { return nullptr; } return model->geometryList[index]; } catch (...) { return nullptr; } } uint32_t GetMaxId(Model* model) { if (!model || model->geometryList.empty()) { return 0; } SEHGuard g; try { uint32_t maxId = 0; for (auto* geometry : model->geometryList) { if (geometry && geometry->id > maxId) { maxId = geometry->id; } } return maxId; } catch (...) { return 0; } } const char* GetLineFromModel(Model*, uint32_t) { return ""; } int GetNumMeshes(NativeGeometry* geom) { if (!geom) { return 0; } SEHGuard g; try { return static_cast(geom->meshes.size()); } catch (...) { return 0; } } Mesh* GetMesh(NativeGeometry* geom, int index) { if (!geom) { return nullptr; } SEHGuard g; try { if (index < 0 || index >= static_cast(geom->meshes.size())) { return nullptr; } return geom->meshes[index].get(); } catch (...) { return nullptr; } } uint32_t GetGeometryId(NativeGeometry* geom) { if (!geom) { return 0; } SEHGuard g; try { return geom->id; } catch (...) { return 0; } } uint32_t GetGeometryType(NativeGeometry*) { return 0; } uint32_t GetLineId(NativeGeometry* geom) { return geom ? geom->id : 0; } uint32_t GetLineType(NativeGeometry*) { return 0; } const char* GetLineArguments(NativeGeometry*, uint32_t) { return ""; } int GetNumVertices(Mesh* mesh) { if (!mesh) { return 0; } SEHGuard g; try { return static_cast(mesh->vertexData.size() / 6); } catch (...) { return 0; } } Vertex* GetVertices(Mesh* mesh) { if (!mesh || mesh->vertexData.empty()) { return nullptr; } SEHGuard g; try { return reinterpret_cast(mesh->vertexData.data()); } catch (...) { return nullptr; } } int GetNumIndices(Mesh* mesh) { if (!mesh) { return 0; } SEHGuard g; try { return static_cast(mesh->indexData.size()); } catch (...) { return 0; } } uint32_t* GetIndices(Mesh* mesh) { if (!mesh || mesh->indexData.empty()) { return nullptr; } SEHGuard g; try { return mesh->indexData.data(); } catch (...) { return nullptr; } } double* GetTransform(Mesh* mesh) { if (!mesh) { return nullptr; } SEHGuard g; try { return mesh->transform.data(); } catch (...) { return nullptr; } } double* GetColor(Mesh* mesh) { if (!mesh) { return nullptr; } SEHGuard g; try { return &mesh->color.R; } catch (...) { return nullptr; } } uint32_t GetMeshId(Mesh* mesh) { if (!mesh) { return 0; } SEHGuard g; try { return mesh->id; } catch (...) { return 0; } } #ifdef _WIN32 #pragma comment(linker, "/EXPORT:GetVersion=WebIfcGetVersion") #endif wchar_t* WebIfcGetVersion() { std::string version(WEB_IFC_VERSION_NUMBER); const auto len = version.size(); wchar_t* result = static_cast(CoTaskMemAlloc((len + 1) * sizeof(wchar_t))); if (!result) { return nullptr; } for (size_t i = 0; i < len; i++) { result[i] = static_cast(version[i]); } result[len] = L'\0'; return result; }