mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 17:58:20 +00:00
Merge pull request #211 from mitmadness/feature/hierarchy
Represent hierarchy in Collada files
This commit is contained in:
@@ -29,11 +29,20 @@
|
||||
#include <COLLADASWBaseInputElement.h>
|
||||
#include <COLLADASWAsset.h>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/numeric/ublas/vector.hpp>
|
||||
#include <boost/numeric/ublas/vector_proxy.hpp>
|
||||
#include <boost/numeric/ublas/matrix.hpp>
|
||||
#include <boost/numeric/ublas/triangular.hpp>
|
||||
#include <boost/numeric/ublas/lu.hpp>
|
||||
#include <boost/numeric/ublas/io.hpp>
|
||||
|
||||
using namespace IfcSchema;
|
||||
using namespace boost::numeric::ublas;
|
||||
|
||||
static void collada_id(std::string &s)
|
||||
{
|
||||
IfcUtil::sanitate_material_name(s);
|
||||
@@ -66,12 +75,12 @@ void ColladaSerializer::ColladaExporter::ColladaGeometries::write(
|
||||
const std::vector<real_t>& uvs)
|
||||
{
|
||||
openMesh(mesh_id);
|
||||
|
||||
|
||||
// The normals vector can be empty for example when the WELD_VERTICES setting is used.
|
||||
// IfcOpenShell does not provide them with multiple face normals collapsed into a single vertex.
|
||||
const bool has_normals = !normals.empty();
|
||||
const bool has_uvs = !uvs.empty();
|
||||
|
||||
|
||||
addFloatSource(mesh_id, COLLADASW::LibraryGeometries::POSITIONS_SOURCE_ID_SUFFIX, positions);
|
||||
if (has_normals) {
|
||||
addFloatSource(mesh_id, COLLADASW::LibraryGeometries::NORMALS_SOURCE_ID_SUFFIX, normals);
|
||||
@@ -179,7 +188,7 @@ void ColladaSerializer::ColladaExporter::ColladaGeometries::close() {
|
||||
|
||||
void ColladaSerializer::ColladaExporter::ColladaScene::add(
|
||||
const std::string& node_id, const std::string& node_name, const std::string& geom_name,
|
||||
const std::vector<std::string>& material_ids, const std::vector<real_t>& matrix)
|
||||
const std::vector<std::string>& material_ids, const std::vector<real_t>& posmatrix)
|
||||
{
|
||||
if (!scene_opened) {
|
||||
openVisualScene(scene_id);
|
||||
@@ -193,13 +202,42 @@ void ColladaSerializer::ColladaExporter::ColladaScene::add(
|
||||
|
||||
// The matrix attribute of an entity is basically a 4x3 representation of its ObjectPlacement.
|
||||
// Note that this placement is absolute, ie it is multiplied with all parent placements.
|
||||
|
||||
double matrix_array[4][4] = {
|
||||
{ (double)matrix[0], (double)matrix[3], (double)matrix[6], (double)matrix[ 9] },
|
||||
{ (double)matrix[1], (double)matrix[4], (double)matrix[7], (double)matrix[10] },
|
||||
{ (double)matrix[2], (double)matrix[5], (double)matrix[8], (double)matrix[11] },
|
||||
{ (double)posmatrix[0], (double)posmatrix[3], (double)posmatrix[6], (double)posmatrix[ 9] },
|
||||
{ (double)posmatrix[1], (double)posmatrix[4], (double)posmatrix[7], (double)posmatrix[10] },
|
||||
{ (double)posmatrix[2], (double)posmatrix[5], (double)posmatrix[8], (double)posmatrix[11] },
|
||||
{ 0, 0, 0, 1 }
|
||||
};
|
||||
|
||||
// If this is not the first parent, get the relative placement
|
||||
if (parentNodes.size() > 0)
|
||||
{
|
||||
double relative[4][4] = {
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
// Multiplication
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
for (int k = 0; k < 4; ++k) {
|
||||
relative[i][j] += matrixStack.top()(i, k) * matrix_array[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy from relative to matrix_array
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
matrix_array[i][j] = relative[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
matrix_array[0][3] += serializer->settings().offset[0];
|
||||
matrix_array[1][3] += serializer->settings().offset[1];
|
||||
matrix_array[2][3] += serializer->settings().offset[2];
|
||||
@@ -218,11 +256,181 @@ void ColladaSerializer::ColladaExporter::ColladaScene::add(
|
||||
node.end();
|
||||
}
|
||||
|
||||
void ColladaSerializer::ColladaExporter::ColladaScene::addParent(const IfcGeom::Element<real_t>& parent){
|
||||
//we open the visual scene tag if it's not.
|
||||
if (!scene_opened) {
|
||||
openVisualScene(scene_id);
|
||||
scene_opened = true;
|
||||
}
|
||||
|
||||
|
||||
const std::vector<real_t> parentMatrix = parent.transformation().matrix().data();
|
||||
|
||||
double matrix_array[4][4] = {
|
||||
{ (double)parentMatrix[0], (double)parentMatrix[3], (double)parentMatrix[6], (double)parentMatrix[9] },
|
||||
{ (double)parentMatrix[1], (double)parentMatrix[4], (double)parentMatrix[7], (double)parentMatrix[10] },
|
||||
{ (double)parentMatrix[2], (double)parentMatrix[5], (double)parentMatrix[8], (double)parentMatrix[11] },
|
||||
{ 0, 0, 0, 1 }
|
||||
};
|
||||
|
||||
// =========
|
||||
|
||||
// If this is not the first parent, get the relative placement
|
||||
if (parentNodes.size() > 0)
|
||||
{
|
||||
double relative[4][4] = {
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
// Multiplication
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
for (int k = 0; k < 4; ++k) {
|
||||
relative[i][j] += matrixStack.top()(i, k) * matrix_array[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO : Handle the case where there was no inverse
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
matrix_array[i][j] = relative[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//adding the offset to the matrix
|
||||
//matrix_array[0][3] += serializer->settings().offset[0];
|
||||
//matrix_array[1][3] += serializer->settings().offset[1];
|
||||
//matrix_array[2][3] += serializer->settings().offset[2];
|
||||
|
||||
|
||||
const std::string id = "representation-" + boost::lexical_cast<std::string>(parent.id());
|
||||
|
||||
// Chose a name of the parent object
|
||||
std::string name = "";
|
||||
if (serializer->settings().get(SerializerSettings::USE_ELEMENT_TYPES)) { name = parent.type() + " " + parent.name(); }
|
||||
else
|
||||
{
|
||||
if (parent.name() == "") { name = "unnamed"; }
|
||||
else { name = parent.name(); }
|
||||
}
|
||||
|
||||
|
||||
COLLADASW::Node *current_node;
|
||||
current_node = new COLLADASW::Node(mSW);
|
||||
current_node->setNodeId(id);
|
||||
current_node->setNodeName(name);
|
||||
current_node->setType(COLLADASW::Node::NODE);
|
||||
current_node->start();
|
||||
current_node->addMatrix(matrix_array);
|
||||
|
||||
// ==== Inverse the matrix and save it ====
|
||||
|
||||
double m[4][4] = {
|
||||
{ (double)parentMatrix[0], (double)parentMatrix[3], (double)parentMatrix[6], (double)parentMatrix[9] },
|
||||
{ (double)parentMatrix[1], (double)parentMatrix[4], (double)parentMatrix[7], (double)parentMatrix[10] },
|
||||
{ (double)parentMatrix[2], (double)parentMatrix[5], (double)parentMatrix[8], (double)parentMatrix[11] },
|
||||
{ 0, 0, 0, 1 }
|
||||
};
|
||||
|
||||
double det =
|
||||
m[0][3] * m[1][2] * m[2][1] * m[3][0] - m[0][2] * m[1][3] * m[2][1] * m[3][0] - m[0][3] * m[1][1] * m[2][2] * m[3][0] + m[0][1] * m[1][3] * m[2][2] * m[3][0] +
|
||||
m[0][2] * m[1][1] * m[2][3] * m[3][0] - m[0][1] * m[1][2] * m[2][3] * m[3][0] - m[0][3] * m[1][2] * m[2][0] * m[3][1] + m[0][2] * m[1][3] * m[2][0] * m[3][1] +
|
||||
m[0][3] * m[1][0] * m[2][2] * m[3][1] - m[0][0] * m[1][3] * m[2][2] * m[3][1] - m[0][2] * m[1][0] * m[2][3] * m[3][1] + m[0][0] * m[1][2] * m[2][3] * m[3][1] +
|
||||
m[0][3] * m[1][1] * m[2][0] * m[3][2] - m[0][1] * m[1][3] * m[2][0] * m[3][2] - m[0][3] * m[1][0] * m[2][1] * m[3][2] + m[0][0] * m[1][3] * m[2][1] * m[3][2] +
|
||||
m[0][1] * m[1][0] * m[2][3] * m[3][2] - m[0][0] * m[1][1] * m[2][3] * m[3][2] - m[0][2] * m[1][1] * m[2][0] * m[3][3] + m[0][1] * m[1][2] * m[2][0] * m[3][3] +
|
||||
m[0][2] * m[1][0] * m[2][1] * m[3][3] - m[0][0] * m[1][2] * m[2][1] * m[3][3] - m[0][1] * m[1][0] * m[2][2] * m[3][3] + m[0][0] * m[1][1] * m[2][2] * m[3][3];
|
||||
|
||||
|
||||
if (det != 0)
|
||||
{
|
||||
double inverse[4][4];
|
||||
inverse[0][0] = m[1][2] * m[2][3] * m[3][1] - m[1][3] * m[2][2] * m[3][1] + m[1][3] * m[2][1] * m[3][2] - m[1][1] * m[2][3] * m[3][2] - m[1][2] * m[2][1] * m[3][3] + m[1][1] * m[2][2] * m[3][3];
|
||||
inverse[0][1] = m[0][3] * m[2][2] * m[3][1] - m[0][2] * m[2][3] * m[3][1] - m[0][3] * m[2][1] * m[3][2] + m[0][1] * m[2][3] * m[3][2] + m[0][2] * m[2][1] * m[3][3] - m[0][1] * m[2][2] * m[3][3];
|
||||
inverse[0][2] = m[0][2] * m[1][3] * m[3][1] - m[0][3] * m[1][2] * m[3][1] + m[0][3] * m[1][1] * m[3][2] - m[0][1] * m[1][3] * m[3][2] - m[0][2] * m[1][1] * m[3][3] + m[0][1] * m[1][2] * m[3][3];
|
||||
inverse[0][3] = m[0][3] * m[1][2] * m[2][1] - m[0][2] * m[1][3] * m[2][1] - m[0][3] * m[1][1] * m[2][2] + m[0][1] * m[1][3] * m[2][2] + m[0][2] * m[1][1] * m[2][3] - m[0][1] * m[1][2] * m[2][3];
|
||||
|
||||
inverse[1][0] = m[1][3] * m[2][2] * m[3][0] - m[1][2] * m[2][3] * m[3][0] - m[1][3] * m[2][0] * m[3][2] + m[1][0] * m[2][3] * m[3][2] + m[1][2] * m[2][0] * m[3][3] - m[1][0] * m[2][2] * m[3][3];
|
||||
inverse[1][1] = m[0][2] * m[2][3] * m[3][0] - m[0][3] * m[2][2] * m[3][0] + m[0][3] * m[2][0] * m[3][2] - m[0][0] * m[2][3] * m[3][2] - m[0][2] * m[2][0] * m[3][3] + m[0][0] * m[2][2] * m[3][3];
|
||||
inverse[1][2] = m[0][3] * m[1][2] * m[3][0] - m[0][2] * m[1][3] * m[3][0] - m[0][3] * m[1][0] * m[3][2] + m[0][0] * m[1][3] * m[3][2] + m[0][2] * m[1][0] * m[3][3] - m[0][0] * m[1][2] * m[3][3];
|
||||
inverse[1][3] = m[0][2] * m[1][3] * m[2][0] - m[0][3] * m[1][2] * m[2][0] + m[0][3] * m[1][0] * m[2][2] - m[0][0] * m[1][3] * m[2][2] - m[0][2] * m[1][0] * m[2][3] + m[0][0] * m[1][2] * m[2][3];
|
||||
|
||||
inverse[2][0] = m[1][1] * m[2][3] * m[3][0] - m[1][3] * m[2][1] * m[3][0] + m[1][3] * m[2][0] * m[3][1] - m[1][0] * m[2][3] * m[3][1] - m[1][1] * m[2][0] * m[3][3] + m[1][0] * m[2][1] * m[3][3];
|
||||
inverse[2][1] = m[0][3] * m[2][1] * m[3][0] - m[0][1] * m[2][3] * m[3][0] - m[0][3] * m[2][0] * m[3][1] + m[0][0] * m[2][3] * m[3][1] + m[0][1] * m[2][0] * m[3][3] - m[0][0] * m[2][1] * m[3][3];
|
||||
inverse[2][2] = m[0][1] * m[1][3] * m[3][0] - m[0][3] * m[1][1] * m[3][0] + m[0][3] * m[1][0] * m[3][1] - m[0][0] * m[1][3] * m[3][1] - m[0][1] * m[1][0] * m[3][3] + m[0][0] * m[1][1] * m[3][3];
|
||||
inverse[2][3] = m[0][3] * m[1][1] * m[2][0] - m[0][1] * m[1][3] * m[2][0] - m[0][3] * m[1][0] * m[2][1] + m[0][0] * m[1][3] * m[2][1] + m[0][1] * m[1][0] * m[2][3] - m[0][0] * m[1][1] * m[2][3];
|
||||
|
||||
inverse[3][0] = m[1][2] * m[2][1] * m[3][0] - m[1][1] * m[2][2] * m[3][0] - m[1][2] * m[2][0] * m[3][1] + m[1][0] * m[2][2] * m[3][1] + m[1][1] * m[2][0] * m[3][2] - m[1][0] * m[2][1] * m[3][2];
|
||||
inverse[3][1] = m[0][1] * m[2][2] * m[3][0] - m[0][2] * m[2][1] * m[3][0] + m[0][2] * m[2][0] * m[3][1] - m[0][0] * m[2][2] * m[3][1] - m[0][1] * m[2][0] * m[3][2] + m[0][0] * m[2][1] * m[3][2];
|
||||
inverse[3][2] = m[0][2] * m[1][1] * m[3][0] - m[0][1] * m[1][2] * m[3][0] - m[0][2] * m[1][0] * m[3][1] + m[0][0] * m[1][2] * m[3][1] + m[0][1] * m[1][0] * m[3][2] - m[0][0] * m[1][1] * m[3][2];
|
||||
inverse[3][3] = m[0][1] * m[1][2] * m[2][0] - m[0][2] * m[1][1] * m[2][0] + m[0][2] * m[1][0] * m[2][1] - m[0][0] * m[1][2] * m[2][1] - m[0][1] * m[1][0] * m[2][2] + m[0][0] * m[1][1] * m[2][2];
|
||||
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
inverse[i][j] /= det;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a save matrix to push to the stack
|
||||
matrix<double> toSave(4, 4);
|
||||
|
||||
// Initialization
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
toSave(i, j) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the inverse matrix
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
toSave(i, j) = inverse[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// Save the matrix
|
||||
matrixStack.push(toSave);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "couldn't inverse the position matrix \n";
|
||||
}
|
||||
|
||||
// Add the node to the parent stack
|
||||
parentNodes.push(current_node);
|
||||
serializer->parentStackId.push(parent.id());
|
||||
|
||||
}
|
||||
|
||||
void ColladaSerializer::ColladaExporter::ColladaScene::closeParent()
|
||||
{
|
||||
// Get the top element
|
||||
COLLADASW::Node *current_node = parentNodes.top();
|
||||
|
||||
// Close the node
|
||||
current_node->end();
|
||||
|
||||
// Remove it from the stack
|
||||
parentNodes.pop();
|
||||
matrixStack.pop();
|
||||
serializer->parentStackId.pop();
|
||||
|
||||
// Free the memory
|
||||
delete current_node;
|
||||
current_node = NULL;
|
||||
}
|
||||
|
||||
void ColladaSerializer::ColladaExporter::ColladaScene::write() {
|
||||
if (scene_opened) {
|
||||
closeVisualScene();
|
||||
closeLibrary();
|
||||
|
||||
|
||||
COLLADASW::Scene scene (mSW, COLLADASW::URI ("#" + scene_id));
|
||||
scene.add();
|
||||
}
|
||||
@@ -301,33 +509,77 @@ void ColladaSerializer::ColladaExporter::startDocument(const std::string& unit_n
|
||||
|
||||
void ColladaSerializer::ColladaExporter::write(const IfcGeom::TriangulationElement<real_t>* o)
|
||||
{
|
||||
const IfcGeom::Representation::Triangulation<real_t>& mesh = o->geometry();
|
||||
const std::string name = serializer->settings().get(SerializerSettings::USE_ELEMENT_GUIDS) ?
|
||||
o->guid() : (serializer->settings().get(SerializerSettings::USE_ELEMENT_NAMES) ? o->name() : o->unique_id());
|
||||
const std::string representation_id = "representation-" + boost::lexical_cast<std::string>(o->geometry().id());
|
||||
|
||||
const IfcGeom::Representation::Triangulation<real_t>& mesh = o->geometry();
|
||||
|
||||
std::string slabSuffix = "";
|
||||
if (o->type() == "IfcSlab")
|
||||
{
|
||||
slabSuffix = differentiateSlabTypes(o);
|
||||
}
|
||||
|
||||
const std::string name = serializer->settings().get(SerializerSettings::USE_ELEMENT_GUIDS) ?
|
||||
o->guid() : (serializer->settings().get(SerializerSettings::USE_ELEMENT_NAMES) ?
|
||||
o->name() : (serializer->settings().get(SerializerSettings::USE_ELEMENT_TYPES) ? o->type() + slabSuffix : o->unique_id()));
|
||||
const std::string representation_id = "representation-" + boost::lexical_cast<std::string>(o->geometry().id());
|
||||
std::vector<std::string> material_references;
|
||||
foreach(const IfcGeom::Material& material, mesh.materials()) {
|
||||
foreach(const IfcGeom::Material& material, mesh.materials()) {
|
||||
if (!materials.contains(material)) {
|
||||
materials.add(material);
|
||||
}
|
||||
std::string material_name = (serializer->settings().get(SerializerSettings::USE_MATERIAL_NAMES)
|
||||
? material.original_name() : material.name());
|
||||
collada_id(material_name);
|
||||
material_references.push_back(material_name);
|
||||
std::string material_name = (serializer->settings().get(SerializerSettings::USE_MATERIAL_NAMES)
|
||||
? material.original_name() : material.name());
|
||||
collada_id(material_name);
|
||||
material_references.push_back(material_name);
|
||||
}
|
||||
|
||||
deferreds.push_back(
|
||||
DeferredObject(name, representation_id, o->type(), o->transformation().matrix().data(), mesh.verts(), mesh.normals(),
|
||||
mesh.faces(), mesh.edges(), mesh.material_ids(), mesh.materials(), material_references, mesh.uvs())
|
||||
);
|
||||
DeferredObject defered = (serializer->settings().get(SerializerSettings::USE_ELEMENT_HIERARCHY) ?
|
||||
DeferredObject(name, representation_id, o->type(), o->transformation().matrix().data(), mesh.verts(), mesh.normals(),
|
||||
mesh.faces(), mesh.edges(), mesh.material_ids(), mesh.materials(), material_references, mesh.uvs(), o->parents()) :
|
||||
DeferredObject(name, representation_id, o->type(), o->transformation().matrix().data(), mesh.verts(), mesh.normals(),
|
||||
mesh.faces(), mesh.edges(), mesh.material_ids(), mesh.materials(), material_references, mesh.uvs()));
|
||||
deferreds.push_back(defered);
|
||||
}
|
||||
|
||||
std::string ColladaSerializer::ColladaExporter::differentiateSlabTypes(const IfcGeom::TriangulationElement<real_t>* o) {
|
||||
IfcSlab* slab = (IfcSlab*)o->product();
|
||||
switch (slab->PredefinedType())
|
||||
{
|
||||
case (IfcSlabTypeEnum::IfcSlabType_FLOOR):
|
||||
return "_Floor";
|
||||
break;
|
||||
case (IfcSlabTypeEnum::IfcSlabType_ROOF):
|
||||
return "_Roof";
|
||||
break;
|
||||
case (IfcSlabTypeEnum::IfcSlabType_LANDING):
|
||||
return "_Landing";
|
||||
break;
|
||||
case (IfcSlabTypeEnum::IfcSlabType_BASESLAB):
|
||||
return "_BaseSlab";
|
||||
break;
|
||||
case (IfcSlabTypeEnum::IfcSlabType_NOTDEFINED):
|
||||
return "_NotDefined";
|
||||
break;
|
||||
default:
|
||||
if (slab->hasObjectType()) { return "_" + slab->ObjectType(); }
|
||||
else { return "_Unknown"; }
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ColladaSerializer::ColladaExporter::endDocument() {
|
||||
// In fact due the XML based nature of Collada and its dependency on library nodes,
|
||||
// only at this point all objects are written to the stream.
|
||||
materials.write();
|
||||
bool use_hierarchy = serializer->settings().get(SerializerSettings::USE_ELEMENT_HIERARCHY);
|
||||
|
||||
std::set<std::string> geometries_written;
|
||||
|
||||
//if the setting USE_ELEMENT_HIERARCHY is in use, we sort the deferreds objects by their parents.
|
||||
|
||||
if (use_hierarchy) {
|
||||
std::sort(deferreds.begin(), deferreds.end());
|
||||
}
|
||||
|
||||
for (std::vector<DeferredObject>::const_iterator it = deferreds.begin(); it != deferreds.end(); ++it) {
|
||||
if (geometries_written.find(it->representation_id) != geometries_written.end()) {
|
||||
continue;
|
||||
@@ -336,11 +588,61 @@ void ColladaSerializer::ColladaExporter::endDocument() {
|
||||
geometries.write(it->representation_id, it->type, it->vertices, it->normals, it->faces, it->edges, it->material_ids, it->materials, it->uvs);
|
||||
}
|
||||
geometries.close();
|
||||
for (std::vector<DeferredObject>::const_iterator it = deferreds.begin(); it != deferreds.end(); ++it) {
|
||||
|
||||
int parent_id = -1;
|
||||
bool is_parent_tag_opened = false;
|
||||
for (std::vector<DeferredObject>::const_iterator it = deferreds.begin(); it != deferreds.end(); ++it){
|
||||
const std::string object_name = it->unique_id;
|
||||
|
||||
if (use_hierarchy)
|
||||
{
|
||||
unsigned parentsNumber = it->parents.size();
|
||||
bool finished = false;
|
||||
|
||||
// If we have no parent in the stack and the object has no parent, nothing to do : skip the loop
|
||||
if (parentsNumber == 0 && serializer->parentStackId.size() == 0) { finished = true; }
|
||||
|
||||
while (!finished)
|
||||
{
|
||||
// If we need to add a parent
|
||||
if (serializer->parentStackId.size() <= parentsNumber)
|
||||
{
|
||||
if (serializer->parentStackId.empty()) { scene.addParent(*(it->parents.at(0))); }
|
||||
else
|
||||
{
|
||||
unsigned diff = parentsNumber - serializer->parentStackId.size();
|
||||
|
||||
// If we have the wrong parent in the list
|
||||
if (serializer->parentStackId.top() != it->parents.at(parentsNumber - diff - 1)->id())
|
||||
{
|
||||
scene.closeParent();
|
||||
}
|
||||
// So far we have the right parents, we just need to add the missing ones
|
||||
else
|
||||
{
|
||||
for (unsigned i = parentsNumber - diff; i < parentsNumber; i++) { scene.addParent(*(it->parents.at(i))); }
|
||||
|
||||
// if diff == 0, we can leave the loop. In fact we have the right number of parents, and the last one is ok
|
||||
if (diff == 0) { finished = true; }
|
||||
}
|
||||
}
|
||||
}
|
||||
// IF serializer->parentStackId.size() > parentsNumber
|
||||
else
|
||||
{
|
||||
// Close the finished nodes. After this we get the first case (serializer->parentStackId.size() <= parentsNumber)
|
||||
while (serializer->parentStackId.size() > parentsNumber) { scene.closeParent(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @todo redundant information using ID as both ID and Name, maybe omit Name or allow specifying what would be used as the name
|
||||
scene.add(object_name, object_name, it->representation_id, it->material_references, it->matrix);
|
||||
}
|
||||
|
||||
//close the remaining parent tags.
|
||||
while (serializer->parentStackId.size() > 0) { scene.closeParent(); }
|
||||
|
||||
scene.write();
|
||||
stream.endDocument();
|
||||
}
|
||||
@@ -357,6 +659,7 @@ void ColladaSerializer::write(const IfcGeom::TriangulationElement<real_t>* o) {
|
||||
exporter.write(o);
|
||||
}
|
||||
|
||||
|
||||
void ColladaSerializer::finalize() {
|
||||
exporter.endDocument();
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#pragma GCC diagnostic ignored "-Wignored-qualifiers"
|
||||
#endif
|
||||
#include <COLLADASWStreamWriter.h>
|
||||
#include <COLLADASWNode.h>
|
||||
#include <COLLADASWLibraryGeometries.h>
|
||||
#include <COLLADASWLibraryVisualScenes.h>
|
||||
#include <COLLADASWLibraryEffects.h>
|
||||
@@ -44,10 +45,16 @@
|
||||
|
||||
#include "../ifcconvert/GeometrySerializer.h"
|
||||
|
||||
#include <boost/numeric/ublas/matrix.hpp>
|
||||
#include <boost/numeric/ublas/io.hpp>
|
||||
|
||||
|
||||
class ColladaSerializer : public GeometrySerializer
|
||||
{
|
||||
// TODO The vast amount of implement details of ColladaSerializer could be hidden to the cpp file.
|
||||
private:
|
||||
std::stack<int> parentStackId;
|
||||
|
||||
class ColladaExporter
|
||||
{
|
||||
private:
|
||||
@@ -78,6 +85,8 @@ private:
|
||||
|
||||
const std::string scene_id;
|
||||
bool scene_opened;
|
||||
std::stack<COLLADASW::Node*> parentNodes;
|
||||
std::stack<boost::numeric::ublas::matrix<double> > matrixStack;
|
||||
public:
|
||||
ColladaScene(const std::string& scene_id, COLLADASW::StreamWriter& stream, ColladaSerializer *_serializer)
|
||||
: COLLADASW::LibraryVisualScenes(&stream)
|
||||
@@ -87,6 +96,9 @@ private:
|
||||
{}
|
||||
void add(const std::string& node_id, const std::string& node_name, const std::string& geom_name,
|
||||
const std::vector<std::string>& material_ids, const std::vector<real_t>& matrix);
|
||||
void addParent(const IfcGeom::Element<real_t>& parent);
|
||||
void closeParent();
|
||||
COLLADASW::Node* GetDirectParent();
|
||||
void write();
|
||||
ColladaSerializer *serializer;
|
||||
};
|
||||
@@ -121,6 +133,24 @@ private:
|
||||
ColladaEffects effects;
|
||||
};
|
||||
class DeferredObject {
|
||||
|
||||
friend bool operator < (const DeferredObject & def_obj1, const DeferredObject & def_obj2)
|
||||
{
|
||||
unsigned size = (def_obj1.parents.size() < def_obj2.parents.size() ? def_obj1.parents.size() : def_obj2.parents.size());
|
||||
int cpt = 0;
|
||||
|
||||
// Skip the shared parents
|
||||
while (cpt < size && *(def_obj1.parents.at(cpt)) == *(def_obj2.parents.at(cpt))) { cpt++; }
|
||||
|
||||
// If a parent list container the other one
|
||||
if (cpt >= size) { return (def_obj1.parents.size() < def_obj2.parents.size() ? true : false); }
|
||||
else
|
||||
{
|
||||
return *(def_obj1.parents.at(cpt)) < *(def_obj2.parents.at(cpt));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
std::string unique_id, representation_id, type;
|
||||
std::vector<real_t> matrix;
|
||||
@@ -132,10 +162,11 @@ private:
|
||||
std::vector<IfcGeom::Material> materials;
|
||||
std::vector<std::string> material_references;
|
||||
std::vector<real_t> uvs;
|
||||
std::vector<const IfcGeom::Element<real_t>*> parents;
|
||||
DeferredObject(const std::string& unique_id, const std::string& representation_id, const std::string& type, const std::vector<real_t>& matrix,
|
||||
const std::vector<real_t>& vertices, const std::vector<real_t>& normals, const std::vector<int>& faces,
|
||||
const std::vector<int>& edges, const std::vector<int>& material_ids, const std::vector<IfcGeom::Material>& materials,
|
||||
const std::vector<std::string>& material_references, const std::vector<real_t>& uvs)
|
||||
const std::vector<std::string>& material_references, const std::vector<real_t>& uvs, const std::vector<const IfcGeom::Element<real_t>*>& parent)
|
||||
: unique_id(unique_id)
|
||||
, representation_id(representation_id)
|
||||
, type(type)
|
||||
@@ -148,11 +179,35 @@ private:
|
||||
, materials(materials)
|
||||
, material_references(material_references)
|
||||
, uvs(uvs)
|
||||
{}
|
||||
, parents(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DeferredObject(const std::string& unique_id, const std::string& representation_id, const std::string& type, const std::vector<real_t>& matrix,
|
||||
const std::vector<real_t>& vertices, const std::vector<real_t>& normals, const std::vector<int>& faces,
|
||||
const std::vector<int>& edges, const std::vector<int>& material_ids, const std::vector<IfcGeom::Material>& materials,
|
||||
const std::vector<std::string>& material_references, const std::vector<real_t>& uvs)
|
||||
: unique_id(unique_id)
|
||||
, representation_id(representation_id)
|
||||
, type(type)
|
||||
, matrix(matrix)
|
||||
, vertices(vertices)
|
||||
, normals(normals)
|
||||
, faces(faces)
|
||||
, edges(edges)
|
||||
, material_ids(material_ids)
|
||||
, materials(materials)
|
||||
, material_references(material_references)
|
||||
, uvs(uvs)
|
||||
{
|
||||
parents.clear();
|
||||
}
|
||||
};
|
||||
COLLADABU::NativeString filename;
|
||||
COLLADASW::StreamWriter stream;
|
||||
ColladaScene scene;
|
||||
std::string differentiateSlabTypes(const IfcGeom::TriangulationElement<real_t>* o);
|
||||
public:
|
||||
/// @param double_precision Whether to use "double precision" (up to 16 decimals) or not (6 or 7 decimals).
|
||||
ColladaExporter(const std::string& scene_name, const std::string& fn, ColladaSerializer *_serializer,
|
||||
@@ -171,7 +226,7 @@ private:
|
||||
std::vector<DeferredObject> deferreds;
|
||||
virtual ~ColladaExporter() {}
|
||||
void startDocument(const std::string& unit_name, float unit_magnitude);
|
||||
void write(const IfcGeom::TriangulationElement<real_t>* o);
|
||||
void write(const IfcGeom::TriangulationElement<real_t>* o);
|
||||
void endDocument();
|
||||
};
|
||||
ColladaExporter exporter;
|
||||
@@ -189,7 +244,7 @@ public:
|
||||
}
|
||||
bool ready();
|
||||
void writeHeader();
|
||||
void write(const IfcGeom::TriangulationElement<real_t>* o);
|
||||
void write(const IfcGeom::TriangulationElement<real_t>* o);
|
||||
void write(const IfcGeom::BRepElement<real_t>* /*o*/) {}
|
||||
void finalize();
|
||||
bool isTesselated() const { return true; }
|
||||
|
||||
@@ -43,8 +43,14 @@ public:
|
||||
/// Use material names instead of unique IDs for naming materials.
|
||||
/// Applicable for OBJ and DAE output.
|
||||
USE_MATERIAL_NAMES = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 3),
|
||||
/// Use element types instead of unique IDs for naming elements.
|
||||
/// Applicable for DAE output.
|
||||
USE_ELEMENT_TYPES = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 4),
|
||||
/// Order the elements using their IfcBuildingStorey parent
|
||||
/// Applicable for DAE output
|
||||
USE_ELEMENT_HIERARCHY = 1 << (IfcGeom::IteratorSettings::NUM_SETTINGS + 5),
|
||||
/// Number of different setting flags.
|
||||
NUM_SETTINGS = 3
|
||||
NUM_SETTINGS = 5
|
||||
};
|
||||
|
||||
SerializerSettings()
|
||||
|
||||
@@ -267,6 +267,12 @@ int main(int argc, char** argv)
|
||||
("use-material-names",
|
||||
"Use material names instead of unique IDs for naming materials upon serialization. "
|
||||
"Applicable for OBJ and DAE output.")
|
||||
("use-element-types",
|
||||
"Use element types instead of unique IDs for naming elements upon serialization. "
|
||||
"Applicable for DAE output.")
|
||||
("use-element-hierarchy",
|
||||
"Order the elements using their IfcBuildingStorey parent. "
|
||||
"Applicable for DAE output.")
|
||||
("center-model",
|
||||
"Centers the elements upon serialization by applying the center point of "
|
||||
"all placements as an offset. Applicable for OBJ and DAE output.")
|
||||
@@ -335,6 +341,8 @@ int main(int argc, char** argv)
|
||||
const bool use_element_names = vmap.count("use-element-names") != 0;
|
||||
const bool use_element_guids = vmap.count("use-element-guids") != 0 ;
|
||||
const bool use_material_names = vmap.count("use-material-names") != 0;
|
||||
const bool use_element_types = vmap.count("use-element-types") != 0;
|
||||
const bool use_element_hierarchy = vmap.count("use-element-hierarchy") != 0;
|
||||
const bool no_normals = vmap.count("no-normals") != 0 ;
|
||||
const bool center_model = vmap.count("center-model") != 0 ;
|
||||
const bool model_offset = vmap.count("model-offset") != 0 ;
|
||||
@@ -459,10 +467,13 @@ int main(int argc, char** argv)
|
||||
settings.set(IfcGeom::IteratorSettings::APPLY_LAYERSETS, enable_layerset_slicing);
|
||||
settings.set(IfcGeom::IteratorSettings::NO_NORMALS, no_normals);
|
||||
settings.set(IfcGeom::IteratorSettings::GENERATE_UVS, generate_uvs);
|
||||
settings.set(IfcGeom::IteratorSettings::SEARCH_FLOOR, use_element_hierarchy);
|
||||
|
||||
settings.set(SerializerSettings::USE_ELEMENT_NAMES, use_element_names);
|
||||
settings.set(SerializerSettings::USE_ELEMENT_GUIDS, use_element_guids);
|
||||
settings.set(SerializerSettings::USE_MATERIAL_NAMES, use_material_names);
|
||||
settings.set(SerializerSettings::USE_ELEMENT_TYPES, use_element_types);
|
||||
settings.set(SerializerSettings::USE_ELEMENT_HIERARCHY, use_element_hierarchy);
|
||||
settings.set_deflection_tolerance(deflection_tolerance);
|
||||
settings.precision = precision;
|
||||
|
||||
@@ -497,6 +508,14 @@ int main(int argc, char** argv)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (serializer->settings().get(SerializerSettings::USE_ELEMENT_HIERARCHY) && output_extension != ".dae")
|
||||
{
|
||||
Logger::Error("--use-element-hierarchy can be used only with .dae output.");
|
||||
write_log();
|
||||
print_usage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const bool is_tesselated = serializer->isTesselated(); // isTesselated() doesn't change at run-time
|
||||
if (!is_tesselated) {
|
||||
if (weld_vertices) {
|
||||
@@ -519,7 +538,7 @@ int main(int argc, char** argv)
|
||||
|
||||
time_t start,end;
|
||||
time(&start);
|
||||
|
||||
|
||||
IfcGeom::Iterator<real_t> context_iterator(settings, &ifc_file, filter_funcs);
|
||||
if (!context_iterator.initialize()) {
|
||||
/// @todo It would be nice to know and print separate error prints for a case where we found no entities
|
||||
@@ -577,11 +596,16 @@ int main(int argc, char** argv)
|
||||
|
||||
do {
|
||||
IfcGeom::Element<real_t> *geom_object = context_iterator.get();
|
||||
if (is_tesselated) {
|
||||
|
||||
if (is_tesselated)
|
||||
{
|
||||
serializer->write(static_cast<const IfcGeom::TriangulationElement<real_t>*>(geom_object));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
serializer->write(static_cast<const IfcGeom::BRepElement<real_t>*>(geom_object));
|
||||
}
|
||||
|
||||
const int progress = context_iterator.progress() / 2;
|
||||
if (old_progress != progress) Logger::ProgressBar(progress);
|
||||
old_progress = progress;
|
||||
|
||||
@@ -51,6 +51,7 @@ public:
|
||||
void writeHeader();
|
||||
void writeMaterial(const IfcGeom::Material& style);
|
||||
void write(const IfcGeom::TriangulationElement<real_t>* o);
|
||||
void write(const IfcGeom::TriangulationElement<real_t>* o, const IfcGeom::Element<real_t>* parent) {}
|
||||
void write(const IfcGeom::BRepElement<real_t>* /*o*/) {}
|
||||
void finalize() {}
|
||||
bool isTesselated() const { return true; }
|
||||
|
||||
@@ -110,6 +110,7 @@ boost::optional<std::string> format_attribute(const Argument* argument, IfcUtil:
|
||||
IfcSchema::IfcLocalPlacement* placement = e->as<IfcSchema::IfcLocalPlacement>();
|
||||
gp_Trsf trsf;
|
||||
IfcGeom::Kernel kernel;
|
||||
|
||||
if (kernel.convert(placement, trsf)) {
|
||||
std::stringstream stream;
|
||||
for (int i = 1; i < 5; ++i) {
|
||||
|
||||
@@ -81,7 +81,33 @@ namespace IfcGeom {
|
||||
std::string _unique_id;
|
||||
Transformation<P> _transformation;
|
||||
IfcSchema::IfcProduct* product_;
|
||||
std::vector<const IfcGeom::Element<P>*> _parents;
|
||||
public:
|
||||
|
||||
friend bool operator == (const Element<P> & element1, const Element<P> & element2)
|
||||
{
|
||||
return element1.id() == element2.id();
|
||||
}
|
||||
|
||||
// Use the id to compare, or the elevation is the elements are IfcBuildingStoreys and the elevation is set
|
||||
friend bool operator < (const Element<P> & element1, const Element<P> & element2)
|
||||
{
|
||||
if (element1.type() == "IfcBuildingStorey" && element2.type() == "IfcBuildingStorey")
|
||||
{
|
||||
IfcSchema::IfcBuildingStorey* storey1 = NULL;
|
||||
IfcSchema::IfcBuildingStorey* storey2 = NULL;
|
||||
|
||||
storey1 = (IfcSchema::IfcBuildingStorey*)element1.product();
|
||||
storey2 = (IfcSchema::IfcBuildingStorey*)element2.product();
|
||||
|
||||
if (storey1 != NULL && storey2 != NULL && storey1->hasElevation() && storey2->hasElevation())
|
||||
{
|
||||
return storey1->Elevation() < storey2->Elevation();
|
||||
}
|
||||
}
|
||||
return element1.id() < element2.id();
|
||||
}
|
||||
|
||||
int id() const { return _id; }
|
||||
int parent_id() const { return _parent_id; }
|
||||
const std::string& name() const { return _name; }
|
||||
@@ -91,14 +117,21 @@ namespace IfcGeom {
|
||||
const std::string& unique_id() const { return _unique_id; }
|
||||
const Transformation<P>& transformation() const { return _transformation; }
|
||||
IfcSchema::IfcProduct* product() const { return product_; }
|
||||
const std::vector<const IfcGeom::Element<P>*> parents() const { return _parents; }
|
||||
void SetParents(std::vector<const IfcGeom::Element<P>*> newparents) { _parents = newparents; }
|
||||
|
||||
Element(const ElementSettings& settings, int id, int parent_id, const std::string& name, const std::string& type,
|
||||
const std::string& guid, const std::string& context, const gp_Trsf& trsf, IfcSchema::IfcProduct *product)
|
||||
: _id(id), _parent_id(parent_id), _name(name), _type(type), _guid(guid), _context(context), _transformation(settings, trsf)
|
||||
, product_(product)
|
||||
{
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "product-" << IfcParse::IfcGlobalId(guid).formatted();
|
||||
try { oss << "product-" << IfcParse::IfcGlobalId(guid).formatted(); }
|
||||
catch (std::exception e)
|
||||
{
|
||||
oss << "product-cannotfindId";
|
||||
}
|
||||
|
||||
if (!_context.empty()) {
|
||||
std::string ctx = _context;
|
||||
boost::to_lower(ctx);
|
||||
|
||||
@@ -573,6 +573,52 @@ namespace IfcGeom {
|
||||
if (current_triangulation) { ret = current_triangulation; }
|
||||
else if (current_serialization) { ret = current_serialization; }
|
||||
else if (current_shape_model) { ret = current_shape_model; }
|
||||
|
||||
// If we want to organize the element considering their hierarchy
|
||||
if (settings.get(IteratorSettings::SEARCH_FLOOR))
|
||||
{
|
||||
// We are going to build a vector with the element parents.
|
||||
// First, create the parent vector
|
||||
std::vector<const IfcGeom::Element<P>*> parents;
|
||||
|
||||
// if the element has a parent
|
||||
if (ret->parent_id() != -1)
|
||||
{
|
||||
const IfcGeom::Element<P>* parent_object = NULL;
|
||||
bool hasParent = true;
|
||||
|
||||
// get the parent
|
||||
try { parent_object = getObject(ret->parent_id()); }
|
||||
catch (std::exception e)
|
||||
{
|
||||
hasParent = false;
|
||||
}
|
||||
|
||||
// Add the previously found parent to the vector
|
||||
if (hasParent) parents.insert(parents.begin(), parent_object);
|
||||
|
||||
// We need to find all the parents
|
||||
while (parent_object != NULL && hasParent)
|
||||
{
|
||||
// Find the next parent
|
||||
try { parent_object = getObject(parent_object->parent_id()); }
|
||||
catch (std::exception e)
|
||||
{
|
||||
std::cout << e.what();
|
||||
hasParent = false;
|
||||
}
|
||||
|
||||
// Add the previously found parent to the vector
|
||||
if (hasParent) parents.insert(parents.begin(), parent_object);
|
||||
|
||||
hasParent = hasParent && parent_object->parent_id() != -1;
|
||||
}
|
||||
|
||||
// when done push the parent list in the Element object
|
||||
ret->SetParents(parents);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -584,18 +630,15 @@ namespace IfcGeom {
|
||||
}
|
||||
|
||||
const Element<P>* getObject(int id) {
|
||||
|
||||
gp_Trsf trsf;
|
||||
int parent_id = -1;
|
||||
std::string instance_type, product_name, product_guid;
|
||||
IfcSchema::IfcProduct* ifc_product = 0;
|
||||
|
||||
try {
|
||||
const IfcUtil::IfcBaseClass* ifc_entity = ifc_file->entityById(id);
|
||||
instance_type = IfcSchema::Type::ToString(ifc_entity->type());
|
||||
if ( ifc_entity->is(IfcSchema::Type::IfcProduct) ) {
|
||||
ifc_product = (IfcSchema::IfcProduct*)ifc_entity;
|
||||
|
||||
product_guid = ifc_product->GlobalId();
|
||||
product_name = ifc_product->hasName() ? ifc_product->Name() : "";
|
||||
|
||||
@@ -606,16 +649,14 @@ namespace IfcGeom {
|
||||
parent_id = parent_object->entity->id();
|
||||
}
|
||||
} catch (...) {}
|
||||
|
||||
try {
|
||||
kernel.convert(ifc_product->ObjectPlacement(), trsf);
|
||||
} catch (...) {}
|
||||
}
|
||||
} catch(...) {}
|
||||
|
||||
ElementSettings element_settings(settings, unit_magnitude, instance_type);
|
||||
|
||||
Element<P>* ifc_object = new Element<P>(element_settings, id, parent_id, product_name, instance_type, product_guid, "", trsf, ifc_product);
|
||||
|
||||
return ifc_object;
|
||||
}
|
||||
|
||||
|
||||
@@ -78,8 +78,10 @@ namespace IfcGeom
|
||||
GENERATE_UVS = 1 << 12,
|
||||
/// Specifies whether to slice representations according to associated IfcLayerSets.
|
||||
APPLY_LAYERSETS = 1 << 13,
|
||||
/// Search for a parent of type IfcBuildingStorey for each representation
|
||||
SEARCH_FLOOR = 1 << 14,
|
||||
/// Number of different setting flags.
|
||||
NUM_SETTINGS = 13
|
||||
NUM_SETTINGS = 14
|
||||
};
|
||||
/// Used to store logical OR combination of setting flags.
|
||||
typedef unsigned SettingField;
|
||||
|
||||
Reference in New Issue
Block a user