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
410 lines
13 KiB
C++
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;
|
|
}
|