vrshoot

annotate libs/assimp/IFCLoader.cpp @ 1:e7ca128b8713

looks nice :)
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 02 Feb 2014 00:35:22 +0200
parents
children
rev   line source
nuclear@0 1 /*
nuclear@0 2 Open Asset Import Library (assimp)
nuclear@0 3 ----------------------------------------------------------------------
nuclear@0 4
nuclear@0 5 Copyright (c) 2006-2012, assimp team
nuclear@0 6 All rights reserved.
nuclear@0 7
nuclear@0 8 Redistribution and use of this software in source and binary forms,
nuclear@0 9 with or without modification, are permitted provided that the
nuclear@0 10 following conditions are met:
nuclear@0 11
nuclear@0 12 * Redistributions of source code must retain the above
nuclear@0 13 copyright notice, this list of conditions and the
nuclear@0 14 following disclaimer.
nuclear@0 15
nuclear@0 16 * Redistributions in binary form must reproduce the above
nuclear@0 17 copyright notice, this list of conditions and the
nuclear@0 18 following disclaimer in the documentation and/or other
nuclear@0 19 materials provided with the distribution.
nuclear@0 20
nuclear@0 21 * Neither the name of the assimp team, nor the names of its
nuclear@0 22 contributors may be used to endorse or promote products
nuclear@0 23 derived from this software without specific prior
nuclear@0 24 written permission of the assimp team.
nuclear@0 25
nuclear@0 26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
nuclear@0 27 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
nuclear@0 28 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
nuclear@0 29 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
nuclear@0 30 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
nuclear@0 31 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
nuclear@0 32 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
nuclear@0 33 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
nuclear@0 34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
nuclear@0 35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
nuclear@0 36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
nuclear@0 37
nuclear@0 38 ----------------------------------------------------------------------
nuclear@0 39 */
nuclear@0 40
nuclear@0 41 /** @file IFCLoad.cpp
nuclear@0 42 * @brief Implementation of the Industry Foundation Classes loader.
nuclear@0 43 */
nuclear@0 44 #include "AssimpPCH.h"
nuclear@0 45
nuclear@0 46 #ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
nuclear@0 47
nuclear@0 48 #include <iterator>
nuclear@0 49 #include <boost/tuple/tuple.hpp>
nuclear@0 50
nuclear@0 51 #ifndef ASSIMP_BUILD_NO_COMPRESSED_IFC
nuclear@0 52 # include "../contrib/unzip/unzip.h"
nuclear@0 53 #endif
nuclear@0 54
nuclear@0 55 #include "IFCLoader.h"
nuclear@0 56 #include "STEPFileReader.h"
nuclear@0 57
nuclear@0 58 #include "IFCUtil.h"
nuclear@0 59
nuclear@0 60 #include "StreamReader.h"
nuclear@0 61 #include "MemoryIOWrapper.h"
nuclear@0 62
nuclear@0 63 namespace Assimp {
nuclear@0 64 template<> const std::string LogFunctions<IFCImporter>::log_prefix = "IFC: ";
nuclear@0 65 }
nuclear@0 66
nuclear@0 67 using namespace Assimp;
nuclear@0 68 using namespace Assimp::Formatter;
nuclear@0 69 using namespace Assimp::IFC;
nuclear@0 70
nuclear@0 71 /* DO NOT REMOVE this comment block. The genentitylist.sh script
nuclear@0 72 * just looks for names adhering to the IfcSomething naming scheme
nuclear@0 73 * and includes all matches in the whitelist for code-generation. Thus,
nuclear@0 74 * all entity classes that are only indirectly referenced need to be
nuclear@0 75 * mentioned explicitly.
nuclear@0 76
nuclear@0 77 IfcRepresentationMap
nuclear@0 78 IfcProductRepresentation
nuclear@0 79 IfcUnitAssignment
nuclear@0 80 IfcClosedShell
nuclear@0 81 IfcDoor
nuclear@0 82
nuclear@0 83 */
nuclear@0 84
nuclear@0 85 namespace {
nuclear@0 86
nuclear@0 87
nuclear@0 88 // forward declarations
nuclear@0 89 void SetUnits(ConversionData& conv);
nuclear@0 90 void SetCoordinateSpace(ConversionData& conv);
nuclear@0 91 void ProcessSpatialStructures(ConversionData& conv);
nuclear@0 92 aiNode* ProcessSpatialStructure(aiNode* parent, const IfcProduct& el ,ConversionData& conv);
nuclear@0 93 void ProcessProductRepresentation(const IfcProduct& el, aiNode* nd, ConversionData& conv);
nuclear@0 94 void MakeTreeRelative(ConversionData& conv);
nuclear@0 95 void ConvertUnit(const EXPRESS::DataType& dt,ConversionData& conv);
nuclear@0 96
nuclear@0 97 } // anon
nuclear@0 98
nuclear@0 99 static const aiImporterDesc desc = {
nuclear@0 100 "Industry Foundation Classes (IFC) Importer",
nuclear@0 101 "",
nuclear@0 102 "",
nuclear@0 103 "",
nuclear@0 104 aiImporterFlags_SupportBinaryFlavour,
nuclear@0 105 0,
nuclear@0 106 0,
nuclear@0 107 0,
nuclear@0 108 0,
nuclear@0 109 "ifc ifczip"
nuclear@0 110 };
nuclear@0 111
nuclear@0 112
nuclear@0 113 // ------------------------------------------------------------------------------------------------
nuclear@0 114 // Constructor to be privately used by Importer
nuclear@0 115 IFCImporter::IFCImporter()
nuclear@0 116 {}
nuclear@0 117
nuclear@0 118 // ------------------------------------------------------------------------------------------------
nuclear@0 119 // Destructor, private as well
nuclear@0 120 IFCImporter::~IFCImporter()
nuclear@0 121 {
nuclear@0 122 }
nuclear@0 123
nuclear@0 124 // ------------------------------------------------------------------------------------------------
nuclear@0 125 // Returns whether the class can handle the format of the given file.
nuclear@0 126 bool IFCImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
nuclear@0 127 {
nuclear@0 128 const std::string& extension = GetExtension(pFile);
nuclear@0 129 if (extension == "ifc" || extension == "ifczip") {
nuclear@0 130 return true;
nuclear@0 131 }
nuclear@0 132
nuclear@0 133 else if ((!extension.length() || checkSig) && pIOHandler) {
nuclear@0 134 // note: this is the common identification for STEP-encoded files, so
nuclear@0 135 // it is only unambiguous as long as we don't support any further
nuclear@0 136 // file formats with STEP as their encoding.
nuclear@0 137 const char* tokens[] = {"ISO-10303-21"};
nuclear@0 138 return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
nuclear@0 139 }
nuclear@0 140 return false;
nuclear@0 141 }
nuclear@0 142
nuclear@0 143 // ------------------------------------------------------------------------------------------------
nuclear@0 144 // List all extensions handled by this loader
nuclear@0 145 const aiImporterDesc* IFCImporter::GetInfo () const
nuclear@0 146 {
nuclear@0 147 return &desc;
nuclear@0 148 }
nuclear@0 149
nuclear@0 150
nuclear@0 151 // ------------------------------------------------------------------------------------------------
nuclear@0 152 // Setup configuration properties for the loader
nuclear@0 153 void IFCImporter::SetupProperties(const Importer* pImp)
nuclear@0 154 {
nuclear@0 155 settings.skipSpaceRepresentations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_IFC_SKIP_SPACE_REPRESENTATIONS,true);
nuclear@0 156 settings.skipCurveRepresentations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_IFC_SKIP_CURVE_REPRESENTATIONS,true);
nuclear@0 157 settings.useCustomTriangulation = pImp->GetPropertyBool(AI_CONFIG_IMPORT_IFC_CUSTOM_TRIANGULATION,true);
nuclear@0 158
nuclear@0 159 settings.conicSamplingAngle = 10.f;
nuclear@0 160 settings.skipAnnotations = true;
nuclear@0 161 }
nuclear@0 162
nuclear@0 163
nuclear@0 164 // ------------------------------------------------------------------------------------------------
nuclear@0 165 // Imports the given file into the given scene structure.
nuclear@0 166 void IFCImporter::InternReadFile( const std::string& pFile,
nuclear@0 167 aiScene* pScene, IOSystem* pIOHandler)
nuclear@0 168 {
nuclear@0 169 boost::shared_ptr<IOStream> stream(pIOHandler->Open(pFile));
nuclear@0 170 if (!stream) {
nuclear@0 171 ThrowException("Could not open file for reading");
nuclear@0 172 }
nuclear@0 173
nuclear@0 174
nuclear@0 175 // if this is a ifczip file, decompress its contents first
nuclear@0 176 if(GetExtension(pFile) == "ifczip") {
nuclear@0 177 #ifndef ASSIMP_BUILD_NO_COMPRESSED_IFC
nuclear@0 178 unzFile zip = unzOpen( pFile.c_str() );
nuclear@0 179 if(zip == NULL) {
nuclear@0 180 ThrowException("Could not open ifczip file for reading, unzip failed");
nuclear@0 181 }
nuclear@0 182
nuclear@0 183 // chop 'zip' postfix
nuclear@0 184 std::string fileName = pFile.substr(0,pFile.length() - 3);
nuclear@0 185
nuclear@0 186 std::string::size_type s = pFile.find_last_of('\\');
nuclear@0 187 if(s == std::string::npos) {
nuclear@0 188 s = pFile.find_last_of('/');
nuclear@0 189 }
nuclear@0 190 if(s != std::string::npos) {
nuclear@0 191 fileName = fileName.substr(s+1);
nuclear@0 192 }
nuclear@0 193
nuclear@0 194 // search file (same name as the IFCZIP except for the file extension) and place file pointer there
nuclear@0 195
nuclear@0 196 if(UNZ_OK == unzGoToFirstFile(zip)) {
nuclear@0 197 do {
nuclear@0 198 //
nuclear@0 199
nuclear@0 200 // get file size, etc.
nuclear@0 201 unz_file_info fileInfo;
nuclear@0 202 char filename[256];
nuclear@0 203 unzGetCurrentFileInfo( zip , &fileInfo, filename, sizeof(filename), 0, 0, 0, 0 );
nuclear@0 204
nuclear@0 205 if (GetExtension(filename) != "ifc") {
nuclear@0 206 continue;
nuclear@0 207 }
nuclear@0 208
nuclear@0 209 uint8_t* buff = new uint8_t[fileInfo.uncompressed_size];
nuclear@0 210
nuclear@0 211 LogInfo("Decompressing IFCZIP file");
nuclear@0 212
nuclear@0 213 unzOpenCurrentFile( zip );
nuclear@0 214 const int ret = unzReadCurrentFile( zip, buff, fileInfo.uncompressed_size);
nuclear@0 215 size_t filesize = fileInfo.uncompressed_size;
nuclear@0 216 if ( ret < 0 || size_t(ret) != filesize )
nuclear@0 217 {
nuclear@0 218 delete[] buff;
nuclear@0 219 ThrowException("Failed to decompress IFC ZIP file");
nuclear@0 220 }
nuclear@0 221 unzCloseCurrentFile( zip );
nuclear@0 222 stream.reset(new MemoryIOStream(buff,fileInfo.uncompressed_size,true));
nuclear@0 223 break;
nuclear@0 224
nuclear@0 225 if (unzGoToNextFile(zip) == UNZ_END_OF_LIST_OF_FILE) {
nuclear@0 226 ThrowException("Found no IFC file member in IFCZIP file (1)");
nuclear@0 227 }
nuclear@0 228
nuclear@0 229 } while(true);
nuclear@0 230 }
nuclear@0 231 else {
nuclear@0 232 ThrowException("Found no IFC file member in IFCZIP file (2)");
nuclear@0 233 }
nuclear@0 234
nuclear@0 235 unzClose(zip);
nuclear@0 236 #else
nuclear@0 237 ThrowException("Could not open ifczip file for reading, assimp was built without ifczip support");
nuclear@0 238 #endif
nuclear@0 239 }
nuclear@0 240
nuclear@0 241 boost::scoped_ptr<STEP::DB> db(STEP::ReadFileHeader(stream));
nuclear@0 242 const STEP::HeaderInfo& head = static_cast<const STEP::DB&>(*db).GetHeader();
nuclear@0 243
nuclear@0 244 if(!head.fileSchema.size() || head.fileSchema.substr(0,3) != "IFC") {
nuclear@0 245 ThrowException("Unrecognized file schema: " + head.fileSchema);
nuclear@0 246 }
nuclear@0 247
nuclear@0 248 if (!DefaultLogger::isNullLogger()) {
nuclear@0 249 LogDebug("File schema is \'" + head.fileSchema + '\'');
nuclear@0 250 if (head.timestamp.length()) {
nuclear@0 251 LogDebug("Timestamp \'" + head.timestamp + '\'');
nuclear@0 252 }
nuclear@0 253 if (head.app.length()) {
nuclear@0 254 LogDebug("Application/Exporter identline is \'" + head.app + '\'');
nuclear@0 255 }
nuclear@0 256 }
nuclear@0 257
nuclear@0 258 // obtain a copy of the machine-generated IFC scheme
nuclear@0 259 EXPRESS::ConversionSchema schema;
nuclear@0 260 GetSchema(schema);
nuclear@0 261
nuclear@0 262 // tell the reader which entity types to track with special care
nuclear@0 263 static const char* const types_to_track[] = {
nuclear@0 264 "ifcsite", "ifcbuilding", "ifcproject"
nuclear@0 265 };
nuclear@0 266
nuclear@0 267 // tell the reader for which types we need to simulate STEPs reverse indices
nuclear@0 268 static const char* const inverse_indices_to_track[] = {
nuclear@0 269 "ifcrelcontainedinspatialstructure", "ifcrelaggregates", "ifcrelvoidselement", "ifcreldefinesbyproperties", "ifcpropertyset", "ifcstyleditem"
nuclear@0 270 };
nuclear@0 271
nuclear@0 272 // feed the IFC schema into the reader and pre-parse all lines
nuclear@0 273 STEP::ReadFile(*db, schema, types_to_track, inverse_indices_to_track);
nuclear@0 274
nuclear@0 275 const STEP::LazyObject* proj = db->GetObject("ifcproject");
nuclear@0 276 if (!proj) {
nuclear@0 277 ThrowException("missing IfcProject entity");
nuclear@0 278 }
nuclear@0 279
nuclear@0 280 ConversionData conv(*db,proj->To<IfcProject>(),pScene,settings);
nuclear@0 281 SetUnits(conv);
nuclear@0 282 SetCoordinateSpace(conv);
nuclear@0 283 ProcessSpatialStructures(conv);
nuclear@0 284 MakeTreeRelative(conv);
nuclear@0 285
nuclear@0 286 // NOTE - this is a stress test for the importer, but it works only
nuclear@0 287 // in a build with no entities disabled. See
nuclear@0 288 // scripts/IFCImporter/CPPGenerator.py
nuclear@0 289 // for more information.
nuclear@0 290 #ifdef ASSIMP_IFC_TEST
nuclear@0 291 db->EvaluateAll();
nuclear@0 292 #endif
nuclear@0 293
nuclear@0 294 // do final data copying
nuclear@0 295 if (conv.meshes.size()) {
nuclear@0 296 pScene->mNumMeshes = static_cast<unsigned int>(conv.meshes.size());
nuclear@0 297 pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]();
nuclear@0 298 std::copy(conv.meshes.begin(),conv.meshes.end(),pScene->mMeshes);
nuclear@0 299
nuclear@0 300 // needed to keep the d'tor from burning us
nuclear@0 301 conv.meshes.clear();
nuclear@0 302 }
nuclear@0 303
nuclear@0 304 if (conv.materials.size()) {
nuclear@0 305 pScene->mNumMaterials = static_cast<unsigned int>(conv.materials.size());
nuclear@0 306 pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]();
nuclear@0 307 std::copy(conv.materials.begin(),conv.materials.end(),pScene->mMaterials);
nuclear@0 308
nuclear@0 309 // needed to keep the d'tor from burning us
nuclear@0 310 conv.materials.clear();
nuclear@0 311 }
nuclear@0 312
nuclear@0 313 // apply world coordinate system (which includes the scaling to convert to meters and a -90 degrees rotation around x)
nuclear@0 314 aiMatrix4x4 scale, rot;
nuclear@0 315 aiMatrix4x4::Scaling(static_cast<aiVector3D>(IfcVector3(conv.len_scale)),scale);
nuclear@0 316 aiMatrix4x4::RotationX(-AI_MATH_HALF_PI_F,rot);
nuclear@0 317
nuclear@0 318 pScene->mRootNode->mTransformation = rot * scale * conv.wcs * pScene->mRootNode->mTransformation;
nuclear@0 319
nuclear@0 320 // this must be last because objects are evaluated lazily as we process them
nuclear@0 321 if ( !DefaultLogger::isNullLogger() ){
nuclear@0 322 LogDebug((Formatter::format(),"STEP: evaluated ",db->GetEvaluatedObjectCount()," object records"));
nuclear@0 323 }
nuclear@0 324 }
nuclear@0 325
nuclear@0 326 namespace {
nuclear@0 327
nuclear@0 328
nuclear@0 329 // ------------------------------------------------------------------------------------------------
nuclear@0 330 void ConvertUnit(const IfcNamedUnit& unit,ConversionData& conv)
nuclear@0 331 {
nuclear@0 332 if(const IfcSIUnit* const si = unit.ToPtr<IfcSIUnit>()) {
nuclear@0 333
nuclear@0 334 if(si->UnitType == "LENGTHUNIT") {
nuclear@0 335 conv.len_scale = si->Prefix ? ConvertSIPrefix(si->Prefix) : 1.f;
nuclear@0 336 IFCImporter::LogDebug("got units used for lengths");
nuclear@0 337 }
nuclear@0 338 if(si->UnitType == "PLANEANGLEUNIT") {
nuclear@0 339 if (si->Name != "RADIAN") {
nuclear@0 340 IFCImporter::LogWarn("expected base unit for angles to be radian");
nuclear@0 341 }
nuclear@0 342 }
nuclear@0 343 }
nuclear@0 344 else if(const IfcConversionBasedUnit* const convu = unit.ToPtr<IfcConversionBasedUnit>()) {
nuclear@0 345
nuclear@0 346 if(convu->UnitType == "PLANEANGLEUNIT") {
nuclear@0 347 try {
nuclear@0 348 conv.angle_scale = convu->ConversionFactor->ValueComponent->To<EXPRESS::REAL>();
nuclear@0 349 ConvertUnit(*convu->ConversionFactor->UnitComponent,conv);
nuclear@0 350 IFCImporter::LogDebug("got units used for angles");
nuclear@0 351 }
nuclear@0 352 catch(std::bad_cast&) {
nuclear@0 353 IFCImporter::LogError("skipping unknown IfcConversionBasedUnit.ValueComponent entry - expected REAL");
nuclear@0 354 }
nuclear@0 355 }
nuclear@0 356 }
nuclear@0 357 }
nuclear@0 358
nuclear@0 359 // ------------------------------------------------------------------------------------------------
nuclear@0 360 void ConvertUnit(const EXPRESS::DataType& dt,ConversionData& conv)
nuclear@0 361 {
nuclear@0 362 try {
nuclear@0 363 const EXPRESS::ENTITY& e = dt.To<ENTITY>();
nuclear@0 364
nuclear@0 365 const IfcNamedUnit& unit = e.ResolveSelect<IfcNamedUnit>(conv.db);
nuclear@0 366 if(unit.UnitType != "LENGTHUNIT" && unit.UnitType != "PLANEANGLEUNIT") {
nuclear@0 367 return;
nuclear@0 368 }
nuclear@0 369
nuclear@0 370 ConvertUnit(unit,conv);
nuclear@0 371 }
nuclear@0 372 catch(std::bad_cast&) {
nuclear@0 373 // not entity, somehow
nuclear@0 374 IFCImporter::LogError("skipping unknown IfcUnit entry - expected entity");
nuclear@0 375 }
nuclear@0 376 }
nuclear@0 377
nuclear@0 378 // ------------------------------------------------------------------------------------------------
nuclear@0 379 void SetUnits(ConversionData& conv)
nuclear@0 380 {
nuclear@0 381 // see if we can determine the coordinate space used to express.
nuclear@0 382 for(size_t i = 0; i < conv.proj.UnitsInContext->Units.size(); ++i ) {
nuclear@0 383 ConvertUnit(*conv.proj.UnitsInContext->Units[i],conv);
nuclear@0 384 }
nuclear@0 385 }
nuclear@0 386
nuclear@0 387
nuclear@0 388 // ------------------------------------------------------------------------------------------------
nuclear@0 389 void SetCoordinateSpace(ConversionData& conv)
nuclear@0 390 {
nuclear@0 391 const IfcRepresentationContext* fav = NULL;
nuclear@0 392 BOOST_FOREACH(const IfcRepresentationContext& v, conv.proj.RepresentationContexts) {
nuclear@0 393 fav = &v;
nuclear@0 394 // Model should be the most suitable type of context, hence ignore the others
nuclear@0 395 if (v.ContextType && v.ContextType.Get() == "Model") {
nuclear@0 396 break;
nuclear@0 397 }
nuclear@0 398 }
nuclear@0 399 if (fav) {
nuclear@0 400 if(const IfcGeometricRepresentationContext* const geo = fav->ToPtr<IfcGeometricRepresentationContext>()) {
nuclear@0 401 ConvertAxisPlacement(conv.wcs, *geo->WorldCoordinateSystem, conv);
nuclear@0 402 IFCImporter::LogDebug("got world coordinate system");
nuclear@0 403 }
nuclear@0 404 }
nuclear@0 405 }
nuclear@0 406
nuclear@0 407
nuclear@0 408 // ------------------------------------------------------------------------------------------------
nuclear@0 409 void ResolveObjectPlacement(aiMatrix4x4& m, const IfcObjectPlacement& place, ConversionData& conv)
nuclear@0 410 {
nuclear@0 411 if (const IfcLocalPlacement* const local = place.ToPtr<IfcLocalPlacement>()){
nuclear@0 412 IfcMatrix4 tmp;
nuclear@0 413 ConvertAxisPlacement(tmp, *local->RelativePlacement, conv);
nuclear@0 414
nuclear@0 415 m = static_cast<aiMatrix4x4>(tmp);
nuclear@0 416
nuclear@0 417 if (local->PlacementRelTo) {
nuclear@0 418 aiMatrix4x4 tmp;
nuclear@0 419 ResolveObjectPlacement(tmp,local->PlacementRelTo.Get(),conv);
nuclear@0 420 m = tmp * m;
nuclear@0 421 }
nuclear@0 422 }
nuclear@0 423 else {
nuclear@0 424 IFCImporter::LogWarn("skipping unknown IfcObjectPlacement entity, type is " + place.GetClassName());
nuclear@0 425 }
nuclear@0 426 }
nuclear@0 427
nuclear@0 428 // ------------------------------------------------------------------------------------------------
nuclear@0 429 void GetAbsTransform(aiMatrix4x4& out, const aiNode* nd, ConversionData& conv)
nuclear@0 430 {
nuclear@0 431 aiMatrix4x4 t;
nuclear@0 432 if (nd->mParent) {
nuclear@0 433 GetAbsTransform(t,nd->mParent,conv);
nuclear@0 434 }
nuclear@0 435 out = t*nd->mTransformation;
nuclear@0 436 }
nuclear@0 437
nuclear@0 438 // ------------------------------------------------------------------------------------------------
nuclear@0 439 bool ProcessMappedItem(const IfcMappedItem& mapped, aiNode* nd_src, std::vector< aiNode* >& subnodes_src, ConversionData& conv)
nuclear@0 440 {
nuclear@0 441 // insert a custom node here, the cartesian transform operator is simply a conventional transformation matrix
nuclear@0 442 std::auto_ptr<aiNode> nd(new aiNode());
nuclear@0 443 nd->mName.Set("IfcMappedItem");
nuclear@0 444
nuclear@0 445 // handle the Cartesian operator
nuclear@0 446 IfcMatrix4 m;
nuclear@0 447 ConvertTransformOperator(m, *mapped.MappingTarget);
nuclear@0 448
nuclear@0 449 IfcMatrix4 msrc;
nuclear@0 450 ConvertAxisPlacement(msrc,*mapped.MappingSource->MappingOrigin,conv);
nuclear@0 451
nuclear@0 452 msrc = m*msrc;
nuclear@0 453
nuclear@0 454 std::vector<unsigned int> meshes;
nuclear@0 455 const size_t old_openings = conv.collect_openings ? conv.collect_openings->size() : 0;
nuclear@0 456 if (conv.apply_openings) {
nuclear@0 457 IfcMatrix4 minv = msrc;
nuclear@0 458 minv.Inverse();
nuclear@0 459 BOOST_FOREACH(TempOpening& open,*conv.apply_openings){
nuclear@0 460 open.Transform(minv);
nuclear@0 461 }
nuclear@0 462 }
nuclear@0 463
nuclear@0 464 const IfcRepresentation& repr = mapped.MappingSource->MappedRepresentation;
nuclear@0 465
nuclear@0 466 bool got = false;
nuclear@0 467 BOOST_FOREACH(const IfcRepresentationItem& item, repr.Items) {
nuclear@0 468 if(!ProcessRepresentationItem(item,meshes,conv)) {
nuclear@0 469 IFCImporter::LogWarn("skipping mapped entity of type " + item.GetClassName() + ", no representations could be generated");
nuclear@0 470 }
nuclear@0 471 else got = true;
nuclear@0 472 }
nuclear@0 473
nuclear@0 474 if (!got) {
nuclear@0 475 return false;
nuclear@0 476 }
nuclear@0 477
nuclear@0 478 AssignAddedMeshes(meshes,nd.get(),conv);
nuclear@0 479 if (conv.collect_openings) {
nuclear@0 480
nuclear@0 481 // if this pass serves us only to collect opening geometry,
nuclear@0 482 // make sure we transform the TempMesh's which we need to
nuclear@0 483 // preserve as well.
nuclear@0 484 if(const size_t diff = conv.collect_openings->size() - old_openings) {
nuclear@0 485 for(size_t i = 0; i < diff; ++i) {
nuclear@0 486 (*conv.collect_openings)[old_openings+i].Transform(msrc);
nuclear@0 487 }
nuclear@0 488 }
nuclear@0 489 }
nuclear@0 490
nuclear@0 491 nd->mTransformation = nd_src->mTransformation * static_cast<aiMatrix4x4>( msrc );
nuclear@0 492 subnodes_src.push_back(nd.release());
nuclear@0 493
nuclear@0 494 return true;
nuclear@0 495 }
nuclear@0 496
nuclear@0 497 // ------------------------------------------------------------------------------------------------
nuclear@0 498 struct RateRepresentationPredicate {
nuclear@0 499
nuclear@0 500 int Rate(const IfcRepresentation* r) const {
nuclear@0 501 // the smaller, the better
nuclear@0 502
nuclear@0 503 if (! r->RepresentationIdentifier) {
nuclear@0 504 // neutral choice if no extra information is specified
nuclear@0 505 return 0;
nuclear@0 506 }
nuclear@0 507
nuclear@0 508
nuclear@0 509 const std::string& name = r->RepresentationIdentifier.Get();
nuclear@0 510 if (name == "MappedRepresentation") {
nuclear@0 511 if (!r->Items.empty()) {
nuclear@0 512 // take the first item and base our choice on it
nuclear@0 513 const IfcMappedItem* const m = r->Items.front()->ToPtr<IfcMappedItem>();
nuclear@0 514 if (m) {
nuclear@0 515 return Rate(m->MappingSource->MappedRepresentation);
nuclear@0 516 }
nuclear@0 517 }
nuclear@0 518 return 100;
nuclear@0 519 }
nuclear@0 520
nuclear@0 521 return Rate(name);
nuclear@0 522 }
nuclear@0 523
nuclear@0 524 int Rate(const std::string& r) const {
nuclear@0 525
nuclear@0 526
nuclear@0 527 if (r == "SolidModel") {
nuclear@0 528 return -3;
nuclear@0 529 }
nuclear@0 530
nuclear@0 531 // give strong preference to extruded geometry.
nuclear@0 532 if (r == "SweptSolid") {
nuclear@0 533 return -10;
nuclear@0 534 }
nuclear@0 535
nuclear@0 536 if (r == "Clipping") {
nuclear@0 537 return -5;
nuclear@0 538 }
nuclear@0 539
nuclear@0 540 // 'Brep' is difficult to get right due to possible voids in the
nuclear@0 541 // polygon boundaries, so take it only if we are forced to (i.e.
nuclear@0 542 // if the only alternative is (non-clipping) boolean operations,
nuclear@0 543 // which are not supported at all).
nuclear@0 544 if (r == "Brep") {
nuclear@0 545 return -2;
nuclear@0 546 }
nuclear@0 547
nuclear@0 548 // Curves, bounding boxes - those will most likely not be loaded
nuclear@0 549 // as we can't make any use out of this data. So consider them
nuclear@0 550 // last.
nuclear@0 551 if (r == "BoundingBox" || r == "Curve2D") {
nuclear@0 552 return 100;
nuclear@0 553 }
nuclear@0 554 return 0;
nuclear@0 555 }
nuclear@0 556
nuclear@0 557 bool operator() (const IfcRepresentation* a, const IfcRepresentation* b) const {
nuclear@0 558 return Rate(a) < Rate(b);
nuclear@0 559 }
nuclear@0 560 };
nuclear@0 561
nuclear@0 562 // ------------------------------------------------------------------------------------------------
nuclear@0 563 void ProcessProductRepresentation(const IfcProduct& el, aiNode* nd, std::vector< aiNode* >& subnodes, ConversionData& conv)
nuclear@0 564 {
nuclear@0 565 if(!el.Representation) {
nuclear@0 566 return;
nuclear@0 567 }
nuclear@0 568
nuclear@0 569
nuclear@0 570 std::vector<unsigned int> meshes;
nuclear@0 571
nuclear@0 572 // we want only one representation type, so bring them in a suitable order (i.e try those
nuclear@0 573 // that look as if we could read them quickly at first). This way of reading
nuclear@0 574 // representation is relatively generic and allows the concrete implementations
nuclear@0 575 // for the different representation types to make some sensible choices what
nuclear@0 576 // to load and what not to load.
nuclear@0 577 const STEP::ListOf< STEP::Lazy< IfcRepresentation >, 1, 0 >& src = el.Representation.Get()->Representations;
nuclear@0 578
nuclear@0 579 std::vector<const IfcRepresentation*> repr_ordered(src.size());
nuclear@0 580 std::copy(src.begin(),src.end(),repr_ordered.begin());
nuclear@0 581 std::sort(repr_ordered.begin(),repr_ordered.end(),RateRepresentationPredicate());
nuclear@0 582
nuclear@0 583 BOOST_FOREACH(const IfcRepresentation* repr, repr_ordered) {
nuclear@0 584 bool res = false;
nuclear@0 585 BOOST_FOREACH(const IfcRepresentationItem& item, repr->Items) {
nuclear@0 586 if(const IfcMappedItem* const geo = item.ToPtr<IfcMappedItem>()) {
nuclear@0 587 res = ProcessMappedItem(*geo,nd,subnodes,conv) || res;
nuclear@0 588 }
nuclear@0 589 else {
nuclear@0 590 res = ProcessRepresentationItem(item,meshes,conv) || res;
nuclear@0 591 }
nuclear@0 592 }
nuclear@0 593 // if we got something meaningful at this point, skip any further representations
nuclear@0 594 if(res) {
nuclear@0 595 break;
nuclear@0 596 }
nuclear@0 597 }
nuclear@0 598
nuclear@0 599 AssignAddedMeshes(meshes,nd,conv);
nuclear@0 600 }
nuclear@0 601
nuclear@0 602 typedef std::map<std::string, std::string> Metadata;
nuclear@0 603
nuclear@0 604 // ------------------------------------------------------------------------------------------------
nuclear@0 605 void ProcessMetadata(const ListOf< Lazy< IfcProperty >, 1, 0 >& set, ConversionData& conv, Metadata& properties,
nuclear@0 606 const std::string& prefix = "",
nuclear@0 607 unsigned int nest = 0)
nuclear@0 608 {
nuclear@0 609 BOOST_FOREACH(const IfcProperty& property, set) {
nuclear@0 610 const std::string& key = prefix.length() > 0 ? (prefix + "." + property.Name) : property.Name;
nuclear@0 611 if (const IfcPropertySingleValue* const singleValue = property.ToPtr<IfcPropertySingleValue>()) {
nuclear@0 612 if (singleValue->NominalValue) {
nuclear@0 613 if (const EXPRESS::STRING* str = singleValue->NominalValue.Get()->ToPtr<EXPRESS::STRING>()) {
nuclear@0 614 std::string value = static_cast<std::string>(*str);
nuclear@0 615 properties[key]=value;
nuclear@0 616 }
nuclear@0 617 else if (const EXPRESS::REAL* val = singleValue->NominalValue.Get()->ToPtr<EXPRESS::REAL>()) {
nuclear@0 618 float value = static_cast<float>(*val);
nuclear@0 619 std::stringstream s;
nuclear@0 620 s << value;
nuclear@0 621 properties[key]=s.str();
nuclear@0 622 }
nuclear@0 623 else if (const EXPRESS::INTEGER* val = singleValue->NominalValue.Get()->ToPtr<EXPRESS::INTEGER>()) {
nuclear@0 624 int64_t value = static_cast<int64_t>(*val);
nuclear@0 625 std::stringstream s;
nuclear@0 626 s << value;
nuclear@0 627 properties[key]=s.str();
nuclear@0 628 }
nuclear@0 629 }
nuclear@0 630 }
nuclear@0 631 else if (const IfcPropertyListValue* const listValue = property.ToPtr<IfcPropertyListValue>()) {
nuclear@0 632 std::stringstream ss;
nuclear@0 633 ss << "[";
nuclear@0 634 unsigned index=0;
nuclear@0 635 BOOST_FOREACH(const IfcValue::Out& v, listValue->ListValues) {
nuclear@0 636 if (!v) continue;
nuclear@0 637 if (const EXPRESS::STRING* str = v->ToPtr<EXPRESS::STRING>()) {
nuclear@0 638 std::string value = static_cast<std::string>(*str);
nuclear@0 639 ss << "'" << value << "'";
nuclear@0 640 }
nuclear@0 641 else if (const EXPRESS::REAL* val = v->ToPtr<EXPRESS::REAL>()) {
nuclear@0 642 float value = static_cast<float>(*val);
nuclear@0 643 ss << value;
nuclear@0 644 }
nuclear@0 645 else if (const EXPRESS::INTEGER* val = v->ToPtr<EXPRESS::INTEGER>()) {
nuclear@0 646 int64_t value = static_cast<int64_t>(*val);
nuclear@0 647 ss << value;
nuclear@0 648 }
nuclear@0 649 if (index+1<listValue->ListValues.size()) {
nuclear@0 650 ss << ",";
nuclear@0 651 }
nuclear@0 652 index++;
nuclear@0 653 }
nuclear@0 654 ss << "]";
nuclear@0 655 properties[key]=ss.str();
nuclear@0 656 }
nuclear@0 657 else if (const IfcComplexProperty* const complexProp = property.ToPtr<IfcComplexProperty>()) {
nuclear@0 658 if(nest > 2) { // mostly arbitrary limit to prevent stack overflow vulnerabilities
nuclear@0 659 IFCImporter::LogError("maximum nesting level for IfcComplexProperty reached, skipping this property.");
nuclear@0 660 }
nuclear@0 661 else {
nuclear@0 662 ProcessMetadata(complexProp->HasProperties, conv, properties, key, nest + 1);
nuclear@0 663 }
nuclear@0 664 }
nuclear@0 665 else {
nuclear@0 666 properties[key]="";
nuclear@0 667 }
nuclear@0 668 }
nuclear@0 669 }
nuclear@0 670
nuclear@0 671
nuclear@0 672 // ------------------------------------------------------------------------------------------------
nuclear@0 673 void ProcessMetadata(uint64_t relDefinesByPropertiesID, ConversionData& conv, Metadata& properties)
nuclear@0 674 {
nuclear@0 675 if (const IfcRelDefinesByProperties* const pset = conv.db.GetObject(relDefinesByPropertiesID)->ToPtr<IfcRelDefinesByProperties>()) {
nuclear@0 676 if (const IfcPropertySet* const set = conv.db.GetObject(pset->RelatingPropertyDefinition->GetID())->ToPtr<IfcPropertySet>()) {
nuclear@0 677 ProcessMetadata(set->HasProperties, conv, properties);
nuclear@0 678 }
nuclear@0 679 }
nuclear@0 680 }
nuclear@0 681
nuclear@0 682 // ------------------------------------------------------------------------------------------------
nuclear@0 683 aiNode* ProcessSpatialStructure(aiNode* parent, const IfcProduct& el, ConversionData& conv, std::vector<TempOpening>* collect_openings = NULL)
nuclear@0 684 {
nuclear@0 685 const STEP::DB::RefMap& refs = conv.db.GetRefs();
nuclear@0 686
nuclear@0 687 // skip over space and annotation nodes - usually, these have no meaning in Assimp's context
nuclear@0 688 if(conv.settings.skipSpaceRepresentations) {
nuclear@0 689 if(const IfcSpace* const space = el.ToPtr<IfcSpace>()) {
nuclear@0 690 IFCImporter::LogDebug("skipping IfcSpace entity due to importer settings");
nuclear@0 691 return NULL;
nuclear@0 692 }
nuclear@0 693 }
nuclear@0 694
nuclear@0 695 if(conv.settings.skipAnnotations) {
nuclear@0 696 if(const IfcAnnotation* const ann = el.ToPtr<IfcAnnotation>()) {
nuclear@0 697 IFCImporter::LogDebug("skipping IfcAnnotation entity due to importer settings");
nuclear@0 698 return NULL;
nuclear@0 699 }
nuclear@0 700 }
nuclear@0 701
nuclear@0 702 // add an output node for this spatial structure
nuclear@0 703 std::auto_ptr<aiNode> nd(new aiNode());
nuclear@0 704 nd->mName.Set(el.GetClassName()+"_"+(el.Name?el.Name.Get():"Unnamed")+"_"+el.GlobalId);
nuclear@0 705 nd->mParent = parent;
nuclear@0 706
nuclear@0 707 conv.already_processed.insert(el.GetID());
nuclear@0 708
nuclear@0 709 // check for node metadata
nuclear@0 710 STEP::DB::RefMapRange children = refs.equal_range(el.GetID());
nuclear@0 711 if (children.first!=refs.end()) {
nuclear@0 712 Metadata properties;
nuclear@0 713 if (children.first==children.second) {
nuclear@0 714 // handles single property set
nuclear@0 715 ProcessMetadata((*children.first).second, conv, properties);
nuclear@0 716 }
nuclear@0 717 else {
nuclear@0 718 // handles multiple property sets (currently all property sets are merged,
nuclear@0 719 // which may not be the best solution in the long run)
nuclear@0 720 for (STEP::DB::RefMap::const_iterator it=children.first; it!=children.second; ++it) {
nuclear@0 721 ProcessMetadata((*it).second, conv, properties);
nuclear@0 722 }
nuclear@0 723 }
nuclear@0 724
nuclear@0 725 if (!properties.empty()) {
nuclear@0 726 aiMetadata* data = new aiMetadata();
nuclear@0 727 data->mNumProperties = properties.size();
nuclear@0 728 data->mKeys = new aiString*[data->mNumProperties]();
nuclear@0 729 data->mValues = new aiString*[data->mNumProperties]();
nuclear@0 730
nuclear@0 731 unsigned int i = 0;
nuclear@0 732 BOOST_FOREACH(const Metadata::value_type& kv, properties) {
nuclear@0 733 data->mKeys[i] = new aiString(kv.first);
nuclear@0 734 if (kv.second.length() > 0) {
nuclear@0 735 data->mValues[i] = new aiString(kv.second);
nuclear@0 736 }
nuclear@0 737 ++i;
nuclear@0 738 }
nuclear@0 739 nd->mMetaData = data;
nuclear@0 740 }
nuclear@0 741 }
nuclear@0 742
nuclear@0 743 if(el.ObjectPlacement) {
nuclear@0 744 ResolveObjectPlacement(nd->mTransformation,el.ObjectPlacement.Get(),conv);
nuclear@0 745 }
nuclear@0 746
nuclear@0 747 std::vector<TempOpening> openings;
nuclear@0 748
nuclear@0 749 IfcMatrix4 myInv;
nuclear@0 750 bool didinv = false;
nuclear@0 751
nuclear@0 752 // convert everything contained directly within this structure,
nuclear@0 753 // this may result in more nodes.
nuclear@0 754 std::vector< aiNode* > subnodes;
nuclear@0 755 try {
nuclear@0 756 // locate aggregates and 'contained-in-here'-elements of this spatial structure and add them in recursively
nuclear@0 757 // on our way, collect openings in *this* element
nuclear@0 758 STEP::DB::RefMapRange range = refs.equal_range(el.GetID());
nuclear@0 759
nuclear@0 760 for(STEP::DB::RefMapRange range2 = range; range2.first != range.second; ++range2.first) {
nuclear@0 761 // skip over meshes that have already been processed before. This is strictly necessary
nuclear@0 762 // because the reverse indices also include references contained in argument lists and
nuclear@0 763 // therefore every element has a back-reference hold by its parent.
nuclear@0 764 if (conv.already_processed.find((*range2.first).second) != conv.already_processed.end()) {
nuclear@0 765 continue;
nuclear@0 766 }
nuclear@0 767 const STEP::LazyObject& obj = conv.db.MustGetObject((*range2.first).second);
nuclear@0 768
nuclear@0 769 // handle regularly-contained elements
nuclear@0 770 if(const IfcRelContainedInSpatialStructure* const cont = obj->ToPtr<IfcRelContainedInSpatialStructure>()) {
nuclear@0 771 if(cont->RelatingStructure->GetID() != el.GetID()) {
nuclear@0 772 continue;
nuclear@0 773 }
nuclear@0 774 BOOST_FOREACH(const IfcProduct& pro, cont->RelatedElements) {
nuclear@0 775 if(const IfcOpeningElement* const open = pro.ToPtr<IfcOpeningElement>()) {
nuclear@0 776 // IfcOpeningElement is handled below. Sadly we can't use it here as is:
nuclear@0 777 // The docs say that opening elements are USUALLY attached to building storey,
nuclear@0 778 // but we want them for the building elements to which they belong.
nuclear@0 779 continue;
nuclear@0 780 }
nuclear@0 781
nuclear@0 782 aiNode* const ndnew = ProcessSpatialStructure(nd.get(),pro,conv,NULL);
nuclear@0 783 if(ndnew) {
nuclear@0 784 subnodes.push_back( ndnew );
nuclear@0 785 }
nuclear@0 786 }
nuclear@0 787 }
nuclear@0 788 // handle openings, which we collect in a list rather than adding them to the node graph
nuclear@0 789 else if(const IfcRelVoidsElement* const fills = obj->ToPtr<IfcRelVoidsElement>()) {
nuclear@0 790 if(fills->RelatingBuildingElement->GetID() == el.GetID()) {
nuclear@0 791 const IfcFeatureElementSubtraction& open = fills->RelatedOpeningElement;
nuclear@0 792
nuclear@0 793 // move opening elements to a separate node since they are semantically different than elements that are just 'contained'
nuclear@0 794 std::auto_ptr<aiNode> nd_aggr(new aiNode());
nuclear@0 795 nd_aggr->mName.Set("$RelVoidsElement");
nuclear@0 796 nd_aggr->mParent = nd.get();
nuclear@0 797
nuclear@0 798 nd_aggr->mTransformation = nd->mTransformation;
nuclear@0 799
nuclear@0 800 std::vector<TempOpening> openings_local;
nuclear@0 801 aiNode* const ndnew = ProcessSpatialStructure( nd_aggr.get(),open, conv,&openings_local);
nuclear@0 802 if (ndnew) {
nuclear@0 803
nuclear@0 804 nd_aggr->mNumChildren = 1;
nuclear@0 805 nd_aggr->mChildren = new aiNode*[1]();
nuclear@0 806
nuclear@0 807
nuclear@0 808 nd_aggr->mChildren[0] = ndnew;
nuclear@0 809
nuclear@0 810 if(openings_local.size()) {
nuclear@0 811 if (!didinv) {
nuclear@0 812 myInv = aiMatrix4x4(nd->mTransformation ).Inverse();
nuclear@0 813 didinv = true;
nuclear@0 814 }
nuclear@0 815
nuclear@0 816 // we need all openings to be in the local space of *this* node, so transform them
nuclear@0 817 BOOST_FOREACH(TempOpening& op,openings_local) {
nuclear@0 818 op.Transform( myInv*nd_aggr->mChildren[0]->mTransformation);
nuclear@0 819 openings.push_back(op);
nuclear@0 820 }
nuclear@0 821 }
nuclear@0 822 subnodes.push_back( nd_aggr.release() );
nuclear@0 823 }
nuclear@0 824 }
nuclear@0 825 }
nuclear@0 826 }
nuclear@0 827
nuclear@0 828 for(;range.first != range.second; ++range.first) {
nuclear@0 829 // see note in loop above
nuclear@0 830 if (conv.already_processed.find((*range.first).second) != conv.already_processed.end()) {
nuclear@0 831 continue;
nuclear@0 832 }
nuclear@0 833 if(const IfcRelAggregates* const aggr = conv.db.GetObject((*range.first).second)->ToPtr<IfcRelAggregates>()) {
nuclear@0 834 if(aggr->RelatingObject->GetID() != el.GetID()) {
nuclear@0 835 continue;
nuclear@0 836 }
nuclear@0 837
nuclear@0 838 // move aggregate elements to a separate node since they are semantically different than elements that are just 'contained'
nuclear@0 839 std::auto_ptr<aiNode> nd_aggr(new aiNode());
nuclear@0 840 nd_aggr->mName.Set("$RelAggregates");
nuclear@0 841 nd_aggr->mParent = nd.get();
nuclear@0 842
nuclear@0 843 nd_aggr->mTransformation = nd->mTransformation;
nuclear@0 844
nuclear@0 845 nd_aggr->mChildren = new aiNode*[aggr->RelatedObjects.size()]();
nuclear@0 846 BOOST_FOREACH(const IfcObjectDefinition& def, aggr->RelatedObjects) {
nuclear@0 847 if(const IfcProduct* const prod = def.ToPtr<IfcProduct>()) {
nuclear@0 848
nuclear@0 849 aiNode* const ndnew = ProcessSpatialStructure(nd_aggr.get(),*prod,conv,NULL);
nuclear@0 850 if(ndnew) {
nuclear@0 851 nd_aggr->mChildren[nd_aggr->mNumChildren++] = ndnew;
nuclear@0 852 }
nuclear@0 853 }
nuclear@0 854 }
nuclear@0 855
nuclear@0 856 subnodes.push_back( nd_aggr.release() );
nuclear@0 857 }
nuclear@0 858 }
nuclear@0 859
nuclear@0 860 conv.collect_openings = collect_openings;
nuclear@0 861 if(!conv.collect_openings) {
nuclear@0 862 conv.apply_openings = &openings;
nuclear@0 863 }
nuclear@0 864
nuclear@0 865 ProcessProductRepresentation(el,nd.get(),subnodes,conv);
nuclear@0 866 conv.apply_openings = conv.collect_openings = NULL;
nuclear@0 867
nuclear@0 868 if (subnodes.size()) {
nuclear@0 869 nd->mChildren = new aiNode*[subnodes.size()]();
nuclear@0 870 BOOST_FOREACH(aiNode* nd2, subnodes) {
nuclear@0 871 nd->mChildren[nd->mNumChildren++] = nd2;
nuclear@0 872 nd2->mParent = nd.get();
nuclear@0 873 }
nuclear@0 874 }
nuclear@0 875 }
nuclear@0 876 catch(...) {
nuclear@0 877 // it hurts, but I don't want to pull boost::ptr_vector into -noboost only for these few spots here
nuclear@0 878 std::for_each(subnodes.begin(),subnodes.end(),delete_fun<aiNode>());
nuclear@0 879 throw;
nuclear@0 880 }
nuclear@0 881
nuclear@0 882 ai_assert(conv.already_processed.find(el.GetID()) != conv.already_processed.end());
nuclear@0 883 conv.already_processed.erase(conv.already_processed.find(el.GetID()));
nuclear@0 884 return nd.release();
nuclear@0 885 }
nuclear@0 886
nuclear@0 887 // ------------------------------------------------------------------------------------------------
nuclear@0 888 void ProcessSpatialStructures(ConversionData& conv)
nuclear@0 889 {
nuclear@0 890 // XXX add support for multiple sites (i.e. IfcSpatialStructureElements with composition == COMPLEX)
nuclear@0 891
nuclear@0 892
nuclear@0 893 // process all products in the file. it is reasonable to assume that a
nuclear@0 894 // file that is relevant for us contains at least a site or a building.
nuclear@0 895 const STEP::DB::ObjectMapByType& map = conv.db.GetObjectsByType();
nuclear@0 896
nuclear@0 897 ai_assert(map.find("ifcsite") != map.end());
nuclear@0 898 const STEP::DB::ObjectSet* range = &map.find("ifcsite")->second;
nuclear@0 899
nuclear@0 900 if (range->empty()) {
nuclear@0 901 ai_assert(map.find("ifcbuilding") != map.end());
nuclear@0 902 range = &map.find("ifcbuilding")->second;
nuclear@0 903 if (range->empty()) {
nuclear@0 904 // no site, no building - fail;
nuclear@0 905 IFCImporter::ThrowException("no root element found (expected IfcBuilding or preferably IfcSite)");
nuclear@0 906 }
nuclear@0 907 }
nuclear@0 908
nuclear@0 909
nuclear@0 910 BOOST_FOREACH(const STEP::LazyObject* lz, *range) {
nuclear@0 911 const IfcSpatialStructureElement* const prod = lz->ToPtr<IfcSpatialStructureElement>();
nuclear@0 912 if(!prod) {
nuclear@0 913 continue;
nuclear@0 914 }
nuclear@0 915 IFCImporter::LogDebug("looking at spatial structure `" + (prod->Name ? prod->Name.Get() : "unnamed") + "`" + (prod->ObjectType? " which is of type " + prod->ObjectType.Get():""));
nuclear@0 916
nuclear@0 917 // the primary site is referenced by an IFCRELAGGREGATES element which assigns it to the IFCPRODUCT
nuclear@0 918 const STEP::DB::RefMap& refs = conv.db.GetRefs();
nuclear@0 919 STEP::DB::RefMapRange range = refs.equal_range(conv.proj.GetID());
nuclear@0 920 for(;range.first != range.second; ++range.first) {
nuclear@0 921 if(const IfcRelAggregates* const aggr = conv.db.GetObject((*range.first).second)->ToPtr<IfcRelAggregates>()) {
nuclear@0 922
nuclear@0 923 BOOST_FOREACH(const IfcObjectDefinition& def, aggr->RelatedObjects) {
nuclear@0 924 // comparing pointer values is not sufficient, we would need to cast them to the same type first
nuclear@0 925 // as there is multiple inheritance in the game.
nuclear@0 926 if (def.GetID() == prod->GetID()) {
nuclear@0 927 IFCImporter::LogDebug("selecting this spatial structure as root structure");
nuclear@0 928 // got it, this is the primary site.
nuclear@0 929 conv.out->mRootNode = ProcessSpatialStructure(NULL,*prod,conv,NULL);
nuclear@0 930 return;
nuclear@0 931 }
nuclear@0 932 }
nuclear@0 933
nuclear@0 934 }
nuclear@0 935 }
nuclear@0 936 }
nuclear@0 937
nuclear@0 938
nuclear@0 939 IFCImporter::LogWarn("failed to determine primary site element, taking the first IfcSite");
nuclear@0 940 BOOST_FOREACH(const STEP::LazyObject* lz, *range) {
nuclear@0 941 const IfcSpatialStructureElement* const prod = lz->ToPtr<IfcSpatialStructureElement>();
nuclear@0 942 if(!prod) {
nuclear@0 943 continue;
nuclear@0 944 }
nuclear@0 945
nuclear@0 946 conv.out->mRootNode = ProcessSpatialStructure(NULL,*prod,conv,NULL);
nuclear@0 947 return;
nuclear@0 948 }
nuclear@0 949
nuclear@0 950 IFCImporter::ThrowException("failed to determine primary site element");
nuclear@0 951 }
nuclear@0 952
nuclear@0 953 // ------------------------------------------------------------------------------------------------
nuclear@0 954 void MakeTreeRelative(aiNode* start, const aiMatrix4x4& combined)
nuclear@0 955 {
nuclear@0 956 // combined is the parent's absolute transformation matrix
nuclear@0 957 const aiMatrix4x4 old = start->mTransformation;
nuclear@0 958
nuclear@0 959 if (!combined.IsIdentity()) {
nuclear@0 960 start->mTransformation = aiMatrix4x4(combined).Inverse() * start->mTransformation;
nuclear@0 961 }
nuclear@0 962
nuclear@0 963 // All nodes store absolute transformations right now, so we need to make them relative
nuclear@0 964 for (unsigned int i = 0; i < start->mNumChildren; ++i) {
nuclear@0 965 MakeTreeRelative(start->mChildren[i],old);
nuclear@0 966 }
nuclear@0 967 }
nuclear@0 968
nuclear@0 969 // ------------------------------------------------------------------------------------------------
nuclear@0 970 void MakeTreeRelative(ConversionData& conv)
nuclear@0 971 {
nuclear@0 972 MakeTreeRelative(conv.out->mRootNode,IfcMatrix4());
nuclear@0 973 }
nuclear@0 974
nuclear@0 975 } // !anon
nuclear@0 976
nuclear@0 977
nuclear@0 978
nuclear@0 979 #endif