vrshoot
diff libs/assimp/IFCLoader.cpp @ 0:b2f14e535253
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 01 Feb 2014 19:58:19 +0200 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/libs/assimp/IFCLoader.cpp Sat Feb 01 19:58:19 2014 +0200 1.3 @@ -0,0 +1,979 @@ 1.4 +/* 1.5 +Open Asset Import Library (assimp) 1.6 +---------------------------------------------------------------------- 1.7 + 1.8 +Copyright (c) 2006-2012, assimp team 1.9 +All rights reserved. 1.10 + 1.11 +Redistribution and use of this software in source and binary forms, 1.12 +with or without modification, are permitted provided that the 1.13 +following conditions are met: 1.14 + 1.15 +* Redistributions of source code must retain the above 1.16 + copyright notice, this list of conditions and the 1.17 + following disclaimer. 1.18 + 1.19 +* Redistributions in binary form must reproduce the above 1.20 + copyright notice, this list of conditions and the 1.21 + following disclaimer in the documentation and/or other 1.22 + materials provided with the distribution. 1.23 + 1.24 +* Neither the name of the assimp team, nor the names of its 1.25 + contributors may be used to endorse or promote products 1.26 + derived from this software without specific prior 1.27 + written permission of the assimp team. 1.28 + 1.29 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.30 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.31 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.32 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.33 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.34 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.35 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.36 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.37 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.38 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.39 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.40 + 1.41 +---------------------------------------------------------------------- 1.42 +*/ 1.43 + 1.44 +/** @file IFCLoad.cpp 1.45 + * @brief Implementation of the Industry Foundation Classes loader. 1.46 + */ 1.47 +#include "AssimpPCH.h" 1.48 + 1.49 +#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER 1.50 + 1.51 +#include <iterator> 1.52 +#include <boost/tuple/tuple.hpp> 1.53 + 1.54 +#ifndef ASSIMP_BUILD_NO_COMPRESSED_IFC 1.55 +# include "../contrib/unzip/unzip.h" 1.56 +#endif 1.57 + 1.58 +#include "IFCLoader.h" 1.59 +#include "STEPFileReader.h" 1.60 + 1.61 +#include "IFCUtil.h" 1.62 + 1.63 +#include "StreamReader.h" 1.64 +#include "MemoryIOWrapper.h" 1.65 + 1.66 +namespace Assimp { 1.67 + template<> const std::string LogFunctions<IFCImporter>::log_prefix = "IFC: "; 1.68 +} 1.69 + 1.70 +using namespace Assimp; 1.71 +using namespace Assimp::Formatter; 1.72 +using namespace Assimp::IFC; 1.73 + 1.74 +/* DO NOT REMOVE this comment block. The genentitylist.sh script 1.75 + * just looks for names adhering to the IfcSomething naming scheme 1.76 + * and includes all matches in the whitelist for code-generation. Thus, 1.77 + * all entity classes that are only indirectly referenced need to be 1.78 + * mentioned explicitly. 1.79 + 1.80 + IfcRepresentationMap 1.81 + IfcProductRepresentation 1.82 + IfcUnitAssignment 1.83 + IfcClosedShell 1.84 + IfcDoor 1.85 + 1.86 + */ 1.87 + 1.88 +namespace { 1.89 + 1.90 + 1.91 +// forward declarations 1.92 +void SetUnits(ConversionData& conv); 1.93 +void SetCoordinateSpace(ConversionData& conv); 1.94 +void ProcessSpatialStructures(ConversionData& conv); 1.95 +aiNode* ProcessSpatialStructure(aiNode* parent, const IfcProduct& el ,ConversionData& conv); 1.96 +void ProcessProductRepresentation(const IfcProduct& el, aiNode* nd, ConversionData& conv); 1.97 +void MakeTreeRelative(ConversionData& conv); 1.98 +void ConvertUnit(const EXPRESS::DataType& dt,ConversionData& conv); 1.99 + 1.100 +} // anon 1.101 + 1.102 +static const aiImporterDesc desc = { 1.103 + "Industry Foundation Classes (IFC) Importer", 1.104 + "", 1.105 + "", 1.106 + "", 1.107 + aiImporterFlags_SupportBinaryFlavour, 1.108 + 0, 1.109 + 0, 1.110 + 0, 1.111 + 0, 1.112 + "ifc ifczip" 1.113 +}; 1.114 + 1.115 + 1.116 +// ------------------------------------------------------------------------------------------------ 1.117 +// Constructor to be privately used by Importer 1.118 +IFCImporter::IFCImporter() 1.119 +{} 1.120 + 1.121 +// ------------------------------------------------------------------------------------------------ 1.122 +// Destructor, private as well 1.123 +IFCImporter::~IFCImporter() 1.124 +{ 1.125 +} 1.126 + 1.127 +// ------------------------------------------------------------------------------------------------ 1.128 +// Returns whether the class can handle the format of the given file. 1.129 +bool IFCImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const 1.130 +{ 1.131 + const std::string& extension = GetExtension(pFile); 1.132 + if (extension == "ifc" || extension == "ifczip") { 1.133 + return true; 1.134 + } 1.135 + 1.136 + else if ((!extension.length() || checkSig) && pIOHandler) { 1.137 + // note: this is the common identification for STEP-encoded files, so 1.138 + // it is only unambiguous as long as we don't support any further 1.139 + // file formats with STEP as their encoding. 1.140 + const char* tokens[] = {"ISO-10303-21"}; 1.141 + return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); 1.142 + } 1.143 + return false; 1.144 +} 1.145 + 1.146 +// ------------------------------------------------------------------------------------------------ 1.147 +// List all extensions handled by this loader 1.148 +const aiImporterDesc* IFCImporter::GetInfo () const 1.149 +{ 1.150 + return &desc; 1.151 +} 1.152 + 1.153 + 1.154 +// ------------------------------------------------------------------------------------------------ 1.155 +// Setup configuration properties for the loader 1.156 +void IFCImporter::SetupProperties(const Importer* pImp) 1.157 +{ 1.158 + settings.skipSpaceRepresentations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_IFC_SKIP_SPACE_REPRESENTATIONS,true); 1.159 + settings.skipCurveRepresentations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_IFC_SKIP_CURVE_REPRESENTATIONS,true); 1.160 + settings.useCustomTriangulation = pImp->GetPropertyBool(AI_CONFIG_IMPORT_IFC_CUSTOM_TRIANGULATION,true); 1.161 + 1.162 + settings.conicSamplingAngle = 10.f; 1.163 + settings.skipAnnotations = true; 1.164 +} 1.165 + 1.166 + 1.167 +// ------------------------------------------------------------------------------------------------ 1.168 +// Imports the given file into the given scene structure. 1.169 +void IFCImporter::InternReadFile( const std::string& pFile, 1.170 + aiScene* pScene, IOSystem* pIOHandler) 1.171 +{ 1.172 + boost::shared_ptr<IOStream> stream(pIOHandler->Open(pFile)); 1.173 + if (!stream) { 1.174 + ThrowException("Could not open file for reading"); 1.175 + } 1.176 + 1.177 + 1.178 + // if this is a ifczip file, decompress its contents first 1.179 + if(GetExtension(pFile) == "ifczip") { 1.180 +#ifndef ASSIMP_BUILD_NO_COMPRESSED_IFC 1.181 + unzFile zip = unzOpen( pFile.c_str() ); 1.182 + if(zip == NULL) { 1.183 + ThrowException("Could not open ifczip file for reading, unzip failed"); 1.184 + } 1.185 + 1.186 + // chop 'zip' postfix 1.187 + std::string fileName = pFile.substr(0,pFile.length() - 3); 1.188 + 1.189 + std::string::size_type s = pFile.find_last_of('\\'); 1.190 + if(s == std::string::npos) { 1.191 + s = pFile.find_last_of('/'); 1.192 + } 1.193 + if(s != std::string::npos) { 1.194 + fileName = fileName.substr(s+1); 1.195 + } 1.196 + 1.197 + // search file (same name as the IFCZIP except for the file extension) and place file pointer there 1.198 + 1.199 + if(UNZ_OK == unzGoToFirstFile(zip)) { 1.200 + do { 1.201 + // 1.202 + 1.203 + // get file size, etc. 1.204 + unz_file_info fileInfo; 1.205 + char filename[256]; 1.206 + unzGetCurrentFileInfo( zip , &fileInfo, filename, sizeof(filename), 0, 0, 0, 0 ); 1.207 + 1.208 + if (GetExtension(filename) != "ifc") { 1.209 + continue; 1.210 + } 1.211 + 1.212 + uint8_t* buff = new uint8_t[fileInfo.uncompressed_size]; 1.213 + 1.214 + LogInfo("Decompressing IFCZIP file"); 1.215 + 1.216 + unzOpenCurrentFile( zip ); 1.217 + const int ret = unzReadCurrentFile( zip, buff, fileInfo.uncompressed_size); 1.218 + size_t filesize = fileInfo.uncompressed_size; 1.219 + if ( ret < 0 || size_t(ret) != filesize ) 1.220 + { 1.221 + delete[] buff; 1.222 + ThrowException("Failed to decompress IFC ZIP file"); 1.223 + } 1.224 + unzCloseCurrentFile( zip ); 1.225 + stream.reset(new MemoryIOStream(buff,fileInfo.uncompressed_size,true)); 1.226 + break; 1.227 + 1.228 + if (unzGoToNextFile(zip) == UNZ_END_OF_LIST_OF_FILE) { 1.229 + ThrowException("Found no IFC file member in IFCZIP file (1)"); 1.230 + } 1.231 + 1.232 + } while(true); 1.233 + } 1.234 + else { 1.235 + ThrowException("Found no IFC file member in IFCZIP file (2)"); 1.236 + } 1.237 + 1.238 + unzClose(zip); 1.239 +#else 1.240 + ThrowException("Could not open ifczip file for reading, assimp was built without ifczip support"); 1.241 +#endif 1.242 + } 1.243 + 1.244 + boost::scoped_ptr<STEP::DB> db(STEP::ReadFileHeader(stream)); 1.245 + const STEP::HeaderInfo& head = static_cast<const STEP::DB&>(*db).GetHeader(); 1.246 + 1.247 + if(!head.fileSchema.size() || head.fileSchema.substr(0,3) != "IFC") { 1.248 + ThrowException("Unrecognized file schema: " + head.fileSchema); 1.249 + } 1.250 + 1.251 + if (!DefaultLogger::isNullLogger()) { 1.252 + LogDebug("File schema is \'" + head.fileSchema + '\''); 1.253 + if (head.timestamp.length()) { 1.254 + LogDebug("Timestamp \'" + head.timestamp + '\''); 1.255 + } 1.256 + if (head.app.length()) { 1.257 + LogDebug("Application/Exporter identline is \'" + head.app + '\''); 1.258 + } 1.259 + } 1.260 + 1.261 + // obtain a copy of the machine-generated IFC scheme 1.262 + EXPRESS::ConversionSchema schema; 1.263 + GetSchema(schema); 1.264 + 1.265 + // tell the reader which entity types to track with special care 1.266 + static const char* const types_to_track[] = { 1.267 + "ifcsite", "ifcbuilding", "ifcproject" 1.268 + }; 1.269 + 1.270 + // tell the reader for which types we need to simulate STEPs reverse indices 1.271 + static const char* const inverse_indices_to_track[] = { 1.272 + "ifcrelcontainedinspatialstructure", "ifcrelaggregates", "ifcrelvoidselement", "ifcreldefinesbyproperties", "ifcpropertyset", "ifcstyleditem" 1.273 + }; 1.274 + 1.275 + // feed the IFC schema into the reader and pre-parse all lines 1.276 + STEP::ReadFile(*db, schema, types_to_track, inverse_indices_to_track); 1.277 + 1.278 + const STEP::LazyObject* proj = db->GetObject("ifcproject"); 1.279 + if (!proj) { 1.280 + ThrowException("missing IfcProject entity"); 1.281 + } 1.282 + 1.283 + ConversionData conv(*db,proj->To<IfcProject>(),pScene,settings); 1.284 + SetUnits(conv); 1.285 + SetCoordinateSpace(conv); 1.286 + ProcessSpatialStructures(conv); 1.287 + MakeTreeRelative(conv); 1.288 + 1.289 + // NOTE - this is a stress test for the importer, but it works only 1.290 + // in a build with no entities disabled. See 1.291 + // scripts/IFCImporter/CPPGenerator.py 1.292 + // for more information. 1.293 +#ifdef ASSIMP_IFC_TEST 1.294 + db->EvaluateAll(); 1.295 +#endif 1.296 + 1.297 + // do final data copying 1.298 + if (conv.meshes.size()) { 1.299 + pScene->mNumMeshes = static_cast<unsigned int>(conv.meshes.size()); 1.300 + pScene->mMeshes = new aiMesh*[pScene->mNumMeshes](); 1.301 + std::copy(conv.meshes.begin(),conv.meshes.end(),pScene->mMeshes); 1.302 + 1.303 + // needed to keep the d'tor from burning us 1.304 + conv.meshes.clear(); 1.305 + } 1.306 + 1.307 + if (conv.materials.size()) { 1.308 + pScene->mNumMaterials = static_cast<unsigned int>(conv.materials.size()); 1.309 + pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials](); 1.310 + std::copy(conv.materials.begin(),conv.materials.end(),pScene->mMaterials); 1.311 + 1.312 + // needed to keep the d'tor from burning us 1.313 + conv.materials.clear(); 1.314 + } 1.315 + 1.316 + // apply world coordinate system (which includes the scaling to convert to meters and a -90 degrees rotation around x) 1.317 + aiMatrix4x4 scale, rot; 1.318 + aiMatrix4x4::Scaling(static_cast<aiVector3D>(IfcVector3(conv.len_scale)),scale); 1.319 + aiMatrix4x4::RotationX(-AI_MATH_HALF_PI_F,rot); 1.320 + 1.321 + pScene->mRootNode->mTransformation = rot * scale * conv.wcs * pScene->mRootNode->mTransformation; 1.322 + 1.323 + // this must be last because objects are evaluated lazily as we process them 1.324 + if ( !DefaultLogger::isNullLogger() ){ 1.325 + LogDebug((Formatter::format(),"STEP: evaluated ",db->GetEvaluatedObjectCount()," object records")); 1.326 + } 1.327 +} 1.328 + 1.329 +namespace { 1.330 + 1.331 + 1.332 +// ------------------------------------------------------------------------------------------------ 1.333 +void ConvertUnit(const IfcNamedUnit& unit,ConversionData& conv) 1.334 +{ 1.335 + if(const IfcSIUnit* const si = unit.ToPtr<IfcSIUnit>()) { 1.336 + 1.337 + if(si->UnitType == "LENGTHUNIT") { 1.338 + conv.len_scale = si->Prefix ? ConvertSIPrefix(si->Prefix) : 1.f; 1.339 + IFCImporter::LogDebug("got units used for lengths"); 1.340 + } 1.341 + if(si->UnitType == "PLANEANGLEUNIT") { 1.342 + if (si->Name != "RADIAN") { 1.343 + IFCImporter::LogWarn("expected base unit for angles to be radian"); 1.344 + } 1.345 + } 1.346 + } 1.347 + else if(const IfcConversionBasedUnit* const convu = unit.ToPtr<IfcConversionBasedUnit>()) { 1.348 + 1.349 + if(convu->UnitType == "PLANEANGLEUNIT") { 1.350 + try { 1.351 + conv.angle_scale = convu->ConversionFactor->ValueComponent->To<EXPRESS::REAL>(); 1.352 + ConvertUnit(*convu->ConversionFactor->UnitComponent,conv); 1.353 + IFCImporter::LogDebug("got units used for angles"); 1.354 + } 1.355 + catch(std::bad_cast&) { 1.356 + IFCImporter::LogError("skipping unknown IfcConversionBasedUnit.ValueComponent entry - expected REAL"); 1.357 + } 1.358 + } 1.359 + } 1.360 +} 1.361 + 1.362 +// ------------------------------------------------------------------------------------------------ 1.363 +void ConvertUnit(const EXPRESS::DataType& dt,ConversionData& conv) 1.364 +{ 1.365 + try { 1.366 + const EXPRESS::ENTITY& e = dt.To<ENTITY>(); 1.367 + 1.368 + const IfcNamedUnit& unit = e.ResolveSelect<IfcNamedUnit>(conv.db); 1.369 + if(unit.UnitType != "LENGTHUNIT" && unit.UnitType != "PLANEANGLEUNIT") { 1.370 + return; 1.371 + } 1.372 + 1.373 + ConvertUnit(unit,conv); 1.374 + } 1.375 + catch(std::bad_cast&) { 1.376 + // not entity, somehow 1.377 + IFCImporter::LogError("skipping unknown IfcUnit entry - expected entity"); 1.378 + } 1.379 +} 1.380 + 1.381 +// ------------------------------------------------------------------------------------------------ 1.382 +void SetUnits(ConversionData& conv) 1.383 +{ 1.384 + // see if we can determine the coordinate space used to express. 1.385 + for(size_t i = 0; i < conv.proj.UnitsInContext->Units.size(); ++i ) { 1.386 + ConvertUnit(*conv.proj.UnitsInContext->Units[i],conv); 1.387 + } 1.388 +} 1.389 + 1.390 + 1.391 +// ------------------------------------------------------------------------------------------------ 1.392 +void SetCoordinateSpace(ConversionData& conv) 1.393 +{ 1.394 + const IfcRepresentationContext* fav = NULL; 1.395 + BOOST_FOREACH(const IfcRepresentationContext& v, conv.proj.RepresentationContexts) { 1.396 + fav = &v; 1.397 + // Model should be the most suitable type of context, hence ignore the others 1.398 + if (v.ContextType && v.ContextType.Get() == "Model") { 1.399 + break; 1.400 + } 1.401 + } 1.402 + if (fav) { 1.403 + if(const IfcGeometricRepresentationContext* const geo = fav->ToPtr<IfcGeometricRepresentationContext>()) { 1.404 + ConvertAxisPlacement(conv.wcs, *geo->WorldCoordinateSystem, conv); 1.405 + IFCImporter::LogDebug("got world coordinate system"); 1.406 + } 1.407 + } 1.408 +} 1.409 + 1.410 + 1.411 +// ------------------------------------------------------------------------------------------------ 1.412 +void ResolveObjectPlacement(aiMatrix4x4& m, const IfcObjectPlacement& place, ConversionData& conv) 1.413 +{ 1.414 + if (const IfcLocalPlacement* const local = place.ToPtr<IfcLocalPlacement>()){ 1.415 + IfcMatrix4 tmp; 1.416 + ConvertAxisPlacement(tmp, *local->RelativePlacement, conv); 1.417 + 1.418 + m = static_cast<aiMatrix4x4>(tmp); 1.419 + 1.420 + if (local->PlacementRelTo) { 1.421 + aiMatrix4x4 tmp; 1.422 + ResolveObjectPlacement(tmp,local->PlacementRelTo.Get(),conv); 1.423 + m = tmp * m; 1.424 + } 1.425 + } 1.426 + else { 1.427 + IFCImporter::LogWarn("skipping unknown IfcObjectPlacement entity, type is " + place.GetClassName()); 1.428 + } 1.429 +} 1.430 + 1.431 +// ------------------------------------------------------------------------------------------------ 1.432 +void GetAbsTransform(aiMatrix4x4& out, const aiNode* nd, ConversionData& conv) 1.433 +{ 1.434 + aiMatrix4x4 t; 1.435 + if (nd->mParent) { 1.436 + GetAbsTransform(t,nd->mParent,conv); 1.437 + } 1.438 + out = t*nd->mTransformation; 1.439 +} 1.440 + 1.441 +// ------------------------------------------------------------------------------------------------ 1.442 +bool ProcessMappedItem(const IfcMappedItem& mapped, aiNode* nd_src, std::vector< aiNode* >& subnodes_src, ConversionData& conv) 1.443 +{ 1.444 + // insert a custom node here, the cartesian transform operator is simply a conventional transformation matrix 1.445 + std::auto_ptr<aiNode> nd(new aiNode()); 1.446 + nd->mName.Set("IfcMappedItem"); 1.447 + 1.448 + // handle the Cartesian operator 1.449 + IfcMatrix4 m; 1.450 + ConvertTransformOperator(m, *mapped.MappingTarget); 1.451 + 1.452 + IfcMatrix4 msrc; 1.453 + ConvertAxisPlacement(msrc,*mapped.MappingSource->MappingOrigin,conv); 1.454 + 1.455 + msrc = m*msrc; 1.456 + 1.457 + std::vector<unsigned int> meshes; 1.458 + const size_t old_openings = conv.collect_openings ? conv.collect_openings->size() : 0; 1.459 + if (conv.apply_openings) { 1.460 + IfcMatrix4 minv = msrc; 1.461 + minv.Inverse(); 1.462 + BOOST_FOREACH(TempOpening& open,*conv.apply_openings){ 1.463 + open.Transform(minv); 1.464 + } 1.465 + } 1.466 + 1.467 + const IfcRepresentation& repr = mapped.MappingSource->MappedRepresentation; 1.468 + 1.469 + bool got = false; 1.470 + BOOST_FOREACH(const IfcRepresentationItem& item, repr.Items) { 1.471 + if(!ProcessRepresentationItem(item,meshes,conv)) { 1.472 + IFCImporter::LogWarn("skipping mapped entity of type " + item.GetClassName() + ", no representations could be generated"); 1.473 + } 1.474 + else got = true; 1.475 + } 1.476 + 1.477 + if (!got) { 1.478 + return false; 1.479 + } 1.480 + 1.481 + AssignAddedMeshes(meshes,nd.get(),conv); 1.482 + if (conv.collect_openings) { 1.483 + 1.484 + // if this pass serves us only to collect opening geometry, 1.485 + // make sure we transform the TempMesh's which we need to 1.486 + // preserve as well. 1.487 + if(const size_t diff = conv.collect_openings->size() - old_openings) { 1.488 + for(size_t i = 0; i < diff; ++i) { 1.489 + (*conv.collect_openings)[old_openings+i].Transform(msrc); 1.490 + } 1.491 + } 1.492 + } 1.493 + 1.494 + nd->mTransformation = nd_src->mTransformation * static_cast<aiMatrix4x4>( msrc ); 1.495 + subnodes_src.push_back(nd.release()); 1.496 + 1.497 + return true; 1.498 +} 1.499 + 1.500 +// ------------------------------------------------------------------------------------------------ 1.501 +struct RateRepresentationPredicate { 1.502 + 1.503 + int Rate(const IfcRepresentation* r) const { 1.504 + // the smaller, the better 1.505 + 1.506 + if (! r->RepresentationIdentifier) { 1.507 + // neutral choice if no extra information is specified 1.508 + return 0; 1.509 + } 1.510 + 1.511 + 1.512 + const std::string& name = r->RepresentationIdentifier.Get(); 1.513 + if (name == "MappedRepresentation") { 1.514 + if (!r->Items.empty()) { 1.515 + // take the first item and base our choice on it 1.516 + const IfcMappedItem* const m = r->Items.front()->ToPtr<IfcMappedItem>(); 1.517 + if (m) { 1.518 + return Rate(m->MappingSource->MappedRepresentation); 1.519 + } 1.520 + } 1.521 + return 100; 1.522 + } 1.523 + 1.524 + return Rate(name); 1.525 + } 1.526 + 1.527 + int Rate(const std::string& r) const { 1.528 + 1.529 + 1.530 + if (r == "SolidModel") { 1.531 + return -3; 1.532 + } 1.533 + 1.534 + // give strong preference to extruded geometry. 1.535 + if (r == "SweptSolid") { 1.536 + return -10; 1.537 + } 1.538 + 1.539 + if (r == "Clipping") { 1.540 + return -5; 1.541 + } 1.542 + 1.543 + // 'Brep' is difficult to get right due to possible voids in the 1.544 + // polygon boundaries, so take it only if we are forced to (i.e. 1.545 + // if the only alternative is (non-clipping) boolean operations, 1.546 + // which are not supported at all). 1.547 + if (r == "Brep") { 1.548 + return -2; 1.549 + } 1.550 + 1.551 + // Curves, bounding boxes - those will most likely not be loaded 1.552 + // as we can't make any use out of this data. So consider them 1.553 + // last. 1.554 + if (r == "BoundingBox" || r == "Curve2D") { 1.555 + return 100; 1.556 + } 1.557 + return 0; 1.558 + } 1.559 + 1.560 + bool operator() (const IfcRepresentation* a, const IfcRepresentation* b) const { 1.561 + return Rate(a) < Rate(b); 1.562 + } 1.563 +}; 1.564 + 1.565 +// ------------------------------------------------------------------------------------------------ 1.566 +void ProcessProductRepresentation(const IfcProduct& el, aiNode* nd, std::vector< aiNode* >& subnodes, ConversionData& conv) 1.567 +{ 1.568 + if(!el.Representation) { 1.569 + return; 1.570 + } 1.571 + 1.572 + 1.573 + std::vector<unsigned int> meshes; 1.574 + 1.575 + // we want only one representation type, so bring them in a suitable order (i.e try those 1.576 + // that look as if we could read them quickly at first). This way of reading 1.577 + // representation is relatively generic and allows the concrete implementations 1.578 + // for the different representation types to make some sensible choices what 1.579 + // to load and what not to load. 1.580 + const STEP::ListOf< STEP::Lazy< IfcRepresentation >, 1, 0 >& src = el.Representation.Get()->Representations; 1.581 + 1.582 + std::vector<const IfcRepresentation*> repr_ordered(src.size()); 1.583 + std::copy(src.begin(),src.end(),repr_ordered.begin()); 1.584 + std::sort(repr_ordered.begin(),repr_ordered.end(),RateRepresentationPredicate()); 1.585 + 1.586 + BOOST_FOREACH(const IfcRepresentation* repr, repr_ordered) { 1.587 + bool res = false; 1.588 + BOOST_FOREACH(const IfcRepresentationItem& item, repr->Items) { 1.589 + if(const IfcMappedItem* const geo = item.ToPtr<IfcMappedItem>()) { 1.590 + res = ProcessMappedItem(*geo,nd,subnodes,conv) || res; 1.591 + } 1.592 + else { 1.593 + res = ProcessRepresentationItem(item,meshes,conv) || res; 1.594 + } 1.595 + } 1.596 + // if we got something meaningful at this point, skip any further representations 1.597 + if(res) { 1.598 + break; 1.599 + } 1.600 + } 1.601 + 1.602 + AssignAddedMeshes(meshes,nd,conv); 1.603 +} 1.604 + 1.605 +typedef std::map<std::string, std::string> Metadata; 1.606 + 1.607 +// ------------------------------------------------------------------------------------------------ 1.608 +void ProcessMetadata(const ListOf< Lazy< IfcProperty >, 1, 0 >& set, ConversionData& conv, Metadata& properties, 1.609 + const std::string& prefix = "", 1.610 + unsigned int nest = 0) 1.611 +{ 1.612 + BOOST_FOREACH(const IfcProperty& property, set) { 1.613 + const std::string& key = prefix.length() > 0 ? (prefix + "." + property.Name) : property.Name; 1.614 + if (const IfcPropertySingleValue* const singleValue = property.ToPtr<IfcPropertySingleValue>()) { 1.615 + if (singleValue->NominalValue) { 1.616 + if (const EXPRESS::STRING* str = singleValue->NominalValue.Get()->ToPtr<EXPRESS::STRING>()) { 1.617 + std::string value = static_cast<std::string>(*str); 1.618 + properties[key]=value; 1.619 + } 1.620 + else if (const EXPRESS::REAL* val = singleValue->NominalValue.Get()->ToPtr<EXPRESS::REAL>()) { 1.621 + float value = static_cast<float>(*val); 1.622 + std::stringstream s; 1.623 + s << value; 1.624 + properties[key]=s.str(); 1.625 + } 1.626 + else if (const EXPRESS::INTEGER* val = singleValue->NominalValue.Get()->ToPtr<EXPRESS::INTEGER>()) { 1.627 + int64_t value = static_cast<int64_t>(*val); 1.628 + std::stringstream s; 1.629 + s << value; 1.630 + properties[key]=s.str(); 1.631 + } 1.632 + } 1.633 + } 1.634 + else if (const IfcPropertyListValue* const listValue = property.ToPtr<IfcPropertyListValue>()) { 1.635 + std::stringstream ss; 1.636 + ss << "["; 1.637 + unsigned index=0; 1.638 + BOOST_FOREACH(const IfcValue::Out& v, listValue->ListValues) { 1.639 + if (!v) continue; 1.640 + if (const EXPRESS::STRING* str = v->ToPtr<EXPRESS::STRING>()) { 1.641 + std::string value = static_cast<std::string>(*str); 1.642 + ss << "'" << value << "'"; 1.643 + } 1.644 + else if (const EXPRESS::REAL* val = v->ToPtr<EXPRESS::REAL>()) { 1.645 + float value = static_cast<float>(*val); 1.646 + ss << value; 1.647 + } 1.648 + else if (const EXPRESS::INTEGER* val = v->ToPtr<EXPRESS::INTEGER>()) { 1.649 + int64_t value = static_cast<int64_t>(*val); 1.650 + ss << value; 1.651 + } 1.652 + if (index+1<listValue->ListValues.size()) { 1.653 + ss << ","; 1.654 + } 1.655 + index++; 1.656 + } 1.657 + ss << "]"; 1.658 + properties[key]=ss.str(); 1.659 + } 1.660 + else if (const IfcComplexProperty* const complexProp = property.ToPtr<IfcComplexProperty>()) { 1.661 + if(nest > 2) { // mostly arbitrary limit to prevent stack overflow vulnerabilities 1.662 + IFCImporter::LogError("maximum nesting level for IfcComplexProperty reached, skipping this property."); 1.663 + } 1.664 + else { 1.665 + ProcessMetadata(complexProp->HasProperties, conv, properties, key, nest + 1); 1.666 + } 1.667 + } 1.668 + else { 1.669 + properties[key]=""; 1.670 + } 1.671 + } 1.672 +} 1.673 + 1.674 + 1.675 +// ------------------------------------------------------------------------------------------------ 1.676 +void ProcessMetadata(uint64_t relDefinesByPropertiesID, ConversionData& conv, Metadata& properties) 1.677 +{ 1.678 + if (const IfcRelDefinesByProperties* const pset = conv.db.GetObject(relDefinesByPropertiesID)->ToPtr<IfcRelDefinesByProperties>()) { 1.679 + if (const IfcPropertySet* const set = conv.db.GetObject(pset->RelatingPropertyDefinition->GetID())->ToPtr<IfcPropertySet>()) { 1.680 + ProcessMetadata(set->HasProperties, conv, properties); 1.681 + } 1.682 + } 1.683 +} 1.684 + 1.685 +// ------------------------------------------------------------------------------------------------ 1.686 +aiNode* ProcessSpatialStructure(aiNode* parent, const IfcProduct& el, ConversionData& conv, std::vector<TempOpening>* collect_openings = NULL) 1.687 +{ 1.688 + const STEP::DB::RefMap& refs = conv.db.GetRefs(); 1.689 + 1.690 + // skip over space and annotation nodes - usually, these have no meaning in Assimp's context 1.691 + if(conv.settings.skipSpaceRepresentations) { 1.692 + if(const IfcSpace* const space = el.ToPtr<IfcSpace>()) { 1.693 + IFCImporter::LogDebug("skipping IfcSpace entity due to importer settings"); 1.694 + return NULL; 1.695 + } 1.696 + } 1.697 + 1.698 + if(conv.settings.skipAnnotations) { 1.699 + if(const IfcAnnotation* const ann = el.ToPtr<IfcAnnotation>()) { 1.700 + IFCImporter::LogDebug("skipping IfcAnnotation entity due to importer settings"); 1.701 + return NULL; 1.702 + } 1.703 + } 1.704 + 1.705 + // add an output node for this spatial structure 1.706 + std::auto_ptr<aiNode> nd(new aiNode()); 1.707 + nd->mName.Set(el.GetClassName()+"_"+(el.Name?el.Name.Get():"Unnamed")+"_"+el.GlobalId); 1.708 + nd->mParent = parent; 1.709 + 1.710 + conv.already_processed.insert(el.GetID()); 1.711 + 1.712 + // check for node metadata 1.713 + STEP::DB::RefMapRange children = refs.equal_range(el.GetID()); 1.714 + if (children.first!=refs.end()) { 1.715 + Metadata properties; 1.716 + if (children.first==children.second) { 1.717 + // handles single property set 1.718 + ProcessMetadata((*children.first).second, conv, properties); 1.719 + } 1.720 + else { 1.721 + // handles multiple property sets (currently all property sets are merged, 1.722 + // which may not be the best solution in the long run) 1.723 + for (STEP::DB::RefMap::const_iterator it=children.first; it!=children.second; ++it) { 1.724 + ProcessMetadata((*it).second, conv, properties); 1.725 + } 1.726 + } 1.727 + 1.728 + if (!properties.empty()) { 1.729 + aiMetadata* data = new aiMetadata(); 1.730 + data->mNumProperties = properties.size(); 1.731 + data->mKeys = new aiString*[data->mNumProperties](); 1.732 + data->mValues = new aiString*[data->mNumProperties](); 1.733 + 1.734 + unsigned int i = 0; 1.735 + BOOST_FOREACH(const Metadata::value_type& kv, properties) { 1.736 + data->mKeys[i] = new aiString(kv.first); 1.737 + if (kv.second.length() > 0) { 1.738 + data->mValues[i] = new aiString(kv.second); 1.739 + } 1.740 + ++i; 1.741 + } 1.742 + nd->mMetaData = data; 1.743 + } 1.744 + } 1.745 + 1.746 + if(el.ObjectPlacement) { 1.747 + ResolveObjectPlacement(nd->mTransformation,el.ObjectPlacement.Get(),conv); 1.748 + } 1.749 + 1.750 + std::vector<TempOpening> openings; 1.751 + 1.752 + IfcMatrix4 myInv; 1.753 + bool didinv = false; 1.754 + 1.755 + // convert everything contained directly within this structure, 1.756 + // this may result in more nodes. 1.757 + std::vector< aiNode* > subnodes; 1.758 + try { 1.759 + // locate aggregates and 'contained-in-here'-elements of this spatial structure and add them in recursively 1.760 + // on our way, collect openings in *this* element 1.761 + STEP::DB::RefMapRange range = refs.equal_range(el.GetID()); 1.762 + 1.763 + for(STEP::DB::RefMapRange range2 = range; range2.first != range.second; ++range2.first) { 1.764 + // skip over meshes that have already been processed before. This is strictly necessary 1.765 + // because the reverse indices also include references contained in argument lists and 1.766 + // therefore every element has a back-reference hold by its parent. 1.767 + if (conv.already_processed.find((*range2.first).second) != conv.already_processed.end()) { 1.768 + continue; 1.769 + } 1.770 + const STEP::LazyObject& obj = conv.db.MustGetObject((*range2.first).second); 1.771 + 1.772 + // handle regularly-contained elements 1.773 + if(const IfcRelContainedInSpatialStructure* const cont = obj->ToPtr<IfcRelContainedInSpatialStructure>()) { 1.774 + if(cont->RelatingStructure->GetID() != el.GetID()) { 1.775 + continue; 1.776 + } 1.777 + BOOST_FOREACH(const IfcProduct& pro, cont->RelatedElements) { 1.778 + if(const IfcOpeningElement* const open = pro.ToPtr<IfcOpeningElement>()) { 1.779 + // IfcOpeningElement is handled below. Sadly we can't use it here as is: 1.780 + // The docs say that opening elements are USUALLY attached to building storey, 1.781 + // but we want them for the building elements to which they belong. 1.782 + continue; 1.783 + } 1.784 + 1.785 + aiNode* const ndnew = ProcessSpatialStructure(nd.get(),pro,conv,NULL); 1.786 + if(ndnew) { 1.787 + subnodes.push_back( ndnew ); 1.788 + } 1.789 + } 1.790 + } 1.791 + // handle openings, which we collect in a list rather than adding them to the node graph 1.792 + else if(const IfcRelVoidsElement* const fills = obj->ToPtr<IfcRelVoidsElement>()) { 1.793 + if(fills->RelatingBuildingElement->GetID() == el.GetID()) { 1.794 + const IfcFeatureElementSubtraction& open = fills->RelatedOpeningElement; 1.795 + 1.796 + // move opening elements to a separate node since they are semantically different than elements that are just 'contained' 1.797 + std::auto_ptr<aiNode> nd_aggr(new aiNode()); 1.798 + nd_aggr->mName.Set("$RelVoidsElement"); 1.799 + nd_aggr->mParent = nd.get(); 1.800 + 1.801 + nd_aggr->mTransformation = nd->mTransformation; 1.802 + 1.803 + std::vector<TempOpening> openings_local; 1.804 + aiNode* const ndnew = ProcessSpatialStructure( nd_aggr.get(),open, conv,&openings_local); 1.805 + if (ndnew) { 1.806 + 1.807 + nd_aggr->mNumChildren = 1; 1.808 + nd_aggr->mChildren = new aiNode*[1](); 1.809 + 1.810 + 1.811 + nd_aggr->mChildren[0] = ndnew; 1.812 + 1.813 + if(openings_local.size()) { 1.814 + if (!didinv) { 1.815 + myInv = aiMatrix4x4(nd->mTransformation ).Inverse(); 1.816 + didinv = true; 1.817 + } 1.818 + 1.819 + // we need all openings to be in the local space of *this* node, so transform them 1.820 + BOOST_FOREACH(TempOpening& op,openings_local) { 1.821 + op.Transform( myInv*nd_aggr->mChildren[0]->mTransformation); 1.822 + openings.push_back(op); 1.823 + } 1.824 + } 1.825 + subnodes.push_back( nd_aggr.release() ); 1.826 + } 1.827 + } 1.828 + } 1.829 + } 1.830 + 1.831 + for(;range.first != range.second; ++range.first) { 1.832 + // see note in loop above 1.833 + if (conv.already_processed.find((*range.first).second) != conv.already_processed.end()) { 1.834 + continue; 1.835 + } 1.836 + if(const IfcRelAggregates* const aggr = conv.db.GetObject((*range.first).second)->ToPtr<IfcRelAggregates>()) { 1.837 + if(aggr->RelatingObject->GetID() != el.GetID()) { 1.838 + continue; 1.839 + } 1.840 + 1.841 + // move aggregate elements to a separate node since they are semantically different than elements that are just 'contained' 1.842 + std::auto_ptr<aiNode> nd_aggr(new aiNode()); 1.843 + nd_aggr->mName.Set("$RelAggregates"); 1.844 + nd_aggr->mParent = nd.get(); 1.845 + 1.846 + nd_aggr->mTransformation = nd->mTransformation; 1.847 + 1.848 + nd_aggr->mChildren = new aiNode*[aggr->RelatedObjects.size()](); 1.849 + BOOST_FOREACH(const IfcObjectDefinition& def, aggr->RelatedObjects) { 1.850 + if(const IfcProduct* const prod = def.ToPtr<IfcProduct>()) { 1.851 + 1.852 + aiNode* const ndnew = ProcessSpatialStructure(nd_aggr.get(),*prod,conv,NULL); 1.853 + if(ndnew) { 1.854 + nd_aggr->mChildren[nd_aggr->mNumChildren++] = ndnew; 1.855 + } 1.856 + } 1.857 + } 1.858 + 1.859 + subnodes.push_back( nd_aggr.release() ); 1.860 + } 1.861 + } 1.862 + 1.863 + conv.collect_openings = collect_openings; 1.864 + if(!conv.collect_openings) { 1.865 + conv.apply_openings = &openings; 1.866 + } 1.867 + 1.868 + ProcessProductRepresentation(el,nd.get(),subnodes,conv); 1.869 + conv.apply_openings = conv.collect_openings = NULL; 1.870 + 1.871 + if (subnodes.size()) { 1.872 + nd->mChildren = new aiNode*[subnodes.size()](); 1.873 + BOOST_FOREACH(aiNode* nd2, subnodes) { 1.874 + nd->mChildren[nd->mNumChildren++] = nd2; 1.875 + nd2->mParent = nd.get(); 1.876 + } 1.877 + } 1.878 + } 1.879 + catch(...) { 1.880 + // it hurts, but I don't want to pull boost::ptr_vector into -noboost only for these few spots here 1.881 + std::for_each(subnodes.begin(),subnodes.end(),delete_fun<aiNode>()); 1.882 + throw; 1.883 + } 1.884 + 1.885 + ai_assert(conv.already_processed.find(el.GetID()) != conv.already_processed.end()); 1.886 + conv.already_processed.erase(conv.already_processed.find(el.GetID())); 1.887 + return nd.release(); 1.888 +} 1.889 + 1.890 +// ------------------------------------------------------------------------------------------------ 1.891 +void ProcessSpatialStructures(ConversionData& conv) 1.892 +{ 1.893 + // XXX add support for multiple sites (i.e. IfcSpatialStructureElements with composition == COMPLEX) 1.894 + 1.895 + 1.896 + // process all products in the file. it is reasonable to assume that a 1.897 + // file that is relevant for us contains at least a site or a building. 1.898 + const STEP::DB::ObjectMapByType& map = conv.db.GetObjectsByType(); 1.899 + 1.900 + ai_assert(map.find("ifcsite") != map.end()); 1.901 + const STEP::DB::ObjectSet* range = &map.find("ifcsite")->second; 1.902 + 1.903 + if (range->empty()) { 1.904 + ai_assert(map.find("ifcbuilding") != map.end()); 1.905 + range = &map.find("ifcbuilding")->second; 1.906 + if (range->empty()) { 1.907 + // no site, no building - fail; 1.908 + IFCImporter::ThrowException("no root element found (expected IfcBuilding or preferably IfcSite)"); 1.909 + } 1.910 + } 1.911 + 1.912 + 1.913 + BOOST_FOREACH(const STEP::LazyObject* lz, *range) { 1.914 + const IfcSpatialStructureElement* const prod = lz->ToPtr<IfcSpatialStructureElement>(); 1.915 + if(!prod) { 1.916 + continue; 1.917 + } 1.918 + IFCImporter::LogDebug("looking at spatial structure `" + (prod->Name ? prod->Name.Get() : "unnamed") + "`" + (prod->ObjectType? " which is of type " + prod->ObjectType.Get():"")); 1.919 + 1.920 + // the primary site is referenced by an IFCRELAGGREGATES element which assigns it to the IFCPRODUCT 1.921 + const STEP::DB::RefMap& refs = conv.db.GetRefs(); 1.922 + STEP::DB::RefMapRange range = refs.equal_range(conv.proj.GetID()); 1.923 + for(;range.first != range.second; ++range.first) { 1.924 + if(const IfcRelAggregates* const aggr = conv.db.GetObject((*range.first).second)->ToPtr<IfcRelAggregates>()) { 1.925 + 1.926 + BOOST_FOREACH(const IfcObjectDefinition& def, aggr->RelatedObjects) { 1.927 + // comparing pointer values is not sufficient, we would need to cast them to the same type first 1.928 + // as there is multiple inheritance in the game. 1.929 + if (def.GetID() == prod->GetID()) { 1.930 + IFCImporter::LogDebug("selecting this spatial structure as root structure"); 1.931 + // got it, this is the primary site. 1.932 + conv.out->mRootNode = ProcessSpatialStructure(NULL,*prod,conv,NULL); 1.933 + return; 1.934 + } 1.935 + } 1.936 + 1.937 + } 1.938 + } 1.939 + } 1.940 + 1.941 + 1.942 + IFCImporter::LogWarn("failed to determine primary site element, taking the first IfcSite"); 1.943 + BOOST_FOREACH(const STEP::LazyObject* lz, *range) { 1.944 + const IfcSpatialStructureElement* const prod = lz->ToPtr<IfcSpatialStructureElement>(); 1.945 + if(!prod) { 1.946 + continue; 1.947 + } 1.948 + 1.949 + conv.out->mRootNode = ProcessSpatialStructure(NULL,*prod,conv,NULL); 1.950 + return; 1.951 + } 1.952 + 1.953 + IFCImporter::ThrowException("failed to determine primary site element"); 1.954 +} 1.955 + 1.956 +// ------------------------------------------------------------------------------------------------ 1.957 +void MakeTreeRelative(aiNode* start, const aiMatrix4x4& combined) 1.958 +{ 1.959 + // combined is the parent's absolute transformation matrix 1.960 + const aiMatrix4x4 old = start->mTransformation; 1.961 + 1.962 + if (!combined.IsIdentity()) { 1.963 + start->mTransformation = aiMatrix4x4(combined).Inverse() * start->mTransformation; 1.964 + } 1.965 + 1.966 + // All nodes store absolute transformations right now, so we need to make them relative 1.967 + for (unsigned int i = 0; i < start->mNumChildren; ++i) { 1.968 + MakeTreeRelative(start->mChildren[i],old); 1.969 + } 1.970 +} 1.971 + 1.972 +// ------------------------------------------------------------------------------------------------ 1.973 +void MakeTreeRelative(ConversionData& conv) 1.974 +{ 1.975 + MakeTreeRelative(conv.out->mRootNode,IfcMatrix4()); 1.976 +} 1.977 + 1.978 +} // !anon 1.979 + 1.980 + 1.981 + 1.982 +#endif