summaryrefslogtreecommitdiff
path: root/src/3rdparty/assimp/code/IFCLoader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/assimp/code/IFCLoader.cpp')
-rw-r--r--src/3rdparty/assimp/code/IFCLoader.cpp228
1 files changed, 207 insertions, 21 deletions
diff --git a/src/3rdparty/assimp/code/IFCLoader.cpp b/src/3rdparty/assimp/code/IFCLoader.cpp
index 381068751..9963ce70a 100644
--- a/src/3rdparty/assimp/code/IFCLoader.cpp
+++ b/src/3rdparty/assimp/code/IFCLoader.cpp
@@ -48,6 +48,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <iterator>
#include <boost/tuple/tuple.hpp>
+#ifndef ASSIMP_BUILD_NO_COMPRESSED_IFC
+# include "../contrib/unzip/unzip.h"
+#endif
#include "IFCLoader.h"
#include "STEPFileReader.h"
@@ -103,7 +106,7 @@ static const aiImporterDesc desc = {
0,
0,
0,
- "ifc"
+ "ifc ifczip"
};
@@ -123,7 +126,7 @@ IFCImporter::~IFCImporter()
bool IFCImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
{
const std::string& extension = GetExtension(pFile);
- if (extension == "ifc") {
+ if (extension == "ifc" || extension == "ifczip") {
return true;
}
@@ -168,6 +171,66 @@ void IFCImporter::InternReadFile( const std::string& pFile,
ThrowException("Could not open file for reading");
}
+
+ // if this is a ifczip file, decompress its contents first
+ if(GetExtension(pFile) == "ifczip") {
+#ifndef ASSIMP_BUILD_NO_COMPRESSED_IFC
+ unzFile zip = unzOpen( pFile.c_str() );
+ if(zip == NULL) {
+ ThrowException("Could not open ifczip file for reading, unzip failed");
+ }
+
+ // chop 'zip' postfix
+ std::string fileName = pFile.substr(0,pFile.length() - 3);
+
+ std::string::size_type s = pFile.find_last_of('\\');
+ if(s == std::string::npos) {
+ s = pFile.find_last_of('/');
+ }
+ if(s != std::string::npos) {
+ fileName = fileName.substr(s+1);
+ }
+
+ // search file (same name as the IFCZIP except for the file extension) and place file pointer there
+ if(UNZ_OK == unzGoToFirstFile(zip)) {
+ do {
+ // get file size, etc.
+ unz_file_info fileInfo;
+ char filename[256];
+ unzGetCurrentFileInfo( zip , &fileInfo, filename, sizeof(filename), 0, 0, 0, 0 );
+ if (GetExtension(filename) != "ifc") {
+ continue;
+ }
+ uint8_t* buff = new uint8_t[fileInfo.uncompressed_size];
+ LogInfo("Decompressing IFCZIP file");
+ unzOpenCurrentFile( zip );
+ const int ret = unzReadCurrentFile( zip, buff, fileInfo.uncompressed_size);
+ size_t filesize = fileInfo.uncompressed_size;
+ if ( ret < 0 || size_t(ret) != filesize )
+ {
+ delete[] buff;
+ ThrowException("Failed to decompress IFC ZIP file");
+ }
+ unzCloseCurrentFile( zip );
+ stream.reset(new MemoryIOStream(buff,fileInfo.uncompressed_size,true));
+ break;
+
+ if (unzGoToNextFile(zip) == UNZ_END_OF_LIST_OF_FILE) {
+ ThrowException("Found no IFC file member in IFCZIP file (1)");
+ }
+
+ } while(true);
+ }
+ else {
+ ThrowException("Found no IFC file member in IFCZIP file (2)");
+ }
+
+ unzClose(zip);
+#else
+ ThrowException("Could not open ifczip file for reading, assimp was built without ifczip support");
+#endif
+ }
+
boost::scoped_ptr<STEP::DB> db(STEP::ReadFileHeader(stream));
const STEP::HeaderInfo& head = static_cast<const STEP::DB&>(*db).GetHeader();
@@ -196,12 +259,11 @@ void IFCImporter::InternReadFile( const std::string& pFile,
// tell the reader for which types we need to simulate STEPs reverse indices
static const char* const inverse_indices_to_track[] = {
- "ifcrelcontainedinspatialstructure", "ifcrelaggregates", "ifcrelvoidselement", "ifcstyleditem"
+ "ifcrelcontainedinspatialstructure", "ifcrelaggregates", "ifcrelvoidselement", "ifcreldefinesbyproperties", "ifcpropertyset", "ifcstyleditem"
};
// feed the IFC schema into the reader and pre-parse all lines
STEP::ReadFile(*db, schema, types_to_track, inverse_indices_to_track);
-
const STEP::LazyObject* proj = db->GetObject("ifcproject");
if (!proj) {
ThrowException("missing IfcProject entity");
@@ -217,9 +279,9 @@ void IFCImporter::InternReadFile( const std::string& pFile,
// in a build with no entities disabled. See
// scripts/IFCImporter/CPPGenerator.py
// for more information.
-#ifdef ASSIMP_IFC_TEST
- db->EvaluateAll();
-#endif
+ #ifdef ASSIMP_IFC_TEST
+ db->EvaluateAll();
+ #endif
// do final data copying
if (conv.meshes.size()) {
@@ -369,7 +431,7 @@ void GetAbsTransform(aiMatrix4x4& out, const aiNode* nd, ConversionData& conv)
bool ProcessMappedItem(const IfcMappedItem& mapped, aiNode* nd_src, std::vector< aiNode* >& subnodes_src, ConversionData& conv)
{
// insert a custom node here, the cartesian transform operator is simply a conventional transformation matrix
- std::unique_ptr<aiNode> nd(new aiNode());
+ std::auto_ptr<aiNode> nd(new aiNode());
nd->mName.Set("IfcMappedItem");
// handle the Cartesian operator
@@ -458,7 +520,7 @@ struct RateRepresentationPredicate {
return -3;
}
- // give strong preference to extruded geometry
+ // give strong preference to extruded geometry.
if (r == "SweptSolid") {
return -10;
}
@@ -495,21 +557,16 @@ void ProcessProductRepresentation(const IfcProduct& el, aiNode* nd, std::vector<
if(!el.Representation) {
return;
}
-
-
std::vector<unsigned int> meshes;
-
// we want only one representation type, so bring them in a suitable order (i.e try those
// that look as if we could read them quickly at first). This way of reading
// representation is relatively generic and allows the concrete implementations
// for the different representation types to make some sensible choices what
// to load and what not to load.
const STEP::ListOf< STEP::Lazy< IfcRepresentation >, 1, 0 >& src = el.Representation.Get()->Representations;
-
std::vector<const IfcRepresentation*> repr_ordered(src.size());
std::copy(src.begin(),src.end(),repr_ordered.begin());
std::sort(repr_ordered.begin(),repr_ordered.end(),RateRepresentationPredicate());
-
BOOST_FOREACH(const IfcRepresentation* repr, repr_ordered) {
bool res = false;
BOOST_FOREACH(const IfcRepresentationItem& item, repr->Items) {
@@ -525,10 +582,89 @@ void ProcessProductRepresentation(const IfcProduct& el, aiNode* nd, std::vector<
break;
}
}
-
AssignAddedMeshes(meshes,nd,conv);
}
+typedef std::map<std::string, std::string> Metadata;
+
+// ------------------------------------------------------------------------------------------------
+void ProcessMetadata(const ListOf< Lazy< IfcProperty >, 1, 0 >& set, ConversionData& conv, Metadata& properties,
+ const std::string& prefix = "",
+ unsigned int nest = 0)
+{
+ BOOST_FOREACH(const IfcProperty& property, set) {
+ const std::string& key = prefix.length() > 0 ? (prefix + "." + property.Name) : property.Name;
+ if (const IfcPropertySingleValue* const singleValue = property.ToPtr<IfcPropertySingleValue>()) {
+ if (singleValue->NominalValue) {
+ if (const EXPRESS::STRING* str = singleValue->NominalValue.Get()->ToPtr<EXPRESS::STRING>()) {
+ std::string value = static_cast<std::string>(*str);
+ properties[key]=value;
+ }
+ else if (const EXPRESS::REAL* val = singleValue->NominalValue.Get()->ToPtr<EXPRESS::REAL>()) {
+ float value = static_cast<float>(*val);
+ std::stringstream s;
+ s << value;
+ properties[key]=s.str();
+ }
+ else if (const EXPRESS::INTEGER* val = singleValue->NominalValue.Get()->ToPtr<EXPRESS::INTEGER>()) {
+ int64_t value = static_cast<int64_t>(*val);
+ std::stringstream s;
+ s << value;
+ properties[key]=s.str();
+ }
+ }
+ }
+ else if (const IfcPropertyListValue* const listValue = property.ToPtr<IfcPropertyListValue>()) {
+ std::stringstream ss;
+ ss << "[";
+ unsigned index=0;
+ BOOST_FOREACH(const IfcValue::Out& v, listValue->ListValues) {
+ if (!v) continue;
+ if (const EXPRESS::STRING* str = v->ToPtr<EXPRESS::STRING>()) {
+ std::string value = static_cast<std::string>(*str);
+ ss << "'" << value << "'";
+ }
+ else if (const EXPRESS::REAL* val = v->ToPtr<EXPRESS::REAL>()) {
+ float value = static_cast<float>(*val);
+ ss << value;
+ }
+ else if (const EXPRESS::INTEGER* val = v->ToPtr<EXPRESS::INTEGER>()) {
+ int64_t value = static_cast<int64_t>(*val);
+ ss << value;
+ }
+ if (index+1<listValue->ListValues.size()) {
+ ss << ",";
+ }
+ index++;
+ }
+ ss << "]";
+ properties[key]=ss.str();
+ }
+ else if (const IfcComplexProperty* const complexProp = property.ToPtr<IfcComplexProperty>()) {
+ if(nest > 2) { // mostly arbitrary limit to prevent stack overflow vulnerabilities
+ IFCImporter::LogError("maximum nesting level for IfcComplexProperty reached, skipping this property.");
+ }
+ else {
+ ProcessMetadata(complexProp->HasProperties, conv, properties, key, nest + 1);
+ }
+ }
+ else {
+ properties[key]="";
+ }
+ }
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void ProcessMetadata(uint64_t relDefinesByPropertiesID, ConversionData& conv, Metadata& properties)
+{
+ if (const IfcRelDefinesByProperties* const pset = conv.db.GetObject(relDefinesByPropertiesID)->ToPtr<IfcRelDefinesByProperties>()) {
+ if (const IfcPropertySet* const set = conv.db.GetObject(pset->RelatingPropertyDefinition->GetID())->ToPtr<IfcPropertySet>()) {
+ ProcessMetadata(set->HasProperties, conv, properties);
+ }
+ }
+}
+
// ------------------------------------------------------------------------------------------------
aiNode* ProcessSpatialStructure(aiNode* parent, const IfcProduct& el, ConversionData& conv, std::vector<TempOpening>* collect_openings = NULL)
{
@@ -550,10 +686,42 @@ aiNode* ProcessSpatialStructure(aiNode* parent, const IfcProduct& el, Conversion
}
// add an output node for this spatial structure
- std::unique_ptr<aiNode> nd(new aiNode());
- nd->mName.Set(el.GetClassName()+"_"+(el.Name?el.Name:el.GlobalId));
+ std::auto_ptr<aiNode> nd(new aiNode());
+ nd->mName.Set(el.GetClassName()+"_"+(el.Name?el.Name.Get():"Unnamed")+"_"+el.GlobalId);
nd->mParent = parent;
+ conv.already_processed.insert(el.GetID());
+
+ // check for node metadata
+ STEP::DB::RefMapRange children = refs.equal_range(el.GetID());
+ if (children.first!=refs.end()) {
+ Metadata properties;
+ if (children.first==children.second) {
+ // handles single property set
+ ProcessMetadata((*children.first).second, conv, properties);
+ }
+ else {
+ // handles multiple property sets (currently all property sets are merged,
+ // which may not be the best solution in the long run)
+ for (STEP::DB::RefMap::const_iterator it=children.first; it!=children.second; ++it) {
+ ProcessMetadata((*it).second, conv, properties);
+ }
+ }
+
+ if (!properties.empty()) {
+ aiMetadata* data = new aiMetadata();
+ data->mNumProperties = properties.size();
+ data->mKeys = new aiString[data->mNumProperties]();
+ data->mValues = new aiMetadataEntry[data->mNumProperties]();
+
+ unsigned int index = 0;
+ BOOST_FOREACH(const Metadata::value_type& kv, properties)
+ data->Set(index++, kv.first, aiString(kv.second));
+
+ nd->mMetaData = data;
+ }
+ }
+
if(el.ObjectPlacement) {
ResolveObjectPlacement(nd->mTransformation,el.ObjectPlacement.Get(),conv);
}
@@ -572,15 +740,24 @@ aiNode* ProcessSpatialStructure(aiNode* parent, const IfcProduct& el, Conversion
STEP::DB::RefMapRange range = refs.equal_range(el.GetID());
for(STEP::DB::RefMapRange range2 = range; range2.first != range.second; ++range2.first) {
+ // skip over meshes that have already been processed before. This is strictly necessary
+ // because the reverse indices also include references contained in argument lists and
+ // therefore every element has a back-reference hold by its parent.
+ if (conv.already_processed.find((*range2.first).second) != conv.already_processed.end()) {
+ continue;
+ }
const STEP::LazyObject& obj = conv.db.MustGetObject((*range2.first).second);
// handle regularly-contained elements
if(const IfcRelContainedInSpatialStructure* const cont = obj->ToPtr<IfcRelContainedInSpatialStructure>()) {
+ if(cont->RelatingStructure->GetID() != el.GetID()) {
+ continue;
+ }
BOOST_FOREACH(const IfcProduct& pro, cont->RelatedElements) {
if(const IfcOpeningElement* const open = pro.ToPtr<IfcOpeningElement>()) {
// IfcOpeningElement is handled below. Sadly we can't use it here as is:
- // The docs say that opening elements are USUALLY attached to building storeys
- // but we want them for the building elements to which they belong to.
+ // The docs say that opening elements are USUALLY attached to building storey,
+ // but we want them for the building elements to which they belong.
continue;
}
@@ -596,7 +773,7 @@ aiNode* ProcessSpatialStructure(aiNode* parent, const IfcProduct& el, Conversion
const IfcFeatureElementSubtraction& open = fills->RelatedOpeningElement;
// move opening elements to a separate node since they are semantically different than elements that are just 'contained'
- std::unique_ptr<aiNode> nd_aggr(new aiNode());
+ std::auto_ptr<aiNode> nd_aggr(new aiNode());
nd_aggr->mName.Set("$RelVoidsElement");
nd_aggr->mParent = nd.get();
@@ -631,10 +808,17 @@ aiNode* ProcessSpatialStructure(aiNode* parent, const IfcProduct& el, Conversion
}
for(;range.first != range.second; ++range.first) {
+ // see note in loop above
+ if (conv.already_processed.find((*range.first).second) != conv.already_processed.end()) {
+ continue;
+ }
if(const IfcRelAggregates* const aggr = conv.db.GetObject((*range.first).second)->ToPtr<IfcRelAggregates>()) {
+ if(aggr->RelatingObject->GetID() != el.GetID()) {
+ continue;
+ }
// move aggregate elements to a separate node since they are semantically different than elements that are just 'contained'
- std::unique_ptr<aiNode> nd_aggr(new aiNode());
+ std::auto_ptr<aiNode> nd_aggr(new aiNode());
nd_aggr->mName.Set("$RelAggregates");
nd_aggr->mParent = nd.get();
@@ -677,6 +861,8 @@ aiNode* ProcessSpatialStructure(aiNode* parent, const IfcProduct& el, Conversion
throw;
}
+ ai_assert(conv.already_processed.find(el.GetID()) != conv.already_processed.end());
+ conv.already_processed.erase(conv.already_processed.find(el.GetID()));
return nd.release();
}