diff --git a/src/ifcconvert/ColladaSerializer.cpp b/src/ifcconvert/ColladaSerializer.cpp index bf3e65b2ca..8532c291a7 100644 --- a/src/ifcconvert/ColladaSerializer.cpp +++ b/src/ifcconvert/ColladaSerializer.cpp @@ -19,48 +19,96 @@ #ifdef WITH_OPENCOLLADA -#include - #include "ColladaSerializer.h" -std::string collada_id(const std::string& s) { - std::string id; - id.reserve(s.size()); - for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) { - const std::string::value_type c = *it; - if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c == '_') || ( c == '-')) { - id.push_back(c); - } - } - return id; +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static void collada_id(std::string &s) +{ + IfcUtil::sanitate_material_name(s); + IfcUtil::escape_xml(s); } -void ColladaSerializer::ColladaExporter::ColladaGeometries::addFloatSource(const std::string& mesh_id, const std::string& suffix, const std::vector& floats, const char* coords /* = "XYZ" */) { +#if 0 +static std::vector boxProjectUVs(const std::vector &vertices, const std::vector &normals) { + assert(vertices.size() == normals.size()); + //TODO if (vertices.size() != normals.size()) log error? + std::vector uvs; + uvs.resize(vertices.size() / 3 * 2); + for(size_t uv_idx = 0, v_idx = 0; + uv_idx < uvs.size() && v_idx < vertices.size() && v_idx < normals.size(); + uv_idx += 2, v_idx += 3) { + + real_t n_x = normals[v_idx], n_y = normals[v_idx+1], n_z = normals[v_idx+2]; + real_t v_x = vertices[v_idx], v_y = vertices[v_idx+1], v_z = vertices[v_idx+2]; + + if (std::abs(n_x) > std::abs(n_y) && std::abs(n_x) > std::abs(n_z)) { + uvs[uv_idx] = v_z; + uvs[uv_idx+1] = v_y; + } + if (std::abs(n_y) > std::abs(n_x) && std::abs(n_y) > std::abs(n_z)) { + uvs[uv_idx] = v_x; + uvs[uv_idx+1] = v_z; + } + if (std::abs(n_z) > std::abs(n_x) && std::abs(n_z) > std::abs(n_y)) { + uvs[uv_idx] = v_x; + uvs[uv_idx+1] = v_y; + } + } + + return uvs; +} +#endif + +void ColladaSerializer::ColladaExporter::ColladaGeometries::addFloatSource(const std::string& mesh_id, + const std::string& suffix, const std::vector& floats, const char* coords /* = "XYZ" */) +{ COLLADASW::FloatSource source(mSW); source.setId(mesh_id + suffix); source.setArrayId(mesh_id + suffix + COLLADASW::LibraryGeometries::ARRAY_ID_SUFFIX); - source.setAccessorStride((unsigned long)strlen(coords)); - source.setAccessorCount((unsigned long)floats.size() / 3); - for (unsigned int i = 0; i < source.getAccessorStride(); ++i) { + const size_t num_elems = strlen(coords); + source.setAccessorStride(static_cast(num_elems)); + source.setAccessorCount(static_cast(floats.size() / num_elems)); + for (size_t i = 0; i < num_elems; ++i) { source.getParameterNameList().push_back(std::string(1, coords[i])); } source.prepareToAppendValues(); - for (std::vector::const_iterator it = floats.begin(); it != floats.end(); ++it) { + for (std::vector::const_iterator it = floats.begin(); it != floats.end(); ++it) { source.appendValues(*it); } source.finish(); } - -void ColladaSerializer::ColladaExporter::ColladaGeometries::write(const std::string mesh_id, const std::string& default_material_name, const std::vector& positions, const std::vector& normals, const std::vector& faces, const std::vector& edges, const std::vector material_ids, const std::vector& materials) { + +void ColladaSerializer::ColladaExporter::ColladaGeometries::write( + const std::string &mesh_id, const std::string& default_material_name, const std::vector& positions, + const std::vector& normals, const std::vector& faces, const std::vector& edges, + const std::vector material_ids, const std::vector& materials) +{ 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(); - +#if 0 + const bool generate_uvs = (has_normals && serializer->settings()..get(IfcGeom::IteratorSettings::GENERATE_UVS)); +#endif addFloatSource(mesh_id, COLLADASW::LibraryGeometries::POSITIONS_SOURCE_ID_SUFFIX, positions); if (has_normals) { addFloatSource(mesh_id, COLLADASW::LibraryGeometries::NORMALS_SOURCE_ID_SUFFIX, normals); +#if 0 + if (generate_uvs) { + addFloatSource(mesh_id, COLLADASW::LibraryGeometries::TEXCOORDS_SOURCE_ID_SUFFIX, boxProjectUVs(positions, normals), "UV"); + } +#endif } COLLADASW::VerticesElement vertices(mSW); @@ -73,20 +121,29 @@ void ColladaSerializer::ColladaExporter::ColladaGeometries::write(const std::str int previous_material_id = -1; for (std::vector::const_iterator it = faces.begin(); !faces.empty(); it += 3) { const int current_material_id = *(material_it++); - const unsigned long num_triangles = (unsigned long)std::distance(index_range_start, it) / 3; + const size_t num_triangles = std::distance(index_range_start, it) / 3; if ((previous_material_id != current_material_id && num_triangles > 0) || (it == faces.end())) { COLLADASW::Triangles triangles(mSW); - triangles.setMaterial(materials[previous_material_id].name()); - triangles.setCount(num_triangles); + std::string material_name = (serializer->settings().get(IfcGeom::IteratorSettings::USE_NAMES) ? materials[previous_material_id].original_name() : materials[previous_material_id].name()); + collada_id(material_name); + triangles.setMaterial(material_name); + triangles.setCount((unsigned long)num_triangles); int offset = 0; - triangles.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::VERTEX,"#" + mesh_id + COLLADASW::LibraryGeometries::VERTICES_ID_SUFFIX, offset++ ) ); + triangles.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::VERTEX,"#" + mesh_id + COLLADASW::LibraryGeometries::VERTICES_ID_SUFFIX, offset++)); if (has_normals) { - triangles.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::NORMAL,"#" + mesh_id + COLLADASW::LibraryGeometries::NORMALS_SOURCE_ID_SUFFIX, offset++ ) ); + triangles.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::NORMAL,"#" + mesh_id + COLLADASW::LibraryGeometries::NORMALS_SOURCE_ID_SUFFIX, offset++)); } +#if 0 + if (generate_uvs) { + triangles.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::TEXCOORD,"#" + mesh_id + COLLADASW::LibraryGeometries::TEXCOORDS_SOURCE_ID_SUFFIX, offset++)); + } +#endif triangles.prepareToAppendValues(); for (std::vector::const_iterator jt = index_range_start; jt != it; ++jt) { const int idx = *jt; - if (has_normals) { + /*if (has_normals && generate_uvs) { + triangles.appendValues(idx, idx, idx); + } else*/ if(has_normals) { triangles.appendValues(idx, idx); } else { triangles.appendValues(idx); @@ -125,10 +182,12 @@ void ColladaSerializer::ColladaExporter::ColladaGeometries::write(const std::str for (linelist_t::const_iterator it = linelist.begin(); it != linelist.end(); ++it) { COLLADASW::Lines lines(mSW); - lines.setMaterial(materials[it->first].name()); + std::string material_name = (serializer->settings().get(IfcGeom::IteratorSettings::USE_NAMES) ? materials[it->first].original_name() : materials[it->first].name()); + collada_id(material_name); + lines.setMaterial(material_name); lines.setCount((unsigned long)it->second.size()); int offset = 0; - lines.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::VERTEX, "#" + mesh_id + COLLADASW::LibraryGeometries::VERTICES_ID_SUFFIX, offset++)); + lines.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::VERTEX, "#" + mesh_id + COLLADASW::LibraryGeometries::VERTICES_ID_SUFFIX, offset)); lines.prepareToAppendValues(); lines.appendValues(it->second); lines.finish(); @@ -141,8 +200,11 @@ void ColladaSerializer::ColladaExporter::ColladaGeometries::write(const std::str void ColladaSerializer::ColladaExporter::ColladaGeometries::close() { closeLibrary(); } - -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) { + +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) +{ if (!scene_opened) { openVisualScene(scene_id); scene_opened = true; @@ -156,18 +218,26 @@ void ColladaSerializer::ColladaExporter::ColladaScene::add(const std::string& no // 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] = { - {matrix[0], matrix[3], matrix[6], matrix[ 9]}, - {matrix[1], matrix[4], matrix[7], matrix[10]}, - {matrix[2], matrix[5], matrix[8], matrix[11]}, - { 0, 0, 0, 1} + { (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] }, + { 0, 0, 0, 1 } }; +#if 0 + matrix_array[0][3] += serializer->settings().offset[0]; + matrix_array[1][3] += serializer->settings().offset[1]; + matrix_array[2][3] += serializer->settings().offset[2]; +#endif + node.start(); node.addMatrix(matrix_array); COLLADASW::InstanceGeometry instanceGeometry(mSW); instanceGeometry.setUrl ("#" + geom_name); - for (std::vector::const_iterator it = material_ids.begin(); it != material_ids.end(); ++it) { - COLLADASW::InstanceMaterial material (*it, "#" + *it); + foreach(std::string material_name, material_ids) { + /// @todo This is done 6 times in this file, try to perform this once and be done with the material naming for the export. + collada_id(material_name); + COLLADASW::InstanceMaterial material (material_name, "#" + material_name); instanceGeometry.getBindMaterial().getInstanceMaterialList().push_back(material); } instanceGeometry.add(); @@ -183,9 +253,12 @@ void ColladaSerializer::ColladaExporter::ColladaScene::write() { scene.add(); } } - -void ColladaSerializer::ColladaExporter::ColladaMaterials::ColladaEffects::write(const IfcGeom::Material& material) { - openEffect(collada_id(material.name()) + "-fx"); + +void ColladaSerializer::ColladaExporter::ColladaMaterials::ColladaEffects::write(const IfcGeom::Material& material) +{ + std::string material_name = (serializer->settings().get(IfcGeom::IteratorSettings::USE_NAMES) ? material.original_name() : material.name()); + collada_id(material_name); + openEffect(material_name + "-fx"); COLLADASW::EffectProfile effect(mSW); effect.setShaderType(COLLADASW::EffectProfile::LAMBERT); if (material.hasDiffuse()) { @@ -214,7 +287,7 @@ void ColladaSerializer::ColladaExporter::ColladaMaterials::ColladaEffects::write void ColladaSerializer::ColladaExporter::ColladaMaterials::ColladaEffects::close() { closeLibrary(); } - + void ColladaSerializer::ColladaExporter::ColladaMaterials::add(const IfcGeom::Material& material) { if (!contains(material)) { effects.write(material); @@ -228,15 +301,18 @@ bool ColladaSerializer::ColladaExporter::ColladaMaterials::contains(const IfcGeo void ColladaSerializer::ColladaExporter::ColladaMaterials::write() { effects.close(); - for (std::vector::const_iterator it = materials.begin(); it != materials.end(); ++it) { - const std::string& material_name = collada_id((*it).name()); + foreach(const IfcGeom::Material& material, materials) { + std::string material_name = (serializer->settings().get(IfcGeom::IteratorSettings::USE_NAMES) ? material.original_name() : material.name()); + std::string material_name_unescaped = material_name; // workaround double-escaping that would occur in addInstanceEffect() + IfcUtil::sanitate_material_name(material_name_unescaped); + collada_id(material_name); openMaterial(material_name); - addInstanceEffect("#" + material_name + "-fx"); + addInstanceEffect("#" + material_name_unescaped + "-fx"); closeMaterial(); } closeLibrary(); } - + void ColladaSerializer::ColladaExporter::startDocument(const std::string& unit_name, float unit_magnitude) { stream.startDocument(); @@ -247,14 +323,19 @@ void ColladaSerializer::ColladaExporter::startDocument(const std::string& unit_n asset.add(); } -void ColladaSerializer::ColladaExporter::write(const std::string& unique_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) { +void ColladaSerializer::ColladaExporter::write(const std::string& unique_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) +{ std::vector material_references; - for (std::vector::const_iterator it = _materials.begin(); it != _materials.end(); ++it) { - const IfcGeom::Material& material = *it; + foreach(const IfcGeom::Material& material, _materials) { if (!materials.contains(material)) { materials.add(material); } - material_references.push_back(collada_id(material.name())); + std::string material_name = (serializer->settings().get(IfcGeom::IteratorSettings::USE_NAMES) ? material.original_name() : material.name()); + collada_id(material_name); + material_references.push_back(material_name); } deferreds.push_back(DeferredObject(unique_id, type, matrix, vertices, normals, faces, edges, material_ids, _materials, material_references)); } @@ -275,7 +356,7 @@ void ColladaSerializer::ColladaExporter::endDocument() { scene.write(); stream.endDocument(); } - + bool ColladaSerializer::ready() { return true; } @@ -284,9 +365,12 @@ void ColladaSerializer::writeHeader() { exporter.startDocument(unit_name, unit_magnitude); } -void ColladaSerializer::write(const IfcGeom::TriangulationElement* o) { - const IfcGeom::Representation::Triangulation& mesh = o->geometry(); - exporter.write(o->unique_id(), o->type(), o->transformation().matrix().data(), mesh.verts(), mesh.normals(), mesh.faces(), mesh.edges(), mesh.material_ids(), mesh.materials()); +void ColladaSerializer::write(const IfcGeom::TriangulationElement* o) { + const IfcGeom::Representation::Triangulation& mesh = o->geometry(); + const std::string name = settings().get(IfcGeom::IteratorSettings::USE_GUIDS) ? + o->guid() : (settings().get(IfcGeom::IteratorSettings::USE_NAMES) ? o->name() : o->unique_id()); + exporter.write(name, o->type(), o->transformation().matrix().data(), mesh.verts(), + mesh.normals(), mesh.faces(), mesh.edges(), mesh.material_ids(), mesh.materials()); } void ColladaSerializer::finalize() { diff --git a/src/ifcconvert/ColladaSerializer.h b/src/ifcconvert/ColladaSerializer.h index cf3a35e733..740b20707a 100644 --- a/src/ifcconvert/ColladaSerializer.h +++ b/src/ifcconvert/ColladaSerializer.h @@ -27,17 +27,10 @@ #pragma warning(disable : 4201 4512) #endif #include -#include #include -#include -#include -#include -#include #include #include #include -#include -#include #ifdef _MSC_VER #pragma warning(pop) #endif @@ -58,12 +51,18 @@ private: ColladaGeometries(const ColladaGeometries&); //N/A ColladaGeometries& operator =(const ColladaGeometries&); //N/A public: - explicit ColladaGeometries(COLLADASW::StreamWriter& stream) + explicit ColladaGeometries(COLLADASW::StreamWriter& stream, ColladaSerializer *_serializer) : COLLADASW::LibraryGeometries(&stream) + , serializer(_serializer) {} - void addFloatSource(const std::string& mesh_id, const std::string& suffix, const std::vector& floats, const char* coords = "XYZ"); - void write(const std::string mesh_id, const std::string& default_material_name, const std::vector& positions, const std::vector& normals, const std::vector& faces, const std::vector& edges, const std::vector material_ids, const std::vector& materials); + void addFloatSource(const std::string& mesh_id, const std::string& suffix, + const std::vector& floats, const char* coords = "XYZ"); + void write(const std::string &mesh_id, const std::string& default_material_name, + const std::vector& positions, const std::vector& normals, + const std::vector& faces, const std::vector& edges, + const std::vector material_ids, const std::vector& materials); void close(); + ColladaSerializer *serializer; }; class ColladaScene : public COLLADASW::LibraryVisualScenes { @@ -74,13 +73,16 @@ private: const std::string scene_id; bool scene_opened; public: - ColladaScene(const std::string& scene_id, COLLADASW::StreamWriter& stream) + ColladaScene(const std::string& scene_id, COLLADASW::StreamWriter& stream, ColladaSerializer *_serializer) : COLLADASW::LibraryVisualScenes(&stream) , scene_id(scene_id) - , scene_opened(false) + , scene_opened(false) + , serializer(_serializer) {} - 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 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 write(); + ColladaSerializer *serializer; }; class ColladaMaterials : public COLLADASW::LibraryMaterials { @@ -97,32 +99,36 @@ private: {} void write(const IfcGeom::Material& material); void close(); + ColladaSerializer *serializer; }; std::vector materials; - ColladaEffects effects; public: - explicit ColladaMaterials(COLLADASW::StreamWriter& stream) + explicit ColladaMaterials(COLLADASW::StreamWriter& stream, ColladaSerializer *_serializer) : COLLADASW::LibraryMaterials(&stream) , effects(stream) + , serializer(_serializer) {} void add(const IfcGeom::Material& material); bool contains(const IfcGeom::Material& material); void write(); + ColladaSerializer *serializer; + ColladaEffects effects; }; class DeferredObject { public: std::string unique_id, type; - std::vector matrix; - std::vector vertices; - std::vector normals; + std::vector matrix; + std::vector vertices; + std::vector normals; std::vector faces; std::vector edges; std::vector material_ids; std::vector materials; std::vector material_references; - DeferredObject(const std::string& unique_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) + DeferredObject(const std::string& unique_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) : unique_id(unique_id) , type(type) , matrix(matrix) @@ -137,35 +143,45 @@ private: }; COLLADABU::NativeString filename; COLLADASW::StreamWriter stream; - ColladaGeometries geometries; ColladaScene scene; - ColladaMaterials materials; public: - ColladaExporter(const std::string& scene_name, const std::string& fn) + ColladaExporter(const std::string& scene_name, const std::string& fn, ColladaSerializer *_serializer) : filename(fn.c_str()) , stream(filename) - , geometries(stream) - , scene(scene_name, stream) - , materials(stream) - {} + , geometries(stream, _serializer) + , scene(scene_name, stream, _serializer) + , materials(stream, _serializer) + , serializer(_serializer) + { + } + ColladaMaterials materials; + ColladaSerializer *serializer; + ColladaGeometries geometries; std::vector deferreds; virtual ~ColladaExporter() {} void startDocument(const std::string& unit_name, float unit_magnitude); - void write(const std::string& unique_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); + void write(const std::string& unique_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); void endDocument(); }; ColladaExporter exporter; std::string unit_name; float unit_magnitude; public: - ColladaSerializer(const std::string& dae_filename) - : GeometrySerializer() - , exporter("IfcOpenShell", dae_filename) - {} + ColladaSerializer(const std::string& dae_filename, const IfcGeom::IteratorSettings &settings) + : GeometrySerializer(settings) + , exporter("IfcOpenShell", dae_filename, this) + { + exporter.serializer = this; + exporter.materials.serializer = this; + exporter.materials.effects.serializer = this; + exporter.geometries.serializer = this; + } bool ready(); void writeHeader(); - void write(const IfcGeom::TriangulationElement* o); - void write(const IfcGeom::BRepElement* /*o*/) {} + void write(const IfcGeom::TriangulationElement* o); + void write(const IfcGeom::BRepElement* /*o*/) {} void finalize(); bool isTesselated() const { return true; } void setUnitNameAndMagnitude(const std::string& name, float magnitude) { diff --git a/src/ifcconvert/GeometrySerializer.h b/src/ifcconvert/GeometrySerializer.h index 0a62defa18..1eb36e6b67 100644 --- a/src/ifcconvert/GeometrySerializer.h +++ b/src/ifcconvert/GeometrySerializer.h @@ -20,17 +20,26 @@ #ifndef GEOMETRYSERIALIZER_H #define GEOMETRYSERIALIZER_H +typedef double real_t; /**< @todo Will be configurable */ + #include "../ifcconvert/Serializer.h" #include "../ifcgeom/IfcGeomIterator.h" class GeometrySerializer : public Serializer { public: + GeometrySerializer(const IfcGeom::IteratorSettings &settings) : settings_(settings) {} virtual ~GeometrySerializer() {} virtual bool isTesselated() const = 0; - virtual void write(const IfcGeom::TriangulationElement* o) = 0; - virtual void write(const IfcGeom::BRepElement* o) = 0; + virtual void write(const IfcGeom::TriangulationElement* o) = 0; + virtual void write(const IfcGeom::BRepElement* o) = 0; virtual void setUnitNameAndMagnitude(const std::string& name, float magnitude) = 0; + + const IfcGeom::IteratorSettings& settings() const { return settings_; } + IfcGeom::IteratorSettings& settings() { return settings_; } + +protected: + IfcGeom::IteratorSettings settings_; }; -#endif \ No newline at end of file +#endif diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index e0483f75df..11d0259295 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -47,7 +47,6 @@ #include #include -typedef double real_t; /**< @todo Will be configurable */ #define INF std::numeric_limits::infinity() real_t bounds_min[3] = { INF, INF, INF }; real_t bounds_max[3] = { -INF, -INF, -INF }; @@ -191,14 +190,14 @@ int main(int argc, char** argv) { ("bounds", boost::program_options::value(&bounds), "Specifies the bounding rectangle, for example 512x512, to which the " "output will be scaled. Only used when converting to SVG.") - /*("use-names", + ("use-names", "Use entity names instead of unique IDs for naming objects and materials " "upon serialization. Applicable for .obj and .dae output.") ("use-guids", "Use entity GUIDs instead of unique IDs for naming objects upon serialization. " "Overrides possible usage of --use-names for objects but not for materials." "Applicable for .obj and .dae output.") - ("center-model", + /*("center-model", "Centers the models upon serialization by applying the center point of " "the scene bounds as an offset. Applicable only for .dae output currently.") ("generate-uvs", @@ -257,8 +256,8 @@ int main(int argc, char** argv) { bool include_entities = vmap.count("include") != 0; const bool include_plan = vmap.count("plan") != 0; const bool include_model = vmap.count("model") != 0 || (!include_plan); - //const bool use_names = vmap.count("use-names") != 0; - //const bool use_guids = vmap.count("use-guids") != 0 ; + const bool use_names = vmap.count("use-names") != 0; + const bool use_guids = vmap.count("use-guids") != 0 ; //const bool no_normals = vmap.count("no-normals") != 0 ; //const bool center_model = vmap.count("center-model") != 0 ; //const bool generate_uvs = vmap.count("generate-uvs") != 0 ; @@ -296,7 +295,7 @@ int main(int argc, char** argv) { boost::to_lower(output_extension); // If no entities are specified these are the defaults to skip from output - if (entity_vector.empty()) { + if (entities.empty()) { entities.insert("IfcSpace"); /// @todo Document in --help that SVG uses "--include --entities IfcSpace" by default. if (output_extension == ".svg") { @@ -339,11 +338,11 @@ int main(int argc, char** argv) { settings.set(IfcGeom::IteratorSettings::DISABLE_OPENING_SUBTRACTIONS, disable_opening_subtractions); settings.set(IfcGeom::IteratorSettings::INCLUDE_CURVES, include_plan); settings.set(IfcGeom::IteratorSettings::EXCLUDE_SOLIDS_AND_SURFACES, !include_model); - //settings.set(IfcConvertSettings::USE_NAMES, use_names); - //settings.set(IfcConvertSettings::USE_GUIDS, use_guids); + settings.set(IfcGeom::IteratorSettings::USE_NAMES, use_names); + settings.set(IfcGeom::IteratorSettings::USE_GUIDS, use_guids); //settings.set(IfcGeom::IteratorSettings::NO_NORMALS, no_normals); - //settings.set(IfcConvertSettings::CENTER_MODEL, center_model); - //settings.set(IfcConvertSettings::GENERATE_UVS, generate_uvs); + //settings.set(IfcGeom::IteratorSettings::CENTER_MODEL, center_model); + //settings.set(IfcGeom::IteratorSettings::GENERATE_UVS, generate_uvs); //if (deflection_tolerance_specified) // settings.set_deflection_tolerance(deflection_tolerance); @@ -354,21 +353,21 @@ int main(int argc, char** argv) { Logger::Message(Logger::LOG_NOTICE, "Using world coords when writing WaveFront OBJ files"); settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, true); } - serializer = new WaveFrontOBJSerializer(output_filename, mtl_filename); + serializer = new WaveFrontOBJSerializer(output_filename, mtl_filename, settings); #ifdef WITH_OPENCOLLADA } else if (output_extension == ".dae") { - serializer = new ColladaSerializer(output_filename); + serializer = new ColladaSerializer(output_filename, settings); #endif } else if (output_extension == ".stp") { - serializer = new StepSerializer(output_filename); + serializer = new StepSerializer(output_filename, settings); } else if (output_extension == ".igs") { // Not sure why this is needed, but it is. // See: http://tracker.dev.opencascade.org/view.php?id=23679 IGESControl_Controller::Init(); - serializer = new IgesSerializer(output_filename); + serializer = new IgesSerializer(output_filename, settings); } else if (output_extension == ".svg") { settings.set(IfcGeom::IteratorSettings::DISABLE_TRIANGULATION, true); - serializer = new SvgSerializer(output_filename); + serializer = new SvgSerializer(output_filename, settings); if (bounding_width && bounding_height) { static_cast(serializer)->setBoundingRectangle(*bounding_width, *bounding_height); } @@ -416,6 +415,8 @@ int main(int argc, char** argv) { return 1; } + serializer->setFile(context_iterator.getFile()); + if (convert_back_units) { serializer->setUnitNameAndMagnitude(context_iterator.getUnitName(), static_cast(context_iterator.getUnitMagnitude())); } else { @@ -470,8 +471,6 @@ int main(int argc, char** argv) { Logger::Status("Serializing geometry..."); - //serializer->setSettings(settings); - if (serializer->isTesselated()) { // isTesselated() doesn't change at run-time foreach(const IfcGeom::Element* geom, geometries) { serializer->write(static_cast*>(geom)); diff --git a/src/ifcconvert/IgesSerializer.h b/src/ifcconvert/IgesSerializer.h index 5e11e7b324..447065c5f5 100644 --- a/src/ifcconvert/IgesSerializer.h +++ b/src/ifcconvert/IgesSerializer.h @@ -32,8 +32,8 @@ class IgesSerializer : public OpenCascadeBasedSerializer private: IGESControl_Writer writer; public: - explicit IgesSerializer(const std::string& out_filename) - : OpenCascadeBasedSerializer(out_filename) + IgesSerializer(const std::string& out_filename, const IfcGeom::IteratorSettings &settings) + : OpenCascadeBasedSerializer(out_filename, settings) {} virtual ~IgesSerializer() {} void writeShape(const TopoDS_Shape& shape) { diff --git a/src/ifcconvert/OpenCascadeBasedSerializer.h b/src/ifcconvert/OpenCascadeBasedSerializer.h index 6ee69635a4..f36a844609 100644 --- a/src/ifcconvert/OpenCascadeBasedSerializer.h +++ b/src/ifcconvert/OpenCascadeBasedSerializer.h @@ -31,18 +31,18 @@ protected: const std::string out_filename; const char* getSymbolForUnitMagnitude(float mag); public: - explicit OpenCascadeBasedSerializer(const std::string& out_filename) - : GeometrySerializer() + explicit OpenCascadeBasedSerializer(const std::string& out_filename, const IfcGeom::IteratorSettings &settings) + : GeometrySerializer(settings) , out_filename(out_filename) {} virtual ~OpenCascadeBasedSerializer() {} void writeHeader() {} bool ready(); virtual void writeShape(const TopoDS_Shape& shape) = 0; - void write(const IfcGeom::TriangulationElement* /*o*/) {} - void write(const IfcGeom::BRepElement* o); + void write(const IfcGeom::TriangulationElement* /*o*/) {} + void write(const IfcGeom::BRepElement* o); bool isTesselated() const { return false; } void setFile(IfcParse::IfcFile*) {} }; -#endif \ No newline at end of file +#endif diff --git a/src/ifcconvert/StepSerializer.h b/src/ifcconvert/StepSerializer.h index ff53fa4ae4..99fff685b8 100644 --- a/src/ifcconvert/StepSerializer.h +++ b/src/ifcconvert/StepSerializer.h @@ -32,8 +32,8 @@ class StepSerializer : public OpenCascadeBasedSerializer private: STEPControl_Writer writer; public: - explicit StepSerializer(const std::string& out_filename) - : OpenCascadeBasedSerializer(out_filename) + explicit StepSerializer(const std::string& out_filename, const IfcGeom::IteratorSettings &settings) + : OpenCascadeBasedSerializer(out_filename, settings) {} virtual ~StepSerializer() {} void writeShape(const TopoDS_Shape& shape) { diff --git a/src/ifcconvert/SvgSerializer.cpp b/src/ifcconvert/SvgSerializer.cpp index 2b24512515..14004d50b9 100644 --- a/src/ifcconvert/SvgSerializer.cpp +++ b/src/ifcconvert/SvgSerializer.cpp @@ -168,7 +168,8 @@ SvgSerializer::path_object& SvgSerializer::start_path(IfcSchema::IfcBuildingStor return p; } -void SvgSerializer::write(const IfcGeom::BRepElement* o) { +void SvgSerializer::write(const IfcGeom::BRepElement* o) +{ IfcSchema::IfcBuildingStorey* storey = 0; IfcSchema::IfcObjectDefinition* obdef = static_cast(file->entityById(o->id())); @@ -340,10 +341,14 @@ void SvgSerializer::writeHeader() { svg_file << "\n"; } -std::string SvgSerializer::nameElement(const IfcGeom::Element* elem) { +std::string SvgSerializer::nameElement(const IfcGeom::Element* elem) +{ std::ostringstream oss; const std::string type = "product"; - oss << "id=\"" << type << "-" << elem->unique_id() << "\""; + const std::string name = (settings().get(IfcGeom::IteratorSettings::USE_GUIDS) + ? elem->guid() : (settings().get(IfcGeom::IteratorSettings::USE_NAMES) + ? elem->name() : elem->unique_id())); + oss << "id=\"" << type << "-" << name<< "\""; return oss.str(); } @@ -352,4 +357,4 @@ std::string SvgSerializer::nameElement(const IfcSchema::IfcProduct* elem) { const std::string type = elem->is(IfcSchema::Type::IfcBuildingStorey) ? "storey" : "product"; oss << "id=\"product-" << IfcParse::IfcGlobalId(elem->GlobalId()).formatted() << "\""; return oss.str(); -} \ No newline at end of file +} diff --git a/src/ifcconvert/SvgSerializer.h b/src/ifcconvert/SvgSerializer.h index 46e44f73bb..08ff97f692 100644 --- a/src/ifcconvert/SvgSerializer.h +++ b/src/ifcconvert/SvgSerializer.h @@ -45,8 +45,8 @@ protected: std::vector< boost::shared_ptr > radii; IfcParse::IfcFile* file; public: - explicit SvgSerializer(const std::string& out_filename) - : GeometrySerializer() + explicit SvgSerializer(const std::string& out_filename, const IfcGeom::IteratorSettings &settings) + : GeometrySerializer(settings) , svg_file(out_filename.c_str()) , xmin(+std::numeric_limits::infinity()) , xmax(-std::numeric_limits::infinity()) @@ -62,8 +62,8 @@ public: virtual ~SvgSerializer() {} virtual void writeHeader(); virtual bool ready(); - virtual void write(const IfcGeom::TriangulationElement* /*o*/) {} - virtual void write(const IfcGeom::BRepElement* o); + virtual void write(const IfcGeom::TriangulationElement* /*o*/) {} + virtual void write(const IfcGeom::BRepElement* o); virtual void write(path_object& p, const TopoDS_Wire& wire); virtual path_object& start_path(IfcSchema::IfcBuildingStorey* storey, const std::string& id); virtual bool isTesselated() const { return false; } diff --git a/src/ifcconvert/WavefrontObjSerializer.cpp b/src/ifcconvert/WavefrontObjSerializer.cpp index 4bd3f243df..0829ec88de 100644 --- a/src/ifcconvert/WavefrontObjSerializer.cpp +++ b/src/ifcconvert/WavefrontObjSerializer.cpp @@ -17,12 +17,12 @@ * * ********************************************************************************/ -#include -#include + +#include "WavefrontObjSerializer.h" #include "../ifcgeom/IfcGeomRenderStyles.h" -#include "WavefrontObjSerializer.h" +#include bool WaveFrontOBJSerializer::ready() { return obj_stream.is_open() && mtl_stream.is_open(); @@ -41,11 +41,14 @@ void WaveFrontOBJSerializer::writeHeader() { mtl_basename = mtl_basename.substr(slash+1); } obj_stream << "mtllib " << mtl_basename << "\n"; - mtl_stream << "# File generated by IfcOpenShell " << IFCOPENSHELL_VERSION << "\n"; + mtl_stream << "# File generated by IfcOpenShell " << IFCOPENSHELL_VERSION << "\n"; } -void WaveFrontOBJSerializer::writeMaterial(const IfcGeom::Material& style) { - mtl_stream << "newmtl " << style.name() << "\n"; +void WaveFrontOBJSerializer::writeMaterial(const IfcGeom::Material& style) +{ + std::string material_name = (settings().get(IfcGeom::IteratorSettings::USE_NAMES) ? style.original_name() : style.name()); + IfcUtil::sanitate_material_name(material_name); + mtl_stream << "newmtl " << material_name << "\n"; if (style.hasDiffuse()) { const double* diffuse = style.diffuse(); mtl_stream << "Kd " << diffuse[0] << " " << diffuse[1] << " " << diffuse[2] << "\n"; @@ -66,27 +69,26 @@ void WaveFrontOBJSerializer::writeMaterial(const IfcGeom::Material& style) { } } } -void WaveFrontOBJSerializer::write(const IfcGeom::TriangulationElement* o) { - obj_stream << "g " << o->unique_id() << "\n"; +void WaveFrontOBJSerializer::write(const IfcGeom::TriangulationElement* o) +{ + obj_stream << "g " << (settings().get(IfcGeom::IteratorSettings::USE_GUIDS) ? o->guid() : (settings().get(IfcGeom::IteratorSettings::USE_NAMES) ? o->name() : o->unique_id())) << "\n"; obj_stream << "s 1" << "\n"; - obj_stream << std::setprecision(std::numeric_limits::digits10); - - const IfcGeom::Representation::Triangulation& mesh = o->geometry(); + const IfcGeom::Representation::Triangulation& mesh = o->geometry(); const int vcount = (int)mesh.verts().size() / 3; - for ( std::vector::const_iterator it = mesh.verts().begin(); it != mesh.verts().end(); ) { - const double x = *(it++); - const double y = *(it++); - const double z = *(it++); + for ( std::vector::const_iterator it = mesh.verts().begin(); it != mesh.verts().end(); ) { + const real_t x = *(it++); + const real_t y = *(it++); + const real_t z = *(it++); obj_stream << "v " << x << " " << y << " " << z << "\n"; } - for ( std::vector::const_iterator it = mesh.normals().begin(); it != mesh.normals().end(); ) { - const double x = *(it++); - const double y = *(it++); - const double z = *(it++); + for ( std::vector::const_iterator it = mesh.normals().begin(); it != mesh.normals().end(); ) { + const real_t x = *(it++); + const real_t y = *(it++); + const real_t z = *(it++); obj_stream << "vn " << x << " " << y << " " << z << "\n"; } @@ -98,7 +100,8 @@ void WaveFrontOBJSerializer::write(const IfcGeom::TriangulationElement* const int material_id = *(material_it++); if (material_id != previous_material_id) { const IfcGeom::Material& material = mesh.materials()[material_id]; - const std::string material_name = material.name(); + std::string material_name = (settings().get(IfcGeom::IteratorSettings::USE_NAMES) ? material.original_name() : material.name()); + IfcUtil::sanitate_material_name(material_name); obj_stream << "usemtl " << material_name << "\n"; if (materials.find(material_name) == materials.end()) { writeMaterial(material); @@ -129,7 +132,8 @@ void WaveFrontOBJSerializer::write(const IfcGeom::TriangulationElement* if (material_id != previous_material_id) { const IfcGeom::Material& material = mesh.materials()[material_id]; - const std::string material_name = material.name(); + std::string material_name = (settings().get(IfcGeom::IteratorSettings::USE_NAMES) ? material.original_name() : material.name()); + IfcUtil::sanitate_material_name(material_name); obj_stream << "usemtl " << material_name << "\n"; if (materials.find(material_name) == materials.end()) { writeMaterial(material); diff --git a/src/ifcconvert/WavefrontObjSerializer.h b/src/ifcconvert/WavefrontObjSerializer.h index 1b1ae7d69d..582bffc6fc 100644 --- a/src/ifcconvert/WavefrontObjSerializer.h +++ b/src/ifcconvert/WavefrontObjSerializer.h @@ -34,8 +34,8 @@ private: unsigned int vcount_total; std::set materials; public: - WaveFrontOBJSerializer(const std::string& obj_filename, const std::string& mtl_filename) - : GeometrySerializer() + WaveFrontOBJSerializer(const std::string& obj_filename, const std::string& mtl_filename, const IfcGeom::IteratorSettings &settings) + : GeometrySerializer(settings) , obj_stream(obj_filename.c_str()) , mtl_filename(mtl_filename) , mtl_stream(mtl_filename.c_str()) @@ -53,4 +53,4 @@ public: void setFile(IfcParse::IfcFile*) {} }; -#endif \ No newline at end of file +#endif diff --git a/src/ifcgeom/IfcGeomIteratorSettings.h b/src/ifcgeom/IfcGeomIteratorSettings.h index 3d65fc3d8f..834f77d305 100644 --- a/src/ifcgeom/IfcGeomIteratorSettings.h +++ b/src/ifcgeom/IfcGeomIteratorSettings.h @@ -72,17 +72,17 @@ namespace IfcGeom /// model in other modelling application in any case. //NO_NORMALS = 1 << 11, /// Use entity names instead of unique IDs for naming objects and materials. - /// Applicable for .obj and .dae output. - //USE_NAMES = 1 << 12, + /// Applicable for OBJ, DAE, and SVG output. + USE_NAMES = 1 << 12, /// Use entity GUIDs instead of unique IDs for naming objects. /// Overrides possible usage of --use-names for objects but not for materials. - /// Applicable for .obj and .dae output. - //USE_GUIDS = 1 << 13, + /// Applicable for OBJ, DAE, and SVG output. + USE_GUIDS = 1 << 13, /// Centers the models upon serialization by the applying the center point of /// the scene bounds as an offset. Applicable only for .dae output currently. //CENTER_MODEL = 1 << 14, /// Generates UVs by using simple box projection. Requires normals. - /// Applicable only for .dae output currently. + /// Applicable only for DAE output currently. //GENERATE_UVS = 1 << 15, //NUM_SETTINGS = 15 }; diff --git a/src/ifcgeom/IfcGeomMaterial.cpp b/src/ifcgeom/IfcGeomMaterial.cpp index b96eeebcc6..3593ed7673 100644 --- a/src/ifcgeom/IfcGeomMaterial.cpp +++ b/src/ifcgeom/IfcGeomMaterial.cpp @@ -30,5 +30,6 @@ const double* IfcGeom::Material::diffuse() const { if (hasDiffuse()) return &((* const double* IfcGeom::Material::specular() const { if (hasSpecular()) return &((*style->Specular()).R()); else return black; } double IfcGeom::Material::transparency() const { if (hasTransparency()) return *style->Transparency(); else return 0; } double IfcGeom::Material::specularity() const { if (hasSpecularity()) return *style->Specularity(); else return 0; } -const std::string IfcGeom::Material::name() const { return style->Name(); } +const std::string &IfcGeom::Material::name() const { return style->Name(); } +const std::string &IfcGeom::Material::original_name() const { return style->original_name(); } bool IfcGeom::Material::operator==(const IfcGeom::Material& other) const { return style == other.style; } diff --git a/src/ifcgeom/IfcGeomMaterial.h b/src/ifcgeom/IfcGeomMaterial.h index 43228c2d00..9dc4dd7995 100644 --- a/src/ifcgeom/IfcGeomMaterial.h +++ b/src/ifcgeom/IfcGeomMaterial.h @@ -41,10 +41,11 @@ namespace IfcGeom { const double* specular() const; double transparency() const; double specularity() const; - const std::string name() const; + const std::string &name() const; + const std::string &original_name() const; bool operator==(const Material& other) const; }; } -#endif \ No newline at end of file +#endif diff --git a/src/ifcgeom/IfcGeomRenderStyles.h b/src/ifcgeom/IfcGeomRenderStyles.h index d84b85d737..68686258c0 100644 --- a/src/ifcgeom/IfcGeomRenderStyles.h +++ b/src/ifcgeom/IfcGeomRenderStyles.h @@ -44,22 +44,22 @@ namespace IfcGeom { double& B() { return data[2]; } }; private: + std::string original_name_; boost::optional name; boost::optional id; boost::optional diffuse, specular; boost::optional transparency; boost::optional specularity; public: - SurfaceStyle() { - this->name = "surface-style"; - } + SurfaceStyle() : name("surface-style") {} SurfaceStyle(int id) : id(id) { std::stringstream sstr; sstr << "surface-style-" << id; this->name = sstr.str(); } - SurfaceStyle(const std::string& name) : name(name) {} - SurfaceStyle(int id, const std::string& name) : id(id) { + SurfaceStyle(const std::string& name) : name(name), original_name_(name) {} + SurfaceStyle(int id, const std::string& name) : id(id), original_name_(name) + { std::stringstream sstr; std::string sanitized = name; std::transform(sanitized.begin(), sanitized.end(), sanitized.begin(), ::tolower); @@ -72,7 +72,7 @@ namespace IfcGeom { // architecture can just as easily be accomplished by comparing the // pointer addresses of the styles, as they are always referenced // from out of a global map of some sort. - bool operator==(const SurfaceStyle& other) { + bool operator==(const SurfaceStyle& other) const { if (name && other.name) { return *name == *other.name; } else if (id && other.id) { @@ -82,7 +82,10 @@ namespace IfcGeom { } } - const std::string& Name() const { return *name; } + /// ID name, e.g. "surface-style-66675-metal---aluminium" + const std::string& Name() const { return *name; } + /// Original name, if available, e.g. "Metal - Aluminium" + const std::string& original_name() const { return original_name_; } const boost::optional& Diffuse() const { return diffuse; } const boost::optional& Specular() const { return specular; } @@ -97,4 +100,4 @@ namespace IfcGeom { const SurfaceStyle* get_default_style(const std::string& ifc_type); } -#endif \ No newline at end of file +#endif diff --git a/src/ifcparse/IfcUtil.cpp b/src/ifcparse/IfcUtil.cpp index 6721b35be2..80bd257b14 100644 --- a/src/ifcparse/IfcUtil.cpp +++ b/src/ifcparse/IfcUtil.cpp @@ -160,3 +160,28 @@ boost::regex IfcUtil::wildcard_string_to_regex(std::string str) boost::replace_all(str, "*", ".*"); return boost::regex(str); } + +void IfcUtil::sanitate_material_name(std::string &str) +{ + // Spaces in material names have been observed to cause problems with obj and dae importers. + // Handle other potential problematic characters here too if observing problems. + boost::replace_all(str, " ", "_"); +} + +void IfcUtil::escape_xml(std::string &str) +{ + boost::replace_all(str, "\"", """); + boost::replace_all(str, "'", "'"); + boost::replace_all(str, "<", "<"); + boost::replace_all(str, ">", ">"); + boost::replace_all(str, "&", "&"); +} + +void IfcUtil::unescape_xml(std::string &str) +{ + boost::replace_all(str, """, "\""); + boost::replace_all(str, "'", "'"); + boost::replace_all(str, "<", "<"); + boost::replace_all(str, ">", ">"); + boost::replace_all(str, "&", "&"); +} diff --git a/src/ifcparse/IfcUtil.h b/src/ifcparse/IfcUtil.h index 553c483d35..5e51d8232a 100644 --- a/src/ifcparse/IfcUtil.h +++ b/src/ifcparse/IfcUtil.h @@ -117,6 +117,11 @@ namespace IfcUtil { bool valid_binary_string(const std::string& s); boost::regex wildcard_string_to_regex(std::string str); + + /// Replaces spaces and potentially other problem causing characters with underscores. + void sanitate_material_name(std::string &str); + void escape_xml(std::string &str); + void unescape_xml(std::string &str); } template