diff --git a/src/ifcconvert/ColladaSerializer.cpp b/src/ifcconvert/ColladaSerializer.cpp index 6ef394ad2e..01c337c60f 100644 --- a/src/ifcconvert/ColladaSerializer.cpp +++ b/src/ifcconvert/ColladaSerializer.cpp @@ -29,11 +29,20 @@ #include #include -#include - #include #include +#include +#include +#include +#include +#include +#include +#include + +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& 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& material_ids, const std::vector& matrix) + const std::vector& material_ids, const std::vector& 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& parent){ + //we open the visual scene tag if it's not. + if (!scene_opened) { + openVisualScene(scene_id); + scene_opened = true; + } + + + const std::vector 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(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 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* o) { - const IfcGeom::Representation::Triangulation& 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(o->geometry().id()); - + const IfcGeom::Representation::Triangulation& 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(o->geometry().id()); std::vector 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* 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 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::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::const_iterator it = deferreds.begin(); it != deferreds.end(); ++it) { + + int parent_id = -1; + bool is_parent_tag_opened = false; + for (std::vector::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* o) { exporter.write(o); } + void ColladaSerializer::finalize() { exporter.endDocument(); } diff --git a/src/ifcconvert/ColladaSerializer.h b/src/ifcconvert/ColladaSerializer.h index faa092203f..9d19da0bfc 100644 --- a/src/ifcconvert/ColladaSerializer.h +++ b/src/ifcconvert/ColladaSerializer.h @@ -30,6 +30,7 @@ #pragma GCC diagnostic ignored "-Wignored-qualifiers" #endif #include +#include #include #include #include @@ -44,10 +45,16 @@ #include "../ifcconvert/GeometrySerializer.h" +#include +#include + + class ColladaSerializer : public GeometrySerializer { // TODO The vast amount of implement details of ColladaSerializer could be hidden to the cpp file. private: + std::stack parentStackId; + class ColladaExporter { private: @@ -78,6 +85,8 @@ private: const std::string scene_id; bool scene_opened; + std::stack parentNodes; + std::stack > 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& material_ids, const std::vector& matrix); + void addParent(const IfcGeom::Element& 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 matrix; @@ -132,10 +162,11 @@ private: std::vector materials; std::vector material_references; std::vector uvs; + std::vector*> parents; DeferredObject(const std::string& unique_id, const std::string& representation_id, const std::string& type, const std::vector& matrix, const std::vector& vertices, const std::vector& normals, const std::vector& faces, const std::vector& edges, const std::vector& material_ids, const std::vector& materials, - const std::vector& material_references, const std::vector& uvs) + const std::vector& material_references, const std::vector& uvs, const std::vector*>& 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& matrix, + const std::vector& vertices, const std::vector& normals, const std::vector& faces, + const std::vector& edges, const std::vector& material_ids, const std::vector& materials, + const std::vector& material_references, const std::vector& 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* 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 deferreds; virtual ~ColladaExporter() {} void startDocument(const std::string& unit_name, float unit_magnitude); - void write(const IfcGeom::TriangulationElement* o); + void write(const IfcGeom::TriangulationElement* o); void endDocument(); }; ColladaExporter exporter; @@ -189,7 +244,7 @@ public: } bool ready(); void writeHeader(); - void write(const IfcGeom::TriangulationElement* o); + void write(const IfcGeom::TriangulationElement* o); void write(const IfcGeom::BRepElement* /*o*/) {} void finalize(); bool isTesselated() const { return true; } diff --git a/src/ifcconvert/GeometrySerializer.h b/src/ifcconvert/GeometrySerializer.h index 7b7997d8ad..8543c468cb 100644 --- a/src/ifcconvert/GeometrySerializer.h +++ b/src/ifcconvert/GeometrySerializer.h @@ -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() diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index 6ec5660ebf..05bb2602b5 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -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 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 *geom_object = context_iterator.get(); - if (is_tesselated) { + + if (is_tesselated) + { serializer->write(static_cast*>(geom_object)); - } else { + } + else + { serializer->write(static_cast*>(geom_object)); } + const int progress = context_iterator.progress() / 2; if (old_progress != progress) Logger::ProgressBar(progress); old_progress = progress; diff --git a/src/ifcconvert/WavefrontObjSerializer.h b/src/ifcconvert/WavefrontObjSerializer.h index 2aa9708bdc..5411d5f64f 100644 --- a/src/ifcconvert/WavefrontObjSerializer.h +++ b/src/ifcconvert/WavefrontObjSerializer.h @@ -51,6 +51,7 @@ public: void writeHeader(); void writeMaterial(const IfcGeom::Material& style); void write(const IfcGeom::TriangulationElement* o); + void write(const IfcGeom::TriangulationElement* o, const IfcGeom::Element* parent) {} void write(const IfcGeom::BRepElement* /*o*/) {} void finalize() {} bool isTesselated() const { return true; } diff --git a/src/ifcconvert/XmlSerializer.cpp b/src/ifcconvert/XmlSerializer.cpp index 94a6dd562f..b0173273f6 100644 --- a/src/ifcconvert/XmlSerializer.cpp +++ b/src/ifcconvert/XmlSerializer.cpp @@ -110,6 +110,7 @@ boost::optional format_attribute(const Argument* argument, IfcUtil: IfcSchema::IfcLocalPlacement* placement = e->as(); gp_Trsf trsf; IfcGeom::Kernel kernel; + if (kernel.convert(placement, trsf)) { std::stringstream stream; for (int i = 1; i < 5; ++i) { diff --git a/src/ifcgeom/IfcGeomElement.h b/src/ifcgeom/IfcGeomElement.h index ef34462df9..21411d806e 100644 --- a/src/ifcgeom/IfcGeomElement.h +++ b/src/ifcgeom/IfcGeomElement.h @@ -81,7 +81,33 @@ namespace IfcGeom { std::string _unique_id; Transformation

_transformation; IfcSchema::IfcProduct* product_; + std::vector*> _parents; public: + + friend bool operator == (const Element

& element1, const Element

& 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

& element1, const Element

& 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

& transformation() const { return _transformation; } IfcSchema::IfcProduct* product() const { return product_; } + const std::vector*> parents() const { return _parents; } + void SetParents(std::vector*> 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); diff --git a/src/ifcgeom/IfcGeomIterator.h b/src/ifcgeom/IfcGeomIterator.h index 1bea3ddaaa..caa0e922a2 100644 --- a/src/ifcgeom/IfcGeomIterator.h +++ b/src/ifcgeom/IfcGeomIterator.h @@ -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*> parents; + + // if the element has a parent + if (ret->parent_id() != -1) + { + const IfcGeom::Element

* 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

* 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

* ifc_object = new Element

(element_settings, id, parent_id, product_name, instance_type, product_guid, "", trsf, ifc_product); - return ifc_object; } diff --git a/src/ifcgeom/IfcGeomIteratorSettings.h b/src/ifcgeom/IfcGeomIteratorSettings.h index edc6e4ac15..b99088db88 100644 --- a/src/ifcgeom/IfcGeomIteratorSettings.h +++ b/src/ifcgeom/IfcGeomIteratorSettings.h @@ -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;