diff options
Diffstat (limited to 'src/3rdparty/assimp/code/IFCLoader.cpp')
-rw-r--r-- | src/3rdparty/assimp/code/IFCLoader.cpp | 228 |
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(); } |