Support --element-hierarchy in .glb output

This commit is contained in:
Thomas Krijnen
2025-11-17 14:05:59 +01:00
parent bf0aa247a0
commit d6daa64c48
4 changed files with 118 additions and 35 deletions
+101 -26
View File
@@ -180,35 +180,108 @@ void GltfSerializer::write(const IfcGeom::TriangulationElement* o) {
return;
}
node_array_.push_back(json_["nodes"].size());
size_t current_node_index = json_["nodes"].size();
auto current_leaf_index = current_node_index;
json_["nodes"].emplace_back();
node_indices_[o->product()] = current_node_index;
node_array_.push_back(current_node_index);
const auto& m = o->transformation().data()->ccomponents();
// @todo check
std::array<double, 16> matrix_flat;
if (settings_.get<ifcopenshell::geometry::settings::WriteGltfEcef>().get()) {
matrix_flat = {
m(0,0), m(1,0), m(2,0), m(3,0),
m(0,1), m(1,1), m(2,1), m(3,1),
m(0,2), m(1,2), m(2,2), m(3,2),
m(0,3), m(1,3), m(2,3), m(3,3)
};
} else {
// nb: note that this contains the Y-UP transform as well.
matrix_flat = {
m(0,0), m(2,0), -m(1,0), m(3,0),
m(0,1), m(2,1), -m(1,1), m(3,1),
m(0,2), m(2,2), -m(1,2), m(3,2),
m(0,3), m(2,3), -m(1,3), m(3,3)
};
auto m = o->transformation().data()->ccomponents();
if (o->parents().empty()) {
roots_.push_back(current_node_index);
}
if (!o->parents().empty()) {
// apply inverse of last parent -> overwrite product transform (m)
m = o->parents().back()->transformation().data()->ccomponents().inverse() * m;
for (auto it = o->parents().rbegin(); it != o->parents().rend(); ++it) {
const auto jt = it + 1;
const bool is_root = jt == o->parents().rend();
auto kt = node_indices_.find((*it)->product());
if (kt != node_indices_.end()) {
// parent already processed as part of other parent sequence
json_["nodes"][kt->second]["children"].push_back(current_node_index);
break;
}
auto mm = (*it)->transformation().data()->ccomponents();
if (!is_root) {
mm = (*jt)->transformation().data()->ccomponents().inverse() * mm;
}
json parent_node = json::object();
std::array<double, 16> matrix_flat;
if (settings_.get<ifcopenshell::geometry::settings::WriteGltfEcef>().get() || !is_root) {
// y-up transform is only accounted for on root
matrix_flat = {
mm(0,0), mm(1,0), mm(2,0), mm(3,0),
mm(0,1), mm(1,1), mm(2,1), mm(3,1),
mm(0,2), mm(1,2), mm(2,2), mm(3,2),
mm(0,3), mm(1,3), mm(2,3), mm(3,3)
};
} else {
// nb: note that this contains the Y-UP transform.
matrix_flat = {
mm(0,0), mm(2,0), -mm(1,0), mm(3,0),
mm(0,1), mm(2,1), -mm(1,1), mm(3,1),
mm(0,2), mm(2,2), -mm(1,2), mm(3,2),
mm(0,3), mm(2,3), -mm(1,3), mm(3,3)
};
}
static const std::array<double, 16> identity_matrix = {1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};
static const std::array<double, 16> identity_matrix = {1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};
if (matrix_flat != identity_matrix) {
// glTF validator complains about identity matrices
parent_node["matrix"] = matrix_flat;
}
size_t new_node_index = json_["nodes"].size();
node_indices_[(*it)->product()] = new_node_index;
node_array_.push_back(new_node_index);
parent_node["name"] = object_id(o);
parent_node["children"] = json::array({current_node_index});
json_["nodes"].push_back(parent_node);
current_node_index = new_node_index;
if (is_root) {
roots_.push_back(current_node_index);
}
}
}
json node;
if (matrix_flat != identity_matrix) {
// glTF validator complains about identity matrices
node["matrix"] = matrix_flat;
{
std::array<double, 16> matrix_flat;
if (settings_.get<ifcopenshell::geometry::settings::WriteGltfEcef>().get() || !o->parents().empty()) {
// y-up transform is only accounted for on root
matrix_flat = {
m(0,0), m(1,0), m(2,0), m(3,0),
m(0,1), m(1,1), m(2,1), m(3,1),
m(0,2), m(1,2), m(2,2), m(3,2),
m(0,3), m(1,3), m(2,3), m(3,3)
};
} else {
// nb: note that this contains the Y-UP transform.
matrix_flat = {
m(0,0), m(2,0), -m(1,0), m(3,0),
m(0,1), m(2,1), -m(1,1), m(3,1),
m(0,2), m(2,2), -m(1,2), m(3,2),
m(0,3), m(2,3), -m(1,3), m(3,3)
};
}
static const std::array<double, 16> identity_matrix = {1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1};
if (matrix_flat != identity_matrix) {
// glTF validator complains about identity matrices
node["matrix"] = matrix_flat;
}
}
node["name"] = object_id(o);
@@ -300,7 +373,7 @@ void GltfSerializer::write(const IfcGeom::TriangulationElement* o) {
}
node["mesh"] = current_mesh_index;
json_["nodes"].push_back(node);
json_["nodes"][current_leaf_index] = node;
}
template <uint32_t>
@@ -372,7 +445,9 @@ void GltfSerializer::finalize() {
}
json scene_0;
if (north_rotation_ || ecef_transform_) {
if (geometry_settings().get<ifcopenshell::geometry::settings::UseElementHierarchy>().get()) {
scene_0["nodes"] = roots_;
} else if (north_rotation_ || ecef_transform_) {
scene_0["nodes"] = std::array<size_t, 1>{json_["nodes"].size() - 1};
} else {
scene_0["nodes"] = node_array_;