diff --git a/src/ifcmax/IfcMax.cpp b/src/ifcmax/IfcMax.cpp index 6252e97353..638c44cfd3 100644 --- a/src/ifcmax/IfcMax.cpp +++ b/src/ifcmax/IfcMax.cpp @@ -22,17 +22,44 @@ #include #include +#include +#include +#include +// should fix a iterator missallignment assertion ? +#define IFOPSH_WITH_ROCKSDB + +#include "../ifcgeom/Iterator.h" +#include "../ifcgeom/taxonomy.h" +#include "../ifcgeom/ConversionSettings.h" +#include "../ifcgeom/hybrid_kernel.h" + +#include "resource.h" #include "IfcMax.h" -#include "../ifcgeom_schema_agnostic/IfcGeomIterator.h" -#include "../ifcgeom_schema_agnostic/IfcGeomMaterial.h" -#include "../ifcgeom/IfcGeomElement.h" +// include those at last, as they cause compile errors regarding boost templates +#include +#include + + +static const Class_ID IFCIMP_CLASS_ID = Class_ID(0x3f230dbf, 0x5b3015c2); +static const TSTR IFCIMP_CLASS_NAME = _T("IFCImp"); +static const TSTR IFCIMP_CATEGORY_NAME = _T("Importer Plugins"); static const int NUM_MATERIAL_SLOTS = 24; -BOOL WINAPI DllMain(HINSTANCE /*hinstDLL*/, ULONG /*fdwReason*/, LPVOID /*lpvReserved*/) { +static HINSTANCE hInstance; +static TCHAR *GetString(int id) +{ + static TCHAR buf[256]; + if (hInstance) + return LoadString(hInstance, id, buf, _countof(buf)) ? buf : NULL; + return NULL; +} + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, ULONG /*fdwReason*/, LPVOID /*lpvReserved*/) { static int controlsInit = false; + hInstance = hinstDLL; if (!controlsInit) { controlsInit = true; InitCommonControls(); @@ -45,65 +72,51 @@ public: int IsPublic() { return 1; } void * Create(BOOL /*loading = FALSE*/) { return new IFCImp; } // TODO Delete() function? - const TCHAR * ClassName() { return _T("IFCImp"); } + const TCHAR* ClassName() { return IFCIMP_CLASS_NAME; } + +#if MAX_VERSION_MAJOR >= 24 + const TCHAR * NonLocalizedClassName() { return ClassName(); } +#endif + SClass_ID SuperClassID() { return SCENE_IMPORT_CLASS_ID; } - Class_ID ClassID() { return Class_ID(0x3f230dbf, 0x5b3015c2); } - const TCHAR* Category() { return _T("Chrutilities"); } + Class_ID ClassID() { return IFCIMP_CLASS_ID; } + const TCHAR* Category() { return IFCIMP_CATEGORY_NAME; } } IFCImpDesc; #define DLLEXPORT __declspec(dllexport) extern "C" { - -DLLEXPORT const TCHAR* LibDescription() { - return _T("IfcOpenShell IFC Importer"); -} +#if BUILD_FREE_TIER +DLLEXPORT const TCHAR* LibDescription() { return GetString(IDS_LIBDESCRIPTION_FREE); } +#else + DLLEXPORT const TCHAR* LibDescription() { return GetString(IDS_LIBDESCRIPTION); } +#endif DLLEXPORT int LibNumberClasses() { return 1; } -DLLEXPORT ClassDesc* LibClassDesc(int i) { - return i == 0 ? &IFCImpDesc : 0; -} +DLLEXPORT ClassDesc* LibClassDesc(int i) { return i == 0 ? &IFCImpDesc : 0; } -DLLEXPORT ULONG LibVersion() { - return VERSION_3DSMAX; -} +DLLEXPORT ULONG LibVersion() { return VERSION_3DSMAX; } } // extern "C" int IFCImp::ExtCount() { return 1; } -const TCHAR * IFCImp::Ext(int n) { - return n == 0 ? _T("IFC") : _T(""); -} +const TCHAR * IFCImp::Ext(int n) { return n == 0 ? _T("IFC") : _T(""); } -const TCHAR * IFCImp::LongDesc() { - return _T("IfcOpenShell IFC Importer for 3ds Max"); -} +const TCHAR * IFCImp::LongDesc() { return GetString(IDS_LONG_DESCRIPTION); } -const TCHAR * IFCImp::ShortDesc() { - return _T("Industry Foundation Classes"); -} +const TCHAR * IFCImp::ShortDesc() { return GetString(IDS_SHORT_DESCRIPTION); } -const TCHAR * IFCImp::AuthorName() { - return _T("Thomas Krijnen"); -} +const TCHAR * IFCImp::AuthorName() { return GetString(IDS_AUTHOR_NAME); } -const TCHAR * IFCImp::CopyrightMessage() { - return _T("Copyright (c) 2011-2016 IfcOpenShell"); -} +const TCHAR * IFCImp::CopyrightMessage() { return GetString(IDS_COPYRIGHT_MESSAGE); } -const TCHAR * IFCImp::OtherMessage1() { - return _T(""); -} +const TCHAR * IFCImp::OtherMessage1() { return _T(""); } -const TCHAR * IFCImp::OtherMessage2() { - return _T(""); -} +const TCHAR * IFCImp::OtherMessage2() { return _T(""); } -unsigned int IFCImp::Version() { - return 12; -} +unsigned int IFCImp::Version() { return 25; } // TODO Use this in IFCImp::ShowAbout() if/when wanted //static BOOL CALLBACK AboutBoxDlgProc(HWND /*hWnd*/, UINT /*msg*/, WPARAM /*wParam*/, LPARAM /*lParam*/) { @@ -122,6 +135,40 @@ DWORD WINAPI fn(LPVOID /*arg*/) { return 0; } # define S(x) (CStr(x.c_str())) #endif + +// Function to log messages to the Listener +static void LogToListener(const MCHAR* format, ...) +{ + if (format == nullptr) { + return; + } + + const int BUFFER_SIZE = 2048; + static MCHAR buffer[BUFFER_SIZE]; + + va_list args; + + va_start(args, format); + int result = _vstprintf_s(buffer, BUFFER_SIZE, format, args); + + va_end(args); + +#if BUILD_FREE_TIER + if(result < 0) { + the_listener->edit_stream->printf(_M("IfcImp (Free): Skipped invalid log output !\n")); + return; + } + the_listener->edit_stream->printf(_M("IfcImp (Free): %s"), buffer); +#else + if(result < 0) { + the_listener->edit_stream->printf(_M("IfcImp: Skipped invalid log output !\n")); + return; + } + the_listener->edit_stream->printf(_M("IfcImp: %s"), buffer); +#endif +} + + static Mtl* FindMaterialByName(MtlBaseLib* library, const std::string& material_name) { TSTR mat_name = S(material_name); const int mat_index = library->FindMtlByName(mat_name); @@ -132,27 +179,32 @@ static Mtl* FindMaterialByName(MtlBaseLib* library, const std::string& material_ return m; } -static Mtl* FindOrCreateMaterial(MtlBaseLib* library, Interface* max_interface, int& slot, const IfcGeom::Material& material) { - Mtl* m = FindMaterialByName(library, material.name()); + +static Mtl* FindOrCreateMaterial(MtlBaseLib* library, Interface* max_interface, int& slot, const ifcopenshell::geometry::taxonomy::style::ptr styleptr) { + + auto& style = *styleptr; + std::string material_name = style.name; + + Mtl* m = FindMaterialByName(library, material_name); if (m == 0) { StdMat2* stdm = NewDefaultStdMat(); const TimeValue t = -1; - if (material.hasDiffuse()) { - const double* diffuse = material.diffuse(); - stdm->SetDiffuse(Color(diffuse[0], diffuse[1], diffuse[2]),t); + if (style.diffuse) { + const ifcopenshell::geometry::taxonomy::colour diffuse = style.diffuse; + stdm->SetDiffuse(Color(diffuse.r(), diffuse.g(), diffuse.b()),t); } - if (material.hasSpecular()) { - const double* specular = material.specular(); - stdm->SetSpecular(Color(specular[0], specular[1], specular[2]),t); + if (style.specular) { + const ifcopenshell::geometry::taxonomy::colour specular = style.specular; + stdm->SetSpecular(Color(specular.r(), specular.g(), specular.b()),t); } - if (material.hasSpecularity()) { - stdm->SetShininess((float)material.specularity(), t); + if (style.has_specularity()) { + stdm->SetShininess((float)style.specularity, t); } - if (material.hasTransparency()) { - stdm->SetOpacity(1.0f - (float)material.transparency(), t); + if (style.has_transparency()) { + stdm->SetOpacity(1.0f - (float)style.transparency, t); } m = stdm; - m->SetName(S(material.name())); + m->SetName(S(material_name)); library->Add(m); if (slot < NUM_MATERIAL_SLOTS) { max_interface->PutMtlToMtlEditor(m,slot++); @@ -161,18 +213,22 @@ static Mtl* FindOrCreateMaterial(MtlBaseLib* library, Interface* max_interface, return m; } + static Mtl* ComposeMultiMaterial(std::map, Mtl*>& multi_mats, MtlBaseLib* library, - Interface* max_interface, int& slot, const std::vector& materials, + Interface* max_interface, int& slot, const std::vector styleptrs, const std::string& object_type, const std::vector& material_ids) { std::vector material_names; bool needs_default = std::find(material_ids.begin(), material_ids.end(), -1) != material_ids.end(); + if (needs_default) { material_names.push_back(object_type); } - for (auto it = materials.begin(); it != materials.end(); ++it) { - material_names.push_back(it->name()); + + for (auto it = styleptrs.begin(); it != styleptrs.end(); ++it) { + material_names.push_back( (*it)->name); } + Mtl* default_material = 0; if (needs_default) { default_material = FindMaterialByName(library, object_type); @@ -185,13 +241,15 @@ static Mtl* ComposeMultiMaterial(std::map, Mtl*>& multi } } } + if (material_names.size() == 1) { if (needs_default) { return default_material; } else { - return FindOrCreateMaterial(library, max_interface, slot, *materials.begin()); + return FindOrCreateMaterial(library, max_interface, slot, *styleptrs.begin()); } } + std::map, Mtl*>::const_iterator i = multi_mats.find(material_names); if (i != multi_mats.end()) { return i->second; @@ -202,7 +260,7 @@ static Mtl* ComposeMultiMaterial(std::map, Mtl*>& multi if (needs_default) { multi_mat->SetSubMtlAndName(mtl_id ++, default_material, default_material->GetName()); } - for (auto it = materials.begin(); it != materials.end(); ++it) { + for (auto it = styleptrs.begin(); it != styleptrs.end(); ++it) { Mtl* mtl = FindOrCreateMaterial(library, max_interface, slot, *it); multi_mat->SetSubMtl(mtl_id ++, mtl); } @@ -214,114 +272,326 @@ static Mtl* ComposeMultiMaterial(std::map, Mtl*>& multi return multi_mat; } -int IFCImp::DoImport(const TCHAR *name, ImpInterface *impitfc, Interface *itfc, BOOL /*suppressPrompts*/) { +int IFCImp::DoImport(const TCHAR *file_name, ImpInterface *impitfc, Interface *ip, BOOL noPrompts ) { - IfcGeom::IteratorSettings settings; - settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, false); - settings.set(IfcGeom::IteratorSettings::WELD_VERTICES, true); - settings.set(IfcGeom::IteratorSettings::SEW_SHELLS, true); + ifcopenshell::geometry::Settings settings; + + //settings.get().value = 0.001; + //settings.get().value = 0.5; + + + // should adapt to 3ds Max Systemm units ? + //settings.get().value = 1.0; + //settings.get().value = 1.0; + //settings.get().value = 0.00001; + + //settings.get().value = 1.0; + //settings.get().value = true; + + //settings.get().value = false; + //settings.get().value = false; + //settings.get().value = false; + //settings.get().value = false; + + + settings.get().value = true; // should be true + settings.get().value = true; // should be true + settings.get().value = false; // should be false to get a pivot with correct coordinates + //settings.get().value = false; + //settings.get().value = false; + + + // some settings which seem to make sense + settings.get().value = true; + settings.get().value = true; // should be true + + // ATTENTION: breaks hierarchy positioning when active ( "site-local-placement" ) + settings.get().value = false; // should be FALSE + + // when vertex welding is enabled, normals calculation is internally disabled ( see OpenCascadeConversionResult.cpp:239 ) + settings.get().value = true; // should be true + + settings.get().value = false; + settings.get().value = false; + + + settings.get().value = 32; // should be 32 + settings.get().value = true; + settings.get().value = ifcopenshell::geometry::settings::SURFACES_AND_SOLIDS; // default is SURFACES_AND_SOLIDS #ifdef _UNICODE - int fn_buffer_size = WideCharToMultiByte(CP_UTF8, 0, name, -1, 0, 0, 0, 0); + int fn_buffer_size = WideCharToMultiByte(CP_UTF8, 0, file_name, -1, 0, 0, 0, 0); char* fn_mb = new char[fn_buffer_size]; - WideCharToMultiByte(CP_UTF8, 0, name, -1, fn_mb, fn_buffer_size, 0, 0); + WideCharToMultiByte(CP_UTF8, 0, file_name, -1, fn_mb, fn_buffer_size, 0, 0); #else const char* fn_mb = name; #endif - IfcParse::IfcFile file(fn_mb); - IfcGeom::Iterator iterator(settings, &file); - delete fn_mb; - if (!iterator.initialize()) return false; + IfcParse::IfcFile file(fn_mb); - itfc->ProgressStart(_T("Importing file..."), TRUE, fn, NULL); + IfcParse::file_open_status status = file.good(); - MtlBaseLib* mats = itfc->GetSceneMtls(); + if (status != IfcParse::file_open_status::SUCCESS) { + const MCHAR* reason = status == IfcParse::file_open_status::UNSUPPORTED_SCHEMA + ? _M("Import aborted: unsupported IFC Schema") + : _M("Import aborted: failure parsing IFC file"); + + ip->DisplayTempPrompt( reason, 10000 ); + return false; + } + + auto mapped = file.instances_by_type( "IfcMappedItem" ); + auto annotations = file.instances_by_type("IfcAnnotation"); + auto solids = file.instances_by_type("IfcSolidModel"); + + // cgal kernels produce std::vector incompatiblities + //auto kernel = ifcopenshell::geometry::kernels::construct(&file, "hybrid-cgal-simple-opencascade", settings); + + //auto kernel = ifcopenshell::geometry::kernels::construct(&file, "opencascade", settings); + //IfcGeom::Iterator iterator( std::move( kernel), settings, &file); + + IfcGeom::Iterator iterator( + ifcopenshell::geometry::kernels::construct(&file, "opencascade", settings), + settings, + &file); + + + delete[] fn_mb; + + LogToListener(_M("Importing '%s'...\n"), file_name); + if (!iterator.initialize()) { + if(!iterator.had_error_processing_elements()) { + LogToListener(_M("No elements found in '%s'...\n"), file_name); + return IMPEXP_SUCCESS; + } + + LogToListener(_M("Error processing elements in '%s'...\n"), file_name); + return IMPEXP_FAIL; + } + + ip->ProgressStart(_T("Importing file..."), TRUE, fn, NULL); + + MtlBaseLib* mats = ip->GetSceneMtls(); int slot = mats->Count(); + std::vector impnode_cache; std::map, Mtl*> material_cache; + + + bool wasCanceled=false; + auto startTime = std::chrono::high_resolution_clock::now(); + std::stringstream log_stream; do{ - const IfcGeom::TriangulationElement* o = static_cast*>(iterator.get()); - TSTR o_type = S(o->type()); - TSTR o_guid = S(o->guid()); + auto iter = iterator.get(); + auto prod = iter->product(); - Mtl *m = ComposeMultiMaterial(material_cache, mats, itfc, slot, o->geometry().materials(), o->type(), o->geometry().material_ids()); + const IfcGeom::Element* element = static_cast(iterator.get()); + const IfcGeom::BRepElement* brepElement = static_cast(iterator.get_native()); + const IfcGeom::TriangulationElement* triElement = static_cast(iterator.get()); - TriObject* tri = CreateNewTriObject(); + const TSTR e_type = TSTR::FromUTF8(element->type().c_str()); + const TSTR e_guid = TSTR::FromUTF8(element->guid().c_str()); + const TSTR e_name = TSTR::FromUTF8(element->name().c_str()); + const TSTR e_idStr = TSTR(std::to_wstring(element->id()).c_str()); - const int numVerts = (int)o->geometry().verts().size()/3; - tri->mesh.setNumVerts(numVerts); - for( int i = 0; i < numVerts; i ++ ) { - tri->mesh.setVert(i,o->geometry().verts()[3*i+0],o->geometry().verts()[3*i+1],o->geometry().verts()[3*i+2]); - } - const int numFaces = (int)o->geometry().faces().size()/3; - tri->mesh.setNumFaces(numFaces); - bool needs_default = std::find(o->geometry().material_ids().begin(), o->geometry().material_ids().end(), -1) != o->geometry().material_ids().end(); - - typedef std::pair edge_t; - - std::set face_boundaries; - for(std::vector::const_iterator it = o->geometry().edges().begin(); it != o->geometry().edges().end();) { - const int v1 = *it++; - const int v2 = *it++; - - const edge_t e((std::min)(v1, v2), (std::max)(v1, v2)); - face_boundaries.insert(e); - } - - for( int i = 0; i < numFaces; i ++ ) { - const int v1 = o->geometry().faces()[3*i+0]; - const int v2 = o->geometry().faces()[3*i+1]; - const int v3 = o->geometry().faces()[3*i+2]; - - const edge_t e1((std::min)(v1, v2), (std::max)(v1, v2)); - const edge_t e2((std::min)(v2, v3), (std::max)(v2, v3)); - const edge_t e3((std::min)(v3, v1), (std::max)(v3, v1)); - - const bool b1 = face_boundaries.find(e1) != face_boundaries.end(); - const bool b2 = face_boundaries.find(e2) != face_boundaries.end(); - const bool b3 = face_boundaries.find(e3) != face_boundaries.end(); - - tri->mesh.faces[i].setVerts(v1, v2, v3); - tri->mesh.faces[i].setEdgeVisFlags(b1, b2, b3); - - MtlID mtlid = (MtlID)o->geometry().material_ids()[i]; - if (needs_default) { - mtlid ++; + // dump out the parent tree up to IfcProject for each element + log_stream.str(""); + auto parents = element->parents(); // keep a persistent copy + if (!parents.empty()) { + for(auto it =parents.rbegin(); it != parents.rend(); ++it) { + auto p_e = *it; + log_stream << "->" << p_e->type() << " [#" << p_e->id() << "]"; } - tri->mesh.faces[i].setMatID(mtlid); } - - tri->mesh.buildNormals(); - // Either use this or undefine the FACESETS_AS_COMPOUND option in IfcGeom.h to have - // properly oriented normals. Using only the line below will result in a consistent - // orientation of normals across shells, but not always oriented towards the - // outside. - // tri->mesh.UnifyNormals(false); - tri->mesh.BuildStripsAndEdges(); - tri->mesh.InvalidateTopologyCache(); - tri->mesh.InvalidateGeomCache(); - ImpNode* node = impitfc->CreateNode(); - node->Reference(tri); - node->SetName(o_guid); - node->GetINode()->Hide(o->type() == "IfcOpeningElement" || o->type() == "IfcSpace"); - if (m) { - node->GetINode()->SetMtl(m); - } - const std::vector& matrix_data = o->transformation().matrix().data(); - node->SetTransform(0,Matrix3 ( Point3(matrix_data[0],matrix_data[1],matrix_data[2]),Point3(matrix_data[3],matrix_data[4],matrix_data[5]), - Point3(matrix_data[6],matrix_data[7],matrix_data[8]),Point3(matrix_data[9],matrix_data[10],matrix_data[11]) )); - impitfc->AddNodeToScene(node); + LogToListener(_M("%3d%% - [#%d] %s: '%s' %s\n"), iterator.progress(), element->id(), e_type, e_name, TSTR::FromUTF8( log_stream.str().c_str() ) ); - itfc->ProgressUpdate(iterator.progress(), true, _T("")); + Mtl *mat = ComposeMultiMaterial(material_cache, mats, ip, slot, triElement->geometry().materials(), triElement->type(), triElement->geometry().material_ids()); + + ImpNode* impNode = impitfc->CreateNode(); + + RefResult refSuccess = REF_INVALID; + + auto mesh = BuildMesh(triElement); + + if ( mesh != nullptr) + refSuccess = impNode->Reference(mesh); + + if (refSuccess != REF_SUCCEED) { + LogToListener(_M("Error creating importer reference for imported element #%d.\n"), element->id()); + continue; + } + + TSTR longName; + BuildFullName( *prod, longName); + impNode->SetName(longName); + + const auto& mtx = triElement->transformation().data()->ccomponents(); + impNode->SetTransform(0, Matrix3(Point3(mtx(0, 0), mtx(1, 0), mtx(2, 0)), Point3(mtx(0, 1), mtx(1, 1), mtx(2, 1)), Point3(mtx(0, 2), mtx(1, 2), mtx(2, 2)), Point3(mtx(0, 3), mtx(1, 3), mtx(2, 3)))); + + INode* inode = impNode->GetINode(); + + inode->Hide(triElement->type() == "IfcOpeningElement" || triElement->type() == "IfcSpace"); + + if (mat != nullptr) { + inode->SetMtl(mat); + + // set wirecolor to material color + inode->SetWireColor(mat->GetDiffuse().toRGB()); + } + + // add to the cache, not to the scene, thisd allows a clean exit if cancel was pressed + impnode_cache.push_back(impNode); + + ip->ProgressUpdate(iterator.progress(), true, _T("")); + + if (ip->GetCancel()) { + wasCanceled = noPrompts ? true : VerifyCancel(); + if (wasCanceled) + break; + else + ip->SetCancel(false); + } } while (iterator.next()); - itfc->ProgressEnd(); - - return true; + ip->ProgressEnd(); + + if(wasCanceled) { + LogToListener(_T("Import canceled by User\n")); + return IMPEXP_CANCEL; + } + // add all objects to the scene + for( int i = 0; i < impnode_cache.size(); i++ ) + impitfc->AddNodeToScene(impnode_cache[i]); + + // Calculate the duration + unsigned int seconds = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - startTime).count(); + LogToListener(_M("Import finished in %u seconds\n"), seconds); + + return IMPEXP_SUCCESS; +} + +BOOL IFCImp::VerifyCancel() { +#if MAX_VERSION_MAJOR < 23 + return IDYES == MaxMsgBox(GetCOREInterface()->GetMAXHWnd(), GetString(IDS_IMPORT_CANCEL_MESSAGE), GetString(IDS_IMPORT_CANCEL_CAPTION), MB_YESNO); +#else + // new MaxMessageBox API + return IDYES == MaxSDK::MaxMessageBox(GetCOREInterface()->GetMAXHWnd(), GetString(IDS_IMPORT_CANCEL_MESSAGE), GetString(IDS_IMPORT_CANCEL_CAPTION), MB_YESNO); +#endif +} + + +void IFCImp::BuildFullName( const IfcUtil::IfcBaseEntity& entity, MSTR& long_name) +{ + auto name = TSTR::FromUTF8(entity.get_value("Name", "").c_str()); + + if( name.length() > 0) { + long_name.printf(_M("%s/%s [#%d]"), TSTR::FromUTF8(entity.declaration().name().c_str()), name, entity.id_); + } + else { + long_name.printf(_M("%s [#%d]"), TSTR::FromUTF8(entity.declaration().name().c_str()), entity.id_); + } +} + +TriObject* IFCImp::BuildMesh(const IfcGeom::TriangulationElement* element) { + TriObject* tri = CreateNewTriObject(); + + const IfcGeom::Representation::Triangulation& ios_mesh = element->geometry(); + + const auto& verts = ios_mesh.verts(); + const int numVerts = (int)verts.size() / 3; + + //const auto& normals = ios_mesh.normals(); + // const int numNormals = (int)normals.size() / 3; + + // const auto& uvs = ios_mesh.uvs(); + // const int numUVs = (int)uvs.size() / 3; + + + tri->mesh.setNumVerts(numVerts); + for (int i = 0; i < numVerts; i++) { + tri->mesh.setVert(i, Point3ByIndex(verts, i)); + //if( i < numNormals ) { + // tri->mesh.setNormal(i, Point3ByIndex(normals, i)); + // } + } + + bool needs_default = std::find(ios_mesh.material_ids().begin(), ios_mesh.material_ids().end(), -1) != ios_mesh.material_ids().end(); + + typedef std::pair edge_t; + + std::set face_boundaries; + for (std::vector::const_iterator it = ios_mesh.edges().begin(); it != ios_mesh.edges().end();) { + const int v1 = *it++; + const int v2 = *it++; + + const edge_t e((std::min)(v1, v2), (std::max)(v1, v2)); + face_boundaries.insert(e); + } + + const auto& faces = ios_mesh.faces(); + + const int numFaces = (int)faces.size() / 3; + + tri->mesh.setNumFaces(numFaces); + + for (int i = 0; i < numFaces; i++) { + const int v1 = faces[3 * i + 0]; + const int v2 = faces[3 * i + 1]; + const int v3 = faces[3 * i + 2]; + + const edge_t e1((std::min)(v1, v2), (std::max)(v1, v2)); + const edge_t e2((std::min)(v2, v3), (std::max)(v2, v3)); + const edge_t e3((std::min)(v3, v1), (std::max)(v3, v1)); + + const bool b1 = face_boundaries.find(e1) != face_boundaries.end(); + const bool b2 = face_boundaries.find(e2) != face_boundaries.end(); + const bool b3 = face_boundaries.find(e3) != face_boundaries.end(); + + tri->mesh.faces[i].setVerts(v1, v2, v3); + tri->mesh.faces[i].setEdgeVisFlags(b1, b2, b3); + + MtlID mtlid = (MtlID)ios_mesh.material_ids()[i]; + if (needs_default) { + mtlid++; + } + tri->mesh.faces[i].setMatID(mtlid); + } + + bool valid = tri->CheckObjectIntegrity(); + + if (!valid) { + return nullptr; + } + + // apply simple box mapping +#if MAX_VERSION_MAJOR < 22 + // in 3ds Max 2017-2019 SDK, Matrix3::Identity was declared in matrix3.h, but did'nt actually exist in the lib .... + Matrix3 ident(TRUE); + tri->mesh.ApplyUVWMap(MAP_ACAD_BOX, 1, 1, 1, 0, 0, 0, 0, ident); +#else + tri->mesh.ApplyUVWMap(MAP_ACAD_BOX, 1, 1, 1, 0, 0, 0, 0, Matrix3::Identity); +#endif + + // this one tends to crash, so we skip it for the time being + // tri->mesh.buildNormals(); + + // Either use this or undefine the FACESETS_AS_COMPOUND option in IfcGeom.h to have + // properly oriented normals. Using only the line below will result in a consistent + // orientation of normals across shells, but not always oriented towards the + // outside. + // tri->mesh.UnifyNormals(false); + + tri->mesh.BuildStripsAndEdges(); + tri->mesh.InvalidateTopologyCache(); + tri->mesh.InvalidateGeomCache(); + return tri; +} + +inline Point3 IFCImp::Point3ByIndex(const std::vector& verts, int index) { + return Point3(verts[3 * index + 0], verts[3 * index + 1], verts[3 * index + 2]); } diff --git a/src/ifcmax/IfcMax.h b/src/ifcmax/IfcMax.h index 19a89d9da0..732d5f363f 100644 --- a/src/ifcmax/IfcMax.h +++ b/src/ifcmax/IfcMax.h @@ -21,23 +21,27 @@ #define IFCMAX_H #include "Max.h" +#include "../ifcgeom/IfcGeomElement.h" -extern ClassDesc* GetIFCImpDesc(); +class IFCImp : public SceneImport { + public: + int ExtCount(); // = 1 + const TCHAR* Ext(int n); // = "IFC" + const TCHAR* LongDesc(); // = "IFC Importer for 3ds Max" + const TCHAR* ShortDesc(); // = "Industry Foundation Classes" + const TCHAR* AuthorName(); // = "Josef Wienerroither" + const TCHAR* CopyrightMessage(); // = "Copyright (c) 2025 Josef Wienerroither, 2011-2016 IfcOpenShell" + const TCHAR* OtherMessage1(); // = "" + const TCHAR* OtherMessage2(); // = "" + unsigned int Version(); // = 25 + void ShowAbout(HWND hWnd); + int DoImport(const TCHAR* name, ImpInterface* ei, Interface* i, BOOL suppressPrompts); + BOOL VerifyCancel(); -class IFCImp : public SceneImport -{ -public: - int ExtCount(); // = 1 - const TCHAR * Ext(int n); // = "IFC" - const TCHAR * LongDesc(); // = "IfcOpenShell IFC Importer for 3ds Max" - const TCHAR * ShortDesc(); // = "Industry Foundation Classes" - const TCHAR * AuthorName(); // = "Thomas Krijnen" - const TCHAR * CopyrightMessage(); // = "Copyright (c) 2011-2016 IfcOpenShell" - const TCHAR * OtherMessage1(); // = "" - const TCHAR * OtherMessage2(); // = "" - unsigned int Version(); // = 12 - void ShowAbout(HWND hWnd); - int DoImport(const TCHAR *name,ImpInterface *ei,Interface *i, BOOL suppressPrompts); + private: + inline Point3 Point3ByIndex(const std::vector& verts, int index); + void BuildFullName(const IfcUtil::IfcBaseEntity& entity, MSTR& long_name); + TriObject* BuildMesh(const IfcGeom::TriangulationElement* triElement); }; #endif