// Apache 2.0 License // Author: Christopher Diggins of Ara 3D Inc for Speckle Systems Ltd. // C++ wrapper around the Web-IFC component library for P/Invoke // ABI: NuGet-compatible (NO Api* first parameter on accessor functions) #include #include #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #define NOMINMAX #include #include #include #endif #include #include #include #include #include #include #include #include "../engine_web-ifc/src/cpp/modelmanager/ModelManager.h" #include "../engine_web-ifc/src/cpp/version.h" #include #include using namespace webifc::manager; using namespace webifc::parsing; using namespace webifc::geometry; using namespace webifc::schema; // Forward declarations class Model; class Api; class Mesh; class Geometry; class Vertex; // ===================================================================== // Exported C functions — NuGet-compatible ABI // NuGet calls: GetNumMeshes(IntPtr geometry), NOT GetNumMeshes(IntPtr api, IntPtr geometry) // Only InitializeApi/FinalizeApi/LoadModel take Api* parameter // ===================================================================== extern "C" { // Lifecycle (take Api*) __declspec(dllexport) Api* InitializeApi(); __declspec(dllexport) void FinalizeApi(Api* api); __declspec(dllexport) Model* LoadModel(Api* api, const wchar_t* fileName); // Model functions (take Model* only) __declspec(dllexport) int GetNumGeometries(Model* model); __declspec(dllexport) ::Geometry* GetGeometryFromId(Model* model, uint32_t id); __declspec(dllexport) ::Geometry* GetGeometryFromIndex(Model* model, int32_t index); __declspec(dllexport) uint32_t GetMaxId(Model* model); __declspec(dllexport) const char* GetLineFromModel(Model* model, uint32_t id); // Geometry functions (take Geometry* only) __declspec(dllexport) int GetNumMeshes(::Geometry* geom); __declspec(dllexport) Mesh* GetMesh(::Geometry* geom, int index); __declspec(dllexport) uint32_t GetGeometryId(::Geometry* geom); __declspec(dllexport) uint32_t GetGeometryType(::Geometry* geom); __declspec(dllexport) uint32_t GetLineId(::Geometry* geom); __declspec(dllexport) uint32_t GetLineType(::Geometry* geom); __declspec(dllexport) const char* GetLineArguments(::Geometry* geom, uint32_t lineId); // Mesh functions (take Mesh* only) __declspec(dllexport) int GetNumVertices(Mesh* mesh); __declspec(dllexport) Vertex* GetVertices(Mesh* mesh); __declspec(dllexport) int GetNumIndices(Mesh* mesh); __declspec(dllexport) uint32_t* GetIndices(Mesh* mesh); __declspec(dllexport) double* GetTransform(Mesh* mesh); __declspec(dllexport) double* GetColor(Mesh* mesh); __declspec(dllexport) uint32_t GetMeshId(Mesh* mesh); // Version __declspec(dllexport) wchar_t* WebIfcGetVersion(); } // Vertex data structure struct Vertex { double Vx, Vy, Vz; double Nx, Ny, Nz; }; // Color data struct Color { double R, G, B, A; Color() : R(0), G(0), B(0), A(0) {} Color(double r, double g, double b, double a) : R(r), G(g), B(b), A(a) {} }; struct Mesh { // Own copies of geometry data (NOT pointers into engine internals!) std::vector vertexData; std::vector indexData; Color color; uint32_t id; std::array transform; Mesh(uint32_t id) : id(id), transform({}), color() { } }; struct Geometry { uint32_t id; IfcFlatMesh* flatMesh; std::vector meshes; Geometry(uint32_t id) : id(id), flatMesh(nullptr) {} }; // SEH-to-C++ exception translator class seh_exception : public std::exception { unsigned int _code; public: 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); } }; // Model class struct Model { uint32_t id; IfcLoader* loader; IfcGeometryProcessor* geometryProcessor; std::vector<::Geometry*> geometryList; std::unordered_map geometries; Model(IfcSchemaManager* schemas, IfcLoader* loader, IfcGeometryProcessor* processor, uint32_t id) : loader(loader), geometryProcessor(processor), id(id) { SEHGuard sehGuard; int totalElements = 0, skippedDupes = 0, skippedErrors = 0; int totalMeshes = 0, meshErrors = 0, emptyMeshes = 0; size_t totalVerts = 0; for (auto type : schemas->GetIfcElementList()) { if (type == IFCOPENINGELEMENT || type == IFCSPACE || type == IFCOPENINGSTANDARDCASE) { continue; } for (auto eId : loader->GetExpressIDsWithType(type)) { totalElements++; if (geometries.count(eId) > 0) { skippedDupes++; continue; } try { auto flatMesh = processor->GetFlatMesh(eId); auto g = new ::Geometry(eId); for (auto& placedGeom : flatMesh.geometries) { totalMeshes++; try { auto mesh = new Mesh(placedGeom.geometryExpressID); mesh->color = Color(placedGeom.color.r, placedGeom.color.g, placedGeom.color.b, placedGeom.color.a); auto& srcGeom = processor->GetGeometry(placedGeom.geometryExpressID); mesh->vertexData = srcGeom.vertexData; mesh->indexData = srcGeom.indexData; mesh->transform = placedGeom.flatTransformation; totalVerts += mesh->vertexData.size(); if (mesh->vertexData.empty()) emptyMeshes++; g->meshes.push_back(mesh); } catch (...) { meshErrors++; } } geometries[eId] = g; geometryList.push_back(g); } catch (...) { skippedErrors++; } } } fprintf(stderr, "GEOM_DEBUG: elements=%d dupes=%d errors=%d geometries=%d meshes=%d meshErrors=%d emptyMeshes=%d totalVerts=%zu\n", totalElements, skippedDupes, skippedErrors, (int)geometryList.size(), totalMeshes, meshErrors, emptyMeshes, totalVerts); } ::Geometry* GetGeometry(uint32_t id) { auto it = geometries.find(id); if (it == geometries.end()) return nullptr; return it->second; } Mesh* ToMesh(IfcPlacedGeometry& pg) { auto r = new Mesh(pg.geometryExpressID); r->color = Color(pg.color.r, pg.color.g, pg.color.b, pg.color.a); auto& srcGeom = geometryProcessor->GetGeometry(pg.geometryExpressID); r->vertexData = srcGeom.vertexData; r->indexData = srcGeom.indexData; r->transform = pg.flatTransformation; return r; } }; struct Api { ModelManager* manager; IfcSchemaManager* schemaManager; LoaderSettings* settings; Api() { schemaManager = new IfcSchemaManager(); manager = new ModelManager(false); manager->SetLogLevel(6); settings = new webifc::manager::LoaderSettings(); } Model* LoadModel(const wchar_t* fileName) { auto modelId = manager->CreateModel(*settings); auto loader = manager->GetIfcLoader(modelId); std::ifstream ifs; std::wstring wpath(fileName); std::string narrowPath(wpath.begin(), wpath.end()); ifs.open(narrowPath, std::ifstream::in | std::ios::binary); if (!ifs.is_open()) return nullptr; loader->LoadFile(ifs); return new ::Model(schemaManager, loader, manager->GetGeometryProcessor(modelId), modelId); } }; // ===================================================================== // Exported function implementations // ===================================================================== // --- Lifecycle --- Api* InitializeApi() { fprintf(stderr, "NATIVE_TRACE: InitializeApi called\n"); fflush(stderr); SEHGuard g; try { auto a = new Api(); fprintf(stderr, "NATIVE_TRACE: InitializeApi -> %p\n", (void*)a); fflush(stderr); return a; } catch (...) { return nullptr; } } void FinalizeApi(Api* api) { if (!api) return; SEHGuard g; try { delete api->manager; delete api->schemaManager; delete api->settings; delete api; } catch (...) {} } Model* LoadModel(Api* api, const wchar_t* fileName) { if (!api || !fileName) return nullptr; SEHGuard g; try { auto m = api->LoadModel(fileName); if (m) fprintf(stderr, "NATIVE_DEBUG: LoadModel OK, geometries=%d\n", (int)m->geometries.size()); else fprintf(stderr, "NATIVE_DEBUG: LoadModel returned NULL\n"); fflush(stderr); return m; } catch (...) { fprintf(stderr, "NATIVE_DEBUG: LoadModel CRASHED\n"); fflush(stderr); return nullptr; } } // --- Model functions --- int GetNumGeometries(Model* model) { if (!model) return 0; SEHGuard g; try { return (int)model->geometries.size(); } catch (...) { return 0; } } ::Geometry* GetGeometryFromId(Model* model, uint32_t id) { if (!model) return nullptr; SEHGuard g; try { return model->GetGeometry(id); } catch (...) { return nullptr; } } ::Geometry* GetGeometryFromIndex(Model* model, int32_t index) { if (!model) return nullptr; SEHGuard g; try { if (index < 0 || index >= (int32_t)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* gg : model->geometryList) { if (gg && gg->id > maxId) maxId = gg->id; } return maxId; } catch (...) { return 0; } } const char* GetLineFromModel(Model* model, uint32_t lineId) { return ""; } // --- Geometry functions --- int GetNumMeshes(::Geometry* geom) { if (!geom) return 0; SEHGuard g; try { return (int)geom->meshes.size(); } catch (...) { return 0; } } Mesh* GetMesh(::Geometry* geom, int index) { if (!geom) return nullptr; SEHGuard g; try { if (index < 0 || index >= (int)geom->meshes.size()) return nullptr; return geom->meshes[index]; } catch (...) { return nullptr; } } uint32_t GetGeometryId(::Geometry* geom) { if (!geom) return 0; SEHGuard g; try { return geom->id; } catch (...) { return 0; } } uint32_t GetGeometryType(::Geometry* geom) { return 0; } uint32_t GetLineId(::Geometry* geom) { return geom ? geom->id : 0; } uint32_t GetLineType(::Geometry* geom) { return 0; } const char* GetLineArguments(::Geometry* geom, uint32_t lineId) { return ""; } // --- Mesh functions --- int GetNumVertices(Mesh* mesh) { if (!mesh) return 0; SEHGuard g; try { return (int)(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 (int)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; } } // --- Version --- #pragma comment(linker, "/EXPORT:GetVersion=WebIfcGetVersion") wchar_t* WebIfcGetVersion() { const wchar_t* ver = L"0.0.54"; size_t len = (wcslen(ver) + 1) * sizeof(wchar_t); wchar_t* result = (wchar_t*)CoTaskMemAlloc(len); if (result) wcscpy_s(result, wcslen(ver) + 1, ver); return result; }