Files
huanld 1686f08040
Release pipeline / Get version (push) Has been cancelled
Release pipeline / Get Chart Name (push) Has been cancelled
Release pipeline / tests (push) Has been cancelled
Release pipeline / builds (push) Has been cancelled
Release pipeline / builds-ghcr (push) Has been cancelled
Release pipeline / test-deployments (push) Has been cancelled
Release pipeline / deploy (push) Has been cancelled
Release pipeline / Helm chart oci (push) Has been cancelled
Release pipeline / npm (push) Has been cancelled
Release pipeline / snyk (push) Has been cancelled
feat: commit IFC-toolkit and engine_web-ifc source code
2026-04-16 07:47:58 +07:00

410 lines
13 KiB
C++

// 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 <string>
#include <cstring>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
#include <combaseapi.h>
#include <eh.h>
#endif
#include <algorithm>
#include <vector>
#include <stack>
#include <cstdint>
#include <memory>
#include <locale>
#include <codecvt>
#include "../engine_web-ifc/src/cpp/modelmanager/ModelManager.h"
#include "../engine_web-ifc/src/cpp/version.h"
#include <iostream>
#include <fstream>
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<double> vertexData;
std::vector<uint32_t> indexData;
Color color;
uint32_t id;
std::array<double, 16> transform;
Mesh(uint32_t id)
: id(id), transform({}), color()
{ }
};
struct Geometry
{
uint32_t id;
IfcFlatMesh* flatMesh;
std::vector<Mesh*> 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<uint32_t, ::Geometry*> 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<Vertex*>(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;
}