Files

662 lines
14 KiB
C++

// Apache 2.0 License
// C ABI wrapper around engine_web-ifc for P/Invoke from ifc-dotnet.
#include <algorithm>
#include <array>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <exception>
#include <filesystem>
#include <fstream>
#include <memory>
#include <vector>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <combaseapi.h>
#include <eh.h>
#include <windows.h>
#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<double> vertexData;
std::vector<uint32_t> indexData;
Color color;
uint32_t id;
std::array<double, 16> transform;
explicit Mesh(uint32_t id)
: id(id), transform({})
{
}
};
struct NativeGeometry
{
uint32_t id;
std::vector<std::unique_ptr<Mesh>> 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<std::unique_ptr<NativeGeometry>> geometryStorage;
std::vector<NativeGeometry*> geometryList;
std::unordered_map<uint32_t, NativeGeometry*> 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<NativeGeometry>(eId);
for (auto& placedGeom : flatMesh.geometries)
{
totalMeshes++;
try
{
auto mesh = std::make_unique<Mesh>(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<int>(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<wmanager::ModelManager> manager;
wmanager::LoaderSettings settings;
std::vector<std::unique_ptr<Model>> loadedModels;
Api()
: manager(std::make_unique<wmanager::ModelManager>(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<Model>(
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<int>(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<int>(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<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* 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<int>(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<int>(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<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 static_cast<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;
}
}
#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<wchar_t*>(CoTaskMemAlloc((len + 1) * sizeof(wchar_t)));
if (!result)
{
return nullptr;
}
for (size_t i = 0; i < len; i++)
{
result[i] = static_cast<wchar_t>(version[i]);
}
result[len] = L'\0';
return result;
}