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