Implement element-hierarchy setting in USD

This commit is contained in:
Thomas Krijnen
2024-06-29 14:16:07 +02:00
parent a92d51aeb1
commit c68dcba087
5 changed files with 113 additions and 37 deletions
+89 -26
View File
@@ -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 <typename T>
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<ifcopenshell::geometry::settings::UseElementHierarchy>().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<std::string> 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<std::string>(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<pxr::UsdGeomMesh>(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<float>(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>((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<double>::const_iterator it = mesh.normals().begin(); it != mesh.normals().end();)
normals.push_back(pxr::GfVec3f(static_cast<float>(*(it++)), static_cast<float>(*(it++)), static_cast<float>(*(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<int> written;
for (auto& [p, q] : parents_) {
if (written.find(p->id()) == written.end() && written_.find(p->id()) == written_.end()) {
written.insert(p->id());
writeNode<pxr::UsdGeomXform>(p, q);
}
}
stage_->Save();
}
template pxr::UsdGeomMesh USDSerializer::writeNode<pxr::UsdGeomMesh>(const IfcGeom::Element*, const IfcGeom::Element*);
template pxr::UsdGeomXform USDSerializer::writeNode<pxr::UsdGeomXform>(const IfcGeom::Element*, const IfcGeom::Element*);
#endif // WITH_USD