diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
index 55dfbf206b..59bfb0acde 100644
--- a/cmake/CMakeLists.txt
+++ b/cmake/CMakeLists.txt
@@ -1,7 +1,7 @@
cmake_minimum_required (VERSION 2.6)
project (IfcOpenShell)
-FIND_PACKAGE(Boost REQUIRED)
+FIND_PACKAGE(Boost REQUIRED COMPONENTS program_options)
MESSAGE(STATUS "Boost include files found in ${Boost_INCLUDE_DIRS}")
MESSAGE(STATUS "Boost libraries found in ${Boost_LIBRARY_DIRS}")
@@ -54,10 +54,39 @@ ENDIF()
FIND_LIBRARY(icu "icuuc" /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 ${ICU_LIBRARY_DIR})
IF(icu)
- MESSAGE(STATUS "ICU libraries found")
+ MESSAGE(STATUS "ICU libraries found")
ADD_DEFINITIONS(-DHAVE_ICU)
ELSE()
- MESSAGE(STATUS "Unable to find ICU library files, continuing")
+ MESSAGE(STATUS "Unable to find ICU library files, continuing")
+ENDIF()
+
+IF("$ENV{OPENCOLLADA_INCLUDE_DIR}" STREQUAL "")
+ MESSAGE(STATUS "No OpenCOLLADA include directory specified")
+ SET(OPENCOLLADA_INCLUDE_DIR "/usr/local/include/opencollada" CACHE FILEPATH "OpenCOLLADA header files")
+ElSE()
+ SET(OPENCOLLADA_INCLUDE_DIR "$ENV{OPENCOLLADA_INCLUDE_DIR}" CACHE FILEPATH "OpenCOLLADA header files")
+ENDIF()
+
+IF("$ENV{OPENCOLLADA_LIBRARY_DIR}" STREQUAL "")
+ MESSAGE(STATUS "No OpenCOLLADA library directory specified")
+ SET(OPENCOLLADA_LIBRARY_DIR "/usr/local/lib/opencollada" CACHE FILEPATH "OpenCOLLADA library files")
+ElSE()
+ SET(OPENCOLLADA_LIBRARY_DIR "$ENV{OPENCOLLADA_LIBRARY_DIR}" CACHE FILEPATH "OpenCOLLADA library files")
+ENDIF()
+
+SET(OPENCOLLADA_INCLUDE_DIRS "${OPENCOLLADA_INCLUDE_DIR}/COLLADABaseUtils" "${OPENCOLLADA_INCLUDE_DIR}/COLLADAStreamWriter")
+
+FIND_FILE(COLLADASWStreamWriter_h "COLLADASWStreamWriter.h" ${OPENCOLLADA_INCLUDE_DIRS})
+IF(COLLADASWStreamWriter_h)
+ MESSAGE(STATUS "OpenCOLLADA header files found")
+ ADD_DEFINITIONS(-DWITH_OPENCOLLADA)
+ SET(OPENCOLLADA_LIBRARIES
+ GeneratedSaxParser MathMLSolver OpenCOLLADABaseUtils
+ OpenCOLLADAFramework OpenCOLLADASaxFrameworkLoader
+ OpenCOLLADAStreamWriter UTF buffer ftoa pcre
+ )
+ELSE()
+ MESSAGE(STATUS "OpenCOLLADA header files not found, continuing without COLLADA support")
ENDIF()
INCLUDE(CheckIncludeFileCXX)
@@ -92,17 +121,23 @@ ElSE(MSVC)
ADD_DEFINITIONS(-fPIC -Wno-non-virtual-dtor)
ENDIF(MSVC)
-INCLUDE_DIRECTORIES(${INCLUDE_DIRECTORIES} ${OCC_INCLUDE_DIR} /usr/inc /usr/local/inc /usr/local/include/oce ${ICU_INCLUDE_DIR} ${Boost_INCLUDE_DIRS})
+INCLUDE_DIRECTORIES(${INCLUDE_DIRECTORIES} ${OCC_INCLUDE_DIR} ${OPENCOLLADA_INCLUDE_DIRS} /usr/inc /usr/local/inc /usr/local/include/oce ${ICU_INCLUDE_DIR} ${Boost_INCLUDE_DIRS})
ADD_LIBRARY(IfcParse STATIC
- ../src/ifcparse/Ifc2x3-rt.cpp
../src/ifcparse/Ifc2x3.cpp
- ../src/ifcparse/IfcCharacterDecoder.cpp
- ../src/ifcparse/IfcGuidHelper.cpp
+ ../src/ifcparse/Ifc4.cpp
+ ../src/ifcparse/IfcUtil.cpp
../src/ifcparse/IfcParse.cpp
+ ../src/ifcparse/IfcCharacterDecoder.cpp
+
+ ../src/ifcparse/IfcWrite.cpp
+ ../src/ifcparse/IfcGuidHelper.cpp
+
+ ../src/ifcparse/IfcHierarchyHelper.cpp
+
+ ../src/ifcparse/Ifc2x3-rt.cpp
+ ../src/ifcparse/Ifc4-rt.cpp
../src/ifcparse/IfcUntypedEntity.cpp
- ../src/ifcparse/IfcUtil.cpp
- ../src/ifcparse/IfcWrite.cpp
)
ADD_LIBRARY(IfcGeom STATIC
@@ -111,6 +146,7 @@ ADD_LIBRARY(IfcGeom STATIC
../src/ifcgeom/IfcGeomFunctions.cpp
../src/ifcgeom/IfcGeomHelpers.cpp
../src/ifcgeom/IfcGeomObjects.cpp
+ ../src/ifcgeom/IfcGeomRenderStyles.cpp
../src/ifcgeom/IfcGeomShapes.cpp
../src/ifcgeom/IfcGeomWires.cpp
../src/ifcgeom/IfcRegister.cpp
@@ -122,11 +158,16 @@ ENDIF()
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})
+LINK_DIRECTORIES (${LINK_DIRECTORIES} ${IfcOpenShell_BINARY_DIR} ${OCC_LIBRARY_DIR} ${OPENCOLLADA_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 ${Boost_LIBRARIES} ${OPENCOLLADA_LIBRARIES})
# Build python wrapper using separate CMakeLists.txt
ADD_SUBDIRECTORY(../src/ifcwrap ifcwrap)
@@ -134,7 +175,7 @@ ADD_SUBDIRECTORY(../src/ifcwrap ifcwrap)
# Build IfcParseExamples using separate CMakeLists.txt
ADD_SUBDIRECTORY(../src/examples examples)
-ADD_SUBDIRECTORY(../src/qtviewer qtviewer)
+# ADD_SUBDIRECTORY(../src/qtviewer qtviewer)
# CMake installation targets
SET(include_files_geom
@@ -152,25 +193,31 @@ SET(include_files_geom
../src/ifcgeom/IfcRegisterIsShapeCollection.h
../src/ifcgeom/IfcRegisterPurgeCache.h
../src/ifcgeom/IfcRegisterUndef.h
- ../src/ifcgeom/IfcShapeList.h
+ ../src/ifcgeom/IfcGeomRenderStyles.h
+ ../src/ifcgeom/IfcRepresentationShapeItem.h
)
SET(include_files_parse
- ../src/ifcparse/ArgumentType.h
- ../src/ifcparse/Ifc2x3-rt.h
../src/ifcparse/Ifc2x3.h
../src/ifcparse/Ifc2x3enum.h
+ ../src/ifcparse/Ifc4.h
+ ../src/ifcparse/Ifc4enum.h
../src/ifcparse/IfcCharacterDecoder.h
../src/ifcparse/IfcException.h
../src/ifcparse/IfcFile.h
+ ../src/ifcparse/IfcHierarchyHelper.h
../src/ifcparse/IfcParse.h
- ../src/ifcparse/IfcSpfStream.h
- ../src/ifcparse/IfcUntypedEntity.h
../src/ifcparse/IfcUtil.h
+ ../src/ifcparse/SharedPointer.h
+
../src/ifcparse/IfcWrite.h
- ../src/ifcparse/IfcWritableEntity.h
- ../src/ifcparse/SharedPointer.h
+ ../src/ifcparse/IfcWritableEntity.h
+
+ ../src/ifcparse/Ifc2x3-rt.h
+ ../src/ifcparse/Ifc4-rt.h
+ ../src/ifcparse/IfcUntypedEntity.h
+ ../src/ifcparse/IfcEntityDescriptor.h
)
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/examples/CMakeLists.txt b/src/examples/CMakeLists.txt
index 523563fdf5..2693ed2668 100644
--- a/src/examples/CMakeLists.txt
+++ b/src/examples/CMakeLists.txt
@@ -1,2 +1,5 @@
ADD_EXECUTABLE(IfcParseExamples IfcParseExamples.cpp)
-TARGET_LINK_LIBRARIES (IfcParseExamples IfcParse)
\ No newline at end of file
+TARGET_LINK_LIBRARIES (IfcParseExamples IfcParse)
+
+ADD_EXECUTABLE(IfcOpenHouse IfcOpenHouse.cpp)
+TARGET_LINK_LIBRARIES (IfcOpenHouse IfcParse IfcGeom TKernel TKMath TKBRep TKGeomBase TKGeomAlgo TKG3d TKG2d TKShHealing TKTopAlgo TKMesh TKPrim TKBool TKBO TKFillet)
diff --git a/src/examples/IfcOpenHouse.cpp b/src/examples/IfcOpenHouse.cpp
new file mode 100644
index 0000000000..907b52bd62
--- /dev/null
+++ b/src/examples/IfcOpenHouse.cpp
@@ -0,0 +1,484 @@
+/********************************************************************************
+ * *
+ * 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
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+
+#ifdef USE_IFC4
+#include "../ifcparse/Ifc4.h"
+#else
+#include "../ifcparse/Ifc2x3.h"
+#endif
+
+#include "../ifcparse/IfcUtil.h"
+#include "../ifcparse/IfcHierarchyHelper.h"
+#include "../ifcgeom/IfcGeom.h"
+
+// Some convenience typedefs and definitions.
+typedef std::string S;
+typedef IfcWrite::IfcGuidHelper guid;
+typedef std::pair XY;
+boost::none_t const null = (static_cast(0));
+
+// The creation of Nurbs-surface for the IfcSite mesh, to be implemented lateron
+void createGroundShape(TopoDS_Shape& shape);
+
+int main(int argc, char** argv) {
+
+ // The IfcHierarchyHelper is a subclass of the regular IfcFile that provides several
+ // convenience functions for working with geometry in IFC files.
+ IfcHierarchyHelper file;
+ file.filename("IfcOpenHouse.ifc");
+
+ // Start by adding a wall to the file, initially leaving most attributes blank.
+ IfcSchema::IfcWallStandardCase* south_wall = new IfcSchema::IfcWallStandardCase(
+ guid(), // GlobalId
+ 0, // OwnerHistory
+ S("South wall"), // Name
+ null, // Description
+ null, // ObjectType
+ 0, // ObjectPlacement
+ 0, // Representation
+ null // Tag
+#ifdef USE_IFC4
+ , IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD
+#endif
+ );
+ file.addBuildingProduct(south_wall);
+
+ // By adding a wall, a hierarchy has been automatically created that consists of the following
+ // structure: IfcProject > IfcSite > IfcBuilding > IfcBuildingStorey > IfcWall
+
+ // Lateron changing the name of the IfcProject can be done by obtaining a reference to the
+ // project, which has been created automatically.
+ file.getSingle()->setName("IfcOpenHouse");
+
+ // An IfcOwnerHistory has been initialized as well, which should be assigned to the wall.
+ south_wall->setOwnerHistory(file.getSingle());
+
+ // The wall will be shaped as a box, with the dimensions specified in millimeters.
+ IfcSchema::IfcProductDefinitionShape* south_wall_shape = file.addBox(10000, 360, 3000);
+
+ // The shape has to be assigned to the representation of the wall and is placed at the origin
+ // of the coordinate system.
+ south_wall->setRepresentation(south_wall_shape);
+ south_wall->setObjectPlacement(file.addLocalPlacement());
+
+ // A pale white colour is assigned to the wall.
+ IfcSchema::IfcPresentationStyleAssignment* wall_colour = file.setSurfaceColour(
+ south_wall->Representation(), 0.75, 0.73, 0.68);
+
+ // Now create a footing for the wall to rest on.
+ IfcSchema::IfcFooting* footing = new IfcSchema::IfcFooting(guid(), file.getSingle(),
+ S("Footing"), null, null, 0, 0, null, IfcSchema::IfcFootingTypeEnum::IfcFootingType_STRIP_FOOTING);
+
+ file.addBuildingProduct(footing);
+
+ // The footing will span the entire floor plan of our building. The IfcRepresentationContext is
+ // something that has been created automatically as well, but representations could have been
+ // assigned to a specific context, for example to add a two dimensional plan representation as well.
+ footing->setRepresentation(file.addBox(10100, 5460, 2000, 0, 0, 0, file.getSingle()));
+ footing->setObjectPlacement(file.addLocalPlacement(0, 2500, -2000));
+ // The footing will have a dark gray colour
+ IfcSchema::IfcPresentationStyleAssignment* footing_colour = file.setSurfaceColour(footing->Representation(), 0.26, 0.22, 0.18);
+
+ // IFC has two ways to apply boolean operations to geometry. IfcBooleanResults are commonly used
+ // to clip geometry to a surface, for example to a slanted roof. For openings that are filled
+ // with another element, for example a door or a window, an IfcOpeningElement is used instead.
+ // An opening element is created with rectangular geometry
+ IfcSchema::IfcOpeningElement* west_opening = new IfcSchema::IfcOpeningElement(guid(), file.getSingle(),
+ null, null, null, file.addLocalPlacement(-2500, 0, 400),
+ file.addBox(6000, 3630, 1600, 0, 0, 0, file.getSingle()), null
+#ifdef USE_IFC4
+ , IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING
+#endif
+ );
+ file.AddEntity(west_opening);
+
+ // Relate the opening element to the wall.
+ IfcSchema::IfcRelVoidsElement* void_element = new IfcSchema::IfcRelVoidsElement(guid(), file.getSingle(),
+ null, null, south_wall, west_opening);
+ file.AddEntity(void_element);
+
+ // Now create an additional opening
+ IfcSchema::IfcOpeningElement* south_opening = new IfcSchema::IfcOpeningElement(guid(), file.getSingle(),
+ null, null, null, file.addLocalPlacement(3000, 0, 400),
+ file.addBox(1860, 3000, 1600, 0, 0, 0, file.getSingle()), null
+#ifdef USE_IFC4
+ , IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING
+#endif
+ );
+ file.AddEntity(south_opening);
+ file.AddEntity(new IfcSchema::IfcRelVoidsElement(guid(), file.getSingle(), null, null, south_wall, south_opening));
+
+ // Create a roof element
+ IfcSchema::IfcRoof* south_roof = new IfcSchema::IfcRoof(guid(), file.getSingle(), S("South roof"), null, null,
+ 0, 0, null, IfcSchema::IfcRoofTypeEnum::IfcRoofType_GABLE_ROOF);
+
+ // The roof geometry is slanted 45 degrees by specifying a direction for the box extrusion
+ south_roof->setRepresentation(file.addBox(10200, 360, sqrt(2.0*2900*2900), 0, file.addPlacement3d(0, 0, 0, 0, 1, 0),
+ file.addTriplet(0, -sqrt(0.5), sqrt(0.5)), file.getSingle()));
+ south_roof->setObjectPlacement(file.addLocalPlacement(0, -400, 2700));
+ file.addBuildingProduct(south_roof);
+
+ // The same roof geometry is re-used on the north side of the roof, by inverting the X-axis of
+ // the local placement the roof is rotated 180 degrees around the Z-axis
+ IfcSchema::IfcRoof* north_roof = new IfcSchema::IfcRoof(guid(), file.getSingle(), S("North roof"),
+ null, null, 0, 0, null, IfcSchema::IfcRoofTypeEnum::IfcRoofType_GABLE_ROOF);
+ north_roof->setOwnerHistory(file.getSingle());
+ north_roof->setRepresentation(south_roof->Representation());
+ north_roof->setObjectPlacement(file.addLocalPlacement(0, 5400, 2700, 0, 0, 1, -1, 0, 0));
+ file.addBuildingProduct(north_roof);
+
+ // By specifying a surface style for the south part of the roof, it gets assigned to the other
+ // roof part as well, because they share the same representation.
+ file.setSurfaceColour(south_roof->Representation(), 0.24, 0.08, 0.04);
+
+ // Copy the south wall to the north
+ file.addBuildingProduct(new IfcSchema::IfcWallStandardCase(guid(), file.getSingle(), S("North wall"),
+ null, null, file.addLocalPlacement(0, 5000, 0), south_wall->Representation(), null
+#ifdef USE_IFC4
+ , IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD
+#endif
+ ));
+
+ // Now create a wall on the east of the building, again starting with just a box shape
+ IfcSchema::IfcWallStandardCase* east_wall = new IfcSchema::IfcWallStandardCase(guid(), file.getSingle(),
+ S("East wall"), null, null, file.addLocalPlacement(4820, 2500, 0, 0, 0, 1, 0, 1, 0), file.addBox(5000, 360, 6000), null
+#ifdef USE_IFC4
+ , IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD
+#endif
+ );
+ file.addBuildingProduct(east_wall);
+
+ // The east wall geometry is clipped using two IfcHalfSpaceSolids, created from an
+ // 'axis 3d placement' that specifies the plane against which the geometry is clipped.
+ file.clipRepresentation(east_wall->Representation(), file.addPlacement3d(-2500, 0, 3000, -1, 0, 1), false);
+ file.clipRepresentation(east_wall->Representation(), file.addPlacement3d(2500, 0, 3000, 1, 0, 1), false);
+
+ file.setSurfaceColour(east_wall->Representation(), wall_colour);
+
+ // The east wall is copied to the west location of the house
+ IfcSchema::IfcWallStandardCase* west_wall = new IfcSchema::IfcWallStandardCase(guid(), file.getSingle(),
+ S("West wall"), null, null, file.addLocalPlacement(-4820, 2500, 0, 0, 0, 1, 0, -1, 0), east_wall->Representation(), null
+#ifdef USE_IFC4
+ , IfcSchema::IfcWallTypeEnum::IfcWallType_STANDARD
+#endif
+ );
+ file.addBuildingProduct(west_wall);
+
+ // The west wall is assigned an opening element we created for the south wall, opening elements are
+ // not shared accross building elements, even if they share the same representation. Hence, the east
+ // wall will not feature this opening.
+ // NB: an Opening Element can only be used to create a single void within a single Element, as per:
+ // http://www.buildingsmart-tech.org/ifc/IFC2x3/TC1/html/ifcproductextension/lexical/ifcfeatureelementsubtraction.htm
+ IfcSchema::IfcOpeningElement* west_opening_copy = new IfcSchema::IfcOpeningElement(guid(), file.getSingle(),
+ null, null, null, west_opening->ObjectPlacement(), west_opening->Representation(), null
+#ifdef USE_IFC4
+ , IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING
+#endif
+ );
+ file.AddEntity(west_opening_copy);
+ file.AddEntity(new IfcSchema::IfcRelVoidsElement(guid(), file.getSingle(), null, null, west_wall, west_opening_copy));
+
+ // Up until now we have only used simple extrusions for the creation of the geometry. For the
+ // ground mesh of the IfcSite we will use a Nurbs surface created in Open Cascade. The surface
+ // will be tesselated using the deflection specified.
+ TopoDS_Shape shape;
+ createGroundShape(shape);
+ IfcEntities geometrical_entities(new IfcEntityList());
+ IfcSchema::IfcProductDefinitionShape* ground_representation = IfcGeom::tesselate(shape, 100., geometrical_entities);
+ file.getSingle()->setRepresentation(ground_representation);
+ file.AddEntities(geometrical_entities);
+ IfcSchema::IfcShapeRepresentation::list ground_reps = geometrical_entities->as();
+ for (IfcSchema::IfcShapeRepresentation::it it = ground_reps->begin(); it != ground_reps->end(); ++it) {
+ (*it)->setContextOfItems(file.getSingle());
+ }
+ file.setSurfaceColour(ground_representation, 0.15, 0.25, 0.05);
+
+ // According to the Ifc2x3 schema an IfcWallStandardCase needs to have an IfcMaterialLayerSet
+ // assigned. Note that this material definition is independent of the surface styles we have
+ // been assigning to the walls already. The surface styles determine the colour in the
+ // '3D viewport' of most applications.
+ // Some BIM authoring applications, such as Autodesk Revit, ignore the geometrical representation
+ // by and large and construct native walls using the layer thickness and reference line offset
+ // provided here.
+#ifdef USE_IFC4
+ IfcSchema::IfcMaterial* material = new IfcSchema::IfcMaterial("Brick", null, null);
+#else
+ IfcSchema::IfcMaterial* material = new IfcSchema::IfcMaterial("Brick");
+#endif
+ IfcSchema::IfcMaterialLayer* layer = new IfcSchema::IfcMaterialLayer(
+ material,
+ 360,
+ null
+#ifdef USE_IFC4
+ , null
+ , null
+ , null
+ , null
+#endif
+ );
+ IfcSchema::IfcMaterialLayer::list layers (new IfcTemplatedEntityList());
+ layers->push(layer);
+ IfcSchema::IfcMaterialLayerSet* layer_set = new IfcSchema::IfcMaterialLayerSet(
+ layers,
+ S("Wall")
+#ifdef USE_IFC4
+ , null
+#endif
+ );
+ IfcSchema::IfcMaterialLayerSetUsage* layer_usage = new IfcSchema::IfcMaterialLayerSetUsage(
+ layer_set,
+ IfcSchema::IfcLayerSetDirectionEnum::IfcLayerSetDirection_AXIS2,
+ IfcSchema::IfcDirectionSenseEnum::IfcDirectionSense_POSITIVE,
+ -180
+#ifdef USE_IFC4
+ , null
+#endif
+ );
+
+ IfcSchema::IfcRelAssociatesMaterial* associates_material = new IfcSchema::IfcRelAssociatesMaterial(
+ guid(),
+ file.getSingle(),
+ null,
+ null,
+#ifdef USE_IFC4
+ file.EntitiesByType()->generalize(),
+#else
+ file.EntitiesByType()->as(),
+#endif
+ layer_usage);
+
+ file.AddEntity(material);
+ file.AddEntity(layer);
+ file.AddEntity(layer_set);
+ file.AddEntity(layer_usage);
+ file.AddEntity(associates_material);
+
+ // In addition, another common way to represent geometry in IFC files is to use extrusions of
+ // planar areas bounded by a polygon.
+ std::vector stair_points;
+ stair_points.push_back(XY( 0, 0));
+ stair_points.push_back(XY(250, 0));
+ stair_points.push_back(XY(250, 200));
+ stair_points.push_back(XY(500, 200));
+ stair_points.push_back(XY(500, 400));
+ stair_points.push_back(XY( 0, 400));
+ IfcSchema::IfcStairFlight* stair = new IfcSchema::IfcStairFlight(guid(), file.getSingle(),
+ null, null, null, file.addLocalPlacement(5050, 1000, 0, 0, 1, 0, 1, 0, 0),
+ file.addExtrudedPolyline(stair_points, 1200), null, 2, 2, 0.2, 0.25
+#ifdef USE_IFC4
+ , IfcSchema::IfcStairFlightTypeEnum::IfcStairFlightType_STRAIGHT
+#endif
+ );
+
+ file.addBuildingProduct(stair);
+ file.setSurfaceColour(stair->Representation(), footing_colour);
+
+ IfcSchema::IfcOpeningElement* door_opening = new IfcSchema::IfcOpeningElement(guid(), file.getSingle(),
+ null, null, null, file.addLocalPlacement(5000-180, 2500-900, 0), file.addBox(1000, 1000, 2200), null
+#ifdef USE_IFC4
+ , IfcSchema::IfcOpeningElementTypeEnum::IfcOpeningElementType_OPENING
+#endif
+ );
+ file.AddEntity(door_opening);
+ file.AddEntity(new IfcSchema::IfcRelVoidsElement(guid(), file.getSingle(), null, null, east_wall, door_opening));
+
+ // A single shape representation can contain multiple representiation items. This way a product
+ // can be a composition of multiple solids. The following door will be composed of four boxes
+ // which constitute the door and its frame.
+ IfcSchema::IfcDoor* door = new IfcSchema::IfcDoor(guid(), file.getSingle(), null, null, null,
+ file.addLocalPlacement(4800, 1600, 0, 0, 0, 1, 0, 1, 0), 0, null, 2200, 1000
+#ifdef USE_IFC4
+ , IfcSchema::IfcDoorTypeEnum::IfcDoorType_DOOR
+ , IfcSchema::IfcDoorTypeOperationEnum::IfcDoorTypeOperation_SINGLE_SWING_LEFT
+ , null
+#endif
+ );
+ door->setRepresentation(file.addBox(80, 80, 2120, 0, file.addPlacement3d(460, 0, 0)));
+ IfcSchema::IfcRepresentation::list door_representations = door->Representation()->Representations();
+ IfcSchema::IfcShapeRepresentation* door_body = 0;
+ for (IfcSchema::IfcRepresentation::it i = door_representations->begin(); i != door_representations->end(); ++i) {
+ IfcSchema::IfcRepresentation* rep = *i;
+ if (rep->is(IfcSchema::Type::IfcShapeRepresentation) && rep->RepresentationIdentifier() == "Body") {
+ door_body = (IfcSchema::IfcShapeRepresentation*) rep;
+ }
+ }
+ file.addBox(door_body, 80, 80, 2120, 0, file.addPlacement3d(-460, 0, 0));
+ file.addBox(door_body, 1000, 80, 80, 0, file.addPlacement3d( 0, 0, 2120));
+ file.addBox(door_body, 860, 30, 2120);
+ file.addBuildingProduct(door);
+ file.setSurfaceColour(door->Representation(), 0.9, 0.9, 0.9);
+ file.AddEntity(new IfcSchema::IfcRelFillsElement(guid(), file.getSingle(), null, null, door_opening, door));
+
+ // Surface styles are assigned to representation items, hence there is no real limitation to
+ // assign different colours within the same representation. However, some viewers have
+ // difficulties rendering products with representation items with different surface styles.
+ // Therefore we will construct the window as a decomposition of beams and a plate, in which
+ // only the plate will have a transparent material assigned.
+
+ // The window frame will consists of four seperate beams.
+ // AutoCAD Architecture will create an internal window type for the IfcWindow created.
+ // Therefore the OverallWidth and OverallHeight of the window attributes will need to
+ // match the bounding box of the representation. Furthermore, the window placement needs
+ // to align with the lowerleft corner of the constituent parts.
+ IfcSchema::IfcProductDefinitionShape::list frame_representations (new IfcTemplatedEntityList());
+ frame_representations->push(file.addBox(1860, 90, 90));
+ frame_representations->push(*frame_representations->begin()); // Add a reference to the shape created above
+ frame_representations->push(file.addBox(90, 90, 1420));
+ frame_representations->push(*(frame_representations->end()-1)); // Add a reference to the shape created above
+
+ // The beams all have the same surface style assigned
+ IfcSchema::IfcPresentationStyleAssignment* frame_style = 0;
+ for (IfcSchema::IfcProductDefinitionShape::it i = frame_representations->begin(); i != frame_representations->end(); ++i) {
+ if (frame_style) {
+ file.setSurfaceColour(*i, frame_style);
+ } else {
+ frame_style = file.setSurfaceColour(*i, 0.5, 0.4, 0.3);
+ }
+ }
+
+ // This window will be placed at five locations within the building. A list of placements is
+ // created and is iterated over to create all window instances.
+ IfcSchema::IfcLocalPlacement::list window_placements (new IfcTemplatedEntityList());
+ window_placements->push(file.addLocalPlacement(2*-1770-430-930, -45, 400));
+ window_placements->push(file.addLocalPlacement( -1770-430-930, -45, 400));
+ window_placements->push(file.addLocalPlacement( -430-930, -45, 400));
+ window_placements->push(file.addLocalPlacement( 3000-930, -45, 400));
+ window_placements->push(file.addLocalPlacement( -4855+45, 885-930, 400, 0, 0, 1, 0, 1, 0));
+
+ for (IfcSchema::IfcLocalPlacement::it it = window_placements->begin(); it != window_placements->end(); ++it) {
+
+ // Create the window at the current location
+ IfcSchema::IfcLocalPlacement* place = *it;
+ IfcSchema::IfcWindow* window = new IfcSchema::IfcWindow(guid(), file.getSingle(),
+ null, null, null, place, 0, null, 1600, 1860
+#ifdef USE_IFC4
+ , IfcSchema::IfcWindowTypeEnum::IfcWindowType_WINDOW
+ , IfcSchema::IfcWindowTypePartitioningEnum::IfcWindowTypePartitioning_SINGLE_PANEL
+ , null
+#endif
+ );
+ file.addBuildingProduct(window);
+
+ // Initalize a list of parts for the window to be composed of
+ IfcSchema::IfcObjectDefinition::list window_parts(new IfcTemplatedEntityList());
+
+ // The placements for the beams are not shared accross the different windows because every
+ // beam is placed relative to its parent window entity.
+ IfcSchema::IfcLocalPlacement::list frame_placements (new IfcTemplatedEntityList());
+ frame_placements->push(file.addLocalPlacement( 930,45));
+ frame_placements->push(file.addLocalPlacement( 930, 45, 1510));
+ frame_placements->push(file.addLocalPlacement(-885+930, 45, 90));
+ frame_placements->push(file.addLocalPlacement( 885+930, 45, 90));
+
+ // Now iterate over the placements and representations of the beam and add them to list of parts
+ IfcSchema::IfcLocalPlacement::it frame_placement;
+ IfcSchema::IfcProductDefinitionShape::it frame_representation;
+ for (frame_placement = frame_placements->begin(), frame_representation = frame_representations->begin();
+ frame_placement != frame_placements->end() && frame_representation != frame_representations->end();
+ ++frame_placement, ++frame_representation)
+ {
+ IfcSchema::IfcMember* frame_part = new IfcSchema::IfcMember(guid(), file.getSingle(),
+ null, null, null, *frame_placement, *frame_representation, null
+#ifdef USE_IFC4
+ , IfcSchema::IfcMemberTypeEnum::IfcMemberType_MULLION
+#endif
+ );
+ file.AddEntity(frame_part);
+ window_parts->push(frame_part);
+ file.relatePlacements(window, frame_part);
+ }
+
+ // Add the glass plate to the list of parts
+ IfcSchema::IfcPlate* glass_part = new IfcSchema::IfcPlate(guid(), file.getSingle(), null,
+ null, null, file.addLocalPlacement(930, 45, 90), file.addBox(1680, 10, 1420), null
+#ifdef USE_IFC4
+ , IfcSchema::IfcPlateTypeEnum::IfcPlateType_SHEET
+#endif
+ );
+ file.AddEntity(glass_part);
+ window_parts->push(glass_part);
+ file.relatePlacements(window, glass_part);
+ file.setSurfaceColour(glass_part->Representation(), 0.6, 0.7, 0.75, 0.1);
+
+ // Now create a decomposition relation between the window and the parts. Most viewers and authoring
+ // tools will consider the window a single entity that can be selected as a whole.
+ IfcSchema::IfcRelDecomposes* decomposition = new IfcSchema::IfcRelAggregates(guid(), file.getSingle(),
+ null, null, window, window_parts);
+ file.AddEntity(decomposition);
+ }
+
+ // Finally create a file stream for our output and write the IFC file to it.
+ std::ofstream f("IfcOpenHouse.ifc");
+ f << file;
+}
+
+void createGroundShape(TopoDS_Shape& shape) {
+ TColgp_Array2OfPnt cv (0, 4, 0, 4);
+ cv.SetValue(0, 0, gp_Pnt(-10000, -10000, -4130));
+ cv.SetValue(0, 1, gp_Pnt(-10000, -4330, -4130));
+ cv.SetValue(0, 2, gp_Pnt(-10000, 0, -5130));
+ cv.SetValue(0, 3, gp_Pnt(-10000, 4330, -7130));
+ cv.SetValue(0, 4, gp_Pnt(-10000, 10000, -7130));
+ cv.SetValue(1, 0, gp_Pnt( -3330, -10000, -5130));
+ cv.SetValue(1, 1, gp_Pnt( -7670, -3670, 5000));
+ cv.SetValue(1, 2, gp_Pnt( -9000, 0, 1000));
+ cv.SetValue(1, 3, gp_Pnt( -7670, 7670, 6000));
+ cv.SetValue(1, 4, gp_Pnt( -3330, 10000, -4130));
+ cv.SetValue(2, 0, gp_Pnt( 0, -10000, -5530));
+ cv.SetValue(2, 1, gp_Pnt( 0, -3670, 3000));
+ cv.SetValue(2, 2, gp_Pnt( 0, 0, -12000));
+ cv.SetValue(2, 3, gp_Pnt( 0, 7670, 1500));
+ cv.SetValue(2, 4, gp_Pnt( 0, 10000, -4130));
+ cv.SetValue(3, 0, gp_Pnt( 3330, -10000, -6130));
+ cv.SetValue(3, 1, gp_Pnt( 7670, -3670, 6000));
+ cv.SetValue(3, 2, gp_Pnt( 9000, 0, 5000));
+ cv.SetValue(3, 3, gp_Pnt( 7670, 9000, 7000));
+ cv.SetValue(3, 4, gp_Pnt( 3330, 10000, -4130));
+ cv.SetValue(4, 0, gp_Pnt( 10000, -10000, -6130));
+ cv.SetValue(4, 1, gp_Pnt( 10000, -4330, -5130));
+ cv.SetValue(4, 2, gp_Pnt( 10000, 0, -4130));
+ cv.SetValue(4, 3, gp_Pnt( 10000, 4330, -4130));
+ cv.SetValue(4, 4, gp_Pnt( 10000, 10000, -8130));
+ TColStd_Array1OfReal knots(0, 1);
+ knots(0) = 0;
+ knots(1) = 1;
+ TColStd_Array1OfInteger mult(0, 1);
+ mult(0) = 5;
+ mult(1) = 5;
+ Handle(Geom_BSplineSurface) surf = new Geom_BSplineSurface(cv, knots, knots, mult, mult, 4, 4);
+#if OCC_VERSION_HEX < 0x60502
+ shape = BRepBuilderAPI_MakeFace(surf);
+#else
+ shape = BRepBuilderAPI_MakeFace(surf, 1);
+#endif
+}
diff --git a/src/examples/IfcParseExamples.cpp b/src/examples/IfcParseExamples.cpp
index 3e729553a7..898507e5c6 100644
--- a/src/examples/IfcParseExamples.cpp
+++ b/src/examples/IfcParseExamples.cpp
@@ -19,7 +19,7 @@
#include "../ifcparse/IfcParse.h"
-using namespace Ifc2x3;
+using namespace IfcSchema;
int main(int argc, char** argv) {
diff --git a/src/ifcconvert/ColladaSerializer.cpp b/src/ifcconvert/ColladaSerializer.cpp
new file mode 100644
index 0000000000..57efa0c968
--- /dev/null
+++ b/src/ifcconvert/ColladaSerializer.cpp
@@ -0,0 +1,285 @@
+/********************************************************************************
+ * *
+ * 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 . *
+ * *
+ ********************************************************************************/
+
+#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 == '_')) {
+ id.push_back(c);
+ }
+ }
+ return id;
+}
+
+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::string& default_material_name, const std::vector& positions, const std::vector& normals, const std::vector& indices, 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();
+
+ addFloatSource(mesh_id, COLLADASW::LibraryGeometries::POSITIONS_SOURCE_ID_SUFFIX, positions);
+ if (has_normals) {
+ 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();
+
+ std::vector::const_iterator index_range_start = indices.begin();
+ std::vector::const_iterator material_it = material_ids.begin();
+ int previous_material_id = -2;
+ for (std::vector::const_iterator it = indices.begin(); ; it += 3) {
+ const int current_material_id = material_it == material_ids.end()
+ ? -3
+ : *(material_it++);
+ const int num_triangles = std::distance(index_range_start, it) / 3;
+ if ((previous_material_id != current_material_id && num_triangles > 0) || (it == indices.end())) {
+ COLLADASW::Triangles triangles(mSW);
+ triangles.setMaterial(collada_id(previous_material_id < 0
+ ? default_material_name
+ : materials[previous_material_id].name()));
+ triangles.setCount(num_triangles);
+ int offset = 0;
+ 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.prepareToAppendValues();
+ for (std::vector::const_iterator jt = index_range_start; jt != it; ++jt) {
+ const int idx = *jt;
+ if (has_normals) {
+ triangles.appendValues(idx, idx);
+ } else {
+ triangles.appendValues(idx);
+ }
+ }
+ triangles.finish();
+ index_range_start = it;
+ }
+ previous_material_id = current_material_id;
+ if (it == indices.end()) {
+ break;
+ }
+ }
+
+ 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_name, const std::vector& material_ids, 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::NODE);
+
+ // 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_name);
+ for (std::vector::const_iterator it = material_ids.begin(); it != material_ids.end(); ++it) {
+ COLLADASW::InstanceMaterial material (*it, "#" + *it);
+ 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 IfcGeomObjects::Material& material) {
+ openEffect(collada_id(material.name()) + "-fx");
+ COLLADASW::EffectProfile effect(mSW);
+ effect.setShaderType(COLLADASW::EffectProfile::LAMBERT);
+ if (material.hasDiffuse()) {
+ const double* diffuse = material.diffuse();
+ effect.setDiffuse(COLLADASW::ColorOrTexture(COLLADASW::Color(diffuse[0],diffuse[1],diffuse[2])));
+ }
+ if (material.hasSpecular()) {
+ const double* specular = material.specular();
+ effect.setSpecular(COLLADASW::ColorOrTexture(COLLADASW::Color(specular[0],specular[1],specular[2])));
+ }
+ if (material.hasSpecularity()) {
+ effect.setShininess(material.specularity());
+ }
+ if (material.hasTransparency()) {
+ const double transparency = material.transparency();
+ if (transparency > 0) {
+ // The default opacity mode for Collada is A_ONE, which apparently indicates a
+ // transparency value of 1 to be fully opaque. Hence transparency is inverted.
+ effect.setTransparency(1.0 - transparency);
+ }
+ }
+ addEffectProfile(effect);
+ closeEffect();
+}
+
+void ColladaSerializer::ColladaExporter::ColladaMaterials::ColladaEffects::close() {
+ closeLibrary();
+}
+
+void ColladaSerializer::ColladaExporter::ColladaMaterials::add(const IfcGeomObjects::Material& material) {
+ if (!contains(material)) {
+ effects.write(material);
+ materials.push_back(material);
+ }
+}
+
+bool ColladaSerializer::ColladaExporter::ColladaMaterials::contains(const IfcGeomObjects::Material& material) {
+ return std::find(materials.begin(), materials.end(), material) != materials.end();
+}
+
+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());
+ openMaterial(material_name);
+ addInstanceEffect("#" + material_name + "-fx");
+ closeMaterial();
+ }
+ closeLibrary();
+}
+
+void ColladaSerializer::ColladaExporter::startDocument(const std::string& unit_name, float unit_magnitude) {
+ stream.startDocument();
+
+ COLLADASW::Asset asset(&stream);
+ asset.getContributor().mAuthoringTool = std::string("IfcOpenShell ") + IFCOPENSHELL_VERSION;
+ asset.setUnit(unit_name, unit_magnitude);
+ asset.setUpAxisType(COLLADASW::Asset::Z_UP);
+ asset.add();
+}
+
+void ColladaSerializer::ColladaExporter::writeTesselated(const std::string& guid, const std::string& name, const std::string& type, int obj_id, const std::vector& matrix, const std::vector& vertices, const std::vector& normals, const std::vector& indices, const std::vector& material_ids, const std::vector& _materials) {
+ const IfcGeomObjects::Material default_for_type = IfcGeomObjects::Material(IfcGeom::get_default_style(type));
+ std::vector material_references;
+ const bool needs_default = std::find(material_ids.begin(), material_ids.end(), -1) != material_ids.end();
+ if (needs_default) {
+ if (!materials.contains(default_for_type)) {
+ materials.add(default_for_type);
+ }
+ material_references.push_back(collada_id(default_for_type.name()));
+ }
+ for (std::vector::const_iterator it = _materials.begin(); it != _materials.end(); ++it) {
+ const IfcGeomObjects::Material& material = *it;
+ if (!materials.contains(material)) {
+ materials.add(material);
+ }
+ material_references.push_back(collada_id(material.name()));
+ }
+ deferreds.push_back(DeferredObject(guid, name, type, obj_id, matrix, vertices, normals, indices, material_ids, _materials, material_references));
+}
+
+const std::string ColladaSerializer::ColladaExporter::DeferredObject::Name() const {
+ std::stringstream ss;
+ if (!this->name.empty()) {
+ ss << this->obj_id << "_" << this->name;
+ } else {
+ ss << this->guid;
+ }
+ return collada_id(ss.str());
+}
+
+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 (std::vector::const_iterator it = deferreds.begin(); it != deferreds.end(); ++it) {
+ const std::string object_name = it->Name();
+ geometries.write(object_name, it->type, it->vertices, it->normals, it->indices, it->material_ids, it->materials);
+ }
+ geometries.close();
+ for (std::vector::const_iterator it = deferreds.begin(); it != deferreds.end(); ++it) {
+ const std::string object_name = it->Name();
+ scene.add(object_name, object_name, object_name, it->material_references, it->matrix);
+ }
+ scene.write();
+ stream.endDocument();
+}
+
+bool ColladaSerializer::ready() {
+ return true;
+}
+
+void ColladaSerializer::writeHeader() {
+ exporter.startDocument(unit_name, unit_magnitude);
+}
+
+void ColladaSerializer::writeTesselated(const IfcGeomObjects::IfcGeomObject* o) {
+ const IfcGeomObjects::IfcRepresentationTriangulation& mesh = o->mesh();
+ exporter.writeTesselated(o->guid(), o->name(), o->type(), o->id(), o->matrix(), mesh.verts(), mesh.normals(), mesh.faces(), mesh.material_ids(), mesh.materials());
+}
+
+void ColladaSerializer::finalize() {
+ exporter.endDocument();
+}
+
+#endif
diff --git a/src/ifcconvert/ColladaSerializer.h b/src/ifcconvert/ColladaSerializer.h
new file mode 100644
index 0000000000..7bb7f80e6d
--- /dev/null
+++ b/src/ifcconvert/ColladaSerializer.h
@@ -0,0 +1,164 @@
+/********************************************************************************
+ * *
+ * 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 . *
+ * *
+ ********************************************************************************/
+
+#ifdef WITH_OPENCOLLADA
+
+#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"
+
+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::string& default_material_name, const std::vector& positions, const std::vector& normals, const std::vector& indices, const std::vector material_ids, const std::vector& materials);
+ 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_name, const std::vector& material_ids, 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 IfcGeomObjects::Material& material);
+ void close();
+ };
+ std::vector materials;
+ ColladaEffects effects;
+ public:
+ explicit ColladaMaterials(COLLADASW::StreamWriter& stream)
+ : COLLADASW::LibraryMaterials(&stream)
+ , effects(stream)
+ {}
+ void add(const IfcGeomObjects::Material& material);
+ bool contains(const IfcGeomObjects::Material& material);
+ void write();
+ };
+ class DeferredObject {
+ public:
+ std::string guid, name, type;
+ int obj_id;
+ std::vector matrix;
+ std::vector vertices;
+ std::vector normals;
+ std::vector indices;
+ std::vector material_ids;
+ std::vector materials;
+ std::vector material_references;
+ DeferredObject(const std::string& guid, const std::string& name, const std::string& type, int obj_id, const std::vector& matrix, const std::vector& vertices,
+ const std::vector& normals, const std::vector& indices, const std::vector& material_ids,
+ const std::vector& materials, const std::vector& material_references)
+ : guid(guid)
+ , name(name)
+ , type(type)
+ , obj_id(obj_id)
+ , matrix(matrix)
+ , vertices(vertices)
+ , normals(normals)
+ , indices(indices)
+ , material_ids(material_ids)
+ , materials(materials)
+ , material_references(material_references)
+ {}
+ const std::string Name() const;
+ };
+ 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(const std::string& unit_name, float unit_magnitude);
+ void writeTesselated(const std::string& guid, const std::string& name, const std::string& type, int obj_id, const std::vector& matrix, const std::vector& vertices, const std::vector& normals, const std::vector& indices, 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)
+ {}
+ bool ready();
+ void writeHeader();
+ void writeTesselated(const IfcGeomObjects::IfcGeomObject* o);
+ void writeShapeModel(const IfcGeomObjects::IfcGeomShapeModelObject* o) {}
+ void finalize();
+ bool isTesselated() const { return true; }
+ void setUnitNameAndMagnitude(const std::string& name, float magnitude) {
+ unit_name = name;
+ unit_magnitude = magnitude;
+ }
+};
+
+#endif
+
+#endif
diff --git a/src/ifcconvert/GeometrySerializer.h b/src/ifcconvert/GeometrySerializer.h
new file mode 100644
index 0000000000..c4895069a0
--- /dev/null
+++ b/src/ifcconvert/GeometrySerializer.h
@@ -0,0 +1,37 @@
+/********************************************************************************
+ * *
+ * 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;
+ virtual void setUnitNameAndMagnitude(const std::string& name, float magnitude) = 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..5ee64d0a07
--- /dev/null
+++ b/src/ifcconvert/IfcConvert.cpp
@@ -0,0 +1,299 @@
+/********************************************************************************
+ * *
+ * 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/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] [