From c68dcba087e74688cebd24f26d30b0f5b718206f Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Sat, 29 Jun 2024 14:16:07 +0200 Subject: [PATCH] Implement element-hierarchy setting in USD --- src/ifcconvert/IfcConvert.cpp | 4 +- src/ifcgeom/IfcGeomElement.h | 4 +- src/ifcgeom/Iterator.h | 20 ++++-- src/serializers/USDSerializer.cpp | 115 +++++++++++++++++++++++------- src/serializers/USDSerializer.h | 7 ++ 5 files changed, 113 insertions(+), 37 deletions(-) diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index 822fcdb707..031049c40d 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -872,8 +872,8 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } - if (geometry_settings.get().get() && output_extension != DAE) { - cerr_ << "[Error] --use-element-hierarchy can be used only with .dae output.\n"; + if (geometry_settings.get().get() && output_extension != DAE && output_extension != USD) { + cerr_ << "[Error] --use-element-hierarchy can be used only with .dae or .usd output.\n"; /// @todo Lots of duplicate error-and-exit code. write_log(!quiet); print_usage(); diff --git a/src/ifcgeom/IfcGeomElement.h b/src/ifcgeom/IfcGeomElement.h index 698966d2d7..da2236be26 100644 --- a/src/ifcgeom/IfcGeomElement.h +++ b/src/ifcgeom/IfcGeomElement.h @@ -96,8 +96,8 @@ namespace IfcGeom { const std::string& unique_id() const { return _unique_id; } const Transformation& transformation() const { return _transformation; } const IfcUtil::IfcBaseEntity* product() const { return product_; } - const std::vector parents() const { return _parents; } - void SetParents(std::vector newparents) { _parents = newparents; } + const std::vector& parents() const { return _parents; } + void SetParents(std::vector& newparents) { _parents = newparents; } Element(const ifcopenshell::geometry::Settings& settings, int id, int parent_id, const std::string& name, const std::string& type, const std::string& guid, const std::string& context, const ifcopenshell::geometry::taxonomy::matrix4::ptr& trsf, const IfcUtil::IfcBaseEntity* product) diff --git a/src/ifcgeom/Iterator.h b/src/ifcgeom/Iterator.h index a88b5d9c7c..045724fb77 100644 --- a/src/ifcgeom/Iterator.h +++ b/src/ifcgeom/Iterator.h @@ -712,20 +712,26 @@ namespace IfcGeom { IfcUtil::IfcBaseEntity* ifc_product = 0; try { - IfcUtil::IfcBaseEntity* ifc_entity = ifc_file->instance_by_id(id)->as(); - instance_type = ifc_entity->declaration().name(); + ifc_product = ifc_file->instance_by_id(id)->as(); + instance_type = ifc_product->declaration().name(); - if (ifc_entity->declaration().is("IfcRoot")) { - product_guid = (std::string) *ifc_entity->get("GlobalId"); - product_name = ifc_entity->get_value("Name", ""); + if (ifc_product->declaration().is("IfcRoot")) { + product_guid = (std::string) *ifc_product->get("GlobalId"); + product_name = ifc_product->get_value("Name", ""); } - auto parent_object = converter_->mapping()->get_decomposing_entity(ifc_entity); + auto parent_object = converter_->mapping()->get_decomposing_entity(ifc_product); if (parent_object) { parent_id = parent_object->data().id(); } - m4 = ifcopenshell::geometry::taxonomy::cast(converter_->mapping()->map(ifc_product))->matrix; + // fails in case of IfcProject + auto mapped = converter_->mapping()->map(ifc_product); + auto casted = mapped ? ifcopenshell::geometry::taxonomy::dcast(mapped) : nullptr; + + if (casted) { + m4 = casted->matrix; + } } catch (const std::exception& e) { Logger::Error(e); } diff --git a/src/serializers/USDSerializer.cpp b/src/serializers/USDSerializer.cpp index 34b60b82fc..50013a95df 100644 --- a/src/serializers/USDSerializer.cpp +++ b/src/serializers/USDSerializer.cpp @@ -53,8 +53,6 @@ USDSerializer::USDSerializer(const std::string& out_filename, const ifcopenshell pxr::UsdGeomSetStageMetersPerUnit(stage_, 1.0f); - auto world = pxr::UsdGeomXform::Define(stage_, pxr::SdfPath("/World")); - stage_->SetDefaultPrim(world.GetPrim()); pxr::UsdGeomScope::Define(stage_, pxr::SdfPath("/Looks")); auto light = pxr::UsdLuxDistantLight::Define(stage_, pxr::SdfPath("/defaultLight")); light.CreateIntensityAttr().Set(1000.0f); @@ -119,26 +117,85 @@ void USDSerializer::writeHeader() { stage_->GetRootLayer()->SetComment("File generated by IfcOpenShell " + std::string(IFCOPENSHELL_VERSION)); } -void USDSerializer::write(const IfcGeom::TriangulationElement* o) { - pxr::UsdGeomMesh usd_mesh; - const IfcGeom::Representation::Triangulation& mesh = o->geometry(); - const auto verts = mesh.verts(); - const auto faces = mesh.faces(); - const auto material_ids = mesh.material_ids(); +namespace { + std::string nameObject(const IfcGeom::Element* o) { + std::string name = o->name(); + const std::string type = o->type(); + const std::string id = std::to_string(o->id()); + if (name.empty()) { + name = type + "_UnNamed_" + id; + } else { + name = type + "_" + usd_utils::toPath(name) + "_" + id; + } + return name; + } +} + +template +T USDSerializer::writeNode(const IfcGeom::Element* o, const IfcGeom::Element* p) { + written_.insert(o->id()); + auto m = o->transformation().data()->ccomponents(); + + // store absolute matrix for calculating relative child matrices later on. + placements_[o->id()] = o->transformation().data(); + + bool is_root = false; + std::string prefix = "/"; + if (geometry_settings_.get().get() && p == nullptr && o->parents().empty()) { + // Emitting hierarchy + // p == nullptr means we're in the first pass serializing geometric elements + // in that case empty parents vector means it's the root + is_root = true; + } else if (p != nullptr) { + // Providing explicit parent pointer, lookup previously serialized m4 and path + prefix = paths_[p->id()] + "/"; + m = placements_[p->id()]->ccomponents().inverse() * m; + } else { + std::vector names; + std::transform(o->parents().begin(), o->parents().end(), std::back_inserter(names), nameObject); + std::ostringstream oss; + std::copy(names.begin(), names.end(), std::ostream_iterator(oss, "/")); + prefix += oss.str(); + + // std::ostringstream pss; + // pss << "MATT" << std::endl << std::endl; + // pss << m << std::endl << std::endl << o->parents().back()->transformation().data()->ccomponents() << std::endl << std::endl; + m = o->parents().back()->transformation().data()->ccomponents().inverse() * m; + // pss << m << std::endl << std::endl; + // auto psss = pss.str(); + // std::wcout << psss.c_str() << std::endl; + } + auto el_path = prefix + nameObject(o); + paths_[o->id()] = el_path; + + T t = T::Define(stage_, pxr::SdfPath(el_path)); + if (is_root) { + stage_->SetDefaultPrim(t.GetPrim()); + } + + t.AddTransformOp().Set(pxr::GfMatrix4d( + m.data()[0], m.data()[1], m.data()[2], m.data()[3], + m.data()[4], m.data()[5], m.data()[6], m.data()[7], + m.data()[8], m.data()[9], m.data()[10], m.data()[11], + m.data()[12], m.data()[13], m.data()[14], m.data()[15] + )); + return t; +} - if (material_ids.empty() || verts.empty() || faces.empty()) - return; +void USDSerializer::write(const IfcGeom::TriangulationElement* o) { + IfcGeom::Element const * previous = nullptr; + for (auto it = o->parents().begin(); it != o->parents().end(); ++it) { + parents_.push_back({ *it, previous }); + previous = *it; + } + pxr::UsdGeomMesh usd_mesh = writeNode(o); + const IfcGeom::Representation::Triangulation& mesh = o->geometry(); + const auto verts = mesh.verts(); + const auto faces = mesh.faces(); + const auto material_ids = mesh.material_ids(); + - std::string name = o->name(); - const std::string type = o->type(); - const std::string id = std::to_string(o->id()); - if(name.empty()) { - name = type + "_UnNamed_" + id; - } else { - name = type + "_" + usd_utils::toPath(name) + "_" + id; - } - usd_mesh = pxr::UsdGeomMesh::Define(stage_, pxr::SdfPath("/World/" + name)); pxr::VtVec3fArray points; for(std::size_t i = 0; i < verts.size(); i+=3) { points.push_back(pxr::GfVec3f(static_cast(verts[i]), @@ -149,13 +206,6 @@ void USDSerializer::write(const IfcGeom::TriangulationElement* o) { usd_mesh.CreateFaceVertexIndicesAttr().Set(usd_utils::toVtArray(faces)); usd_mesh.CreateFaceVertexCountsAttr().Set(pxr::VtArray((int) faces.size() / 3, 3)); - usd_mesh.AddTransformOp().Set(pxr::GfMatrix4d( - m.data()[0], m.data()[1], m.data()[2], m.data()[3], - m.data()[4], m.data()[5], m.data()[6], m.data()[7], - m.data()[8], m.data()[9], m.data()[10], m.data()[11], - m.data()[12], m.data()[13], m.data()[14], m.data()[15] - )); - pxr::VtVec3fArray normals; for (std::vector::const_iterator it = mesh.normals().begin(); it != mesh.normals().end();) normals.push_back(pxr::GfVec3f(static_cast(*(it++)), static_cast(*(it++)), static_cast(*(it++)))); @@ -178,7 +228,20 @@ void USDSerializer::write(const IfcGeom::TriangulationElement* o) { } void USDSerializer::finalize() { + // we write the parents at the end, because some parents might actually be + // geometrical entities such as the IfcSite. + std::set written; + for (auto& [p, q] : parents_) { + if (written.find(p->id()) == written.end() && written_.find(p->id()) == written_.end()) { + written.insert(p->id()); + writeNode(p, q); + } + } + stage_->Save(); } +template pxr::UsdGeomMesh USDSerializer::writeNode(const IfcGeom::Element*, const IfcGeom::Element*); +template pxr::UsdGeomXform USDSerializer::writeNode(const IfcGeom::Element*, const IfcGeom::Element*); + #endif // WITH_USD \ No newline at end of file diff --git a/src/serializers/USDSerializer.h b/src/serializers/USDSerializer.h index b14dab06a7..37cefdb6db 100644 --- a/src/serializers/USDSerializer.h +++ b/src/serializers/USDSerializer.h @@ -71,6 +71,13 @@ private: std::map meshes_; std::vector createMaterials(const std::vector&); + template + T writeNode(const IfcGeom::Element*, const IfcGeom::Element* = nullptr); + std::vector> parents_; + + std::set written_; + std::map paths_; + std::map placements_; public: USDSerializer(const std::string&, const ifcopenshell::geometry::Settings&, const ifcopenshell::geometry::SerializerSettings&); virtual ~USDSerializer();