diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
index bb2f42a687..0925215314 100644
--- a/cmake/CMakeLists.txt
+++ b/cmake/CMakeLists.txt
@@ -125,9 +125,14 @@ TARGET_LINK_LIBRARIES(IfcGeom IfcParse)
LINK_DIRECTORIES (${LINK_DIRECTORIES} ${IfcOpenShell_BINARY_DIR} ${OCC_LIBRARY_DIR} /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 ${ICU_LIBRARY_DIR} ${Boost_LIBRARY_DIRS})
-ADD_EXECUTABLE(IfcObj ../src/ifcobj/IfcObj.cpp)
+ADD_EXECUTABLE(IfcConvert
+ ../src/ifcconvert/ColladaSerializer.cpp
+ ../src/ifcconvert/IfcConvert.cpp
+ ../src/ifcconvert/OpenCascadeBasedSerializer.cpp
+ ../src/ifcconvert/WavefrontObjSerializer.cpp
+)
-TARGET_LINK_LIBRARIES (IfcObj IfcParse IfcGeom TKernel TKMath TKBRep TKGeomBase TKGeomAlgo TKG3d TKG2d TKShHealing TKTopAlgo TKMesh TKPrim TKBool TKBO TKFillet)
+TARGET_LINK_LIBRARIES (IfcConvert IfcParse IfcGeom TKernel TKMath TKBRep TKGeomBase TKGeomAlgo TKG3d TKG2d TKShHealing TKTopAlgo TKMesh TKPrim TKBool TKBO TKFillet TKSTEP TKSTEPBase TKSTEPAttr TKXSBase TKSTEP209 TKIGES)
# Build python wrapper using separate CMakeLists.txt
ADD_SUBDIRECTORY(../src/ifcwrap ifcwrap)
@@ -171,5 +176,5 @@ SET(include_files_parse
)
INSTALL(FILES ${include_files_geom} DESTINATION include/ifcgeom)
INSTALL(FILES ${include_files_parse} DESTINATION include/ifcparse)
-INSTALL(TARGETS IfcObj DESTINATION bin)
+INSTALL(TARGETS IfcConvert DESTINATION bin)
INSTALL(TARGETS IfcParse IfcGeom DESTINATION lib)
diff --git a/src/ifcconvert/ColladaSerializer.cpp b/src/ifcconvert/ColladaSerializer.cpp
new file mode 100644
index 0000000000..ec8ebbb77c
--- /dev/null
+++ b/src/ifcconvert/ColladaSerializer.cpp
@@ -0,0 +1,206 @@
+/********************************************************************************
+ * *
+ * This file is part of IfcOpenShell. *
+ * *
+ * IfcOpenShell is free software: you can redistribute it and/or modify *
+ * it under the terms of the Lesser GNU General Public License as published by *
+ * the Free Software Foundation, either version 3.0 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * IfcOpenShell is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * Lesser GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the Lesser GNU General Public License *
+ * along with this program. If not, see . *
+ * *
+ ********************************************************************************/
+
+#include "ColladaSerializer.h"
+
+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(strlen(coords));
+ source.setAccessorCount(floats.size() / 3);
+ for (unsigned int i = 0; i < source.getAccessorStride(); ++i) {
+ source.getParameterNameList().push_back(std::string(1, coords[i]));
+ }
+ source.prepareToAppendValues();
+ 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::vector& positions, const std::vector& normals, const std::vector& indices) {
+ openMesh(mesh_id);
+
+ addFloatSource(mesh_id, COLLADASW::LibraryGeometries::POSITIONS_SOURCE_ID_SUFFIX, positions);
+ if (!normals.empty()) {
+ // 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.
+ addFloatSource(mesh_id, COLLADASW::LibraryGeometries::NORMALS_SOURCE_ID_SUFFIX, normals);
+ }
+
+ COLLADASW::VerticesElement vertices(mSW);
+ vertices.setId(mesh_id + COLLADASW::LibraryGeometries::VERTICES_ID_SUFFIX );
+ vertices.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::POSITION, "#" + mesh_id + COLLADASW::LibraryGeometries::POSITIONS_SOURCE_ID_SUFFIX));
+ vertices.add();
+
+ COLLADASW::Triangles triangles(mSW);
+ triangles.setCount(indices.size() / 3);
+ int offset = 0;
+ triangles.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::VERTEX,"#" + mesh_id + COLLADASW::LibraryGeometries::VERTICES_ID_SUFFIX, offset++ ) );
+ if (!normals.empty()) {
+ triangles.getInputList().push_back(COLLADASW::Input(COLLADASW::InputSemantic::NORMAL,"#" + mesh_id + COLLADASW::LibraryGeometries::NORMALS_SOURCE_ID_SUFFIX, offset++ ) );
+ }
+ triangles.prepareToAppendValues();
+ for (auto it = indices.begin(); it != indices.end(); ++it) {
+ const auto& idx = *it;
+ if (!normals.empty()) {
+ triangles.appendValues(idx, idx);
+ } else {
+ triangles.appendValues(idx);
+ }
+ }
+ triangles.finish();
+
+ closeMesh();
+ closeGeometry();
+}
+
+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_id, const std::string& material_id, const std::vector& matrix) {
+ if (!scene_opened) {
+ openVisualScene(scene_id);
+ scene_opened = true;
+ }
+
+ COLLADASW::Node node(mSW);
+ node.setNodeId(node_id);
+ node.setNodeName(node_name);
+ node.setType(COLLADASW::Node::DEFAULT);
+
+ // 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}
+ };
+
+ node.start();
+ node.addMatrix(matrix_array);
+
+ COLLADASW::InstanceGeometry instanceGeometry(mSW);
+ instanceGeometry.setUrl ("#" + geom_id);
+ COLLADASW::InstanceMaterial material ("ColorMaterial", "#" + material_id);
+ instanceGeometry.getBindMaterial().getInstanceMaterialList().push_back(material);
+ instanceGeometry.add();
+ node.end();
+}
+
+void ColladaSerializer::ColladaExporter::ColladaScene::write() {
+ if (scene_opened) {
+ closeVisualScene();
+ closeLibrary();
+
+ COLLADASW::Scene scene (mSW, COLLADASW::URI ("#" + scene_id));
+ scene.add();
+ }
+}
+
+void ColladaSerializer::ColladaExporter::ColladaMaterials::ColladaEffects::write(const SurfaceStyle& style) {
+ openEffect(style.Name() + "-fx");
+ COLLADASW::EffectProfile effect(mSW);
+ effect.setShaderType(COLLADASW::EffectProfile::LAMBERT);
+ effect.setDiffuse(COLLADASW::ColorOrTexture(COLLADASW::Color(style.Diffuse().R(),style.Diffuse().G(),style.Diffuse().B())));
+ addEffectProfile(effect);
+}
+
+void ColladaSerializer::ColladaExporter::ColladaMaterials::ColladaEffects::close() {
+ closeLibrary();
+}
+
+void ColladaSerializer::ColladaExporter::ColladaMaterials::add(const SurfaceStyle& style) {
+ if (!contains(style.Name())) {
+ effects.write(style);
+ surface_styles.push_back(style);
+ }
+}
+
+bool ColladaSerializer::ColladaExporter::ColladaMaterials::contains(const std::string& name) {
+ for (auto it = surface_styles.begin(); it != surface_styles.end(); ++it) {
+ if (it->Name() == name) return true;
+ }
+ return false;
+}
+
+void ColladaSerializer::ColladaExporter::ColladaMaterials::write() {
+ effects.close();
+ for (auto it = surface_styles.begin(); it != surface_styles.end(); ++it) {
+ const std::string& material_name = it->Name();
+ openMaterial(material_name);
+ addInstanceEffect("#" + material_name + "-fx");
+ closeLibrary();
+ }
+}
+
+void ColladaSerializer::ColladaExporter::startDocument() {
+ stream.startDocument();
+
+ COLLADASW::Asset asset(&stream);
+ asset.getContributor().mAuthoringTool = std::string("IfcOpenShell ") + IFCOPENSHELL_VERSION;
+ // TODO: Get the appropriate unit from the IFC file.
+ asset.setUnit("meter", 1.0);
+ asset.setUpAxisType(COLLADASW::Asset::Z_UP);
+ asset.add();
+}
+
+void ColladaSerializer::ColladaExporter::writeTesselated(const std::string& type, int obj_id, const std::vector& matrix, const std::vector& vertices, const std::vector& normals, const std::vector& indices) {
+ if (!materials.contains(type)) materials.add(GetDefaultMaterial(type));
+ deferreds.push_back(DeferedObject(type, obj_id, matrix, vertices, normals, indices));
+}
+
+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();
+ for (auto it = deferreds.begin(); it != deferreds.end(); ++it) {
+ std::stringstream ss; ss << "object" << it->obj_id;
+ const std::string object_id = ss.str();
+ geometries.write(object_id, it->vertices, it->normals, it->indices);
+ }
+ geometries.close();
+ for (auto it = deferreds.begin(); it != deferreds.end(); ++it) {
+ std::stringstream ss; ss << "object" << it->obj_id;
+ const std::string object_id = ss.str();
+ scene.add(object_id, object_id, object_id, it->type, it->matrix);
+ }
+ scene.write();
+ stream.endDocument();
+}
+
+bool ColladaSerializer::ready() {
+ return true;
+}
+
+void ColladaSerializer::writeHeader() {
+ exporter.startDocument();
+}
+
+void ColladaSerializer::writeTesselated(const IfcGeomObjects::IfcGeomObject* o) {
+ exporter.writeTesselated(o->type, o->id, o->matrix, o->mesh->verts, o->mesh->normals, o->mesh->faces);
+}
+
+void ColladaSerializer::finalize() {
+ exporter.endDocument();
+}
+
diff --git a/src/ifcconvert/ColladaSerializer.h b/src/ifcconvert/ColladaSerializer.h
new file mode 100644
index 0000000000..1c2aa1e9b2
--- /dev/null
+++ b/src/ifcconvert/ColladaSerializer.h
@@ -0,0 +1,145 @@
+/********************************************************************************
+ * *
+ * This file is part of IfcOpenShell. *
+ * *
+ * IfcOpenShell is free software: you can redistribute it and/or modify *
+ * it under the terms of the Lesser GNU General Public License as published by *
+ * the Free Software Foundation, either version 3.0 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * IfcOpenShell is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * Lesser GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the Lesser GNU General Public License *
+ * along with this program. If not, see . *
+ * *
+ ********************************************************************************/
+
+#ifndef COLLADASERIALIZER_H
+#define COLLADASERIALIZER_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "../ifcgeom/IfcGeomObjects.h"
+
+#include "../ifcconvert/GeometrySerializer.h"
+#include "../ifcconvert/SurfaceStyle.h"
+
+class ColladaSerializer : public GeometrySerializer
+{
+private:
+ class ColladaExporter
+ {
+ private:
+ class ColladaGeometries : public COLLADASW::LibraryGeometries
+ {
+ public:
+ explicit ColladaGeometries(COLLADASW::StreamWriter& stream)
+ : COLLADASW::LibraryGeometries(&stream)
+ {}
+ 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::vector& positions, const std::vector& normals, const std::vector& indices);
+ void close();
+ };
+ class ColladaScene : public COLLADASW::LibraryVisualScenes
+ {
+ private:
+ const std::string scene_id;
+ bool scene_opened;
+ public:
+ ColladaScene(const std::string& scene_id, COLLADASW::StreamWriter& stream)
+ : COLLADASW::LibraryVisualScenes(&stream)
+ , scene_id(scene_id)
+ , scene_opened(false)
+ {}
+ void add(const std::string& node_id, const std::string& node_name, const std::string& geom_id, const std::string& material_id, const std::vector& matrix);
+ void write();
+ };
+ class ColladaMaterials : public COLLADASW::LibraryMaterials
+ {
+ private:
+ class ColladaEffects : public COLLADASW::LibraryEffects
+ {
+ public:
+ explicit ColladaEffects(COLLADASW::StreamWriter& stream)
+ : COLLADASW::LibraryEffects(&stream)
+ {}
+ void write(const SurfaceStyle& style);
+ void close();
+ };
+ std::vector surface_styles;
+ ColladaEffects effects;
+ public:
+ explicit ColladaMaterials(COLLADASW::StreamWriter& stream)
+ : COLLADASW::LibraryMaterials(&stream)
+ , effects(stream)
+ {}
+ void add(const SurfaceStyle& style);
+ bool contains(const std::string& name);
+ void write();
+ };
+ class DeferedObject {
+ public:
+ const std::string type;
+ int obj_id;
+ const std::vector matrix;
+ const std::vector vertices;
+ const std::vector normals;
+ const std::vector indices;
+ DeferedObject(const std::string& type, int obj_id, const std::vector& matrix, const std::vector& vertices,
+ const std::vector& normals, const std::vector& indices)
+ : type(type)
+ , obj_id(obj_id)
+ , matrix(matrix)
+ , vertices(vertices)
+ , normals(normals)
+ , indices(indices)
+ {}
+ };
+ COLLADABU::NativeString filename;
+ COLLADASW::StreamWriter stream;
+ ColladaGeometries geometries;
+ ColladaScene scene;
+ ColladaMaterials materials;
+ public:
+ ColladaExporter(const std::string& scene_name, const std::string& fn)
+ : filename(fn.c_str())
+ , stream(filename)
+ , geometries(stream)
+ , scene(scene_name, stream)
+ , materials(stream)
+ {}
+ std::vector deferreds;
+ virtual ~ColladaExporter() {}
+ void startDocument();
+ void writeTesselated(const std::string& type, int obj_id, const std::vector& matrix, const std::vector& vertices, const std::vector& normals, const std::vector& indices);
+ void endDocument();
+ };
+ ColladaExporter exporter;
+public:
+ ColladaSerializer(const std::string& dae_filename)
+ : GeometrySerializer()
+ , exporter("IfcOpenShell", dae_filename)
+ {}
+ bool ready();
+ void writeHeader();
+ void writeTesselated(const IfcGeomObjects::IfcGeomObject* o);
+ void writeShapeModel(const IfcGeomObjects::IfcGeomShapeModelObject* o) {}
+ void finalize();
+ bool isTesselated() const { return true; }
+};
+
+#endif
\ No newline at end of file
diff --git a/src/ifcconvert/GeometrySerializer.h b/src/ifcconvert/GeometrySerializer.h
new file mode 100644
index 0000000000..4524f8b681
--- /dev/null
+++ b/src/ifcconvert/GeometrySerializer.h
@@ -0,0 +1,36 @@
+/********************************************************************************
+ * *
+ * This file is part of IfcOpenShell. *
+ * *
+ * IfcOpenShell is free software: you can redistribute it and/or modify *
+ * it under the terms of the Lesser GNU General Public License as published by *
+ * the Free Software Foundation, either version 3.0 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * IfcOpenShell is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * Lesser GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the Lesser GNU General Public License *
+ * along with this program. If not, see . *
+ * *
+ ********************************************************************************/
+
+#ifndef GEOMETRYSERIALIZER_H
+#define GEOMETRYSERIALIZER_H
+
+#include "../ifcgeom/IfcGeomObjects.h"
+
+class GeometrySerializer {
+public:
+ virtual bool ready() = 0;
+ virtual void writeHeader() = 0;
+ virtual void finalize() = 0;
+ virtual bool isTesselated() const = 0;
+ virtual ~GeometrySerializer() {}
+ virtual void writeTesselated(const IfcGeomObjects::IfcGeomObject* o) = 0;
+ virtual void writeShapeModel(const IfcGeomObjects::IfcGeomShapeModelObject* o) = 0;
+};
+
+#endif
\ No newline at end of file
diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp
new file mode 100644
index 0000000000..7ddaa11c3e
--- /dev/null
+++ b/src/ifcconvert/IfcConvert.cpp
@@ -0,0 +1,288 @@
+/********************************************************************************
+ * *
+ * This file is part of IfcOpenShell. *
+ * *
+ * IfcOpenShell is free software: you can redistribute it and/or modify *
+ * it under the terms of the Lesser GNU General Public License as published by *
+ * the Free Software Foundation, either version 3.0 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * IfcOpenShell is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * Lesser GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the Lesser GNU General Public License *
+ * along with this program. If not, see . *
+ * *
+ ********************************************************************************/
+
+/********************************************************************************
+ * *
+ * This started as a brief example of how IfcOpenShell can be interfaced from *
+ * within a C++ context, it has since then evolved into a fullfledged command *
+ * line application that is able to convert geometry in an IFC files into *
+ * several tesselated and topological output formats. *
+ * *
+ ********************************************************************************/
+
+#include
+#include
+#include
+#include
+
+#include
+
+#include "../ifcgeom/IfcGeomObjects.h"
+
+#include "../ifcconvert/SurfaceStyle.h"
+#include "../ifcconvert/ColladaSerializer.h"
+#include "../ifcconvert/IgesSerializer.h"
+#include "../ifcconvert/StepSerializer.h"
+#include "../ifcconvert/WavefrontObjSerializer.h"
+
+void printVersion() {
+ std::cerr << "IfcOpenShell IfcConvert " << IFCOPENSHELL_VERSION << std::endl;
+}
+
+void printUsage(const boost::program_options::options_description& generic_options, const boost::program_options::options_description& geom_options) {
+ printVersion();
+ std::cerr << "Usage: IfcConvert [options] [